Monogame - iOS 上的每像素碰撞

Monogame - Per Pixel Collision on iOS

首先,我想说,我使用这段代码进行逐像素碰撞检测:

    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<> 没有在 iOS 上实现...所以,有没有可能在 [=29= 上进行像素完美的碰撞检测? ]? (不调用 GetData<>)

对于任何错误,我很抱歉,我是这个论坛的新手。 并感谢您的帮助。

I searched the web and i found that Texture2D.GetData<> is not implemented on iOS...

事情是这样的。 Texture2D.GetData 不打算按照您使用它的方式使用。它实际上做的是在纹理已经发送到显卡后,从GPU out读回纹理数据。如果可以避免,那不是你想做的事情。

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

We need to put this in big letters in the documentation. Don't call GetData() every frame. Avoid it entirely if possible. GPUs are designed for data to go into them, not back the other way.

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


soo, is there any possibility to make pixel-perfect collision detection on iOS? (without calling GetData<>)

所以这里真正的问题是如何完全避免使用 GetData。正如评论中所提到的,如果您想要快速而肮脏的东西,已经有一个非常合理的 workaround 可以使用。

Saving or reading files may affect game performance.

我发现有点讽刺的是,当您已经通过逐像素碰撞破坏了渲染性能时,您还担心游戏性能。

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

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

我认为进行逐像素碰撞的最佳方法是以某种二进制格式存储纹理数据。但除此之外,理想情况下,您希望通过 MonoGame 内容管道或其他方式预处理数据,这样您就不会在运行时从慢速源读取数据。像这样的方法可以让您两全其美(在游戏启动时快速加载和快速渲染)。

工作量很大,但可能是值得考虑的事情。