如何逆转力量?

How to reverse a Force?

(你好,抱歉,如果这是一个非常愚蠢的问题。我不擅长编码,我可能不明白我在做什么)

我在 Unity 3D 上做 boids,我试图将它们阻挡在一个方形区域中。因此我添加了力,如果它们在 x 轴上很远,另一个在 z 轴上。 但是我不明白如何施加力,使它们不会在 -x 和 -z 轴上走得太远。我该如何扭转这些力量,让那些 boids 留在我的区域内?

这是我的 x 和 z 边界:

        //Force x
        float distX = Mathf.Max(0, transform.position.x);
        if (distX > 20)
        {
            float forceReject = Mathf.Clamp(5, 1.0f - (distX / 2), 2) * forceRejectGround;
            sumForces += new Vector3(-forceReject, 0, 0);
        }
        
        //Force z
        float distZ = Mathf.Max(0, transform.position.z);
        if (distZ > 20)
        {
            float forceReject = Mathf.Clamp(5, 1.0f - (distZ / 2), 2) * -forceRejectGround;
            sumForces += new Vector3(0, 0, forceReject);
        }
      

这是完整的代码,因为有些人问过它(但抱歉,它是法语的)

public class Boid : MonoBehaviour
{
    public float zoneRepulsion = 2;
    public float zoneAlignement = 9;
    public float zoneAttraction = 30;
    public float hauteurSol = 0;

    public float forceRepulsion = 15;
    public float forceAlignement = 3;
    public float forceAttraction = 20;
    public float forceRejetSol = 100;

    public Vector3 target = new Vector3();
    public float forceTarget = 15;
    public bool goToTarget = false;
    public bool stopToTarget = false;
    private bool atTarget = false;

    public Vector3 velocity = new Vector3();
    public float maxSpeed = 10;
    public float minSpeed = 5;

    public bool drawGizmos = true;
    public bool drawLines = true;

    // Update is called once per frame
    void Update()
    {
        Vector3 sumForces = new Vector3();
        Color colorDebugForce = Color.black;
        float nbForcesApplied = 0;

        foreach (Boid otherBoid in BoidManager.sharedInstance.roBoids)
        {
            Vector3 vecToOtherBoid = otherBoid.transform.position - transform.position;
            Vector3 forceToApply = new Vector3();

            //Si on doit prendre en compte cet autre boid (plus grande zone de perception)
            if (vecToOtherBoid.sqrMagnitude < zoneAttraction * zoneAttraction)
            {
                //Si on est entre attraction et alignement
                if (vecToOtherBoid.sqrMagnitude > zoneAlignement * zoneAlignement)
                {
                    //On est dans la zone d'attraction uniquement
                    forceToApply = vecToOtherBoid.normalized * forceAttraction;
                    float distToOtherBoid = vecToOtherBoid.magnitude;
                    float normalizedDistanceToNextZone = ((distToOtherBoid - zoneAlignement) / (zoneAttraction - zoneAlignement));
                    float boostForce = (4 * normalizedDistanceToNextZone);
                    if (!goToTarget) //Encore plus de cohésion si pas de target
                        boostForce *= boostForce;
                    forceToApply = vecToOtherBoid.normalized * forceAttraction * boostForce;
                    colorDebugForce += Color.green;
                }
                else
                {
                    //On est dans alignement, mais est on hors de répulsion ?
                    if (vecToOtherBoid.sqrMagnitude > zoneRepulsion * zoneRepulsion)
                    {
                        //On est dans la zone d'alignement uniquement
                        forceToApply = otherBoid.velocity.normalized * forceAlignement;
                        colorDebugForce += Color.blue;
                    }
                    else
                    {
                        //On est dans la zone de repulsion
                        float distToOtherBoid = vecToOtherBoid.magnitude;
                        float normalizedDistanceToPreviousZone = 1 - (distToOtherBoid / zoneRepulsion);
                        float boostForce = (4 * normalizedDistanceToPreviousZone);
                        forceToApply = vecToOtherBoid.normalized * -1 * (forceRepulsion * boostForce);
                        colorDebugForce += Color.red;

                    }
                }


                sumForces += forceToApply;
                nbForcesApplied++;
                
            }
        }

        //On fait la moyenne des forces, ce qui nous rend indépendant du nombre de boids
        sumForces /= nbForcesApplied;

        //On ajoute le rejet du sol
        float distSol = Mathf.Max(0,transform.position.y - hauteurSol);
        if (distSol < 2)
        {
            float forceRejet = Mathf.Pow(1.0f - (distSol / 2), 2) * forceRejetSol;
            sumForces += new Vector3(0, forceRejet, 0);
        }

        //Rejet du plafond
        float distPlaf = Mathf.Max(5, transform.position.y);
        if (distPlaf > 10)
        {
            float forceRejet = Mathf.Clamp(5, 1.0f - (distPlaf / 2), 2) * -forceRejetSol;
            sumForces += new Vector3(0, forceRejet, 0);
        }

        //Rejet pour la limite d' x
        float distX = Mathf.Max(0, transform.position.x);
        if (distX > 20)
        {
            float forceRejet = Mathf.Clamp(5, 1.0f - (distX / 2), 2) * forceRejetSol;
            sumForces += new Vector3(-forceRejet, 0, 0);
        }
        //Rejet pour la limite de -x
        if (distX < -15)
        {
            float forceRejetXNeg = Mathf.Clamp(5, 1.0f - (distX / 2), 2) * forceRejetSol;
            sumForces += new Vector3(forceRejetXNeg, 0, 0);
        }
        //Rejet pour la limite d' z
        float distZ = Mathf.Max(0, transform.position.z);
        if (distZ > 20)
        {
            float forceRejet = Mathf.Clamp(5, 1.0f - (distZ / 2), 2) * -forceRejetSol;
            sumForces += new Vector3(0, 0, forceRejet);
        }
        //Rejet pour la limite de -z
        if (distZ < -15)
        {
            float forceRejetZNeg = Mathf.Clamp(5, 1.0f - (distZ / 2), 2) * -forceRejetSol;
            sumForces += new Vector3(0, 0, forceRejetZNeg);
        }

        //Si on a une target, on l'ajoute
        float distToTarget = 0;
        if (goToTarget)
        {
            Vector3 vecToTarget = target - transform.position;
            distToTarget = vecToTarget.magnitude;
            if (distToTarget < 0.5f)
            {
                goToTarget = false;
                atTarget = true;
            }
            else
            {
                atTarget = false;
                Vector3 forceToTarget = vecToTarget.normalized * forceTarget;
                if (distToTarget < 5 && stopToTarget)
                    forceToTarget *= 10;
                sumForces += forceToTarget;
                colorDebugForce += Color.magenta;
                nbForcesApplied++;
                if (drawLines)
                    Debug.DrawLine(transform.position, target, Color.magenta);
            }
        }

        //Debug
        if (drawLines)
            Debug.DrawLine(transform.position, transform.position + sumForces, colorDebugForce / nbForcesApplied);

        //On freine
        velocity += -velocity * 10 * Vector3.Angle(sumForces, velocity) / 180.0f * Time.deltaTime;

        //on applique les forces
        velocity += sumForces * Time.deltaTime;

        //On limite la vitesse
        if (velocity.sqrMagnitude > maxSpeed * maxSpeed)
            velocity = velocity.normalized * maxSpeed;
        if (velocity.sqrMagnitude < minSpeed * minSpeed)
            velocity = velocity.normalized * minSpeed;

        //Si on doit freiner sur la cible, on limite la vitesse
        if ((goToTarget || atTarget) && stopToTarget)
            if (distToTarget < 3)
                velocity = velocity.normalized * distToTarget/10.0f;


        //On regarde dans la bonne direction        
        if (velocity.sqrMagnitude > 0)
            transform.LookAt(transform.position + velocity);

        //Debug
        if (drawLines)
            Debug.DrawLine(transform.position, transform.position + velocity, Color.blue);

        //Deplacement du boid
        transform.position += velocity * Time.deltaTime;
    }

