[Из песочницы] Птички и Unity3D, попытка оптимизации

Прочитав пост про создание птичек на Unity3D, я решил предложить свой вариант оптимизации.

В Unity плохо размещать скрипт на каждом объекте, особенно если их очень много, появляются сильные лаги. Чтобы не вешать скрипт Boid.cs можно сделать по другому.

Создадим класс Boid:

public class Boid
{
    public Vector3 velocity;
    public Vector3 position;//Добавляем положение птицы в пространстве

    private float cohesionRadius = 10;
    private float separationDistance = 5;
    private Boid[] boids;
    private Vector3 cohesion;
    private Vector3 separation;
    private int separationCount;
    private Vector3 alignment;
    private float maxSpeed = 15;

    public Boid(Vector3 pos)
    {
        position = pos;
    }
}


В этом классе будут производиться все расчёты. Чтобы сделать его независимым от игровых объектов необходимо заменить функцию Physics.OverlapSphere:

    public static Boid[] boids;
    static public Boid[] Sphere(Vector3 position, float radius)
    {
        List mids = new List();
        for (int i = 0; i < boids.Length; i++)
        {
            if (Vector3.Distance(position, boids[i].position) < radius)
                mids.Add(boids[i]);
        }
        return mids.ToArray();
    }


Эта функция будет находиться в другом классе.
Теперь можно добавить саму функцию расчётов:

public void CalculateVelocity()
    {
        Vector3 newVelocity = Vector3.zero;
        cohesion = Vector3.zero;
        separation = Vector3.zero;
        separationCount = 0;
        alignment = Vector3.zero;
        boids = Boids.Sphere(position, cohesionRadius); //Здесь мы заменили функцию Physics.OverlapSphere на свою
        foreach (Boid boid in boids)
        {
            alignment += boid.velocity;//Нам больше не нужно получать компонент объекта
            cohesion += boid.position;//Больше не обрашаемся к Transfom
            if (boid != this && (position - boid.position).magnitude < separationDistance)
            {
                separation += (position - boid.position) / (position - boid.position).magnitude;
                separationCount++;
            }
        }
        cohesion = cohesion / boids.Length;
        cohesion = cohesion - position;
        cohesion = Vector3.ClampMagnitude(cohesion, maxSpeed);

        if (separationCount > 0)
            separation = separation / separationCount;
        separation = Vector3.ClampMagnitude(separation, maxSpeed);

        alignment = alignment / boids.Length;
        alignment = Vector3.ClampMagnitude(alignment, maxSpeed);

        newVelocity += cohesion * 0.5f + separation * 15f + alignment * 1.5f;
        newVelocity = Vector3.ClampMagnitude(newVelocity, maxSpeed);
        velocity = (velocity * 2f + newVelocity) / 3f;//Добавлено для большей гладкости движения
        if (position.magnitude > 40)
        {
            velocity += -position.normalized;
        }
    }


С помощью этой функции мы получаем velocity. Теперь нужно двигать птицу, для этого добавим функцию Update.

public void Update()
    {
        position += velocity * Time.deltaTime;

        Debug.DrawRay(position, separation, Color.green);//Здесь же мы рисуем лучи для отладки
        Debug.DrawRay(position, cohesion, Color.magenta);
        Debug.DrawRay(position, alignment, Color.blue);
    }


На этом класс Boid закончен.
Теперь нужно как ни будь отобразить птиц.
Для этого я создал массив примитивов которые и перемещал на сцене. Всё это делалось в скрипте Boids.cs:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Boids : MonoBehaviour {

    GameObject[] boidsObjects;

        // Use this for initialization
        void Start () {
        boidsObjects = new GameObject[1500];//Создаём 1500 птиц
        boids = new Boid[boidsObjects.Length];
        for (int i = 0; i < boids.Length; i++)
        {
            GameObject boid = GameObject.CreatePrimitive(PrimitiveType.Cube);//Птицы будут кубами
            boid.transform.position = Random.insideUnitSphere * 35f;
            Destroy(boid.collider);//Удаляем колайдеры чтобы уменьшить лаги
            boidsObjects[i] = boid;
            boids[i] = new Boid(boid.transform.position);//Создаём объект Boid
        }
        StartCoroutine(UpdatePhis());//Функция InvokeRepeating мне не нравится
        }
    IEnumerator UpdatePhis()
    {
        while (true)
        {
            int UpdateCount = 0;
            for (int i = 0; i < boids.Length; i++)
            {
                boids[i].CalculateVelocity();
                UpdateCount++;
                if (UpdateCount > 100)//Чтобы ещё увеличить fps обрабатываем не всех птиц сразу
                {
                    yield return null;
                    UpdateCount = 0;
                }
            }
        }
    }
        // Update is called once per frame
        void Update () {
        for (int i = 0; i < boids.Length; i++)
        {
            boids[i].Update();//Двигаем птиц
            boidsObjects[i].transform.position = boids[i].position;
        }
        }

//Здесь я разместил замену OverlapSphere
    public static Boid[] boids;
    static public Boid[] Sphere(Vector3 position, float radius)
    {
        List mids = new List();
        for (int i = 0; i < boids.Length; i++)
        {
            if (Vector3.Distance(position, boids[i].position) < radius)
                mids.Add(boids[i]);
        }
        return mids.ToArray();
    }
}


Вешаем Boids.cs на камеру и запускаем. При 1500 птиц я получил 70 fps на своём компьютере.
WebPlayer: тут
Код: Скачать

© Habrahabr.ru