问题介绍
在使用第一人称控制人物移动过程中,每次当代表人物的胶囊体和墙壁或场景中模型的碰撞体进行碰撞时,人物会被反弹,并且会进行旋转。
下图是出现Bug时的人物刚体设置:
问题分析
会出现这个问题是由于,物体的物理逻辑一般写在FixedUpdate中。根据Unity运行时序,移动物体加刚体后,在Update中碰撞体跟着物体进行了移动,那么这一次的生命周期循环里,Update之后没有物理判断了。这一帧的画面渲染出来的时候,物体碰撞体是嵌入了墙体。而在下一帧的FixedUpdate进行了物理判断,发现碰撞体是嵌入的。那么按照物理规则,物理引擎把物体给弹出来保证物理正确。这样也就导致我们看到物体发生碰撞时,人物会发生抖动。
解决问题
对于这个问题,可以从下面两个角度进行检查:
1.刚体的移动代码是否是写在FixedUpdate()函数中;
2.是否对刚体进行正确的坐标轴限定。
解决方案
(1)人物移动(绑定到人物模型上):
using System.Collections.Generic;
using UnityEngine;
public class FirstPersonMovement : MonoBehaviour
{
public float speed = 5;
[Header("Running")]
public bool canRun = true;
public bool IsRunning {
get; private set; }
public float runSpeed = 9;
public KeyCode runningKey = KeyCode.LeftShift;
Rigidbody rigidbody;
/// <summary> Functions to override movement speed. Will use the last added override. </summary>
public List<System.Func<float>> speedOverrides = new List<System.Func<float>>();
void Awake()
{
// Get the rigidbody on this.
rigidbody = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
if (Manager.Instance.IsStart)
{
// Update IsRunning from input.
IsRunning = canRun && Input.GetKey(runningKey);
// Get targetMovingSpeed.
float targetMovingSpeed = IsRunning ? runSpeed : speed;
if (speedOverrides.Count > 0)
{
targetMovingSpeed = speedOverrides[speedOverrides.Count - 1]();
}
// Get targetVelocity from input.
Vector2 targetVelocity = new Vector2(Input.GetAxis("Horizontal") * targetMovingSpeed, Input.GetAxis("Vertical") * targetMovingSpeed);
// Apply movement.
rigidbody.velocity = transform.rotation * new Vector3(targetVelocity.x, rigidbody.velocity.y, targetVelocity.y);
}
}
}
(2)刚体设置:
注意:刚体中冻结XYZ的旋转轴,并不会影响到代码中对人物进行视角控制时,人物模型的旋转
(3)视角控制(绑定到摄像机上):
using UnityEngine;
public class FirstPersonLook : MonoBehaviour
{
[SerializeField]
Transform character;
public float sensitivity = 2;
public float smoothing = 1.5f;
//Vector2 velocity;
//Vector2 frameVelocity;
// 水平视角移动的敏感度
public float sensitivityHor = 1f;
// 垂直视角移动的敏感度
public float sensitivityVer = 1f;
// 视角向上移动的角度范围,该值越小范围越大
public float upVer = -50;
// 视角向下移动的角度范围,该值越大范围越大
public float downVer = 50;
// 垂直旋转角度
private float rotVer;
void Reset()
{
// Get the character from the FirstPersonMovement in parents.
character = GetComponentInParent<FirstPersonMovement>().transform;
}
void Start()
{
// Lock the mouse cursor to the game screen.
//Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
if(Manager.Instance.IsStart)
{
/*if (Input.GetMouseButton(0))
{
// Get smooth velocity.
Vector2 mouseDelta = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
Vector2 rawFrameVelocity = Vector2.Scale(mouseDelta, Vector2.one * sensitivity);
frameVelocity = Vector2.Lerp(frameVelocity, rawFrameVelocity, 1 / smoothing);
velocity += frameVelocity;
velocity.y = Mathf.Clamp(velocity.y, -90, 90);
// Rotate camera up-down and controller left-right from velocity.
transform.localRotation = Quaternion.AngleAxis(-velocity.y, Vector3.right);
Quaternion rotation = Quaternion.AngleAxis(velocity.x, Vector3.up);
character.localRotation = rotation;
}*/
if (Input.GetMouseButton(0))
{
// 获取鼠标上下的移动位置
float mouseVer = Input.GetAxis("Mouse Y");
// 获取鼠标左右的移动位置
float mouseHor = Input.GetAxis("Mouse X");
// 鼠标往上移动,视角其实是往下移,所以要想达到视角也往上移的话,就要减去它
rotVer -= mouseVer * sensitivityVer;
// 限定上下移动的视角范围,即垂直方向不能360度旋转
rotVer = Mathf.Clamp(rotVer, upVer, downVer);
// 设置视角的移动值
transform.localEulerAngles = new Vector3(rotVer, 0, 0);//控制摄像机的上下视角移动(玩家不动)
/*Debug.Log(character.transform.localEulerAngles.y);
Debug.Log(mouseHor);
Debug.Log(Vector3.up);
Debug.Log(Vector3.up * mouseHor);
character.transform.Rotate(Vector3.up * mouseHor);//转动玩家的水平视角移动(摄像机也会动)
Debug.Log(character.transform.localEulerAngles.y);
Debug.Log("11111111111111111111111111111111111111111");*/
character.transform.localEulerAngles += Vector3.up * mouseHor;
}
}
}
}