
如图,我正在做一个带有瞄准线发射球游戏,我根据射线检测的轨迹做了这一条瞄准线,这一条线检测到有其它刚体时我给它做另外一条反射线。但我们的射线是一条无限细的线,如图我最后发射时,射线是没有检测到其它刚体是在射线的轨迹上的,但实际情况是刚体的宽度远大于这条射线,最后还是发生碰撞到的。我们要如何做才能让这条射线是更加精确的呢?
中间和两边都发射线
如果是都发射的话,那三条线可能都有不同反射轨迹,这样会显得很乱吧?
三条线只取最先检测到有刚体的轨迹的结果
好的我试试
你可以在这个思路上再加个递归处理,去更精准的检测碰撞点。比如第一次检测左中右三条射线,左和中能碰撞,就在左和中的中间再生成一条射线再去检测,这样多去递归几次,最后发现三条射线都能碰撞就按距离取最近的。
public static class SphereCastManager
{
public static bool SphereCast(List<Board> boards, List<BallManager> balls, Vector3 point, Vector3 direction, out HitInfo hitInfo, float maxDistance, BallManager cueBall = null, int excludeBallId = -1)
{
if (balls == null || balls.Count == 0)
{
hitInfo = new HitInfo();
return false;
}
float radius = 0.5f * balls[0].Diameter;
float diameterSqr = balls[0].Diameter * balls[0].Diameter;
float maxDistanceSqr = maxDistance * maxDistance;
float ballDistance = maxDistance;
BallManager hitBall = null;
foreach (BallManager ball in balls)
{
if(ball.id == excludeBallId)
{
continue;
}
if (Vector3.Dot(direction, ball.transform.position - point) < 0f)
{
continue;
}
Vector3 pointOnRay = VectorGeometry.ProjectPointOnRay(ball.transform.position, point, direction);
float distanceSqr = (ball.transform.position - pointOnRay).sqrMagnitude;
if(distanceSqr > diameterSqr)
{
continue;
}
float cDistance = Vector3.Distance(pointOnRay, point) - Mathf.Sqrt(diameterSqr - distanceSqr);
if (ballDistance > cDistance)
{
ballDistance = cDistance;
hitBall = ball;
}
}
if(cueBall != null)
{
if (Vector3.Dot(direction, cueBall.transform.position - point) > 0f)
{
Vector3 pointOnRay = VectorGeometry.ProjectPointOnRay(cueBall.transform.position, point, direction);
float distanceSqr = (cueBall.transform.position - pointOnRay).sqrMagnitude;
if (distanceSqr <= diameterSqr)
{
float cDistance = Vector3.Distance(pointOnRay, point) - Mathf.Sqrt(diameterSqr - distanceSqr);
if (ballDistance > cDistance)
{
ballDistance = cDistance;
hitBall = cueBall;
}
}
}
}
Board hitBoard = default;
bool hasHitBoard = false;
float boardDistance = maxDistance;
foreach (Board board in boards)
{
float dot = -Vector3.Dot(direction, board.normal);
if (dot <= 0 || Vector3.Dot(point - board.position, board.normal) < 0f)
{
continue;
}
Vector3 pointOnLine = VectorGeometry.ProjectPointOnLine(point, board.position, board.normal);
float distanceH = (point - pointOnLine).magnitude;
float length = distanceH / dot;
float distanceL = Mathf.Sqrt((length * length) - (distanceH * distanceH));
float distanceP = distanceL - ((distanceL * radius) / distanceH);
Vector3 hitPoint = pointOnLine + distanceP * Vector3.ProjectOnPlane(direction, board.normal).normalized;
if (Vector3.Distance(hitPoint, board.position) > 0.5f * board.length)
{
continue;
}
Vector3 spherePoint = hitPoint + radius * board.normal;
float cDistance = Vector3.Distance(spherePoint, point);
if (boardDistance > cDistance)
{
boardDistance = cDistance;
hitBoard = board;
hasHitBoard = true;
}
}
if(ballDistance < boardDistance)
{
if (hitBall != null)
{
hitInfo = new HitInfo
{
SpherePosition = point + ballDistance * direction,
HitObjectID = hitBall.id
};
hitInfo.Normal = (hitInfo.SpherePosition - hitBall.transform.position).normalized;
hitInfo.Point = hitBall.transform.position + 0.5f * hitBall.Diameter * hitInfo.Normal;
hitInfo.Type = HitType.Ball;
return true;
}
}
else if (boardDistance < ballDistance)
{
if (hasHitBoard)
{
hitInfo = new HitInfo
{
SpherePosition = point + boardDistance * direction,
HitObjectID = -1,
Normal = hitBoard.normal
};
hitInfo.Point = hitInfo.SpherePosition - radius * hitBoard.normal;
hitInfo.Type = HitType.Board;
return true;
}
}
hitInfo = new HitInfo();
return false;
}
}
可以参考这个算法,球和球的检测判断,球和边的检测
就是这条射线距离最短,则能取为实际的射线吗?
请问具体是啥意思,意思是从球的两边开始发射射线吗?
是的,球的直径就是一条线,线是由上万个点组成的。你就把它想象成以球的宽度发射了一万根射线,最短的距离就是碰撞点。而递归就只是优化了下遍历次数,不需要真正的一万根射线去做检测。
好的我试试