我正在尝试制作一个根据鼠标移动围绕目标旋转的相机系统。到目前为止,我通过创建一个以目标为中心的球体网格并将相机连接到其上并将其放置在其表面上来实现这一目标。因此,通过旋转球体,我也旋转了相机。到目前为止一切顺利。
我实现了一个系统,如果有东西挡住了视线,让相机靠近,我通过在球体中心和相机位置之间进行球体投射,然后将球体的比例更改为球体之间的距离来实现这一点中心和球体投射命中位置。可能不是最好的系统,但它似乎有效。
我面临的问题是,当目标在地面上时,不可能让相机向上看,因为这样做会使其离目标越来越近,直到挡住整个屏幕。
为了解决这个问题,当沿 Z 轴的旋转低于一定量时(现在我使用 -0.3),我尝试旋转相机而不是球体。这样,相机将保持在同一个位置,但向上看,而目标不会妨碍。然而,当我这样做时,相机以与我想要的完全不同的方式旋转,尽管我使用了与球体几乎相同的代码;我还在另一个项目中使用类似的代码旋转了相机,它也起作用了,这让我想到这里的问题可能是相机是球体的子对象这一事实,但我可能是错的。
这是我用于刚才描述的所有内容的脚本
public class CameraMovement : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 targetOffset;
private Transform radius;
[SerializeField] float _defaultRadius;
private float _radius;
[SerializeField] float unclipDistance;
[SerializeField] private LayerMask obstacleMask;
[SerializeField] private float _mouseSensitivity;
private float _rotationX, _rotationY;
private Vector3 _currentRotation;
[SerializeField] private float _cameraRotationX;
private Vector3 _currentCameraRotation;
[SerializeField] private Vector2 minRotation;
[SerializeField] private Vector2 maxRotation;
private Vector3 _smoothVelocity = Vector3.zero;
[SerializeField] private float _smoothTime;
void Start()
{
radius = transform.parent.transform;
radius.localScale = new Vector3(_defaultRadius *2, _defaultRadius *2, _defaultRadius *2);
}
void Update()
{
radius.position = target.position + targetOffset;
float mouseX = Input.GetAxis("Mouse X") * _mouseSensitivity;
float mouseY = Input.GetAxis("Mouse Y") * _mouseSensitivity;
_rotationY += mouseX;
_rotationX -= mouseY;
if (_rotationX < minRotation.x)
touchingGroundMode(mouseY);
else return;
_rotationX = Mathf.Clamp(_rotationX, minRotation.x, maxRotation.x);
Vector3 nextRotation = new Vector3(_currentRotation.x, _rotationY, _rotationX);
_currentRotation = Vector3.SmoothDamp(_currentRotation, nextRotation, ref _smoothVelocity, _smoothTime);
radius.localEulerAngles = _currentRotation;
checKCollision();
}
void checKCollision()
{
Vector3 rayDirection = (transform.position - radius.position).normalized;
Vector3 hypotheticalPos = rayDirection * _defaultRadius;
Ray ray = new Ray(radius.position, rayDirection * _defaultRadius);
Debug.DrawRay(radius.position, rayDirection * _defaultRadius, Color.green);
RaycastHit hit;
if (Physics.SphereCast(ray, 0.5f, out hit, Vector3.Distance(hypotheticalPos, radius.position), obstacleMask))
{
_radius = Vector3.Distance(hit.point, radius.position) - unclipDistance;
}
else _radius = _defaultRadius;
radius.localScale = new Vector3(_radius * 2, _radius * 2, _radius * 2);
}
void touchingGroundMode(float mouseY)
{
float leftoverMouseY = mouseY;
_cameraRotationX -= leftoverMouseY;
Vector3 nextRotation = new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y, _cameraRotationX);
_currentCameraRotation = Vector3.SmoothDamp(_currentCameraRotation, nextRotation, ref _smoothVelocity, _smoothTime);
transform.localEulerAngles = _currentCameraRotation;
}
}
我希望你可以使用这个代码来制作你想要的东西。
public class CameraMovement : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 targetOffset;
private Transform radius;
[SerializeField] float _defaultRadius;
private float _radius;
[SerializeField] float unclipDistance;
[SerializeField] private LayerMask obstacleMask;
[SerializeField] private float _mouseSensitivity;
private float _rotationX, _rotationY;
private Vector3 _currentRotation;
[SerializeField] private Camera camera;
[SerializeField] private Vector2 minRotation;
[SerializeField] private Vector2 maxRotation;
private Vector3 _smoothVelocity = Vector3.zero;
[SerializeField] private float _smoothTime;
void Start()
{
radius = transform.parent.transform;
radius.localScale = new Vector3(_defaultRadius *2, _defaultRadius *2, _defaultRadius *2);
}
void Update()
{
radius.position = target.position + targetOffset;
float mouseX = Input.GetAxis("Mouse X") * _mouseSensitivity;
float mouseY = Input.GetAxis("Mouse Y") * _mouseSensitivity;
_rotationY += mouseX;
_rotationX -= mouseY;
if (_rotationX < minRotation.x)
touchingGroundMode(mouseY);
else return;
_rotationX = Mathf.Clamp(_rotationX, minRotation.x, maxRotation.x);
Vector3 nextRotation = new Vector3(_currentRotation.x, _rotationY, _rotationX);
_currentRotation = Vector3.SmoothDamp(_currentRotation, nextRotation, ref _smoothVelocity, _smoothTime);
// Decouple the camera from the sphere
camera.transform.rotation = _currentRotation;
}
void checKCollision()
{
Vector3 rayDirection = (transform.position - radius.position).normalized;
Vector3 hypotheticalPos = rayDirection * _defaultRadius;
Ray ray = new Ray(radius.position, rayDirection * _defaultRadius);
Debug.DrawRay(radius.position, rayDirection * _defaultRadius, Color.green);
RaycastHit hit;
if (Physics.SphereCast(ray, 0.5f, out hit, Vector3.Distance(hypotheticalPos, radius.position), obstacleMask))
{
_radius = Vector3.Distance(hit.point, radius.position) - unclipDistance;
}
else _radius = _defaultRadius;
radius.localScale = new Vector3(_radius * 2, _radius * 2, _radius * 2);
}
void touchingGroundMode(float mouseY)
{
float leftoverMouseY = mouseY;
camera.transform.Rotate(Vector3.right, leftoverMouseY);
}
}