Пташки і 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;
public static Boid[] Sphere(Vector3 position, float radius)
{
List<Boid> mids = new List<Boid>();
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.5 f + separation * 15f + alignment * 1.5 f;
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;
public static Boid[] Sphere(Vector3 position, float radius)
{
List<Boid> mids = new List<Boid>();
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: тут
Код: Завантажити

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.