Monogame - iOS上的每像素碰撞

时间:2017-03-07 20:20:22

标签: c# ios debugging xamarin.ios monogame

首先,我想说,我正在使用此代码进行每像素碰撞检测:

    public bool CollidesWith(Sprite other)
    {
        // Default behavior uses per-pixel collision detection
        return CollidesWith(other, true);
    }

    public bool CollidesWith(Sprite other, bool calcPerPixel)
    {
        // Get dimensions of texture
        int widthOther = other.Texture.Width;
        int heightOther = other.Texture.Height;
        int widthMe = Texture.Width;
        int heightMe = Texture.Height;

        if (calcPerPixel &&                                // if we need per pixel
            ((Math.Min(widthOther, heightOther) > 10) ||  // at least avoid doing it
            (Math.Min(widthMe, heightMe) > 10)))          // for small sizes (nobody will notice :P)
        {
            return Rectangle.Intersects(other.Rectangle) // If simple intersection fails, don't even bother with per-pixel
                && PerPixelCollision(this, other);
        }

        return Rectangle.Intersects(other.Rectangle);
    }

    public bool PerPixelCollision(Sprite a, Sprite b)
    {
        // Get Color data of each Texture
        Color[] bitsA = new Color[a.Texture.Width * a.Texture.Height];
        a.Texture.GetData(bitsA);
        Color[] bitsB = new Color[b.Texture.Width * b.Texture.Height];
        b.Texture.GetData(bitsB);

        // Calculate the intersecting rectangle
        int x1 = Math.Max(a.Rectangle.X, b.Rectangle.X);
        int x2 = Math.Min(a.Rectangle.X + a.Rectangle.Width, b.Rectangle.X + b.Rectangle.Width);

        int y1 = Math.Max(a.Rectangle.Y, b.Rectangle.Y);
        int y2 = Math.Min(a.Rectangle.Y + a.Rectangle.Height, b.Rectangle.Y + b.Rectangle.Height);

        // For each single pixel in the intersecting rectangle
        for (int y = y1; y < y2; ++y)
        {
            for (int x = x1; x < x2; ++x)
            {
                // Get the color from each texture
                Color a1 = bitsA[(x - a.Rectangle.X) + (y - a.Rectangle.Y) * a.Texture.Width];
                Color b1 = bitsB[(x - b.Rectangle.X) + (y - b.Rectangle.Y) * b.Texture.Width];

                if (a1.A != 0 && b1.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
                {
                    return true;
                }
            }
        }
        // If no collision occurred by now, we're clear.
        return false;
    }

这段代码在我的“Sprite”结构中。它适用于Windows,OpenGL(MonoGame跨平台桌面项目),Android和Windows UWP。

但是在iOS上我调用Sprite.CollidesWith(...);我的程序停止渲染屏幕。一切正常但它无法渲染新的帧(它呈现静态图像)。 (我添加了单击声音以测试程序是否崩溃。(点击屏幕后冻结,然后我就能听到声音了))

问题出在这一行:

a.Texture.GetData(bitsA);

我搜索了网页,发现Texture2D.GetData&lt;&gt;在iOS上没有实现... soo,有没有可能在iOS上进行像素完美的碰撞检测? (不调用GetData&lt;&gt;)

对不起任何错误,我是这个论坛的新手。 并感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

  

我搜索了网页,发现Texture2D.GetData&lt;&gt;没有在iOS上实现...

所以这就是事情。 Texture2D.GetData并非旨在以您使用它的方式使用。实际上它的作用是在纹理已经发送到图形卡之后读取GPU 的纹理数据 out 。如果你能避免的话,这不是你想要做的事情。

如果你不相信我,请阅读this thread on the MonoGame forums。这是来自该线程的引用,很好地总结了它。

  

我们需要在文档中加入大写字母。不要每帧调用GetData()。如果可能的话,完全避免它GPU设计用于进入数据,而不是反过来。

在大多数平台上,这是一个相当慢的操作。在某些平台上,正如您所发现的那样,它甚至都不可能。

  

soo,有没有可能在iOS上进行像素完美的碰撞检测? (不调用GetData&lt;&gt;)

所以这里真正的问题是你如何避免使用GetData。正如评论中所提到的那样,如果你想要快速和肮脏的东西,你可以使用非常合理的workaround

  

保存或阅读文件可能会影响游戏性能。

当你已经通过逐像素碰撞杀死渲染性能时,我觉得你有点讽刺的是你担心游戏性能。

事实上,由于性能原因,大多数游戏都没有进行逐像素碰撞检测。如果您使用更简单的碰撞形状(如矩形和圆形),那几乎肯定不会让玩家注意到。我认为如果你真的需要它们是值得考虑的。

我不知道你正在制作什么样的游戏。有些游戏确实需要按像素碰撞,所以如果确实如此,我会告诉你如何处理它的想法。

我认为进行逐像素碰撞的最佳方法是将纹理数据存储为某种二进制格式。但更重要的是,理想情况下,您需要通过MonoGame内容管道或其他方式预处理数据,以便您不会在运行时从慢速源读取数据。这样的方法可以为您提供两全其美的优势(在游戏启动时快速加载以及快速渲染)。

这是一项很多工作,但可能需要考虑一下。