【问题标题】:Workaround for Texture2D.GetData methodTexture2D.GetData 方法的解决方法
【发布时间】:2013-11-28 21:31:40
【问题描述】:

我正在使用 Monogame 将游戏从 XNA 转换到 iOS。
在下面的代码 sn-p 中,smallDeform 是一个 Texture2D,我在其上调用了 GetData 方法。

smallDeform = Game.Content.Load<Texture2D>("Terrain/...");
smallDeform.GetData(smallDeformData, 0, smallDeform.Width * smallDeform.Height);

我在使用 Monogame 时遇到了一些问题,因为 iOS 中的功能还没有实现,因为它返回了这个异常。

#if IOS 
   throw new NotImplementedException();
#elif ANDROID

我尝试从 Windows 序列化 XML 文件中的数据以从 iOS 加载整个文件。序列化后的文件每个都超过 100MB,无法解析。

基本上,我正在寻找一种解决方法,以便在不使用 GetData 方法的情况下从纹理中获取数据(例如 uint[]Color[])。

PS:我在 Mac 上,所以我不能使用 Monogame SharpDX 库。

提前致谢。

【问题讨论】:

    标签: c# ios xna textures monogame


    【解决方案1】:

    序列化数据实际上是一个非常聪明的解决方法,但 XML 对于这项工作来说太繁重了。我要做的是使用定制但简单的二进制格式,以便您可以控制输出的大小。

    以下是我提出的几种应该可行的方法,但请注意,我专门制作它们是为了回答这个问题,并且它们尚未经过测试。

    保存数据..

        private void SaveTextureData(Texture2D texture, string filename)
        {
            int width = texture.Width;
            int height = texture.Height;
            Color[] data = new Color[width * height];
            texture.GetData<Color>(data, 0, data.Length);
    
            using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Create)))
            {
                writer.Write(width);
                writer.Write(height);
                writer.Write(data.Length);
    
                for (int i = 0; i < data.Length; i++)
                {
                    writer.Write(data[i].R);
                    writer.Write(data[i].G);
                    writer.Write(data[i].B);
                    writer.Write(data[i].A);
                }
            }
        }
    

    并加载数据..

        private Texture2D LoadTextureData(string filename)
        {
            using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open)))
            {                
                var width = reader.ReadInt32();
                var height = reader.ReadInt32();
                var length = reader.ReadInt32();
                var data = new Color[length];
    
                for (int i = 0; i < data.Length; i++)
                {
                    var r = reader.ReadByte();
                    var g = reader.ReadByte();
                    var b = reader.ReadByte();
                    var a = reader.ReadByte();
                    data[i] = new Color(r, g, b, a);
                }
    
                var texture = new Texture2D(GraphicsDevice, width, height);
                texture.SetData<Color>(data, 0, data.Length);
                return texture;
            }
        }
    

    二进制数据远小于 XML 数据。它与没有压缩的情况下一样小。请注意,二进制格式在更改时非常严格。如果您需要更改格式,在大多数情况下写出新文件会更容易。

    如果您需要对方法进行更改,请注意使它们保持同步。每个 Write 都应该以完全相同的顺序和数据类型与相同的 Read 匹配。

    我很想知道文件变小了多少。告诉我进展如何?

    【讨论】:

    • 是的,这是我尝试过的另一种方式,但不幸的是,问题是 Monogame 中似乎不存在 File 类。我应该找到另一种保存文件的方法。
    • 我找到了一个不同的解决方案,但 +1 为您提供帮助。
    • 我不明白,有 texture.GetData 调用,在 iOS 中没有实现。 @pinckerman 你能分享你的解决方案吗?我想不通。
    • @Kostkac 我的解决方案在另一个答案中
    【解决方案2】:

    如果有人有同样的问题,我会回答我自己的问题。

    按照 craftworkgame 的建议,我使用字节流保存了我的数据,但由于没有 File 类,我不得不使用 WinRT 函数:

    private async void SaveColorArray(string filename, Color[] array)
    {
        StorageFile sampleFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename + ".dat", CreationCollisionOption.ReplaceExisting);
        IRandomAccessStream writeStream = await sampleFile.OpenAsync(FileAccessMode.ReadWrite);
        IOutputStream outputSteam = writeStream.GetOutputStreamAt(0);
        DataWriter dataWriter = new DataWriter(outputSteam);
        for (int i = 0; i < array.Length; i++)
        {
            dataWriter.WriteByte(array[i].R);
            dataWriter.WriteByte(array[i].G);
            dataWriter.WriteByte(array[i].B);
            dataWriter.WriteByte(array[i].A);
        }
    
        await dataWriter.StoreAsync();
        await outputSteam.FlushAsync();
    }
    
    protected async Task<Color[]> LoadColorArray(string filename)
    {
        StorageFile sampleFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename + ".dat", CreationCollisionOption.OpenIfExists);
        IRandomAccessStream readStream = await sampleFile.OpenAsync(FileAccessMode.Read);
        IInputStream inputSteam = readStream.GetInputStreamAt(0);
        DataReader dataReader = new DataReader(inputSteam);
        await dataReader.LoadAsync((uint)readStream.Size);
    
        Color[] levelArray = new Color[dataReader.UnconsumedBufferLength / 4];
        int i = 0;
        while (dataReader.UnconsumedBufferLength > 0)
        {
            byte[] colorArray = new byte[4];
            dataReader.ReadBytes(colorArray);
            levelArray[i++] = new Color(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
        }
    
        return levelArray;
    }
    

    【讨论】:

      【解决方案3】:

      如果您愿意构建自己的 MonoGame 版本,您可以让 Texture2D.GetData 在 iOS 上运行。在此处查看修复:https://github.com/scottlerch/MonoGame/commit/24c1c3d5398f4ed9dbd9b60c0bbdbc096311632c)。

      来自关于同一问题的讨论:https://monogame.codeplex.com/discussions/442667

      已确认该修复适用于 iPad1 v5.1.1+ 和 iPhone4 v6.1+。

      【讨论】:

      • 很高兴知道,但几个月前它会更有用:)
      猜你喜欢
      • 1970-01-01
      • 2014-07-28
      • 2012-10-11
      • 1970-01-01
      • 1970-01-01
      • 2018-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多