mesh generator работет медленно

Рейтинг: -1Ответов: 1Опубликовано: 15.06.2023

Я делаю игру с генерацией мира, что-то похожее на майнкрафт, но без возможности разрушать блоки. Cтолкнулся с проблемой, скрипт который отрисовывает часть мира слишком долго думает. Хотел узнать есть ли способы оптимизировать либо уменьшить кол-во вычислений.

Вот код:

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

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider))]

public class AGchunk : MonoBehaviour
{
public AGdata AGdata;

private List<Vector3> verticies = new List<Vector3>();
private List<Vector2> uvs = new List<Vector2>();
private List<int> triangles = new List<int>();



public Blockinfo[] Blocks;

private Mesh chunkMesh;



private float raduis = 94;
private float holeoffset;


AGBlockType[] Blockss = new AGBlockType[AGworld.LvlkWidth + (AGworld.LvlHeight + 2) * 
AGworld.LvlkWidthSq + AGworld.LvlkWidth * AGworld.LvlkWidth];  

public Vector3Int chankpos;
private int uborka = (AGworld.LvlkWidth / AGworld.ChunkWidth) - 1;


public GameObject Lamppref;
public GameObject HookBlockpref;



public Transform player;
public float VisionDistanse;
private float a;
bool generated;

void Start()
{

    player = AGdata.Player;

    a = VisionDistanse * 100;
    holeoffset = AGworld.LvlkWidth / 2 - raduis ;
    Blockss = AGdata.Blocks;

    chunkMesh = new Mesh();

    //RegenerateMesh();
    StartCoroutine(RegenerateMesh());
    GetComponent<MeshFilter>().sharedMesh = chunkMesh;


}

private IEnumerator RegenerateMesh()
{
    verticies.Clear();
    uvs.Clear();
    triangles.Clear();


    int a = Mathf.FloorToInt(gameObject.transform.position.x );
    int b = Mathf.FloorToInt(gameObject.transform.position.z );


    for (int y = 1; y < AGworld.ChunkHeight + 1; y++)
    {
        yield return new WaitForSecondsRealtime(0.005f);
        for (int x = 0; x < AGworld.ChunkWidth; x++)
        {
            for (int z = 0; z < AGworld.ChunkWidth; z++)
            {
                GenerateBlock(x + a , y , z + b );
            }
        }
    }

    chunkMesh.triangles = new int[0];
    chunkMesh.vertices = verticies.ToArray();
    chunkMesh.uv = uvs.ToArray();
    chunkMesh.triangles = triangles.ToArray();

    chunkMesh.Optimize();

    chunkMesh.RecalculateBounds(); //для коллайдеров и взаимодействия
    chunkMesh.RecalculateNormals(); //для хорошего взвимодействия с освещением

    GetComponent<MeshCollider>().sharedMesh = chunkMesh;

    gameObject.transform.position = new Vector3(0, gameObject.transform.position.y, 0);
}


public void SpawnBlock(Vector3Int Blockpos)
{
    int index = Blockpos.x + Blockpos.y * AGworld.LvlkWidthSq + Blockpos.z * 
AGworld.LvlkWidth;
    Blockss[index] = AGBlockType.Stone;

    RegenerateMesh();

}
public void DestroyBlock(Vector3Int Blockpos)
{
    int index = Blockpos.x + Blockpos.y * AGworld.LvlkWidthSq + Blockpos.z * 
AGworld.LvlkWidth;
    Blockss[index] = AGBlockType.Air;

    RegenerateMesh();

}

void spawnhook(Vector3Int pos)
{

    GameObject hk = Instantiate(HookBlockpref, new Vector3(0, 0, 0), 
    Quaternion.identity);
    hk.transform.SetParent(gameObject.transform.parent.transform);


    //Debug.Log("спавн"+ pos.x + " " + pos.y + " " + pos.z);
    hk.transform.position = new Vector3(pos.x + 0.5f, pos.y + 
    gameObject.transform.parent.position.y + 0.5f, pos.z + 0.5f);
    //hk.transform.localScale = new Vector3(2,2, 2);
    //Renderer rend = hk.GetComponent<MeshRenderer>();
    //rend.material = Resources.Load<Material>("AGDebugMat");

    //hk.transform.SetParent(gameObject.transform);

    if (GetBlockOnPos(pos + Vector3Int.right * 2) != AGBlockType.Air) { 
  hk.transform.Rotate(0, -90, 0); hk.name = "hook  right"; }
    else if (GetBlockOnPos(pos + Vector3Int.left * 2) != AGBlockType.Air) { hk.transform.Rotate(0, 90, 0); hk.name = "hook  left"; }
    else if (GetBlockOnPos(pos + Vector3Int.forward * 2) != AGBlockType.Air) { hk.transform.Rotate(0, 180, 0); hk.name = "hook  forward"; }
    else if (GetBlockOnPos(pos + Vector3Int.back * 2) != AGBlockType.Air) { hk.transform.Rotate(0, 0, 0); hk.name = "hook  back"; } 
    else
    {
        Destroy(hk);
    }


}

void spawnLamp(Vector3Int pos)
{    
    GameObject hk = Instantiate(Lamppref, new Vector3(0,0,0), Quaternion.identity);
    hk.transform.SetParent(gameObject.transform.parent.transform);  
    hk.transform.position = new Vector3(pos.x + 0.5f, pos.y + gameObject.transform.parent.position.y + 0.2f, pos.z + 0.5f);
    hk.name = "Lamp";
}

private void GenerateBlock(int x, int y, int z)
{
    int a = Mathf.FloorToInt(gameObject.transform.position.x);
    int b = Mathf.FloorToInt(gameObject.transform.position.z);
    var Blockpos = new Vector3Int(x, y, z);

    AGBlockType blockType = GetBlockOnPos(Blockpos);

    if (blockType == AGBlockType.Air) return; //если в массиве по данным координатам нет блока то продолжаем


    if (blockType == AGBlockType.Lamp || blockType == AGBlockType.Hook)
    {
        if (blockType == AGBlockType.Lamp)
        {
            spawnLamp(Blockpos);
        }
        if (blockType == AGBlockType.Hook)
        {
            spawnhook(Blockpos);
        }

    }

    else
    {
        if (GetBlockOnPos(Blockpos + Vector3Int.right) == 0)
        {

            if (x - a == AGworld.ChunkWidth - 1 && chankpos.x == uborka)//или Blockpos.z == 0 убирает ненужные границы блоквоидного мира
            {
            }
            else
            {
                GenRightSide(Blockpos);//проверка нет ли рядом блока для генерации сторон
                AddUvs(GetBlockOnPos(Blockpos));
            }

        }

        if (GetBlockOnPos(Blockpos + Vector3Int.left) == 0)
        {
            if (x - a == 0 && chankpos.x == 0)//или Blockpos.z == 0
            {
            }
            else
            {
                GenLeftSide(Blockpos);
                AddUvs(GetBlockOnPos(Blockpos));
            }

        }

        if (GetBlockOnPos(Blockpos + Vector3Int.forward) == 0)
        {
            if (z - b == AGworld.ChunkWidth - 1 && chankpos.z == uborka)//или Blockpos.z == 0
            {
            }
            else
            {
                GenFrontSide(Blockpos);
                AddUvs(GetBlockOnPos(Blockpos));
            }

        }

        if (GetBlockOnPos(Blockpos + Vector3Int.back) == 0)
        {
            if (z - b == 0 && chankpos.z == 0)//или Blockpos.z == 0
            {
            }
            else
            {
                GenBackSide(Blockpos);
                AddUvs(GetBlockOnPos(Blockpos));
            }

        }

        if (GetBlockOnPos(Blockpos + Vector3Int.up) == 0)//
        {

            GenTopSide(Blockpos);
            AddUvs(GetBlockOnPos(Blockpos));
        }

        if (GetBlockOnPos(Blockpos + Vector3Int.down) == 0)//&& Blockpos.y > 0
        {
            GenBottomSide(Blockpos);
            AddUvs(GetBlockOnPos(Blockpos));
        }
    }


}

private AGBlockType GetBlockOnPos(Vector3Int blockposition)//возвращает блок по положению - проверка координат
{
    float opt4 = Vector2.Distance(new Vector3(blockposition.x, blockposition.z), Vector2.one * raduis + new Vector2(holeoffset, holeoffset));

    if (opt4 >= AGTerrainGenerator.raduis && opt4 >= AGTerrainGenerator.raduis + AGTerrainGenerator.bloksafterradius + 1)
    {
        return AGBlockType.Stone;
    }
    else
    {

            if (blockposition.x >= 0 && blockposition.x < AGworld.LvlkWidth &&
            blockposition.y >= 0 && blockposition.y < AGworld.LvlHeight + 2 &&
            blockposition.z >= 0 && blockposition.z < AGworld.LvlkWidth)
            {

                int index = blockposition.x + blockposition.y * AGworld.LvlkWidthSq + blockposition.z * AGworld.LvlkWidth;
                return Blockss[index];

            }
            else
            {

                return AGBlockType.Air;
            }

    }

}

private void GenRightSide(Vector3Int blockposition)
{
    //вершины квадрата
    verticies.Add(new Vector3(1, 0, 0) + blockposition);
    verticies.Add(new Vector3(1, 1, 0) + blockposition);
    verticies.Add(new Vector3(1, 0, 1) + blockposition);
    verticies.Add(new Vector3(1, 1, 1) + blockposition);

    AddLastVerticiesSquere();
}
private void GenLeftSide(Vector3Int blockposition)
{
    //вершины квадрата


    verticies.Add(new Vector3(0, 0, 0) + blockposition);
    verticies.Add(new Vector3(0, 0, 1) + blockposition);
    verticies.Add(new Vector3(0, 1, 0) + blockposition);
    verticies.Add(new Vector3(0, 1, 1) + blockposition);

    AddLastVerticiesSquere();
}
private void GenFrontSide(Vector3Int blockposition) //передняя
{
    //вершины квадрата
    verticies.Add(new Vector3(0, 0, 1) + blockposition);
    verticies.Add(new Vector3(1, 0, 1) + blockposition);
    verticies.Add(new Vector3(0, 1, 1) + blockposition);
    verticies.Add(new Vector3(1, 1, 1) + blockposition);

    AddLastVerticiesSquere();
}
private void GenBackSide(Vector3Int blockposition) //передняя
{
    //вершины квадрата

    verticies.Add(new Vector3(0, 0, 0) + blockposition);
    verticies.Add(new Vector3(0, 1, 0) + blockposition);
    verticies.Add(new Vector3(1, 0, 0) + blockposition);
    verticies.Add(new Vector3(1, 1, 0) + blockposition);

    AddLastVerticiesSquere();
}
private void GenTopSide(Vector3Int blockposition) //передняя
{
    //вершины квадрата

    verticies.Add(new Vector3(0, 1, 0) + blockposition);
    verticies.Add(new Vector3(0, 1, 1) + blockposition);
    verticies.Add(new Vector3(1, 1, 0) + blockposition);
    verticies.Add(new Vector3(1, 1, 1) + blockposition);

    AddLastVerticiesSquere();
}
private void GenBottomSide(Vector3Int blockposition) //передняя
{
    //вершины квадрата

    verticies.Add(new Vector3(0, 0, 0) + blockposition);
    verticies.Add(new Vector3(1, 0, 0) + blockposition);
    verticies.Add(new Vector3(0, 0, 1) + blockposition);
    verticies.Add(new Vector3(1, 0, 1) + blockposition);

    AddLastVerticiesSquere();
}
private void AddLastVerticiesSquere()
{



    //первый треугольник
    triangles.Add(verticies.Count - 4); //0
    triangles.Add(verticies.Count - 3); //1
    triangles.Add(verticies.Count - 2); //2

    //второй треугольник
    triangles.Add(verticies.Count - 3); //1
    triangles.Add(verticies.Count - 1); //3
    triangles.Add(verticies.Count - 2); //2

}




private void AddUvs(AGBlockType blockType)
{
    //uvs.Add(new Vector2(0, 0));
    //uvs.Add(new Vector2(0, 1));
    //uvs.Add(new Vector2(1, 0));
    //uvs.Add(new Vector2(1, 1));



    Blockinfo info = Blocks.FirstOrDefault(b => b.Type == blockType);
    if (info != null)
    {
        uvs.Add(new Vector2(info.PixelOffset.x / 64, info.PixelOffset.y / 64));
        uvs.Add(new Vector2(info.PixelOffset.x / 64, (info.PixelOffset.y + 16f) / 64));
        uvs.Add(new Vector2((info.PixelOffset.x + 16f) / 64, info.PixelOffset.y / 64));
        uvs.Add(new Vector2((info.PixelOffset.x + 16f) / 64, (info.PixelOffset.y + 16f) / 64));

    }
    else
    {
        uvs.Add(new Vector2(16f / 64, 48f / 64));//левая нижняя точка
        uvs.Add(new Vector2(16f / 64, 1));//левая верхняя точка
        uvs.Add(new Vector2(32f / 64, 48f / 64));//правая нижняя
        uvs.Add(new Vector2(32f / 64, 1));//правая верхняя
    }

}
}

