unity3d 如何将一个圆重新定位到另外两个圆的圆周之外?

um6iljoc  于 2023-02-09  发布在  其他
关注(0)|答案(1)|浏览(99)

这是一个给Unity人或数学天才的问题。
我正在做一个游戏,我有一个圆圈对象,我可以移动,但我不希望它相交或进入其他(静态)圆圈在世界上(物理系统是不够好,在统一简单地使用,顺便说一句)。
这是在3D世界,但圆只在两个轴上移动。
我能够得到这个完美的工作,如果圆圈只击中1个其他圆圈,但不是2个或更多。
仅供参考:所有圆圈的大小相同。
下面是我的工作公式1圆移动到边缘的碰撞圆,如果相交:

newPosition = PositionOfStaticCircleThatWasJustIntersected + ((positionCircleWasMovedTo - PositionOfStaticCircleThatWasJustIntersected).normalized * circleSize);

但是,如果移动的圆同时碰到2个(或更多)静止的圆,我就无法计算出**公式。
最让我困惑的一件事是方向问题,这取决于所有的圆是如何定位的,以及移动的圆来自哪个方向。
这是一个我正在尝试做的事情的例子。

brccelvz

brccelvz1#

由于我们是在2D空间中操作的,让我们用一些几何图形来处理这个问题。仔细看看你想要的结果,一个特定的形状会变得很明显:

这里有一个三角形!因为所有的圆都有相同的半径,我们知道的更多:这是一个等腰三角形,其中两条边的长度相同。2有了这些信息,问题基本上可以归结为:

我们知道d* 是什么,因为它是碰撞的两个圆之间的距离;我们也知道a* 是什么,因为它是所有圆的半径;有了这些信息,我们就可以计算出移动后的圆的位置,我们需要在两个圆之间移动d/2*(因为两个圆之间的距离相等),距离为h*。
计算高度 h 很简单,因为这是一个直角三角形。根据毕达哥拉斯定理:

// a^2 + b^2 = c^2, or rewritten as:
// a = root(c^2 - b^2)
float h = Mathf.Sqrt(Mathf.Pow(2 * a, 2) - Mathf.Pow(d / 2, 2))

现在我们需要把这些标量转换成博弈空间中的向量。对于两个圆之间的向量来说,这很简单:

Vector3 betweenVector = circle2Position - circle1Position

那么,沿着 h 方向的高度矢量呢?既然所有的运动都是在2D空间中进行的,那么,找到一个圆不沿其运动的方向,并使用Vector3.Cross()计算betweenVector的叉积(垂直矢量)。例如,如果圆只横向运动:

Vector3 heightVector = Vector3.Cross(betweenVector, Vector3.up)

把这些放在一起,你可能有一个方法,如:

Vector3 GetNewPosition(Vector3 movingCirclePosition, Vector3 circle1Position,
    Vector3 circle2Position, float radius)
{
    float halfDistance = Vector3.Distance(circle1Position, circle2Position) / 2;
    float height = Mathf.Sqrt(Mathf.Pow(2 * radius, 2) - Mathf.Pow(halfDistance, 2));

    Vector3 betweenVector = circle2Position - circle1Position;
    Vector3 heightVector = Vector3.Cross(betweenVector, Vector3.up);    

    // Two possible positions, on either side of betweenVector
    Vector3 candidatePosition1 = circle1Position
        + betweenVector.normalized * halfDistance
        + heightVector.normalized * height;
    Vector3 candidatePosition2 = circle1Position
        + betweenVector.normalized * halfDistance
        - heightVector.normalized * height;

    // Absent any other information, the closer position will be assumed as correct
    float distToCandidate1 = Vector3.Distance(movingCirclePosition, candidatePosition1);
    float distToCandidate2 = Vector3.Distance(movingCirclePosition, candidatePosition2);
    
    if (distToCandidate1 < distToCandidate2){
        return candidatePosition1;
    }
    else{
        return candidatePosition2;
    }
}

相关问题