Here is my Fixed Update code… It has very poor performance on mobile devices. There is anywhere from 100-500 objects in the scene running this code at any given time. I was hoping maybing someone could give me some insight on how this code could potentionally be optimized. Once the game reaches about 300+ of these objects on a high end mobile device frame rate drops to around 15 fps.
void FixedUpdate()
{
if (GetComponent<Chicken>().isFollowing)
{
transform.LookAt(GetComponent<Chicken>().GetFollowingPlayer());
GetComponent<Rigidbody>().velocity = Vector3.Slerp(GetComponent<Rigidbody>().velocity, transform.forward * GetComponent<Chicken>().followSpeed, Time.deltaTime * turnSpeed);
}
else
{
Wander();
}
}
public void Wander()
{
transform.LookAt(wanderDestination);
GetComponent<Rigidbody>().velocity = Vector3.Slerp(GetComponent<Rigidbody>().velocity, transform.forward * speed, Time.deltaTime * turnSpeed);
distanceToDestination = wanderDestination - GetComponent<Rigidbody>().position;
if (distanceToDestination.magnitude < 1)
{
SetRandomDestination();
}
}
public void SetRandomDestination()
{
wanderDestination = RandomNavSphere(transform.position, wanderRadius, -1);
}
public static Vector3 RandomNavSphere(Vector3 origin, float dist, int layermask)
{
NavMesh.SamplePosition((Random.insideUnitSphere * dist) + origin, out navHit, dist, layermask);
return navHit.position;
}
The biggest piece of advice here:
CACHE YOUR COMPONENT CALLS!
Every time you call GetComponent(), you’re making a very complex check for a component on your GameObject. If you cache the results of those into each object, that alone will save a ton of unnecessary calculation work.
As an example of how to approach this:
Rigidbody rb;
Chicken ckn;
void Start()
{
rb = GetComponent<Rigidbody>();
ckn = GetComponent<Chicken>();
}
void FixedUpdate()
{
if (ckn.isFollowing)
// etc.
}
Separate Physics updates from your ai/decision making code and run these in batches:
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class Mob : MonoBehaviour
{
static List<Mob> _instances = new List<Mob>(100);
[SerializeField] [HideInInspector] Rigidbody _rigidbody;
Vector3 _targetVelocity;
#if UNITY_EDITOR
void OnValidate ()
{
if( _rigidbody==null ) { _rigidbody = GetComponent<Rigidbody>(); }
}
void OnDrawGizmos ()
{
Vector3 position = transform.position;
Gizmos.color = Color.blue;
Gizmos.DrawLine( position , position + _targetVelocity );
Gizmos.color = Color.cyan;
Gizmos.DrawLine( position , position + _rigidbody.velocity );
}
#endif
void OnEnable ()
{
//register instance:
if( _instances.Count==_instances.Capacity )
{
_instances.Capacity *= 2;
}
_instances.Add( this );
}
void OnDisable ()
{
//unregister instance:
_instances.Remove( this );
}
void FixedUpdate ()
{
//NO AI CODE, simple operations only
_rigidbody.velocity = Vector3.Lerp( _rigidbody.velocity , _targetVelocity , Time.fixedDeltaTime );
}
async Task AiTick ()
{
//AI CODE GOES HERE
if( Random.value>0.5f )
{
Vector2 vec = Random.insideUnitCircle;
_targetVelocity = new Vector3( vec.x , 0f , vec.y );
}
else
{
_targetVelocity = Vector3.zero;
}
await Task.CompletedTask;
}
[RuntimeInitializeOnLoadMethod]
static async void MobAi ()
{
List<Mob> batch = new List<Mob>();
while( Application.isPlaying )
{
//grab new batch:
batch.Clear();
batch.AddRange( _instances );
//process batch:
foreach( Mob mob in batch )
{
//execute ai code:
await mob.AiTick();
}
//await:
await Task.Delay( 1000/4 );
}
}
}