如何逆转力量?
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
(你好,抱歉,如果这是一个非常愚蠢的问题。我不擅长编码,我可能不明白我在做什么)
我在 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