- Home /
What's the best move/rotate method to simulate a herd of animals moving around?
Since there are so many different methods of moving and rotating gameObjects in Unity, I can't decide which one to use for this specific purpose without having too much performance cost.
I have 20-30 gameObjects that I want to keep moving towards random targets (at random directions) until they either reach the target or collide with another object at which point they get a new random target, smoothly turnand start moving towards it.
I believe that I definitely have to use non-kinematic rigidbodies and freeze their X and Z rotations, but I can't decide which rigidbody methods I should use?!
Answer by Captain_Pineapple · Sep 22, 2021 at 01:51 PM
Hey and welcome,
mostly there is no real best way to do X. However in your case i'd suggest the following:
Since you do not want to have physics based interactions between your animals and their environment (except for collisions) you should make them kinematic. Then use GetComponent().MovePosition(). For rotations MoveRotation(...). Why these functions? because with this (if called properly in FixedUpdate) you will get proper collision interactions. Just setting a transforms position/rotation will not give you the same result.
The last thing you should do for performance optimzation is cache your Rigidbody reference so that you do not call GetComponent
each frame.
Thanks for answering Captain ;-) I'll share the updates after I write the code and test it.
This is what I got so far:`
public class BunnyMovement : MonoBehaviour
{
private Animator animator;
private Rigidbody rb;
[SerializeField] private Vector3 targetPos;
[SerializeField] private float linearVelocity = 1;
[SerializeField] private Vector3 angularVelocity = new Vector3(0, 100, 0);
private void Awake()
{
animator = GetComponentInChildren<Animator>(includeInactive: true);
rb = GetComponentInChildren<Rigidbody>(includeInactive: true);
animator.enabled = true;
NewTarget();
}
private void FixedUpdate()
{
if (transform.position == targetPos)
{
NewTarget();
}
if (!IsFacingTarget())
{
FaceTarget();
}
else
{
MoveForward();
}
}
private void OnCollisionEnter(Collision collision)
{
NewTarget();
}
private void NewTarget()
{
float targetX = Random.Range(-24.5f, -16);
float targetZ = Random.Range(-26.5f, -19);
targetPos = new Vector3(targetX, 0, targetZ);
}
private void FaceTarget()
{
Quaternion targetRot = Quaternion.LookRotation(targetPos - transform.position);
targetRot.x = 0; targetRot.z = 0;
Quaternion step = Quaternion.Euler(targetRot.y * angularVelocity * Time.fixedDeltaTime);
animator.SetFloat("Turn", targetRot.y);
rb.MoveRotation(rb.rotation * step);
}
private void MoveForward()
{
Vector3 step = transform.forward * linearVelocity * Time.fixedDeltaTime;
rb.MovePosition(rb.position + step);
}
private bool IsFacingTarget()
{
Vector3 normalizedDir = (targetPos - transform.position).normalized;
if (Vector3.Dot(transform.forward, normalizedDir) < 0.7f)
{
return false;
}
return true;
}
}
Notes: 1. When rigidbody is marked kinematic, no collision is detected. 2. Even after unckecking isKinematic, object sometimes falls through the ground. 3. The objects never stop turning.
Fair point, i just checked the collider manual once more (link here)
You cannot use kinematic after all when wanting to detect collisions with static colliders. You will only get collisions with other rigidbodys otherwise. So you will have to uncheck isKinematic and go for FreezeAll Rotation and position in the end.
Answer by nastaranarasteh · Sep 23, 2021 at 01:34 AM
So, I ended up giving the non-kinenatic rigidbody a constant forward velocity for movement and setting the transform.rotation value directly in a Slerp coroutine for rotation. I also removed
if (transform.position == targetPos) { NewTarget(); }
and only call the NewTarget() method in OnCollisionEnter, now. However, I've discovered that every time OnCollisionEnter is called, I'm getting multiple Target positions, which ends up confusing the object rotation, but I guess I would have to create a new question for that.
OnCollisionEnter triggers for all colliders you hit. So if there is an object with 3 colliders and you hit them all indipendently you get 3 calls to OnCollision Enter.
Apart from that it might be worth checking if triggers are not more suitable for your case as you don't care for the collision itself either way if i understood that correctly.
But yes, in general -> new questions should be posted as such.
For anyone out there looking for something like this, here's what I ended up with:
private Animator animator;
private Rigidbody rb;
[SerializeField] private Quaternion targetRot;
[SerializeField] private float speed = 1;
[SerializeField] private float turnDuration = 2;
[SerializeField] private bool isTurning = false;
private float lastCheckTime;
private Vector3 lastCheckPos;
private void Awake()
{
animator ??= GetComponentInChildren<Animator>(includeInactive: true);
rb ??= GetComponentInChildren<Rigidbody>(includeInactive: true);
if (animator != null) { animator.enabled = true; }
TurnTowardsNewTarget();
}
private void FixedUpdate()
{
if (!isTurning && (Time.time - lastCheckTime) > 2)
{
if ((transform.position - lastCheckPos).magnitude < 1)
{
TurnTowardsNewTarget();
}
lastCheckPos = transform.position;
lastCheckTime = Time.time;
}
rb.velocity = transform.forward * speed;
}
private void OnCollisionEnter(Collision collision)
{
if (!isTurning) { TurnTowardsNewTarget(); }
}
private void TurnTowardsNewTarget()
{
float yRotation = Random.Range(0, 345);
targetRot = Quaternion.Euler(0, yRotation, 0);
StartCoroutine(Turn());
}
private IEnumerator Turn()
{
isTurning = true;
float elapsedTime = 0;
while (elapsedTime < turnDuration)
{
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, elapsedTime / turnDuration);
elapsedTime += Time.deltaTime;
yield return null;
}
transform.rotation = targetRot;
isTurning = false;
}
Of course it can be manipulated to add more variations to the animals' behaviours.
No problem :-) I'm sure there are better ways to do this, but this is the best I could come up with, with my limited knowledge.
Your answer
Follow this Question
Related Questions
Networked Movement Jitter 0 Answers
Rigidbody controller slide up walls?(Sidescroller) 2 Answers
How can I make a camera shake more smoothly (Random.insideUnitSphere)? 1 Answer
Rigidbody bumping on smooth surface 2 Answers
Aligning a rigidbody smoothly with the perpendicular of a vector angle 0 Answers