使用ReaderWriterLockSlim进行多线程更新

时间:2013-10-18 11:30:33

标签: c# multithreading xna readerwriterlockslim

我认为当我第一次开始设计我的游戏并且会给你一些甚至所有代码时我犯了一个很大的错误,但它太复杂了。所以请耐心等待。

现在我进入了设计的更高阶段,我遇到了线程化的主要问题。

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

  • 更新需要进行所有物体的移动(包括物理)和动画。
  • HitTesting会在数千个对象之间进行所有Hit测试,但仍然需要进行大量优化....像Devide和征服这样的事情来做对。
  • AI向更新周期执行的对象发出命令。像左转或右转,火灾等等。

当然我们有

  • 画画......或者在我的情况下画画中的GetSprites。除了更新过程之外,这必须具有完全优先权。
  • 尚未实现的声音和信息系统。

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

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

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

  • 由于Update是我绘制数据的唯一作者。
  • 绘图是纯读者
  • 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; }
    }

2 个答案:

答案 0 :(得分:1)

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

执行

答案 1 :(得分:1)

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

1)按照类别(小行星,船只,子弹等)将四个精灵表分成几个小的精灵表。 MS总是说,几个较小的图像源比一个更大的图像源更好。

2)摆脱星场等背景图块。使用HLSL创建星场,爆炸和效果。对于那种任务,GPU性能接近“无限”,实际上是使用CUDA的良好替代方案。

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

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

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