我在 Unity 3D 项目中使用 MVC。我有 CollisionPoller 来确定接触和移动。如果我使用 MoveController 和 CollisionPoller,我的角色每帧每 +/- 0.01 都会晃动 Y 轴。 如果我使用“Is kinematic”,它是有效的,但我的角色会穿过斜坡。我可以降低胶囊对撞机的高度,但它在斜坡上不起作用(我的角色再次摇晃)。
public class CollisionPoller
{
private readonly PlayerView _playerView;
private const int MaxBounces = 5;
private const float Threshold = 0.015f;
private const float MaxSlopeAngle = 45f;
private Bounds _bounds;
public CollisionPoller(PlayerView playerView)
{
_playerView = playerView;
_bounds = _playerView.Collider.bounds;
_bounds.Expand(-2 * Threshold);
}
public Vector3 CollideAndSlide(Vector3 velocity, Vector3 position, int depth, bool gravityPass, Vector3 velocityInit)
{
if(depth >= MaxBounces)
return Vector3.zero;
var distance = velocity.magnitude + Threshold;
if (!Physics.SphereCast(position, _bounds.extents.x, velocity.normalized, out var hit, distance))
return velocity;
var snapToSurface = velocity.normalized * (hit.distance - Threshold);
var leftOver = velocity - snapToSurface;
var angle = Vector3.Angle(Vector3.up, hit.normal);
if(snapToSurface.magnitude <= Threshold)
snapToSurface = Vector3.zero;
if (angle <= MaxSlopeAngle)
{
if(gravityPass)
return snapToSurface;
leftOver = ProjectAndScale(leftOver, hit.normal);
}
else
{
var scale = 1 - Vector3.Dot(new Vector3(hit.normal.x, 0, hit.normal.z).normalized,
-new Vector3(velocityInit.x, 0, velocityInit.z).normalized);
var isGrounded = Physics.Raycast(_playerView.Transform.position, Vector3.down, _bounds.extents.y + 0.1f);
if (isGrounded && !gravityPass)
{
leftOver = ProjectAndScale(new Vector3(leftOver.x, 0, leftOver.z),
new Vector3(hit.normal.x, 0, hit.normal.z)).normalized;
leftOver *= scale;
}
else
{
leftOver = ProjectAndScale(leftOver, hit.normal) * scale;
}
}
return snapToSurface + CollideAndSlide(leftOver, position + snapToSurface, depth + 1, gravityPass, velocityInit);
}
private static Vector3 ProjectAndScale(Vector3 vector, Vector3 normal)
{
var magnitude = vector.magnitude;
vector = Vector3.ProjectOnPlane(vector, normal).normalized;
vector *= magnitude;
return vector;
}
}
public class MoveController : BaseController
{
private readonly PlayerView _playerView;
private readonly GameModel _gameModel;
private readonly CollisionPoller _collisionPoller;
public MoveController(PlayerView playerView, GameModel gameModel, CollisionPoller collisionPoller)
{
_playerView = playerView;
_gameModel = gameModel;
_collisionPoller = collisionPoller;
}
public void FixedUpdate()
{
var horizontalInput = Input.GetAxisRaw("Horizontal");
var verticalInput = Input.GetAxisRaw("Vertical");
var direction = new Vector3(horizontalInput, 0, verticalInput).normalized;
var moveDirection = _collisionPoller.CollideAndSlide(direction, _playerView.Transform.position, 0, false, direction);
moveDirection += _collisionPoller.CollideAndSlide(Vector3.down, _playerView.Transform.position + moveDirection, 0, true, Vector3.down);
_playerView.Rigidbody.MovePosition(_playerView.Rigidbody.position + moveDirection * (_gameModel.CurrentPlayer.WalkSpeed * Time.fixedDeltaTime));
}
}
我不完全理解你的代码示例。但我怀疑你正在捕捉到表面,但对撞机与表面的对撞机稍微重叠。
这种碰撞会导致物体想要彼此远离,因为它们正在碰撞。所以它又开始离开了。
但是当它移开时,它就在你的捕捉距离之内,所以它会再次保持向下状态。如此重复,导致抖动。
当您捕捉时,您可能想要关闭该对象的物理特性(您通过 iskinematic 注意到这一点),然后当需要停止捕捉时,一旦该对象足够远而不会引起任何奇怪的碰撞,您就可以重新打开物理特性。