Пишу скрипт для генерации ландшафта для 2D игры с видом сбоку. Предполагается возможность разрушать блоки. Воспользовался алгоритмом "midpoint displacement". За один проход рекурсивно заполняется один слой блоков, далее - второй слой, потом заполняется полость между ними. Учитывая размер одного блока (8 пикселей х 8 пикселей) получается примерно 11 тысяч ГО на экране в каждый момент времени, что точно негативно отражается на производительности. Каким образом можно оптимизировать?
Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LandscapeGenerator : MonoBehaviour {
public int counter = 0;
[SerializeField] GameObject ground;
[SerializeField] GameObject stone;
private GameObject ActiveLayer;
float xBegin = -5;
float xEnd = 5;
[SerializeField] float roughness; //во сколько блоков перепад высот
private float distance;
SortedDictionary<float, float> PreviousLayer;//для хранения координат блоков слоя, который заполняется в данный момент
SortedDictionary<float, float> thisLayer;//для хранения координат блоков предыдущего слоя
void Awake(){
PreviousLayer = new SortedDictionary<float, float>();
thisLayer = new SortedDictionary<float, float>();
distance = ground.GetComponent<SpriteRenderer>().bounds.size.x;//размер блока(слава квадратности)
roughness *= distance;//перевод рандомного числа в блоки
float[] begin = {1, -5};//рандомные значения по y для левого края всех слоев
float[] end = {3, -5};//аналогично, для правого
GameObject[] layers = {ground, stone};//из каких ГО составлять слой
StartGenerator(layers, begin, end);
}
public void StartGenerator(GameObject[] Layers, float[] Begin, float[] End){
for(int i = 0; i < Layers.Length; i++){
ActiveLayer = Layers[i];
float yBegin = Begin[i];
float yEnd = End[i];
CheckValues(xBegin, yBegin);
CheckValues(xEnd, yEnd);
GenerateLayer(xBegin, yBegin, xEnd, yEnd);
foreach(KeyValuePair<float, float> entry in PreviousLayer){
float topValue = thisLayer[entry.Key];
Debug.Log((entry.Value + distance) + " " + topValue);
for(float y = entry.Value + distance; y > topValue; y -= distance){
putBlock(entry.Key, y);
}
}
PreviousLayer = new SortedDictionary<float,float>();
foreach(KeyValuePair<float, float> entry in thisLayer)
PreviousLayer.Add(entry.Key, entry.Value);
thisLayer = new SortedDictionary<float, float>();
}
Debug.Log(counter);
}
private void GenerateLayer(float x1, float y1, float x2, float y2){
float x = (x1 + x2) / 2;
float y = (y1 + y2) / 2 + Random.Range(roughness * (x - x1) * (-1), roughness * (x - x1));
CheckValues(x, y);
if(x - x1 > distance)
GenerateLayer(x1, y1, x, y);
if(x2 - x > distance)
GenerateLayer(x, y, x2, y2);
}
private void CheckValues(float x, float y){//чтобы блок не оказался не там, где нужно. По сути - создание квадратной сетки
y = Mathf.Round(y / distance) * distance;
x = Mathf.Round(x / distance) * distance;
if(!thisLayer.ContainsKey(x)){
thisLayer.Add(x, y);
putBlock(x, y);
}
}
private void putBlock(float x, float y){
Instantiate(ActiveLayer, new Vector3(x, y, 0), Quaternion.identity);
counter++;
}
}
using System.Collections.Generic;
using UnityEngine;
public class LandscapeGenerator : MonoBehaviour {
public int counter = 0;
[SerializeField] GameObject ground;
[SerializeField] GameObject stone;
private GameObject ActiveLayer;
float xBegin = -5;
float xEnd = 5;
[SerializeField] float roughness; //во сколько блоков перепад высот
private float distance;
SortedDictionary<float, float> PreviousLayer;//для хранения координат блоков слоя, который заполняется в данный момент
SortedDictionary<float, float> thisLayer;//для хранения координат блоков предыдущего слоя
void Awake(){
PreviousLayer = new SortedDictionary<float, float>();
thisLayer = new SortedDictionary<float, float>();
distance = ground.GetComponent<SpriteRenderer>().bounds.size.x;//размер блока(слава квадратности)
roughness *= distance;//перевод рандомного числа в блоки
float[] begin = {1, -5};//рандомные значения по y для левого края всех слоев
float[] end = {3, -5};//аналогично, для правого
GameObject[] layers = {ground, stone};//из каких ГО составлять слой
StartGenerator(layers, begin, end);
}
public void StartGenerator(GameObject[] Layers, float[] Begin, float[] End){
for(int i = 0; i < Layers.Length; i++){
ActiveLayer = Layers[i];
float yBegin = Begin[i];
float yEnd = End[i];
CheckValues(xBegin, yBegin);
CheckValues(xEnd, yEnd);
GenerateLayer(xBegin, yBegin, xEnd, yEnd);
foreach(KeyValuePair<float, float> entry in PreviousLayer){
float topValue = thisLayer[entry.Key];
Debug.Log((entry.Value + distance) + " " + topValue);
for(float y = entry.Value + distance; y > topValue; y -= distance){
putBlock(entry.Key, y);
}
}
PreviousLayer = new SortedDictionary<float,float>();
foreach(KeyValuePair<float, float> entry in thisLayer)
PreviousLayer.Add(entry.Key, entry.Value);
thisLayer = new SortedDictionary<float, float>();
}
Debug.Log(counter);
}
private void GenerateLayer(float x1, float y1, float x2, float y2){
float x = (x1 + x2) / 2;
float y = (y1 + y2) / 2 + Random.Range(roughness * (x - x1) * (-1), roughness * (x - x1));
CheckValues(x, y);
if(x - x1 > distance)
GenerateLayer(x1, y1, x, y);
if(x2 - x > distance)
GenerateLayer(x, y, x2, y2);
}
private void CheckValues(float x, float y){//чтобы блок не оказался не там, где нужно. По сути - создание квадратной сетки
y = Mathf.Round(y / distance) * distance;
x = Mathf.Round(x / distance) * distance;
if(!thisLayer.ContainsKey(x)){
thisLayer.Add(x, y);
putBlock(x, y);
}
}
private void putBlock(float x, float y){
Instantiate(ActiveLayer, new Vector3(x, y, 0), Quaternion.identity);
counter++;
}
}