Optimisation in Unity on mobile games

Hello everyone,

I’m trying to make a game like “Crowd City” (android & iOS) and I’m at the very start. I have a working prototype of a player and and AI that can both walk around the map and get units to follow them as they touch them.

The thing is I’m trying to start the project thinking like DOTS, optimisation by default. I have done a lot of research and watched loooooooooooooooots of talks about ECS, Jobs and the burst compiler and I have also done a project on Unity Tiny that got me a little bit more familiar with ECS.

My main problem is that I need to be thinking about optimisation first. My game is supposed to be displaying hundreds (thousands ?) of units at the same time and the same amount of stuff as for animations, vfx etc … and I would like to make better than crowd city that heats up phones very fast.

Thing is, I’ve seen on the forums that DOTS and Navmesh are not working together that much as the navmesh was not originally designed to be working with DOTS.
Maybe there as some ways to make them work together, like calculating paths with jobs, I don’t know.

Any kind of help would be greatly appreciated ! Thanks for your time ! :slight_smile:

I will leave it here.
** 100,000 entities traversing navmesh **

Actually, I’ve seen that post, but I pretty much don’t understand most of it :expressionless:
And I don’t need to have a 100k units either, and I need to handle collisions (seems like he is not) so I was wondering if there was a simpler way to do this, mostly by adapting the code with a few parts written with DOTS, not all of it.

Boids + A* path finding may be an option, just as example.

I’m gonna try that whenever I can. Is A* more optimized than the navmesh ?

Unity’s NavMesh does use A* and I think there is an experimental NavMeshQuery API using the nordeus demo back in 2016. You can probably take a look at that or you can implement your own A* pathfinding which fits your games needs that can work better than Unity’s current NavMesh.

The crowd city videos I’ve seen do not appear to require anything remotely as drastic as DOTS. It looks achievable with any engine / platform if I’m honest, with the basics: pooling and reusing objects that aren’t on screen any more.

This is a case of maximum premature optimisation and if you have no idea how to do this in regular Unity then I suggest you steer clear of DOTS at the present time.

100 AI units on screen using navmesh but only updating destinations for a few (say 5) per frame seems it would not tax any mobile I know of that runs Unity.

Thanks a lot for replying ! :slight_smile:

@hippocoder

That’s why I’m not going full on DOTS right now, I’m just building the prototype the exact same way I usually do, using regular OOP. Also because I’m really having trouble to get DOTS to work, felt like a nightmare only using ECS on project tiny. I will get better on this later on.

The thing is, crowd city heats up phones pretty fast and is a battery consumer, I’d like to avoid that. And if I want to add one or two features to the game that needs particles for every units on screen, it would likely start to have performance issues.

Technically, it’s what I’m doing, I’m pretty much using pools for everything in the game but what do you mean by reusing objects that are not on screen anymore ? I’m not sure I understand, do you talk about the objects rendered by the camera or all objects in the scene ?

It is more than just 100 AI, in crowd city you can have up to something like 700 units with most of then rendered on screen. And what do you mean by updating destinations only for a few ? Actually, im updating all units on screen every frame, else it gives a laggy feeling.

Thanks a lot if you ever find the courage to answer that ! :slight_smile:

Hey everyone, the project is doing okay right now and I manage to have units moving as I like but there are a few more optimisation questions I have.

I have read on unity’s documentation that accessing gameobject properties such as transform could cause some performance issues and instead we should be keeping a reference to the Transform component.
I have run tests on my own and I didn’t notice any performance impact when using transform.position or Transform trans then trans.position. Has this been handled by unity and the doc is outdated or am I missing something ?

Also, like in this Ryan Hipple talk (

), I have been using scriptable objects a lot to store data for a year or so now. As I have thousands of objects in my game, I thought, why not use scriptable objects to store references to components in big arrays, and only store an index in each of my unit ? And so I did, and it workds pretty well.
So, what is the difference in terms of performance and accuracy between :

public class TransformArray : ScriptableObject
{
    public Transform[] array;
}

public class RendererArray : ScriptableObject
{
    public Renderer[] array;
}

public class Unit : MonoBehaviour
{
    public TransformArray allUnitsTransforms;
    public RendererArray allUnitsRenderers;
    int id;
}

and

public class Unit : MonoBehaviour
{
    public Transform trans;
    public Renderer rend;
}

Also, what happens when we are keeping reference of these components in script that we dragged and dropped from inspector, is the object doing something like GetComponent on start ? How is it stored ? What is the difference between a public Transform or a public float variable ? I’m wondering this because the editor crashed on play when I had a reference of a rigidbody (talking about 1000 units though) and not when I had reference in scriptable objects (as it is written on disk).

Sorry if I was not clear but this is really messy in my head, thank you :slight_smile:

Providing I got this right, in some recent version of unity, GetComponent has been improved by far, in what it was in past.

However, storing references in area are probably the fastest route. I can assume only, that inspector stores references as well.

Your best bet, is to profile 100k objects or so and see, if there are any differences. And please let us know with results.

Hi @Neriad . Do you done this game? Can you teach me about algorithm agent follow player, or some things like optimaze more than 700 agent on screen. My boss request me create game like Crowd city but I am stucking.
I tried make with navmesh agent but it so laggy when >200 agent. And algorithm follow destination by navmesh doesn’t good. Please help, thanks a lot!

@unity_SqKRi3Klpu8M1w how much you offer to pay us?

2 Likes

Oops sorry didn’t see the thread replies

@unity_SqKRi3Klpu8M1w
You should check all posts I have created, a few talks about these issues I had making the game.
I stopped using the navmesh and played only with physics, and I doubled the fixed timestep, making less physics updates.

So What is you use to pathfinding?

I stopped using pathfinding. Mostly physics calculations

Oh, really exciting, can you teach me :((

Basicly I did something like that :

    class Leader : MonoBehaviour
    {
        [SerializeField]
        List<Unit> allUnits = new List<Unit>();



        // Fixed update for physics calculations, with fixed timestep increased to 0.04 reducing
        // fixed update calls per second and increasing performance
        void FixedUpdate ()
        {
            for (int i = 0; i < allUnits.Count; i++)
                allUnits[i].Flock(transform.position);
        }
    }

    class Unit : MonoBehaviour
    {
        [SerializeField]
        Leader leader = null;
        [SerializeField]
        float sqrMaxForce = 55, sqrMaxVelocity = 12;

        Vector3 currentForce = Vector3.zero;



        public void Flock (Vector3 _goalPos)
        {
            currentForce = Seek(_goalPos).normalized;
            Move(currentForce * 50);
        }

        // Used to find the vector towards the leader
        Vector3 Seek (Vector3 _target)
        {
            return _target - transform.position;
        }

        void Move (Vector3 _force)
        {
            Vector3 force = new Vector3(_force.x, 0, _force.z);

            if (force.sqrMagnitude > sqrMaxForce)
            {
                force = force.normalized;
                force *= sqrMaxForce;
            }
            rb.AddForce(force);

            if (rb.velocity.sqrMagnitude > sqrMaxVelocity)
            {
                rb.velocity = rb.velocity.normalized;
                rb.velocity *= sqrMaxVelocity;
            }

            // Used to align the units with their leader
            transform.forward = Vector3.Lerp(transform.forward, leader.transform.forward, 5 * Time.deltaTime);
        }
    }

It’s not perfect, the units do not bypass other objects if they run into it, but in my case it was ok.

Thanks bro!