我正在制作一个破碎脚本,其中有一些节点,每个节点都拥有与其他节点的任意数量的连接。
碎片和大块:
每个节点代表一个预先断裂的“块”的“碎片”。
一个分片损坏时,它与其他分片的所有连接及其与之连接的连接都将被删除。
发生这种情况时,我需要遍历碎片,找出哪些碎片连接在一起,以及是否将碎片分开以创建新的碎片。
最快的方法是什么?
目前,我有:
具有Shard类的
具有Chunk类的
public class Shard : MonoBehaviour
{
public GameObject parentChunk;
public List<GameObject> connectedShards = new List<GameObject>();
public void Break()
{
// remove all connections that point to this shard
foreach (GameObject shard in connectedShards)
{
// remove connection to this shard
shard.GetComponent<Shard>().connectedShards.Remove(gameObject);
}
// clear connected shards list
connectedShards.Clear();
// check that there is a parent chunk
if (parentChunk)
{
// force parent chunk to update
parentChunk.GetComponent<Chunk>().UpdateChunk();
// set parent chunk to null
parentChunk = null;
}
}
}
public class Chunk : MonoBehaviour
{
public List<GameObject> shards = new List<GameObject>();
private List<GameObject> unprocessedShards = new List<GameObject>();
public void UpdateChunk()
{
// if there is only one shard in this chunk
if (shards.Count == 1)
{
// break shard
shards[0].GetComponent<Shard>().parentChunk = null;
shards[0].GetComponent<Shard>().Break();
// destroy this chunk
Destroy(gameObject);
}
else // there are multiple shards in this chunk
{
// fill the unprocessed shards list
unprocessedShards = new List<GameObject>(shards);
// process all shards in this chunk recursively starting from the first
ProcessShard(unprocessedShards[0], gameObject);
// if there are still unprocessed shards (this chunk was split)
if (unprocessedShards.Count > 0)
{
// remove the unprocessed shards from this chunk's shard list
foreach (GameObject unprocessedShard in unprocessedShards)
{
shards.Remove(unprocessedShard);
}
// create new chunk(s) from the leftover shards
parentObject.GetComponent<Fragmenter>().NewChunk(unprocessedShards);
}
}
}
// process shard
private void ProcessShard(GameObject shard, GameObject chunk)
{
// remove shard from unprocessed list
unprocessedShards.Remove(shard);
// add shard to the given chunk
shard.GetComponent<Shard>().parentChunk = chunk;
// loop through all shards connected to the given shard
foreach (GameObject connectedShard in shard.GetComponent<Shard>().connectedShards)
{
// connected shard has not been processed yet
if (unprocessedShards.Contains(connectedShard))
{
// process all shards connected to the connected shard
ProcessShard(connectedShard, chunk);
}
}
}
}
我主要考虑的是ProcessShard()方法,该方法通常会多次处理每个分片,因为一个可以与多个分片相邻。如何最有效地忽略已处理的内容?
答案 0 :(得分:1)
首先,我建议您远离List.Remove
,因为在最坏的情况下,它需要检查每一项以找到要删除的项。可以从保留的未处理碎片列表中删除正在处理的碎片,而可以保留另一个已处理碎片列表并添加到该列表中。然后,检查连接的碎片是否未处理的条件将变为if(!processedShards.Contains(connectedShard))
。
您可以在microsoft docs上阅读有关List操作性能的更多信息,只需向下滚动至“备注”部分。
另一个改进是在检查是否已处理分片时找到List.Contains
的替代方法。与List.Remove
类似,它需要潜在地检查列表中的每个项目,然后才能判断该项目是否存在。
一种方法是使用HashSet而不是列表。它提供了访问特定项目以及检查项目中是否存在项目的更快方法。它还提供了许多相同的方法,因此重构应该轻而易举。
请注意,就性能而言,我个人不会为优化而烦恼。您可能会发现这些改进几乎使您无所适从,因为您的收藏还不够大。如果您还没有,请查看Unity profiler,然后从那里查看您的出色表现来源。
答案 1 :(得分:0)
在ProcessShard()方法的递归中,脚本似乎使速度最慢,因此我将其重命名并重写为ProcessShards(),该方法使用while循环和Queue处理所有分片。在具有100个分片的数据块上,性能提高了大约十倍。
// process shards
private void ProcessShards()
{
// create a list for shards to process
Queue<GameObject> shardsToProcess = new Queue<GameObject>();
// select the first shard to process
shardsToProcess.Enqueue(unprocessedShards[0]);
// remove shard from unprocessed list
unprocessedShards.Remove(unprocessedShards[0]);
// loop while there are shards to process
while (shardsToProcess.Count > 0)
{
// get the first shard to process
GameObject shard = shardsToProcess.Dequeue();
// add the shard to this chunk
shard.GetComponent<Shard>().parentChunk = gameObject;
// loop through all shards connected to the given shard
foreach (GameObject connectedShard in shard.GetComponent<Shard>().connectedShards)
{
// connected shard has not been processed yet
if (unprocessedShards.Contains(connectedShard))
{
// add shard to be processed
shardsToProcess.Enqueue(connectedShard);
// remove shard from unprocessed list
unprocessedShards.Remove(connectedShard);
}
}
}
}