射线检测的瞄准线怎么能更精确?

ivc28-j68pc
如图,我正在做一个带有瞄准线发射球游戏,我根据射线检测的轨迹做了这一条瞄准线,这一条线检测到有其它刚体时我给它做另外一条反射线。但我们的射线是一条无限细的线,如图我最后发射时,射线是没有检测到其它刚体是在射线的轨迹上的,但实际情况是刚体的宽度远大于这条射线,最后还是发生碰撞到的。我们要如何做才能让这条射线是更加精确的呢?

中间和两边都发射线

如果是都发射的话,那三条线可能都有不同反射轨迹,这样会显得很乱吧?

三条线只取最先检测到有刚体的轨迹的结果

好的我试试

你可以在这个思路上再加个递归处理,去更精准的检测碰撞点。比如第一次检测左中右三条射线,左和中能碰撞,就在左和中的中间再生成一条射线再去检测,这样多去递归几次,最后发现三条射线都能碰撞就按距离取最近的。

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;

    }

}

可以参考这个算法,球和球的检测判断,球和边的检测

就是这条射线距离最短,则能取为实际的射线吗?

请问具体是啥意思,意思是从球的两边开始发射射线吗?

是的,球的直径就是一条线,线是由上万个点组成的。你就把它想象成以球的宽度发射了一万根射线,最短的距离就是碰撞点。而递归就只是优化了下遍历次数,不需要真正的一万根射线去做检测。

好的我试试