(Hello, sorry if this is a really stupid question. I'm not good at coding and I probably don't understand what I'm doing)
I'm doing boids on Unity 3D and I'm trying to block them in a square area. Therefore I added Forces, one if they were going to far on the x and another one on the z axis. However I don't understand how to make a force so that they don't go too far on the -x and -z axis. How do I reverse the forces so those boids stay inside my area ?
Here's my x and z boundaries :
//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);
}
Here's the whole code because some asked it (however it's in french sorry)
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);
}
}
}
CodePudding user response:
Well if you say this worked for you and you just ask about the "negative" direction you can simply invert check.
However, this
var forceReject = Mathf.Clamp(5, 1.0f - distX / 2, 2) * forceRejectGround;
seems to make little sense to me. You already know that distX > 20
so that you basically do
var forceReject = Mathf.Clamp(5, -9f /*or less*/, 2) * forceRejectGround;
which of course will always return 2 * forceRejectGround
anyway. So this is either wrong or just completely redundant ;)
Also
distX < -15
can never be true since you already did
distX = Mathf.Max(0, transform.x);
so it can only be 0
or positive!
So basically you could probably simply do something like e.g.
//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;
}
However, as mentioned in the comments, in general you rather want to use a proper Rigidbody
not do anything via transform
at all but rather only through e.g. AddForce
which already calculates the resulting velocities for you and reacts with collisions etc.
Or even further directly go with a Particle System and e.g. have your boid particles enclosed in Force fields