私はある種の Geometry Wars にインスパイアされたゲームを書いていますが、追加された 2D 剛体物理学、Ai パスファインディング、いくつかのウェイポイント分析、視線チェック、ロード バランシングなどがあります。全部で 250 個 (敵 150 個) のオブジェクトに到達すると、パフォーマンスが完全に低下します。コード内の O(n^2) 部分を検索しましたが、残っていないようです。空間グリッドも使用しています。
おそらく高価なAi関連の処理からほとんどすべてを無効にしても、それは問題ではないようですが、それでも150人の敵で故障するようです.
現在、すべてのコードをゼロから実装しました。現在は行列乗算コードも含めて、ほぼ完全に GC に依存しているだけでなく、C# クロージャーを使用しているため、これは最適化にはほど遠いと予想されますが、それでも処理作業の 1/15 で、オブジェクトが 2 倍になると、ゲームが突然速度を落としてクロールし始めるのは、私には意味がありません。これは正常ですか? XNA プラットフォームは、処理されるオブジェクトの量に関する限り、通常どのようにスケーリングすることになっていますか?
私が最初にやったスラープ回転キューブのことは一度に1000以上を処理できたので、何か間違ったことをしていると思いますか?
編集:グリッド構造のクラスは次のとおりです
パブリック抽象クラス GridBase{
public const int WORLDHEIGHT = (int)AIGridInfo.height;
public const int WORLDWIDTH = (int)AIGridInfo.width;
protected float cellwidth;
protected float cellheight;
int no_of_col_types;
// a dictionary of lists that gets cleared every frame
// 3 (=no_of_col_types) groups of objects (enemy side, players side, neutral)
// 4000 initial Dictionary hash positions for each group
// I have also tried using an array of lists of 100*100 cells
//with pretty much identical results
protected Dictionary<CoordsInt, List<Collidable>>[] grid;
public GridBase(float cellwidth, float cellheight, int no_of_col_types)
{
this.no_of_col_types = no_of_col_types;
this.cellheight=cellheight;
this.cellwidth=cellwidth;
grid = new Dictionary<CoordsInt, List<Collidable>>[no_of_col_types];
for (int u = 0; u < no_of_col_types; u++)
grid[u] = new Dictionary<CoordsInt, List<Collidable>>(4000);
}
public abstract void InsertCollidable(Collidable c);
public abstract void InsertCollidable(Grid_AI_Placeable aic);
//gets called in the update loop
public void Clear()
{
for (int u = 0; u < no_of_col_types; u++)
grid[u].Clear();
}
//gets the grid cell of the left down corner
protected void BaseCell(Vector3 v, out int gx, out int gy)
{
gx = (int)((v.X + (WORLDWIDTH / 2)) / cellwidth);
gy = (int)((v.Y + (WORLDHEIGHT / 2)) / cellheight);
}
//gets all cells covered by the AABB
protected void Extent(Vector3 pos, float aabb_width, float aabb_height, out int totalx, out int totaly)
{
var xpos = pos.X + (WORLDWIDTH / 2);
var ypos = pos.Y + (WORLDHEIGHT / 2);
totalx = -(int)((xpos / cellwidth)) + (int)((xpos + aabb_width) / cellwidth) + 1;
totaly = -(int)((ypos / cellheight)) + (int)((ypos + aabb_height) / cellheight) + 1;
}
}
public class GridBaseImpl1 : GridBase{
public GridBaseImpl1(float widthx, float widthy)
: base(widthx, widthy, 3)
{
}
//adds a collidable to the grid /
//caches for intersection test
//checks if it should be tested to prevent penetration /
//tests penetration
//updates close, intersecting, touching lists
//Collidable is an interface for all objects that can be tested geometrically
//the dictionary is indexed by some simple struct that wraps the row and column number in the grid
public override void InsertCollidable(Collidable c)
{
//some tag so that objects don't get checked more than once
Grid_Query_Counter.current++;
//the AABB is allocated in the heap
var aabb = c.CollisionAABB;
if (aabb == null) return;
int gx, gy, totalxcells, totalycells;
BaseCell(aabb.Position, out gx, out gy);
Extent(aabb.Position, aabb.widthx, aabb.widthy, out totalxcells, out totalycells);
//gets which groups to test this object with in an IEnumerable (from a statically created array)
var groupstestedagainst = CollidableCalls.GetListPrevent(c.CollisionType).Select(u => CollidableCalls.group[u]);
var groups_tested_against = groupstestedagainst.Distinct();
var own_group = CollidableCalls.group[c.CollisionType];
foreach (var list in groups_tested_against)
for (int i = -1; i < totalxcells + 1; i++)
for (int j = -1; j < totalycells + 1; j++)
{
var index = new CoordsInt((short)(gx + i), (short)(gy + j));
if (grid[list].ContainsKey(index))
foreach (var other in grid[list][index])
{
if (Grid_Query_Counter.Check(other.Tag))
{
//marks the pair as close, I've tried only keeping the 20 closest but it's still slow
other.Close.Add(c);
c.Close.Add(other);
//caches the pair it so that checking if the pair intersects doesn't go through the grid //structure loop again
c.CachedIntersections.Add(other);
var collision_function_table_id = c.CollisionType * CollidableCalls.size + other.CollisionType;
//gets the function to use on the pair for testing penetration
//the function is in a delegate array statically created to simulate multiple dispatch
//the function decides what coarse test to use until descending to some complete //geometric query
var prevent_delegate = CollidableCalls.preventfunctions[collision_function_table_id];
if (prevent_delegate == null) { Grid_Query_Counter.Put(other.Tag); continue; }
var a = CollidableCalls.preventfunctions[collision_function_table_id](c, other);
//if the query returns true mark as touching
if (a) { c.Contacted.Add(other); other.Contacted.Add(c); }
//marks it as tested in this query
Grid_Query_Counter.Put(other.Tag);
}
}
}
//adds it to the grid if the key doesn't exist it creates the list first
for (int i = -1; i < totalxcells + 1; i++)
for (int j = -1; j < totalycells + 1; j++)
{
var index = new CoordsInt((short)(gx + i), (short)(gy + j));
if (!grid[own_group].ContainsKey(index)) grid[own_group][index] = new List<Collidable>();
grid[own_group][index].Add(c);
}
}
[...]
}