最初にゲームのデザインを始めたときに大きな間違いを犯したと思います。コードの一部またはすべてを提供することもできますが、複雑すぎます。だから我慢してください。
デザインのより高度な段階に到達した今、スレッドに関する大きな問題に直面しています。
更新中、3 つのタスクを同時に実行します。Update、HitTesting、AI はさらに多くのスレッドに分割されています。
- 更新では、すべてのオブジェクトの動き (物理を含む) とアニメーションを実行する必要があります。
- HitTesting は、何千ものオブジェクト間ですべてのヒット テストを実行しますが、まだ多くの最適化が必要です…. 分断と征服のようなものは正しくなります。
- AI は、更新サイクルによって実行されるオブジェクトにコマンドを発行します。左折、右折、火事など。
そしてもちろん、私たちは持っています
- draw… または私の場合は、描画中に GetSprites を実行します。これは、更新プロセス以外のすべてよりも完全に優先する必要があります。
- そして、まだ実装されていないサウンドとメッセージ システム。
お分かりのように、これはマルチタスキングにとって最適なプロセスではありません。それらはすべて同じオブジェクトで機能するためですが、そうする必要があります。
だから… System.Threading.ReaderWriterLockSlim を実装することを考えました
そして、これが私の本当の質問です。どうすればそれを行うことができますか?
- Update が私の描画データへの唯一のライターであるためです。
- Drawing は純粋な Reader です
- HitTesting は、boundingRectangle と Matrix を再計算する必要があるかもしれませんが、描画には影響しません
- AI は位置データを読み取ってコマンドを発行するだけで済み、次のサイクルで Update によって読み取られ、個別のクラスのグループに分けられます (マスター/パペットと呼んでいますが、おそらく正式な/より適切な名前が付けられています)。
スレッドが必要とするさまざまなプロパティをロックするために、さまざまな ReaderWriterLockSlim オブジェクトを実装することは理にかなっていますか?
更新中にロックされたオブジェクトをバイパスして (AI または HitTesting のいずれかによって) 制御し、次のオブジェクトを取得して、後でロックが解除されたときにそれを実行できるようにしたい (または、更新に時間がかかりすぎる場合はスキップすることもできますが、実行します)次のサイクルで)
高度なスレッド化に関する本やサイトを知っている人はいますか? どこにでもある通常の小さな例ではないので、これを理解できますか?
1 週間以上立ち往生しているので、先に進みたいと思います。
どんな助けでも感謝します。
これは、オブジェクト間の衝突検出に使用するコードです。ずいぶん前に見つけた C++ の例から変換しましたが、場所を思い出せません。
public abstract class HitTestInfo : Object
{
static protected Random RND = new Random();
static protected Dictionary<String, Color[]> m_TextureDataDictionary;
public static Matrix GetMatrix(iSpriteInfo vDrawObject)
{
Matrix Transform =
Matrix.CreateTranslation(new Vector3(-vDrawObject.Origin, 0.0f)) *
Matrix.CreateScale(vDrawObject.Scale) *
Matrix.CreateRotationZ(vDrawObject.Angle) *
Matrix.CreateTranslation(new Vector3(vDrawObject.X, vDrawObject.Y, 0.0f));
return Transform;
}
/// <summary>
/// Calculates an axis aligned rectangle which fully contains an arbitrarily
/// transformed axis aligned rectangle.
/// </summary>
/// <param name="rectangle">Original bounding rectangle.</param>
/// <param name="transform">World transform of the rectangle.</param>
/// <returns>A new rectangle which contains the trasnformed rectangle.</returns>
public static Rectangle CalculateBoundingRectangle(Rectangle vrectangle,
Matrix transform)
{
Rectangle rectangle = vrectangle;
rectangle.X = 0;
rectangle.Y = 0;
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return that as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
/// <summary>
/// Determines if there is overlap of the non-transparent pixels between two
/// sprites.
/// </summary>
/// <param name="transformA">World transform of the first sprite.</param>
/// <param name="widthA">Width of the first sprite's texture.</param>
/// <param name="heightA">Height of the first sprite's texture.</param>
/// <param name="dataA">Pixel color data of the first sprite.</param>
/// <param name="transformB">World transform of the second sprite.</param>
/// <param name="widthB">Width of the second sprite's texture.</param>
/// <param name="heightB">Height of the second sprite's texture.</param>
/// <param name="dataB">Pixel color data of the second sprite.</param>
/// <returns>True if non-transparent pixels overlap; false otherwise</returns>
public static bool IntersectPixels(
Matrix transformA, int widthA, int heightA, Color[] dataA,
Matrix transformB, int widthB, int heightB, Color[] dataB)
{
// Calculate a matrix which transforms from A's local space into
// world space and then into B's local space
Matrix transformAToB = transformA * Matrix.Invert(transformB);
// When a point moves in A's local space, it moves in B's local space with a
// fixed direction and distance proportional to the movement in A.
// This algorithm steps through A one pixel at a time along A's X and Y axes
// Calculate the analogous steps in B:
Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);
// Calculate the top left corner of A in B's local space
// This variable will be reused to keep track of the start of each row
Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);
// For each row of pixels in A
for (int yA = 0; yA < heightA; yA++)
{
// Start at the beginning of the row
Vector2 posInB = yPosInB;
// For each pixel in this row
for (int xA = 0; xA < widthA; xA++)
{
// Round to the nearest pixel
int xB = (int)Math.Round(posInB.X);
int yB = (int)Math.Round(posInB.Y);
// If the pixel lies within the bounds of B
if (0 <= xB && xB < widthB &&
0 <= yB && yB < heightB)
{
// Get the colors of the overlapping pixels
Color colorA = dataA[xA + yA * widthA];
Color colorB = dataB[xB + yB * widthB];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
// Move to the next pixel in the row
posInB += stepX;
}
// Move to the next row
yPosInB += stepY;
}
// No intersection found
return false;
}
public static List<CollisionData> CollisionCheck<T1, T2>(List<T1> List1, List<T2> List2)
{
List<CollisionData> RetList = new List<CollisionData>();
foreach (T1 obj1 in List1)
{
iSpriteInfo SI1 = obj1 as iSpriteInfo;
if (SI1 != null)
{
Matrix Matrix1 = SI1.Matrix;
Rectangle Rect1 = SI1.BoundingRectangle;
Color[] TextureData1 = SI1.TextureData;
foreach (T2 obj2 in List2)
{
iSpriteInfo SI2 = obj2 as iSpriteInfo;
if (SI1 != null)
{
Matrix Matrix2 = SI2.Matrix;
Rectangle Rect2 = SI2.BoundingRectangle;
Color[] TextureData2 = SI2.TextureData;
// The per-pixel check is expensive, so check the bounding rectangles
// first to prevent testing pixels when collisions are impossible.
if (Rect1.Intersects(Rect2))
{
// Check collision with Player and planets
if (IntersectPixels(Matrix1, (int)SI1.DestinationRectangle.Width,
(int)SI1.DestinationRectangle.Height, TextureData1,
Matrix2, (int)SI2.DestinationRectangle.Width,
(int)SI2.DestinationRectangle.Height, TextureData2))
{
RetList.Add(new CollisionData(SI1, SI2));
}
}
}
}
}
}
return RetList;
}
public static List<CollisionData> CollisionCheck<T1, T2>(T1 Obj1, List<T2> List2)
{
List<CollisionData> RetList = new List<CollisionData>();
lock (Obj1)
{
lock (List2)
{
iSpriteInfo SI1 = Obj1 as iSpriteInfo;
if (SI1 != null)
{
Matrix Matrix1 = SI1.Matrix;
Rectangle Rect1 = SI1.BoundingRectangle;
Color[] TextureData1 = SI1.TextureData;
foreach (T2 obj2 in List2)
{
iSpriteInfo SI2 = obj2 as iSpriteInfo;
if (SI1 != null)
{
Matrix Matrix2 = SI2.Matrix;
Rectangle Rect2 = SI2.BoundingRectangle;
Color[] TextureData2 = SI2.TextureData;
// The per-pixel check is expensive, so check the bounding rectangles
// first to prevent testing pixels when collisions are impossible.
if (Rect1.Intersects(Rect2))
{
// Check collision with Player and planets
if (IntersectPixels(Matrix1, (int)SI1.DestinationRectangle.Width,
(int)SI1.DestinationRectangle.Height, TextureData1,
Matrix2, (int)SI2.DestinationRectangle.Width,
(int)SI2.DestinationRectangle.Height, TextureData2))
{
RetList.Add(new CollisionData(SI1, SI2));
}
}
}
}
}
}
}
return RetList;
}
public static bool CollisionCheck<T1, T2>(T1 Obj1, T2 Obj2)
{
Matrix Matrix1;
Rectangle Rect1;
Color[] TextureData1;
Matrix Matrix2;
Rectangle Rect2;
Color[] TextureData2;
iSpriteInfo SI1 = Obj1 as iSpriteInfo;
if (SI1 != null)
{
lock (SI1)
{
Matrix1 = SI1.Matrix;
Rect1 = SI1.BoundingRectangle;
TextureData1 = SI1.TextureData;
}
iSpriteInfo SI2 = Obj2 as iSpriteInfo;
if (SI1 != null)
{
lock (SI2)
{
Matrix2 = SI2.Matrix;
Rect2 = SI2.BoundingRectangle;
TextureData2 = SI2.TextureData;
}
// The per-pixel check is expensive, so check the bounding rectangles
// first to prevent testing pixels when collisions are impossible.
if (Rect1.Intersects(Rect2))
{
// Check collision with Player and planets
if (IntersectPixels(Matrix1, (int)SI1.DestinationRectangle.Width,
(int)SI1.DestinationRectangle.Height, TextureData1,
Matrix2, (int)SI2.DestinationRectangle.Width,
(int)SI2.DestinationRectangle.Height, TextureData2))
{
return true;
}
}
}
}
return false;
}
}
iSpriteInfo は次のように定義されています
public interface iSpriteInfo
{
float X { get; set; }
float Y { get; set; }
float Angle { get; set; }
Vector2 Origin { get; set; }
float Scale { get; set; }
float Depth { get; set; }
Color Color { get; set; }
Boolean Visible { get; set; }
Rectangle SourceRectangle { get; set; }
Rectangle DestinationRectangle { get; set; }
Rectangle BoundingRectangle { get; }
Matrix Matrix { get; }
SpriteSheet SpriteSheet { get; set; }
int SpriteSheetNum { get;}
Color[] TextureData { get; set; }
Vector2 GetVector2 { get; }
Vector3 GetVector3 { get; }
}