Unity中的第三人称相机剪贴墙



在Unity中,我正在创建一个第三人称游戏,相机跟随玩家,同时也能够自己旋转。现在,当我旋转摄像机时,有一堵墙,摄像机似乎穿过了墙。

我试过添加一个球体碰撞器和刚体到相机,但这没有帮助。我还创建了一个空的GameObject,将球体碰撞器作为父对象,并将相机作为子对象。我还试着用一个球体碰撞器创建一个空子。

我已经附上了我的相机脚本代码的情况下的视频。照片中的阴影实际上是我的相机穿过的墙。

剪墙视频,有什么建议吗?

public class OrbitCamera : MonoBehaviour
{
[SerializeField] private Transform target;
public float rotSpeed = 1.5f;
private float vertRotY;
private float horiRotX;
private Vector3 offSet;

// Start is called before the first frame update
void Start()
{
vertRotY = transform.eulerAngles.y;
offSet = target.position - transform.position;
}
void FixedUpdate()
{
RaycastHit hit;

if (Physics.Raycast(transform.position, Vector3.forward, out hit, 100f))
{
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "Wall")
{
print("touching wall");
}
}
// Update is called once per frame
void LateUpdate()
{
float horiInput = Input.GetAxisRaw("Horizontal");
float vertInput = Input.GetAxisRaw("Vertical");
if (horiInput != 0)
{
vertRotY += horiInput * rotSpeed;

}
else
{
vertRotY += Input.GetAxis("Mouse X") * rotSpeed * 3;
}
if (vertInput == 0)
{
horiRotX += Input.GetAxis("Mouse Y") * rotSpeed * 3;
horiRotX = Mathf.Clamp(horiRotX, -45f, 20f);
}
Quaternion rotation = Quaternion.Euler(-horiRotX, vertRotY, 0);
transform.position = target.position - (rotation * offSet);

//transform.Rotate(rotation.eulerAngles);
transform.LookAt(target);
//transform.RotateAround(target.transform.position, Vector3.up, vertRotY);
//transform.RotateAround(target.transform.position, Vector3.left, rotSpeed * 3);
}
}

这是我的球员移动脚本:

public class RelativeMovement : MonoBehaviour {
[SerializeField] private Transform target;

public float moveSpeed = 6.0f;
public float rotSpeed = 15.0f;
public float jumpSpeed = 15.0f;
public float gravity = -9.8f;
public float terminalVelocity = -20.0f;
public float minFall = -1.5f;
private float _vertSpeed;
private ControllerColliderHit _contact;
private CharacterController _charController;
private Animator _animator;

//Physics force
public float pushForce = 3.0f;
// Use this for initialization
void Start() {
_vertSpeed = minFall;
_charController = GetComponent<CharacterController>();
_animator = GetComponent<Animator>();
}

// Update is called once per frame
void Update() {
// start with zero and add movement components progressively
Vector3 movement = Vector3.zero;
// x z movement transformed relative to target
float horInput = Input.GetAxis("Horizontal");
float vertInput = Input.GetAxis("Vertical");
if (horInput != 0 || vertInput != 0) {
movement.x = horInput * moveSpeed;
movement.z = vertInput * moveSpeed;
movement = Vector3.ClampMagnitude(movement, moveSpeed);
Quaternion tmp = target.rotation;
target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0);
movement = target.TransformDirection(movement);
target.rotation = tmp;
// face movement direction
//transform.rotation = Quaternion.LookRotation(movement);
Quaternion direction = Quaternion.LookRotation(movement);
transform.rotation = Quaternion.Slerp(transform.rotation, direction, rotSpeed * Time.deltaTime);
_animator.SetBool("isRunning", true);
}
else
{
_animator.SetBool("isRunning", false);
}
//_animator.SetFloat("Speed", movement.sqrMagnitude);
// raycast down to address steep slopes and dropoff edge
bool hitGround = false;
RaycastHit hit;
if (_vertSpeed < 0 && Physics.Raycast(transform.position, Vector3.down, out hit)) {
float check = (_charController.height + _charController.radius) / 1.9f;
hitGround = hit.distance <= check;  // to be sure check slightly beyond bottom of capsule
}
// y movement: possibly jump impulse up, always accel down
// could _charController.isGrounded instead, but then cannot workaround dropoff edge
if (hitGround) {
if (Input.GetButtonDown("Jump")) {
_vertSpeed = jumpSpeed;
} else {
_vertSpeed = minFall;
_animator.SetBool("isJumping", false);
}
} else {
_vertSpeed += gravity * 5 * Time.deltaTime;
if (_vertSpeed < terminalVelocity) {
_vertSpeed = terminalVelocity;
}
if (_contact != null ) {    // not right at level start
_animator.SetBool("isJumping", true);
}
// workaround for standing on dropoff edge
if (_charController.isGrounded) {
if (Vector3.Dot(movement, _contact.normal) < 0) {
movement = _contact.normal * moveSpeed;
} else {
movement += _contact.normal * moveSpeed;
}
}
}
movement.y = _vertSpeed;
movement *= Time.deltaTime;
_charController.Move(movement);
}

// store collision to use in Update
void OnControllerColliderHit(ControllerColliderHit hit) {
_contact = hit;
//Physics method portion
Rigidbody body = hit.collider.attachedRigidbody;
if (body != null && !body.isKinematic){
body.velocity = hit.moveDirection * pushForce;
}
}
}

