В настоящее время я разрабатываю 2D-игру на C # / XNA. Ключевая особенность игры - пули с совершенно разным поведением (это будет своего рода игра с пулями). Обновление всех пуль может занять некоторое время, поскольку их поведение может быть бесконечно сложным. И все они должны выполнить одну проверку на столкновение. Изначально я просто хранил их в списке, обновлял и рисовал все, удаляя неактивные маркеры из списка в каждом кадре. Это, однако, быстро замедлило игру, когда на экране было 8k маркеров, поэтому я решил реализовать многопоточность и использовать LINQ для повышения производительности.
Дело в том, что он все еще замедляется на отметке 16к пуль. Мне сказали, что я могу получить до 7 МИЛЛИОНОВ активных пуль, если я все сделаю правильно, поэтому меня не устраивает 16k ...
Есть ли что-нибудь еще, что я могу сделать, чтобы улучшить производительность здесь?
Дополнительная информация ранее код: Мои пули имеют поля для скорости, направления, угловой скорости, ускорения, ограничения скорости и поведения. Единственная особенность, о которой упоминалось, - это поведение. Он может изменять любое из полей маркеров в любое время или порождать больше пуль и даже встраиваться в них, поэтому мне сложно применить решение на основе данных и просто сохранить все эти поля в массивах вместо того, чтобы иметь список пули.
internal class BulletManager : GameComponent
{
public static float CurrentDrawDepth = .82f;
private readonly List<Bullet> _bullets = new List<Bullet>();
private readonly int _processorCount;
private int _counter;
private readonly Task[] _tasks;
public BulletManager(Game game)
: base(game)
{
_processorCount = VariableProvider.ProcessorCount;
_tasks = new Task[_processorCount];
}
public void ClearAllBullets()
{
_bullets.Clear();
}
public void AddBullet(Bullet bullet)
{
_bullets.Add(bullet);
}
public override void Update(GameTime gameTime)
{
if (StateManager.GameState != GameStates.Ingame &&
(StateManager.GameState != GameStates.Editor || EngineStates.GameStates != EEngineStates.Running))
return;
var bulletCount = _bullets.Count;
var bulletsToProcess = bulletCount / _processorCount;
//Split up the bullets to update among all available cores using Tasks and a lambda expression
for (var i = 0; i < _processorCount; ++i )
{
var x = i;
_tasks[i] = Task.Factory.StartNew( () =>
{
for(var j = bulletsToProcess * x; j < bulletsToProcess * x + bulletsToProcess; ++j)
{
if (_bullets[j].Active)
_bullets[j].Update();
}
});
}
//Update the remaining bullets (if any)
for (var i = bulletsToProcess * _processorCount; i < bulletCount; ++i)
{
if (_bullets[i].Active)
_bullets[i].Update();
}
//Wait for all tasks to finish
Task.WaitAll(_tasks);
//This is an attempt to reduce the load per frame, originally _bullets.RemoveAll(s => !s.Active) ran every frame.
++_counter;
if (_counter != 300) return;
_counter = 0;
_bullets.RemoveAll(s => !s.Active);
}
public void Draw(SpriteBatch spriteBatch)
{
if (StateManager.GameState != GameStates.Ingame && StateManager.GameState != GameStates.Editor) return;
spriteBatch.DrawString(FontProvider.GetFont("Mono14"), _bullets.Count.ToString(), new Vector2(100, 20),
Color.White);
//Using some LINQ to only draw bullets in the viewport
foreach (var bullet in _bullets.Where(bullet => Camera.ViewPort.Contains(bullet.CircleCollisionCenter.ToPoint())))
{
bullet.Draw(spriteBatch);
CurrentDrawDepth -= .82e-5f;
}
CurrentDrawDepth = .82f;
}
}