我设法完成了所需的工作,主要是在此博客文章的帮助下完成了拼图的表面捕捉,并提出了自己的玩家移动和相机想法。
将播放器捕捉到对象表面
基本设置由一个较大的球体(世界)和一个较小的球体(玩家)组成,两个球体都附加有球体碰撞器。
正在完成的大部分工作是通过以下两种方法进行的:
private void UpdatePlayerTransform(Vector3 movementDirection)
{
RaycastHit hitInfo;
if (GetRaycastDownAtNewPosition(movementDirection, out hitInfo))
{
Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
Quaternion finalRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, float.PositiveInfinity);
transform.rotation = finalRotation;
transform.position = hitInfo.point + hitInfo.normal * .5f;
}
}
private bool GetRaycastDownAtNewPosition(Vector3 movementDirection, out RaycastHit hitInfo)
{
Vector3 newPosition = transform.position;
Ray ray = new Ray(transform.position + movementDirection * Speed, -transform.up);
if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity, WorldLayerMask))
{
return true;
}
return false;
}
的 Vector3 movementDirection
参数仅仅是因为它的声音,我们将要移动玩家在这个框架,以及计算矢量,而结束了在这个例子比较简单的方向,是有点棘手,让我在第一弄清楚。稍后再详细介绍,但请记住,它是玩家移动此帧方向上的归一化向量。
逐步进行操作,我们要做的第一件事是检查是否使用起源于脚本的公共LayerMask属性WorldLayerMask的,指向玩家向下向量(-transform.up)的假想未来位置的光线击中了世界。如果您想要更复杂的碰撞或多层,则必须构建自己的图层蒙版。如果光线投射成功击中了某物,则使用hitInfo检索法线和击中点,以计算播放器的新位置和旋转角度,该位置和旋转角度应恰好在对象上。根据所涉及的玩家对象的大小和原点,可能需要抵消玩家的位置。
最后,这实际上只是经过测试,可能仅在简单的物体(例如球体)上能很好地工作。正如我根据解决方案所基于的博客文章所暗示的那样,您可能希望执行多个射线广播,并平均其位置和旋转角度,以便在更复杂的地形上移动时获得更好的过渡效果。在这一点上,我可能还没有想到其他陷阱。
相机和运动
一旦玩家将物体粘在物体表面上,下一个要解决的任务就是移动。我本来是从相对于玩家的移动开始的,但是我开始遇到球体两极的问题,方向突然改变,使我的玩家一遍又一遍地迅速改变方向,以至于我无法越过两极。我最后要做的是让我的玩家相对于相机移动。
满足我需求的最佳方法是拥有一台仅根据玩家位置严格跟随玩家的摄像机。结果,即使摄影机在技术上旋转,向上按压也总是将播放器移向屏幕顶部,向下移至底部,依此类推。
为此,在目标对象是播放器的摄像机上执行以下操作:
private void FixedUpdate()
{
// Calculate and set camera position
Vector3 desiredPosition = this.target.TransformPoint(0, this.height, -this.distance);
this.transform.position = Vector3.Lerp(this.transform.position, desiredPosition, Time.deltaTime * this.damping);
// Calculate and set camera rotation
Quaternion desiredRotation = Quaternion.LookRotation(this.target.position - this.transform.position, this.target.up);
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, desiredRotation, Time.deltaTime * this.rotationDamping);
}
最后,为了移动播放器,我们利用了主摄像头的变换,从而使控件向上,向下移动,向下移动等。在这里,我们调用UpdatePlayerTransform,它将位置锁定到世界对象。
void Update ()
{
Vector3 movementDirection = Vector3.zero;
if (Input.GetAxisRaw("Vertical") > 0)
{
movementDirection += cameraTransform.up;
}
else if (Input.GetAxisRaw("Vertical") < 0)
{
movementDirection += -cameraTransform.up;
}
if (Input.GetAxisRaw("Horizontal") > 0)
{
movementDirection += cameraTransform.right;
}
else if (Input.GetAxisRaw("Horizontal") < 0)
{
movementDirection += -cameraTransform.right;
}
movementDirection.Normalize();
UpdatePlayerTransform(movementDirection);
}
要实现更有趣的摄像头,但控件与我们在此处所使用的控件大致相同,则可以轻松实现不渲染的摄像头,或者仅实现另一个虚拟对象的移动基础,然后使用更有趣的摄像头来渲染您希望游戏看起来像。当您在不破坏控件的情况下围绕对象移动时,这将允许很好的相机过渡。