尝试用碰撞器创建一个新的空游戏对象,并使其成为具有相同位置的相机的子对象。

我也很纠结。我想让镜头跟随玩家,但同时也"旋转",这样当我上下看的时候,镜头就会继续看着玩家。剪辑当然随之而来。

我通过不试图改变相机的localPosition或碰撞检查来解决这个问题。使用相机对象的RotateAround方法将改变相机对象的localPosition值,使得在发生碰撞时难以正确定位相机。

相反,只需将一个空的游戏对象作为子对象放置在玩家的相机所需高度。我把它叫做CameraPivot。然后将相机设置为CameraPivot对象的一个子对象,并根据需要对其进行定位。

这里的技巧是,除了z轴,不要改变相机的位置。如果你改变了cameraPivot对象的localRotation,相机自然会跟随那个对象,而相机的localPosition值不会改变。然后,使用从相机枢轴到相机的光线投射来检测是否有墙壁,地面,物体等,如果你击中一个物体,使用hitinfo的距离属性为z轴。

下面是示例脚本,其中包括注释。这在我的一款样本游戏中进行了测试。现在脚本还允许第一人称和第三人称视图。注意,墙壁,地板和屋顶上可能会出现一些剪切,所以你要检查相机的视野和剪切平面附近。我还使用了偏移。这些可以调整,以获得所需的结果。另外请注意,我使用look方法来查看相机枢轴,但没有理由不能根据需要随时更改相机的旋转。
using UnityEngine;
#region Description
// This class manages Player and camera rotation
// It rotates the player object on the Y axis based on mouse x input;
// If in third person view, it also rotates the camera around the player object on the X axis based on mouse y input
// and moves the camera back and forth along the Z axis if it collides with an object.
// You get behavior similar to a camera.main.transform.RotateAround() methodology,
// but with the added benefit of collision detection and easy control over the camera's max distance
// In first person view, it rotates the camera on the X axis based on mouse y input
// This class is attached to the camera object
// transform.parent.parent is the player object
// transform.parent is the camera's parent object (cameraPivot)
#endregion Description
public class CameraClipViewer : MonoBehaviour
{
private bool isFirstPerson;
private Vector3 firstPersonView;
private Vector3 thirdPersonView;
private float mouseX; // the mouse x-axis input
private float mouseY; // the mouse y-axis input
private float rotationX = 0f; // the current x-axis rotation of the camera, frame by frame
private float rotationY = 0f; // the current y-axis rotation of the Player, frame by frame
[SerializeField]
private LayerMask mask; // the layers the camera should collide with
[SerializeField]
private float sensitivity = 5f; // the sensitivity of the mouse x and y-axis rotation
[SerializeField]
private float maxXRotation = 90f; // the maximum x-axis rotation of the camera
[SerializeField]
private float maxDistance; // the maximum distance the camera can be from an object before it is moved back
[SerializeField]
private float zOffset = 0.35f; // distance to maintain between the camera and collider to avoid clipping
[SerializeField]
private float nearClippingPlane = 0.01f; // min camera clipping plane to avoid clipping

private void Start()
{
isFirstPerson = false;
firstPersonView = new Vector3(0, 0, 0.3f);
thirdPersonView = new Vector3(0, 0, -4f);
transform.localPosition = isFirstPerson ? firstPersonView : thirdPersonView;
// make sure the max distance is positive
maxDistance = Vector3.Distance(transform.parent.position, transform.localPosition);
// make sure the near clipping plane is positive and set it to avoid clipping sideways along a wall.
if (nearClippingPlane < 0) nearClippingPlane = 0;
transform.GetComponent<Camera>().nearClipPlane = nearClippingPlane;
}
private void Update()
{
mouseX = Input.GetAxis("Mouse X");
mouseY = Input.GetAxis("Mouse Y");
SwitchCameraView();
RotateCameraView();
}
private void SwitchCameraView()
{
if (!Input.GetKeyDown(KeyCode.C)) return;
isFirstPerson = !isFirstPerson;
transform.localPosition = isFirstPerson ? firstPersonView : thirdPersonView;
}
private void RotateCameraView()
{
// Update current rotation values
rotationX -= mouseY * sensitivity;
rotationX = Mathf.Clamp(rotationX, -maxXRotation, maxXRotation);
rotationY += mouseX * sensitivity;
// Rotate the player object based on mouse x input
transform.parent.parent.rotation = Quaternion.Euler(0f, rotationY, 0f);
// Perform rotations based on current camera view settings
if (isFirstPerson)
transform.localRotation = Quaternion.Euler(rotationX, 0f, 0f); // rotate the camera based on mouse y input
else
{
// rotate the camera parent object based on mouse y input
transform.parent.localRotation = Quaternion.Euler(rotationX, 0f, 0f);
// Get the normalized direction from camera to parent object
Vector3 direction = (transform.position - transform.parent.position).normalized;
// Position the camera based on if the camera is colliding with an object
Ray ray = new Ray(transform.parent.position, direction);
transform.localPosition = Physics.Raycast(ray, out RaycastHit hit, maxDistance + zOffset, mask)
? new Vector3(0, 0, -(hit.distance - zOffset))
: new Vector3(0, 0, -maxDistance);
// Finally, make the camera look at the parent object
transform.LookAt(transform.parent);
}
}
}

最新更新