Ответы

▲ 0Принят

Оптимизировать тут можно дофигище чего. Но поскольку у тебя абсолютно отвратительное и безалаберное форматирование, которое даже хуже нейминга, который скверно плох, а на комментарии нельзя полагаться, в виду того, что они нагло врут, причем практически ВСЕ, разобраться в этих авгиевых конюшнях затруднительно.

  • chunkMesh.triangles = new int[0]; зачем эта строчка? мусор!
  • //для коллайдеров и взаимодействия у Collider тоже есть Bounds, но MeshRenderer использует их явно не для столкновений, ему нужно знать попадает ли он в обзор камеры. Да и вообще для вексельного мира скорее всего экономнее будет использовать множество простых для вычислений BoxCollider для тех векселей, что имеют соседей, чем сложную полигональную структуру.
  • //для хорошего взвимодействия с освещением без нормалей у света вообще не будет информации, куда ему отражаться от полигона. Для воксельной структуры их не сложно проставить и руками. Для вертексов поверхности смотрящих в верх Vector3.up, вниз Vector3.down, в даль Vector.forward и т.д.
  • opt4 = Vector2.Distance(... дистанция это квадратный корень суммы квадратов осей, что в количестве тысяч, а уж тем более десятков тысяч, с лёгкостью даст тормоза. Зачем вообще считать дистанцию для мать его КАЖДОГО куба? В майнкрафте чанки 16х16 и если он прорисовывается то весь. А вообще люди обходятся сравнением квадратов расстояний не вычисляя квадратного корня Vector2.sqrMagnitude > radius * radius, в виду бесполезности этих трудоемких вычислений!
  • a = Mathf.FloorToInt(...x) папа не хотел, мама не старалась, поэтому назвали x и y вокселя a и b 🤦! Если генерация по сетке то округление по целому числу a = (int)x!
  • List<T> очень удобен, но на самом деле это T[] и при переполнении он создает новый в два раза длинее и все копирует в него. Когда длины на десятки тысяч это не круто. Если делать по уму, то без List и вообщето это не трудно, просто составить структуру данных о вокселях и генерировать по ней, когда заранее известно количество всего.
  • мусор...
  • повторение кода...
  • повторение кода...
  • повторение кода...
  • мусор...
  • повторение кода...
  • и т.д.

Не то что до оптимизированного, а просто до приемлемого, еще ОЧЕНЬ далеко!