【发布时间】:2020-06-29 03:59:06
【问题描述】:
我一直在查看 Source SDK 并阅读了它如何处理玩家与周围空间的碰撞。受此启发,我一直在编写一个应该具有类似行为的角色控制器。
我有代码来控制变换transform,通过计算它的速度vecVelocity 并尝试沿着该向量移动它。我用半径为capsuleRadius 和球心capsuleStart, capsuleEnd 的胶囊来代表玩家,但我还没有给它一个合适的对撞机
在尝试处理碰撞时,我想出了以下函数
void TryPlayerMove()
{
Vector3 transDirection;
float transMagnitude;
Vector3 translation;
Vector3 currentMotion = Vector3.zero;
int i, j, maxHits = 4; //Bump up to 4 times before giving up
RaycastHit capsule;
bool collision;
Vector3[] collNormal = new Vector3[maxHits];
float collDistance;
//Find starting translation vector
translation = vecVelocity * Time.deltaTime;
//Store initial displacement
Vector3 oldTranslation = translation;
for (i = 0; i < maxHits; i++)
{
//Find translation vector magnitude and direction
transDirection = translation.normalized;
transMagnitude = translation.magnitude;
//If we wouldn't move anyway, feel free to break the loop
if (transMagnitude == 0)
{
break;
}
//Shoot a capsule to desired endpoint
collision = Physics.CapsuleCast(capsuleStart + currentMotion, capsuleEnd + currentMotion, capsuleRadius, transDirection, out capsule, transMagnitude);
if (collision)
{
//If we hit something, hug it and take off what is left of translation in that direction
collNormal[i] = capsule.normal.normalized;
collDistance = capsule.distance;
//currentMotion += transDirection * collDistance; //WHY DOESN'T THIS WORK???
translation = ClipVector(translation*(1f-collDistance/transMagnitude), collNormal[i]);
Debug.Log(transDirection.magnitude);
//If we're going towards something we've hit before, stop moving so we don't go into weird corner loops
for (j = 0; j < i; j++)
{
if (Vector3.Dot(translation, collNormal[j]) < 0)
{
translation = Vector3.zero;
break;
}
}
}
else
{
//Just move
currentMotion += translation;
break;
}
}
//Translate the player character and take note of its velocity for future computation
vecVelocity = currentMotion / Time.deltaTime;
transform.Translate(currentMotion, Space.World);
}
剪裁功能只是确保我们的向量真的没有指向对撞机
Vector3 ClipVector(Vector3 inputVector, Vector3 normalVector)
{
float projection;
Vector3 outputVector;
//Determine how much to take out
projection = Vector3.Dot(inputVector, normalVector);
//Subtract the perpendicular component
outputVector = inputVector - normalVector * projection;
//Iterate once more just to make sure
float adjust = Vector3.Dot(outputVector, normalVector);
if (adjust < 0f)
{
outputVector -= normalVector * adjust;
}
return outputVector;
}
这可以正常工作,并且控件的响应与我预期的一样,但有一个外观错误:每次玩家“碰撞”某物时,它都会停在与物体不同的距离处,我'已经记录了它,它在距离玩家的胶囊半径的 5-10% 左右波动。
在TryPlayerMove() 函数中,有一个命令我希望让它拥抱对撞机,它被注释并装饰着绝望的音符
//currentMotion += transDirection * collDistance; //WHY DOESN'T THIS WORK???
每当我取消注释时,控制器总是会通过它接触到的任何对撞机,并完全打乱它应该具有的任何运动。不过,我不知道为什么会这样。
我如何为我的玩家实现一个功能来拥抱它所接触的碰撞器,看到这似乎根本不起作用?
【问题讨论】:
标签: c# unity3d collision-detection linear-algebra