1

最初にゲームのデザインを始めたときに大きな間違いを犯したと思います。コードの一部またはすべてを提供することもできますが、複雑すぎます。だから我慢してください。

デザインのより高度な段階に到達した今、スレッドに関する大きな問題に直面しています。

更新中、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; }
    }
4

2 に答える 2

1

Update の一部の計算は、CUDA テクノロジ ( https://developer.nvidia.com/gpu-computing-sdk )を使用して GPU によって実行される場合があります。

于 2013-10-18T12:11:51.507 に答える
1

いくつかの手順をお勧めします。そのうちのいくつかが役立つことを願っています。

1) 4 つのスプライトシートをカテゴリ (小惑星、船、弾丸など) ごとにいくつかの小さなスプライトシートに分割します。MS は常に、複数の小さな画像ソースは 1 つの巨大な画像ソースよりも優れていると言っています。

2) スター フィールドなどの背景タイルを取り除きます。HLSL を使用して、スター フィールド、爆発、および効果を作成します。GPU のパフォーマンスは、この種のタスクではほぼ「無制限」であり、実際には CUDA を使用する代わりに適しています。

3) 独立したタスクでの衝突検出プロセスの分割: a) アクティブなユニット間 b) アクティブなユニットと非アクティブな環境の間 (衝突マップを使用)

戦略では、ユニットはパスを事前に計算する必要があります。そのため、パスの交差を検出してパスを再計算し、衝突を防ぐ必要があります。

すべての衝突は、ユーザーのビューポート ゾーンでのみ再計算する必要があります。アクティブなユニットは、自分自身の近くでのみ衝突をチェックする必要があります。

于 2013-10-18T17:30:03.433 に答える