1

我想当我第一次开始设计我的游戏时我犯了一个巨大的错误,我会给你我的部分甚至全部代码,但它太复杂了。所以请多多包涵。

现在我进入了更高级的设计阶段,我在线程方面遇到了很大的麻烦。

在更新期间,我同时运行 3 个任务。更新、HitTesting 和 AI 甚至被拆分为更多线程。

  • 更新需要对我的所有对象进行移动(包括物理)和动画。
  • HitTesting 在数千个对象之间进行所有 Hit 测试,但仍需要大量优化……。诸如分而治之之类的事情是正确的。
  • AI 向更新周期执行的对象发出命令。像左转或右转,火灾等。

当然我们有

  • 绘制……或者在我的情况下,绘制期间的 GetSprites。除了更新过程之外,它必须具有所有优先级。
  • 还有尚未实现的声音和信息系统。

正如您所看到的,这不是多任务处理的最佳过程,因为它们都在相同的对象上运行,但它必须如此。

所以……我想实现 System.Threading.ReaderWriterLockSlim

这是我真正的问题:我该怎么做?

  • 因为更新是我绘制数据的唯一作者。
  • 绘图是一个纯粹的阅读器
  • HitTesting 可能需要重新计算 boundingRectangle 和 Matrix 但这不会影响绘图
  • AI 只需要读取位置数据并发出命令,在下一个周期由 Update 读取并被分隔在一组单独的类中(我称之为 master/puppet,但可能有一个官方/更好的名称)

实现不同的 ReaderWriterLockSlim 对象来锁定线程可能需要的不同属性是否有意义?

我想在更新期间进行控制以绕过锁定的对象(通过 AI 或 HitTesting)并获取下一个对象,以便稍后在解锁时进行操作(或者如果更新花费的时间太长,甚至可以跳过它,但要这样做在下一个周期)

你们中有人知道有关高级线程的书籍或网站吗?不是我到处都能找到的常见小例子,所以我可以弄清楚吗?

我已经被困了一个多星期了,我想继续。

任何帮助表示赞赏。

这是我用于对象之间的碰撞检测的代码。我从很久以前找到的 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

更新中的某些计算可能由 GPU 使用 CUDA 技术执行 ( https://developer.nvidia.com/gpu-computing-sdk )

于 2013-10-18T12:11:51.507 回答
1

我可以推荐几个步骤,希望其中一些有用:

1) 按类别(小行星、船舶、子弹等)将您的四个精灵表拆分为几个较小的精灵表。MS总是说,几个较小的图像源比一个巨大的要好。

2) 去掉星域等的背景图块。使用 HLSL 来创建星域、爆炸和效果。对于此类任务,GPU 性能接近“无限”,实际上是使用 CUDA 的不错选择。

3) 在独立任务上拆分碰撞检测过程:a) 活动单元之间 b) 活动单元和非活动环境之间(使用碰撞图)

在战略中,单位应该预先计算他们的路径。因此有必要检测路径交叉点以重新计算路径并防止碰撞。

所有碰撞都应仅在用户视口区域中重新计算。活动单位应该只检查自己附近的碰撞。

于 2013-10-18T17:30:03.433 回答