【问题标题】:Simple binary reader-writer code not working简单的二进制读写器代码不起作用
【发布时间】:2015-06-20 23:51:58
【问题描述】:

MS VS 2010,XNA 4.0
所以,我有一个类 Planet,它具有保存和加载功能。

public void SaveToFile(ContentManager content)
    {
        string path = content.RootDirectory + @"\Objects\Planets\" + this.name +@".planet"; 
        using (BinaryWriter bw = new BinaryWriter(File.Open(path, FileMode.Create)))
        {
            bw.Write(name);

            //sprite names
            bw.Write(planet.Sprite.Name); //"planet" is an animation.
            bw.Write(planet.HorFrames);   //and thats why it has a sprite
            bw.Write(planet.VerFrames);   //that has a name
            bw.Write((double)planet.FPS);

            bw.Write(asteroid1.Name);
            bw.Write(asteroid2.Name);
            bw.Write(asteroid3.Name);
            bw.Write(backgroundA.Name);

            //update related
            bw.Write((double)RSpeed);
            bw.Write((double)ASVariety);
            bw.Write((double)A1Chance);
            bw.Write((double)A2Chance);
            bw.Write((double)A3Chance);
            bw.Close();
        }
    }

public void LoadFromFile(ContentManager content, string name)
    {
        string path = content.RootDirectory + @"\Objects\Planets\" + name + @".planet";
        using (BinaryReader br = new BinaryReader(File.OpenRead(path)))
        {
            this.name = br.ReadString();
            this.planet = new Animation(Cont.Texture2D(br.ReadString()), br.ReadInt32(), br.ReadInt32(), (float)br.ReadDecimal(), true);
            this.asteroid1 = Cont.Texture2D(br.ReadString());
            this.asteroid2 = Cont.Texture2D(br.ReadString());
            this.asteroid3 = Cont.Texture2D(br.ReadString());
            this.backgroundA = Cont.Texture2D(br.ReadString());
            this.RSpeed = (float)br.ReadDouble();
            this.ASVariety = (float)br.ReadDouble();
            this.A1Chance = (float)br.ReadDouble();
            this.A2Chance = (float)br.ReadDouble();
            this.A3Chance = (float)br.ReadDouble();

            bposB = new Vector2(backgroundA.Width - 1, 0);
            bspd = new Vector2(-RSpeed / 8f, 0);

            pEngine1 = new ParticleEngine(MSTime, 40, new Vector2(20, -30), new Vector2(2, 1), asteroid1, new Color(100, 100, 100));
            pEngine1.Follow(new Vector2(-200, Game1.window_height / 2));

            pEngine2 = new ParticleEngine(MSTime * 3, 40, new Vector2(20, -30), new Vector2(2, 1), asteroid2, new Color(100, 100, 100));
            pEngine2.Follow(new Vector2(-200, Game1.window_height / 2));

            br.Close();
        }

这很容易理解,不是吗?
现在,当我尝试为行星动画加载精灵时,我收到一个错误,上面写着:
"值不能为空。
参数名称:assetName"
这发生在生产线上
this.planet = new Animation(Cont.Texture2D(br.ReadString()), br.ReadInt32(), br.ReadInt32(), (float)br.ReadDecimal(), true);
Cont.Texture2D 是一个静态函数,它返回一个 Texture2D 女巫名称等于它加载的路径,它看起来像这样:

public static Texture2D Texture2D(string path)
    {
        Texture2D sprite = content.Load<Texture2D>(path);
        sprite.Name = path;
        return sprite;
    }

所以,当我保存它时,它会保存正确的路径。测试函数如下所示:

private void testFunction()
    {
        /*
        p = new Planet("Mars", 
            new Animation(Cont.Texture2D("Sprites\\Planets\\mars"), 8, 2, 0.9f, true),
            Cont.Texture2D("Sprites\\Backgrounds\\space"),
            Cont.Texture2D("Sprites\\Asteroids\\small\\small 1"),
            Cont.Texture2D("Sprites\\Asteroids\\medium\\medium 1"),
            Cont.Texture2D("Sprites\\Asteroids\\big\\big 1"),
            100, 50, 40, 20, 40, 40);
        p.SaveToFile(Content);
        */
        p = new Planet(Content, "Mars");
        tests = p.ToString();
    }

"tests" 只是一个测试字符串。现在,我首先执行注释代码,以便保存文件,然后执行未注释代码。这个行星构造函数只调用 LoadFromFile 函数,仅此而已。另外,我的项目内容中有保存的文件。我确实说过将该文件视为内容(不要编译它)。因此,代码“看到”了文件,这就是舒尔,但它无法在从 mars.planet 读取的路径上找到 .png。如果我一个接一个地存储 2 个字符串,然后读者看不到第一个字符串的结尾在哪里,可能会有错误吗?我可能保存错了吗?

我的目标是拥有将被加载和保存的二进制文件,其中包括行星、火箭、地图等,它们是这些类的构造函数所需的名称和值的集合。

【问题讨论】:

  • 您将planet.FPS 写为double,但您尝试将其读为decimal
  • 错误信息告诉你有一个名为assetName的参数,当需要非空值时,其值为null。当然,这与BinaryReader 没有直接关系。至于它可能是什么,除非您提供可靠地重现问题的a good, minimal, complete code example,否则这是不可能的。
  • FPS 是一个浮点数。二进制读写器没有浮点数,所以当我保存浮点数时,我将它保存为十进制,当我加载它时,我加载小数并将其转换为浮点数:(float)br.ReadDecimal();。
  • @PeterDuniho :我没有更多的代码可以帮助您理解我的问题。我让“Cont”成为一个可以加载纹理/精灵/音乐的静态类......我在运行测试功能之前加载了它,这意味着它可以正常运行。我还在其他精灵路径上对其进行了测试,并且可以正常工作。在准备好 Cont 类之后,我调用了测试函数。我不知道如何才能使这个更优秀、更简洁、更完整的代码示例可靠地重现问题。

标签: c# file xna binaryreader binarywriter


【解决方案1】:

由于 Binary writer-reader 的不必要的复杂性,我决定使用 Stream writer-reader。非常感谢@Blas Soriano 和@Peter Duniho。

二进制写入器似乎无法以二进制读取器可以读取它们的方式一个接一个地写入 2 个字符串,至少在我的情况下是这样。我确实尝试在另一个项目中使用二进制 w-r 编写和读取 2 个字符串,它们似乎在那里工作正常。我希望没有人经历过发生在我身上的这种事情。再次感谢 Blas 和 Peter。

编辑:
似乎我包含在解决方案内容“Mars.planet”中的文件在其属性中:复制到输出,有 3 个可能的选择:如果更新则复制,从不复制,始终复制,选择了始终复制,并且不允许对该文件进行更改,因此,我确实通过使用我的功能保存文件来更改文件,但是,然后我退出游戏,所以文件去了回到旧版本(不是真的舒尔如何),那个版本真的很旧,没有我现在需要的所有行,所以当我注释保存代码并取消注释加载代码时,启动游戏,加载功能实际上是加载文件的第一个版本(从我包含文件时开始的版本)。

它现在可以工作,但不是因为我使用的是 Stream w-r,而是因为我将该文件的属性更改为:不要复制。我将回到 Binary w-r。

【讨论】:

    【解决方案2】:

    在您的testFunction() 中,您创建了一个新行星并将其保存到一个文件中而不分配精灵名称(我猜它没有在动画构造函数中分配),所以当它被保存时,精灵名称将为空。 为避免这种情况,您需要先分配 planet.Sprite.Name,然后再将其保存到文件中。

    private void testFunction()
    {
        Animation planetAnimation = new Animation(Cont.Texture2D("Sprites\\Planets\\mars"));
        planetAnimation.Sprite.Name = "Sprites\\Planets\\mars";
        p = new Planet("Mars", 
            planetAnimation , 8, 2, 0.9f, true),
            Cont.Texture2D("Sprites\\Backgrounds\\space"),
            Cont.Texture2D("Sprites\\Asteroids\\small\\small 1"),
            Cont.Texture2D("Sprites\\Asteroids\\medium\\medium 1"),
            Cont.Texture2D("Sprites\\Asteroids\\big\\big 1"),
            100, 50, 40, 20, 40, 40);
        p.SaveToFile(Content);
        /*
        p = new Planet(Content, "Mars");
        tests = p.ToString();
        */
    }
    

    除此之外,您可以修改 LoadSave 方法以避免保存两倍的字符串。 先删除或注释SaveToFile()中的bw.Write(planet.Sprite.Name)这一行:

    public void SaveToFile(ContentManager content)
    {
        string path = content.RootDirectory + @"\Objects\Planets\" + this.name +@".planet"; 
        using (BinaryWriter bw = new BinaryWriter(File.Open(path, FileMode.Create)))
        {
            bw.Write(name);
    
            //sprite names
            //bw.Write(planet.Sprite.Name); //"planet" is an animation.
            bw.Write(planet.HorFrames);   //and thats why it has a sprite
            bw.Write(planet.VerFrames);   //that has a name
            bw.Write((double)planet.FPS);
    ...
    

    然后将LoadFromFile()改成重用字符串:

    public void LoadFromFile(ContentManager content, string name)
    {
        string path = content.RootDirectory + @"\Objects\Planets\" + name + @".planet";
        using (BinaryReader br = new BinaryReader(File.OpenRead(path)))
        {
            this.name = br.ReadString();
            this.planet = new Animation(Cont.Texture2D(this.name), br.ReadInt32(), br.ReadInt32(), br.ReadDouble(), true);
            this.asteroid1 = Cont.Texture2D(br.ReadString());
            this.asteroid2 = Cont.Texture2D(br.ReadString());
    

    请注意,按照cubrr 的建议,我在阅读planet.FPS 时将十进制替换为双精度。


    编辑:从您的评论看来,您认为应该使用静态方法分配空字符串,但这里有两种不同的东西。
    先看看Cont.Texture2D()里面的那个名字:

    public static Texture2D Texture2D(string path)
    {
        Texture2D sprite = content.Load<Texture2D>(path);
        sprite.Name = path;
        return sprite;
    }
    

    这个名字是 Texture2D.Name
    现在来看看SaveToFile()

    bw.Write(name);
    
    //sprite names
    bw.Write(planet.Sprite.Name); //"planet" is an animation.
    

    在这里我们可以看到 2 个不同的名称,Planet.NamePlanet.Sprite.Name。 你在哪里分配第二个名字?这就是我使用planetAnimation.Sprite.Name = "Sprites\\Planets\\mars";的原因。

    【讨论】:

    • this.name 代表行星的名称。对不起,我没有提到行星有它的名字。因此,行星的名称是一个简单的名称,可以说,asteroid1.Name 是精灵的路径。正如你没有注意到的,每次我加载 Texture2D 时,我都会使用 Cont.Texture2D(string path) 来执行此操作,并且该函数会返回一个新的 Texture2D,其名称为从中加载的路径。
    • @Monset 通过编辑我的答案添加了解释。关于 FPS,您可以使用 BinaryReader.ReadSingle() 用于浮点数,BinaryReader.ReadDouble() 用于双精度。
    • 如你所说,“name”是Planet类实例的名称,“planet”是Planet类中行星的动画,它有一个Texure2D,名为Sprite。对不起,我的变量命名系统很差,我在这堂课上没有太注意它。当我将行星保存到文件时,首先保存行星的名称,然后将 path 保存到行星动画精灵。对此没有错误或误解。我的问题是另一种。
    • 我会再解释一次。首先我通过测试函数保存新制作的星球,然后注释,然后尝试加载保存的文件(文件确实找到了,不是找不到那个文件),而是 LoadFromFile 函数,在线--- this.planet = new Animation(Cont.Texture2D(this.name),...--- 转到 Cont 的函数“公共静态 Texture2D(字符串路径)”,该函数找不到精灵,即使那个确切sprite 是我在保存之前制作星球时发现的。
    • @Monset 好的,我现在明白了。该 sprite.name 不能为 null,否则在使用 bw.Write(planet.Sprite.Name); 时会出现 NullArgumentException。所以问题肯定和字符串内容本身有关。我会做两件事: 1- 在该行设置一个断点来检查该字符串。 2- 使用十六进制编辑器检查二进制文件。检查this screenshot
    猜你喜欢
    • 2015-05-13
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    相关资源
    最近更新 更多