目录
零.参考资料1.文章2.视频 一.角色控制 - 不含视角控制1.单轴移动1.1.直接改变位置1.2.使用 Character Controller 组件1.x.效果 2.双轴移动2.1.直接改变位置2.2.使用 Character Controller 组件2.x.效果 二.角色控制 - 含视角控制1.第三人称视角2.俯视角(鼠标控制方向)3.等距俯视视角 ❀.使用 Character Controller组件时模拟重力、跳跃❀.1.模拟重力❀.2.跳跃 ☽.细节知识点☽.1.使用`Input.GetAxis()`获取轴向上的向量时需要进行归一化☽.2.游戏角色跳跃所需的垂直速度取决于重力和高度零.参考资料
1.文章
第三人称视角 - 手动实现
第三人称视角 +鼠标滚轮缩放视野+视角拖拽
2.视频
角色移动方式汇总
第三人称视角+双轴移动 - 使用Cinemachine
第三人称视角+俯视视角+单/双轴移动+鼠标控制方向 - 使用Cinemachine
等距俯视视角+双轴移动
一.角色控制 - 不含视角控制
注:单轴移动指在一个轴向(包含正反两个方向)上移动,在另一个轴向上进行旋转。双轴移动是指两个轴向均可控制角色移动。
1.单轴移动
1.1.直接改变位置
[SerializeField] private float moveSpeed = 5f;[SerializeField] private float rotateSpeed = 1f;void Update(){//获取输入var horizontalInput = Input.GetAxis("Horizontal");var verticalInput = Input.GetAxis("Vertical");//Position累加//transform.Translate(Vector3.forward * verticalInput * moveSpeed * Time.deltaTime, Space.Self);transform.position += transform.forward * verticalInput * moveSpeed * Time.deltaTime;//角色旋转transform.Rotate(Vector3.up, horizontalInput * rotateSpeed);}
1.2.使用 Character Controller 组件
[SerializeField] private float moveSpeed = 5f;[SerializeField] private float rotateSpeed = 1f;private CharacterController characterController;private void Start(){characterController = GetComponent<CharacterController>();}void Update(){//获取输入var horizontalInput = Input.GetAxis("Horizontal");var verticalInput = Input.GetAxis("Vertical");//计算包含方向和速度的向量 var move = transform.forward * verticalInput * moveSpeed * Time.deltaTime;//移动角色characterController.Move(move);//角色旋转transform.Rotate(Vector3.up, horizontalInput * rotateSpeed);}
1.x.效果
2.双轴移动
2.1.直接改变位置
[SerializeField] float moveSpeed = 5f;[SerializeField] float smoothTime = 0.1f;//旋转平滑时间private Vector3 input;private void Update(){//移动input = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;if (input.magnitude == 0)return;transform.Translate(input * moveSpeed * Time.deltaTime, Space.World);//transform.position += input * moveSpeed * Time.deltaTime;//旋转Quaternion newQuaternion = Quaternion.LookRotation(input, transform.up);transform.rotation = Quaternion.Slerp(transform.rotation, newQuaternion, smoothTime);}
2.2.使用 Character Controller 组件
[SerializeField] float moveSpeed = 5f;[SerializeField] float smoothTime= 0.1f;//旋转平滑时间private Vector3 input;private CharacterController characterController;private void Start(){characterController = GetComponent<CharacterController>();}private void Update(){//获取输入input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")).normalized;if (input.magnitude == 0)return;var move = input * moveSpeed * Time.deltaTime;//移动角色characterController.Move(move);//旋转Quaternion newQuaternion = Quaternion.LookRotation(input, transform.up);transform.rotation = Quaternion.Slerp(transform.rotation,newQuaternion,smoothTime);}}
2.x.效果
二.角色控制 - 含视角控制
注:以下视角控制有些使用了Cinemachine
插件。
1.第三人称视角
[SerializeField] private float moveSpeed = 5f;[SerializeField] private Transform camera;[SerializeField] private float rotateSpeed = 1f;private CharacterController characterController;private float turnSmoothVelocity;void Start(){characterController = GetComponent<CharacterController>();}void Update(){//获取输入Vector3 input = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;if (input.magnitude >= 0.1f){float targetAngle = Mathf.Atan2(input.x, input.z) * Mathf.Rad2Deg + camera.eulerAngles.y;float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, rotateSpeed);transform.rotation = Quaternion.Euler(0f, angle, 0f);Vector3 moveDir = Quaternion.Euler(0, targetAngle, 0f) * Vector3.forward;characterController.Move(moveDir.normalized * moveSpeed * Time.deltaTime);}}
1.1.玩家层级结构
ThirdPersonPlayer
:空物体,角色控制脚本以及CharacterController组件均挂载在该空物体上。
Player
:玩家。
CM FreeLook1
:虚拟相机(FreeLook Camera)。
1.2.虚拟相机参数设置
Follow
和Look At
均设置为Player
。设置Orbits - Binding Mode
为Word Space
。调整上中下视角轨道(Orbits - TopRig/MiddleRig/BottomRig
)的位置和大小。(根据需要设置)调整视角旋转速度(X/YAxis - Speed
)或者鼠标是否反转(X/Y Axis - InputAxisValue - Invert
)。(根据需要设置)
1.3.玩家控制脚本参数设置
注:Camera
属性为Unity中的MainCamera,而不是虚拟相机。
1.x.效果
2.俯视角(鼠标控制方向)
[SerializeField] private float moveSpeed = 5f;private CharacterController characterController;void Start(){characterController = GetComponent<CharacterController>();}void Update(){//计算包含方向和速度的向量 var input = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;var move = input * moveSpeed * Time.deltaTime;//移动角色characterController.Move(move);//转换玩家坐标系var playerScreenPoint = Camera.main.WorldToScreenPoint(transform.position);//计算旋转角度var point = Input.mousePosition - playerScreenPoint;var angle = Mathf.Atan2(point.x, point.y) * Mathf.Rad2Deg;transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle, transform.eulerAngles.z);}
2.1.玩家层级结构
Player
:玩家,玩家控制脚本和CharacterController组件均挂载在此物体上。
CM vcam1
:虚拟相机(Virtual Camera)。
2.2.虚拟相机参数设置
Follow
和Look At
均设置为Player
。修改Body - Follow Offset
属性和Aim - Tracked Object Offect
属性,将相机视角设置为俯视视角。修改Body - Binding Mode
为Lock To Target On Assign
修改Aim
后的属性为Do nothing
。修改Body - X/Y/ZDamping
调整视角跟随延迟。(根据需要修改)
2.x.效果
3.等距俯视视角
3.1.玩家层级结构 / 设置相机视角
红框部分为必须进行的设置,黄框部分根据实际需要进行调整。Player
:玩家。IsoViewCamera
:空物体,在该物体上设置相机的倾斜角度。Main Camera
:主相机。
3.2.编写辅助脚本旋转输入向量
public static class IsoViewPlayerControllerHelpers{private static Matrix4x4 isoMatrix = Matrix4x4.Rotate(Quaternion.Euler(0, 45, 0));//给Vector3增加扩展方法public static Vector3 ToIso(this Vector3 input) => isoMatrix.MultiplyPoint3x4(input);}
3.3.控制玩家移动
[SerializeField] private float moveSpeed = 5f;[SerializeField] private float turnSpeed = 360;private CharacterController characterController;private void Start(){characterController = GetComponent<CharacterController>();}private void Update(){//获取输入向量并转换成等距视图向量(Isometry View,将输入向量倾斜45度),再将其归一化。Vector3 input = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).ToIso().normalized;if (input.magnitude > 0){//通过输入向量获取一个方向仅指向玩家正前方,大小由输入控制的增向量。Vector3 move = transform.forward * input.magnitude * moveSpeed * Time.deltaTime;//移动玩家characterController.Move(move);//旋转玩家(有两种方式)Quaternion newQuaternion = Quaternion.LookRotation(input, transform.up);//transform.rotation = Quaternion.Slerp(transform.rotation, newQuaternion, 0.1f);transform.rotation = Quaternion.RotateTowards(transform.rotation, newQuaternion, turnSpeed * Time.deltaTime);}}
注:在以上代码中,控制玩家移动是采用玩家朝正前方移动 + 旋转的结果 = 自由移动
。而不是玩家朝任意方向移动 + 旋转表现 = 自由移动
。旋转表现
可有可无,即使玩家没有旋转,也不影响玩家自由移动。我觉得第一种方式比较真实,所以就采用这种方式移动角色。
3.x.效果
❀.使用 Character Controller组件时模拟重力、跳跃
注:Character Controller组件没有包含物理效果,如果想要重力效果则需要自己实现。
❀.1.模拟重力
[SerializeField] private Transform groundCheck;//用于与地面碰撞检测的物体(位置在玩家模型底部)[SerializeField] private float checkRadius = 0.2f;//检测半径[SerializeField] private LayerMask groundlayerMask;//检测的层[SerializeField] private float gravity = -9.8f;//重力private Vector3 velocity = Vector3.zero;//速度,重力(垂直方向)所影响的向量private bool isGround;//是否接触地面void Update(){isGround = Physics.CheckSphere(groundCheck.position, checkRadius, groundlayerMask);if (isGround && velocity.y < 0)velocity.y = 0;velocity.y += gravity * Time.deltaTime;characterController.Move(velocity * Time.deltaTime);}
❀.2.跳跃
[SerializeField] private Transform groundCheck;//用于与地面碰撞检测的物体(位置在玩家模型底部)[SerializeField] private float checkRadius = 0.2f;//检测半径[SerializeField] private LayerMask groundlayerMask;//检测的层[SerializeField] private float gravity = -9.8f;//重力private Vector3 velocity = Vector3.zero;//速度,重力(垂直方向)所影响的向量private bool isGround;//是否接触地面void Update(){isGround = Physics.CheckSphere(groundCheck.position, checkRadius, groundlayerMask);if (isGround && Input.GetButtonDown("Jump"))velocity.y += Mathf.Sqrt(jumpHeight * -2 * gravity);velocity.y += gravity * Time.deltaTime;characterController.Move(velocity * Time.deltaTime);}
☽.细节知识点
☽.1.使用Input.GetAxis()
获取轴向上的向量时需要进行归一化
使用Input.GetAxis()
获取到的值在-1~1
之间,假如同时按下W
和D
,获取到的水平和垂直轴的值均为1
,根据勾股定理,斜向上方向量大小≈1.41
,这样会导致斜向上的速度偏快。
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")).normalized;
☽.2.游戏角色跳跃所需的垂直速度取决于重力和高度
g:
重力(一般为-9.8f)h:
跳跃高度
velocity.y += Mathf.Sqrt(-2f * gravity * jumpHeight);