#c# #multithreading #unity3d #terrain
#c# #Многопоточность #unity3d #ландшафт
Вопрос:
Я очень новичок в концепции многопоточности, поэтому я все еще учусь и понятия не имею, можно ли считать то, что я делаю, ересью или нет. Прямо сейчас я разрабатываю игру в свободное время для развлечения в Unity, в основном для того, чтобы узнать больше о 3D-средах, и в нее я добавляю генерацию ландшафта. Я пытался добавить многопоточность, но не думаю, что сделал это правильно или эффективно, поскольку моя игра сильно заикается при загрузке фрагментов.
Вот как выглядит многопоточность в моем классе генерации ландшафта:
public class TerrainGenerator : MonoBehaviour {
// Other global variables
public GameObject angularWPPref;
private List<AngularWP> renderList;
private List<AngularWP> awps;
private bool threadRunning = false;
private Thread myThread;
void Start() {
renderList = new List<AngularWP>();
awps = new List<AngularWP>();
for (int x = 0; x < wpAmount.x; x ) {
for (int z = 0; z < wpAmount.y; z ) {
GameObject worldPiece = Instantiate(angularWPPref); // Create the game object for the chunk/worldPiece
worldPiece.GetComponent<AngularWP>().setPosition(new Vector3(x * worldPieceSize.x * voxelScalar, 0, z * worldPieceSize.z * voxelScalar));
// Set the position of the worldPiece
awps.Add(worldPiece.GetComponent<AngularWP>());
// Add the Angular World Piece class object from the game object to a list so the chunk data can be added to it in the other thread.
}
}
}
void Update() {
if (awps.Count > 0 amp;amp; !threadRunning) {
ThreadStart newThread = delegate {
generateVoxels(awps[0].position, awps[0]);
};
myThread = new Thread(newThread);
myThread.Start();
threadRunning = true;
}
// Start a new thread if there are more chunks to be loaded and myThread isn't running
if (renderList.Count > 0) {
renderList[0].setup(); // Initialize the mesh and add the material
renderList[0].refreshMesh(); // Clear the mesh
renderList[0].finalize(); // Add all vertices and tris to the mesh, recalculate normals, and add the mesh to the game object
renderList[0].initialPhysicsSetup(); // Set up the physics of the mesh
renderList[0].setPosition(new Vector3(renderList[0].position.x, 0, renderList[0].position.z)); // Set position again (because for some reason it was getting set in random places on the y-axis)
renderList.RemoveAt(0); // Remove the current world piece from the render list
}
}
public void generateVoxels(Vector3 position, AngularWP awp) {
awps.RemoveAt(0); // Remove the current world piece from the list
Thread.Sleep(1);
// Terrain Generation Code
awp.renderMesh(); // Create the mesh
renderList.Add(awp); // Add the world piece to the render list
threadRunning = false; // Tell the program that this thread is done running
}
}
Есть ли что-нибудь действительно заметное, что я сделал неправильно, чего я не вижу? Код работает правильно и правильно генерирует ландшафт, поэтому ошибок нет, просто он заикается.
Единственное, что, по моему мнению, вызывает проблемы, это тот факт, что я использую только 1 дополнительный поток для загрузки ландшафта, а не несколько, так что, возможно, он все еще замедляется из-за этого? Это не имеет большого смысла, исходя из моего понимания того, как работает многопоточность, но, возможно, я ошибаюсь.
Еще один момент, когда это может быть проблемой, — запуск потока или завершение рендеринга текущих фрагментов при использовании данных в основном потоке. Я мог видеть, что это вызывает проблемы, но я не уверен, как я мог бы это улучшить.
Я взглянул на систему заданий Unity, но не смог понять, как преобразовать свой код для работы с ним, и решил, что попытка научиться многопоточности может быть более полезной, поскольку я мог бы использовать навыки в других проектах, отличных от Unity, в будущем. Это то, к чему я должен вернуться и попробовать еще раз, или я могу добиться того же с помощью многопоточности?
Я действительно теряюсь, когда дело доходит до этого, поэтому любые предложения приветствуются.
Комментарии:
1. У меня нет специального навыка Unity, но у вас, похоже, есть одновременный доступ к awp и renderList. Являются ли эти контейнеры потокобезопасными? Некоторая отладочная распечатка текущего времени в миллисекундах внутри цикла обновления может дать вам подсказку, почему частота кадров неравномерна.
2. Спасибо! Я проверил документацию Microsoft и, по-видимому, List<> не является потокобезопасным, поэтому мне определенно придется поэкспериментировать. Я снова отвечу обновлениями о времени распечатки, возможно, это даст мне мое решение.
3. Я взглянул на свой код, и похоже, что фактический цикл рендеринга моей функции вызывал мое отставание, а не многопоточность. Что сбивает с толку, так это то, что я запускаю этот код во время выполнения всякий раз, когда пытаюсь добавить модификацию ландшафта, поэтому на самом деле не имеет особого смысла объяснять, почему это вызвало бы так много проблем сейчас. Я урезал то, что делает рендеринг, но он все еще довольно медленный. Может быть, есть какие-то проблемы с многопоточностью, которые я упустил из виду? Я все еще очень смущен тем, как все это работает и что я должен делать. Также не сработало использование потокобезопасных контейнеров, таких как ConcurrentQueue.
4. Если вы добавите достаточно отладочных распечаток, вы это поймете. Исходя из моего собственного опыта работы с многопоточной графикой (opengl), ваш подход с запуском нового потока при каждом обновлении кажется неправильным и имеющим поток. Sleep (1); также кажется неправильным. Вместо этого, возможно, вам следует иметь поток, работающий непрерывно на полной скорости, генерирующий ландшафт, а другой поток отображает результаты. При необходимости вы можете синхронизировать потоки с помощью барьеров learn.microsoft.com/en-us/dotnet/api /…
5. А, хорошо, спасибо. Я заметил значительное улучшение, добавив паузу между новыми потоками на 0,75 секунды после запуска предыдущего, но, возможно, мне следует попробовать запустить непрерывный поток. Я предполагаю, что в какой-то момент это должно будет закончиться, поскольку у меня закончатся данные для его прохождения, но я попытаюсь написать новую функцию генерации ландшафта, которая будет принимать все фрагменты вместо отдельных и перебирать их. Спасибо!