我有一个程序景观。有瓦片(块),还有一个 Terrain 对象,当接近景观边缘时,由于生成瓦片时有强烈的 FPS 下降,因此通过 Coroutine 生成。如何解决?
左上角 x/y/z 和 fps 坐标。
GIF:https ://imgur.com/a/GCSFpI2
这是放置块的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class _Chunk
{
public GameObject theChunk;
public float creationTime;
public _Chunk(GameObject t, float ct)
{
theChunk = t;
creationTime = ct;
}
}
public class WorldProcedural : MonoBehaviour
{
public GameObject chunk;
public GameObject player;
public int ChunkSize = 16;
public int halfTilesX = 16;
public int halfTilesZ = 16;
Vector3 startPos;
Hashtable chunks = new Hashtable();
private void FixedUpdate()
{
halfTilesX = ESCManager.DrawDistance;
halfTilesZ = ESCManager.DrawDistance;
StartCoroutine(UpdateWorld());
}
private void Start()
{
player = GameObject.FindGameObjectWithTag("Player");
this.gameObject.transform.position = Vector3.zero;
startPos = Vector3.zero;
float updateTime = Time.realtimeSinceStartup;
for (int x = -halfTilesX; x < halfTilesX; x++)
{
for (int z = -halfTilesZ; z < halfTilesZ; z++)
{
Vector3 pos = new Vector3((x * ChunkSize + startPos.x), 0, (z * ChunkSize + startPos.z));
GameObject t = (GameObject)Instantiate(this.chunk, pos, Quaternion.identity);
t.transform.SetParent(transform);
string tilename = "Chunk_" + ((int)(pos.x)).ToString() +
"_" + ((int)(pos.z)).ToString();
t.name = tilename;
_Chunk chunk = new _Chunk(t, updateTime);
chunks.Add(tilename, chunk);
}
}
}
private IEnumerator UpdateWorld()
{
int xMove = (int)(player.transform.position.x - startPos.x);
int ZMove = (int)(player.transform.position.z - startPos.z);
if (Mathf.Abs(xMove) >= ChunkSize || Mathf.Abs(ZMove) >= ChunkSize)
{
float updateTime = Time.realtimeSinceStartup;
int playerX = (int)(Mathf.Floor(player.transform.position.x / ChunkSize) * ChunkSize);
int playerZ = (int)(Mathf.Floor(player.transform.position.z / ChunkSize) * ChunkSize);
for (int x = -halfTilesX; x < halfTilesX; x++)
{
for (int z = -halfTilesZ; z < halfTilesZ; z++)
{
Vector3 pos = new Vector3((x * ChunkSize + playerX), 0, (z * ChunkSize + playerZ));
string tilename = "Chunk_" + ((int)(pos.x)).ToString() +
"_" + ((int)(pos.z)).ToString();
if (!chunks.ContainsKey(tilename))
{
GameObject t = (GameObject)Instantiate(this.chunk, pos, Quaternion.identity);
t.transform.SetParent(transform);
t.name = tilename;
_Chunk chunk = new _Chunk(t, updateTime);
chunks.Add(tilename, chunk);
}
else
{
(chunks[tilename] as _Chunk).creationTime = updateTime;
}
}
yield return new WaitForSeconds(0.5f);
}
Hashtable newTerrain = new Hashtable();
foreach (_Chunk chk in chunks.Values)
{
if (chk.creationTime != updateTime)
{
Destroy(chk.theChunk);
}
else
{
newTerrain.Add(chk.theChunk.name, chk);
}
}
chunks = newTerrain;
startPos = player.transform.position;
}
}
}
最糟糕的是每次 FixedUpdate 你都会启动一个协程。虽然前一个甚至还没有工作到最后,但很可能您正在启动一个新的,依此类推。在协程中,您使用嵌套循环遍历循环中的所有图块,然后等待半秒钟,然后一切都会再次重复(很可能)。然后你会惊讶于一切都变慢了。
尝试每 n 次迭代调用一次 yield return null 。
这是第一个。第二,代替
这样做
不要在循环中对字符串执行此操作:
至少应该是这样的:string tilename = $"Chunk_{(int)pos.x}_{(int)pos.z}"; //或者使用string.Format..如果你在循环中的某个地方写,那么你应该一般使用stringbuilder。这样你就不会搞砸你的垃圾收集器。
最后,transform.SetParent() 和 Destroy() 也会给你很多 GC 调用。是否认为可以在循环中没有它。在对 Destroy 的 n 次调用之间,也可以跳过一帧。在这一点上,看看你需要什么“速度”来更新世界。