    void OnDrawGizmosSelected()
    {
        if (drawGizmos)
        {
            // Répulsion
            Gizmos.color = new Color(1, 0, 0, 1.0f);
            Gizmos.DrawWireSphere(transform.position, zoneRepulsion);
            // Alignement
            Gizmos.color = new Color(0, 1, 0, 1.0f);
            Gizmos.DrawWireSphere(transform.position, zoneAlignement);
            // Attraction
            Gizmos.color = new Color(0, 0, 1, 1.0f);
            Gizmos.DrawWireSphere(transform.position, zoneAttraction);
        }
    }
}

好吧,如果你说这对你有用,而你只是询问“负”方向,你可以简单地反转检查。

然而,这

var forceReject = Mathf.Clamp(5, 1.0f - distX / 2, 2) * forceRejectGround;

对我来说似乎没有什么意义。你已经知道 distX > 20 所以你基本上做到了

var forceReject = Mathf.Clamp(5, -9f /*or less*/, 2) * forceRejectGround;

这当然总是 return 2 * forceRejectGround 无论如何。所以这要么是错误的,要么完全是多余的 ;)

还有

distX < -15

不可能是真的,因为你已经做到了

distX = Mathf.Max(0, transform.x);

所以只能是0或正数!


所以基本上你可以简单地做一些事情,例如

//Force x
var x = transform.position.x;
if (Mathf.Abs(x) > 20) 
// basically equals
//if(x > 20 || x < -20)
{
    // will be positive if x > 20 and negative if x < -20 
    // (assuming forceRejectGround is positive of course)
    var forceReject = forceRejectGround * Mathf.Sign(x);

    sumForces -= Vector3.right * forceReject;
}
        
//Force z
var z = transform.position.z;
if (Mathf.Abs(z) > 20)
{
    var forceReject = forceRejectGround * Mathf.Sign(z);

    sumForces -= Vector3.up * forceReject;
}

但是,正如评论中提到的,一般来说,您更愿意使用适当的 Rigidbody not do anything via transform at all but rather only through e.g. AddForce,它已经为您计算出最终速度并对碰撞等做出反应。

或者更进一步直接使用 Particle System and e.g. have your boid particles enclosed in Force fields