一个标准的第三人称角色移动控制
- 按下W面向前移动; 按下S面向后移动; 按下A面向左移动; 按下D面向右移动
- 按下LeftShift加速移动
- Player静止时摄像头自由旋转观察角色
- 移动时角色rotation跟随鼠标 , 也就是鼠标能控制角色移动方向
层级面板目录结构
Player(PlayerControl挂载在此处)
模型 : 模型在Player的子目录下
Target : Target是个空物体, 大概在角色头部位置, 用于附着相机对象
Camera(CameraControl挂载在此处) : 就是游戏的主摄像机 , 在空物体Target的子目录下
其中Player要有Animator组件, Rigidbody组件, Capsule Collider组件和自定义脚本PlayerControl组件
Camera要有自定义脚本CameraControl组件
之所以弄成这么个层级 , 主要是为了分离角色和摄像机 , 摄像机可以自由旋转而不影响角色 , 那个Target大概就是为了能让相机正冲角色后脑勺.
怎样实现?理一下思路
PlayerControl脚本的内容
- 获取WASD输入 , 根据输入改变整个Player父对象的rotation , 并且控制角色移动.
- 最后要做到角色平滑转向 , 还要实现0速到移动速度的平滑转向
需要在类中声明的变量
//***变量声明区域***/ float walkSpeed = 5; float runSpeed = 10; Animator animator;//创建一个animator变量以待赋值(在start()方法里赋值) public float turnSmoothTime = 0.13f;//设定角色转向平滑时间 float turnSmoothVelocity;//平滑函数需要这么一个平滑加速度, 不需要为他赋值, 但是需要把这个变量当参数传入 public float speedSmoothTime = 0.13f;//用于平滑速度 float speedSmoothVelocity; float currentSpeed; public Transform cameraT;
//cameraT这个本来是想用cameraT = Camera.main.transform;在start函数赋值的,但是不知道这个函数是不是过时了,赋值不上,所以还是采用public的方式,在编辑器拖动给变量赋值
最普通的WASD移动方式
void Update()
{
float hor = Input.GetAxis("Horizontal");
float ver = Input.GetAxis("Vertical"); Vector3 PlayerMovement = new vector3(hor,0f,ver)*targetSpeed*Time.deltaTime; transform.Translate(PlayerMovement,Space.Self);
}
威力加强版未加入平滑函数的WASD移动方式
void Update()
{ //***WASD输入***// Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical")); Vector2 inputDir = input.normalized;//反正就是返回一个单位长度的这个变量 if(inputDir != Vector2.zero)
//可能是因为键盘自动输入0, 玩家不输入的时候角色就会自动面向正方向, 所以加一个判定, 输入为0的话就是没输入, 所以就不要转向 {//未加入平滑函数的转向 transform.eulerAngles =Vector3.up* Mathf.Atan2(inputDir.x,inputDir.y)*Mathf.Rad2Deg;
//关于上边这句话详情看unity坐标分析(还没写),这其实就是根据玩家键盘WASD算出来的旋转角度 } transform.Translate(transform.forward*targetSpeed*Time.deltaTime,Space.World);
//因为上边更改了transform的朝向,所以可以直接向transform的正方向前进就能达到按照键盘输入前进的效果.
}
威力加强版加入平滑和鼠标干预的WASD移动方式
void Update()
{
Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical")); Vector2 inputDir = input.normalized; bool running = Input.GetKey(KeyCode.LeftShift);//根据是否按下左shift设定一个布尔变量在下一句设定是奔跑速度还是走路速度
float targetSpeed = ((running)?runSpeed:walkSpeed)*inputDir.magnitude;
//根据布尔变量获取速度后乘以输入向量的长度,当没有任何键盘输入时,这个长度是0,有任何输入时长度都是1,所以其实是用来在键盘没有输入时将目标速度置0用的 transform.Translate(transform.forward*targetSpeed*Time.deltaTime,Space.World); //***转向部分代码***// if(inputDir != Vector2.zero) {
//***加入鼠标干预***// float targetRotation = Mathf.Atan2(inputDir.x,inputDir.y)*Mathf.Rad2Deg+cameraT.eulerAngles.y;
//这个cameraT是引用的主摄像机(也不知道具体是不是引用,等回学校看看headfirstC#),传入了主摄像机的y值,而在后面摄像机的控制代码中,这个y值是受鼠标控制的 transform.eulerAngles = Vector3.up*Mathf.SmoothDampAngle(transform.eulerAngles.y,targetRotation,ref turnSmoothVelocity,turnSmoothTime); //上边这个函数是角度渐变, 也可以叫平滑吧, 这个ref是什么意思, 以后再说, 还有这个turnSmoothVelocity也以后再说 }
}
动画状态机代码控制
float animationSpeedPercent = ((running)?0.75f:0.25f)*inputDir.magnitude;//通过布尔变量给这个变量赋值,下一句要用 animator.SetFloat("speedPercent",animationSpeedPercent,speedSmoothTime,Time.deltaTime); //1. 通过调用animator变量的setfloat函数给混合状态机的speedPercent参数赋值 //2. 这个函数有四个参数, 第三个是自带的平滑参数, 也就是说这个aimator自带平滑功能,直接输入参数就可以调用
需要创建一个状态机, 然后创建一个混合状态图层, 新建一个控制参数, 就是这个speedPercent, 导入角色动画, 静止动画, 走路动画, 跑步动画啥的, 就能通过给这个speedPercent赋值来控制角色动画
这个动画状态机后面还要专门整理, 包括动画融合等内容
摄像机控制(还需要补充层级面板配置结构)
void LateUpdate()//摄像机控制代码一般放在lateupdate函数里 { CameraControl(); } void CameraControl() { mouseX +=Input.GetAxis("Mouse X")*rotationSpeed; mouseY -=Input.GetAxis("Mouse Y")*rotationSpeed; mouseY = Mathf.Clamp(mouseY,-35,60);//限制摄像机旋转角度 //transform.LookAt(Target);这句话没啥用(我也不知道为啥就自动看着Target Target.rotation = Quaternion.Euler(mouseY,mouseX,0); //Player.rotation = Quaternion.Euler(0,mouseX,0);这句话会因为和PlayerControl内代码同时改变Player角度而引发摄像机剧烈抖动
//现在PlayerControl脚本里旋转那里加入了摄像机的y轴的值,所以不需要用这句话来画蛇添足了
}
全部代码保存(如果有一天我真要用这份代码 , 最好还是能直接复制粘贴全部代码)
PlayerController代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { /***变量声明区域***/ float walkSpeed = 5; float runSpeed = 10; Animator animator;//创建一个animator变量以待赋值(在start()方法里赋值) public float turnSmoothTime = 0.13f;//设定角色转向平滑时间 float turnSmoothVelocity;//平滑函数需要这么一个平滑加速度, 不需要为他赋值, 但是需要把这个变量当参数传入 public float speedSmoothTime = 0.13f;//用于平滑速度 float speedSmoothVelocity; float currentSpeed; public Transform cameraT; // Start is called before the first frame update void Start() { animator = GetComponent<Animator>(); //cameraT = Camera.main.transform; } // Update is called once per frame void Update() { //***WASD输入***// Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical"));//获取键盘输入 Vector2 inputDir = input.normalized;//反正就是返回一个单位长度的这个变量 //***角色移动部分***// bool running = Input.GetKey(KeyCode.LeftShift);//按了左shift则bool变量running就是1 float targetSpeed = ((running)?runSpeed:walkSpeed)*inputDir.magnitude; //Vector3 PlayerMovement = new Vector3(hor,0f,ver)*targetSpeed*Time.deltaTime; transform.Translate(transform.forward*targetSpeed*Time.deltaTime,Space.World);//让游戏角色位置移动 //transform.Translate(PlayerMovement,Space.Self); //***转向部分***// if(inputDir != Vector2.zero) //可能是因为键盘自动收入0, 玩家不输入的时候角色就会自动面向正方向, 所以加一个判定, 输入为0的话就是没输入, 所以就不要转向 {//平滑转向代码 float targetRotation = Mathf.Atan2(inputDir.x,inputDir.y)*Mathf.Rad2Deg+cameraT.eulerAngles.y;//这就是根据玩家键盘输入算出来的目标转向角度(y轴的) transform.eulerAngles = Vector3.up*Mathf.SmoothDampAngle(transform.eulerAngles.y,targetRotation,ref turnSmoothVelocity,turnSmoothTime); //上边这个函数是角度渐变, 也可以叫平滑吧, 这个ref是什么意思还存疑, 以后肯定能解决, 还有这个turnSmoothVelocity是什么意思以后迟早能知道, 不急于一时
//这个ref就是引用参数 , 学点C#就知道了 , 用这个参数把方法内的数据给保存出来 } /***位置运动部分***/ //这个inputDir是个单位向量,inputDir.magnitude是他的长度,有任何输入的时候单位向量的长度都是1, 在键盘没有输入的时候这个长度就是0 //其实之所以乘以这个长度就是为了能够在玩家没有输入的时候把速度变成0 //currentSpeed = Mathf.SmoothDamp(currentSpeed,targetSpeed,ref speedSmoothVelocity,speedSmoothTime); //看起来这个函数SmoothDamp以及上边的SmoothDampAngle一样, 他们的第一个参数其实是被赋值的, 直接把空参数传进去, 就能获得合适的值 float animationSpeedPercent = ((running)?0.75f:0.25f)*inputDir.magnitude;//通过布尔变量给动画状态机控制变量赋值 animator.SetFloat("speedPercent",animationSpeedPercent,speedSmoothTime,Time.deltaTime); //1. 通过调用animator变量的setfloat函数给混合状态机的speedPercent参数赋值 //2. 这个函数有四个参数, 第三个是自带的平滑参数, 可以通过给这个参数赋值来实现动画状态机自动平滑动作 } }
CameraController代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ThirdPersonCamera : MonoBehaviour { //****变量声明部分*** float yaw; //yaw的中文意思是航向, 也就是物体绕y轴旋转角度 float pitch;//pitch是俯仰, 也就是物体绕x轴旋转角度 public float mouseSensitivity = 10f;//鼠标灵敏度,因为鼠标要控制摄像机转向 public Transform target,player; //1. 这里想要获取一个对象的transform组件的实例, 先声明一个transform以待赋值, 要获取的是游戏角色子对象, 用于确定相机位置 //2. 这个target会通过编辑器赋值, 就是我创建的CameraPosition子对象, 但是其实直接把相机作为角色的子对象岂不是更好?? //public float dstFromTarget = 2;//就是相机就距离CameraPosition这个对象两米远 // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } void LateUpdate() { yaw+=Input.GetAxis("Mouse X")*mouseSensitivity; pitch-=Input.GetAxis("Mouse Y")*mouseSensitivity; Vector3 targetRotation = new Vector3(pitch,yaw,0);//显然我们是不需要摄像机左右倾斜的, 所以z轴锁定为0 Vector3 playerRotation = new Vector3(0,yaw,0);//显然我们是不需要摄像机左右倾斜的, 所以z轴锁定为0 //***设置相机旋转*** //这句话必须要放在下边那句给摄像机角度赋值的语句前边才有用 //transform.LookAt(target.position); //transform.eulerAngles = targetRotation; // player.transform.eulerAngles = playerRotation; //***设置相机位置*** //transform.position = target.position-transform.forward*dstFromTarget; //总之是Vector3变量做加减, target.position说的是cameraPosition子对象的位置,是在编辑器里赋值的, 这个位置的x轴减两米就是本对象位置(也就是相机位置) target.rotation = Quaternion.Euler(pitch,yaw,0);//设置摄像机旋转 // if(Input.GetKey(KeyCode.W)) // { // player.rotation = Quaternion.Euler(0,yaw,0);//设置角色旋转 // } } }