【问题标题】:Saving objects with writeObject: What objects can be saved using this method?使用 writeObject 保存对象:使用该方法可以保存哪些对象?
【发布时间】:2016-11-22 21:04:00
【问题描述】:

我正在努力进入激动人心的 byteArrays 世界!

最终目标

我想保存每个游戏元素的位置和其他属性(在本例中,是突破克隆中的块)作为我的应用程序关卡设计功能的一部分,同时也可以更轻松地为游戏设计关卡。

目前的方法

将数据从自定义类实例(砖块)的 Vector 转换为 ByteArray 并将该数据保存到文本文件中。 似乎到目前为止一切正常(直到我成功将该数据提取回 Vector 对象后才能确定,因为保存的文本文件是纯粹的 gobbledygook)。 p>

我通过将文本文件读入 byteArray 然后将 writeObject() 写入 Vector 来加载关卡设计,目的是现在拥有一个包含所有砖块的向量(这不起作用)。

问题

当我尝试运行我的加载函数时,文件加载,并且 byteArray 被数据“填充”,但是当我尝试执行 writeObject 时,我得到了所有这些错误(以下错误的副本每个向量中的砖块)。

TypeError: Error #1034: Type Coercion failed: cannot convert Object@92cdcb9 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object@92cde09 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object@92df041 to flash.geom.ColorTransform.
TypeError: Error #1034: Type Coercion failed: cannot convert Object@92df161 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object@92df281 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object@92df431 to flash.media.SoundTransform.
TypeError: Error #2004: One of the parameters is invalid.

我的自定义积木类是 Sprite 类的扩展。但它还具有依赖于PointColorTransform 对象的属性。奇怪的是,在我的自定义类中,我没有任何引用或使用 SoundTransform... 所以这个错误看起来非常奇怪。如果有人想看,我会发布我的自定义课程。

我的保存和加载方法

    private function saveLevelDesign(brVec:Vector.<LineTestBlock>):void{
        trace("save");
        var file:File = File.documentsDirectory; 
        file = file.resolvePath("AnimationFiles/brickLevels/lev_001.txt"); 
        fileStream.open(file,FileMode.WRITE);
        var bytes:ByteArray = new ByteArray();
        bytes = brickArrayToByteArray(brVec);
        //fileStream.close();
    }

    private function loadLevelDesign():void{
        trace("loadLevelDesign");
        var file:File = File.documentsDirectory;
        file = file.resolvePath("AnimationFiles/brickLevels/lev_001.txt");
        fileStream.open(file,FileMode.READ);
        file.addEventListener(IOErrorEvent.IO_ERROR,ioError);
        file.addEventListener(Event.COMPLETE, loaded); 
        file.load(); 
        //fileStream.open(file,FileMode.READ);          
    }

    private function ioError(ioE:IOErrorEvent):void{
        trace("oops",ioE);
    }

    private function loaded(e:Event):void{
        trace("loaded");
        var bytes:ByteArray = new ByteArray();
        fileStream.readBytes(bytes);
        trace(bytes.length,"bytes length"); // 0 bytes length
        var vec:Vector.<LineTestBlock> = new Vector.<LineTestBlock>;
        for (var i:int = 4; i < _playerTurn._brickArray.length; i++){
            vec.push(_playerTurn._brickArray[i]);
        }
        bytes.writeObject(vec);
        trace(bytes.length,"bytes length"); // 53516 bytes length

        destroyBricks(_playerTurn); // just removes all bricks on the stage 

        vec = byteArrayToBrickArray(bytes); // calling this function throws all those errors
        trace("vector length:",vec.length); // vector length 208 (this is the correct number of bricks, so that's good)
    }

我的字节转换方法

    private function byteArrayToBrickArray(bytes:ByteArray):Vector.<LineTestBlock>{
        bytes.position = 0;

        var blocks:Vector.<LineTestBlock> = bytes.readObject() as Vector.<LineTestBlock>;
        trace(bytes.position);
        return blocks;
    }

    private function brickArrayToByteArray(brVec:Vector.<LineTestBlock>):ByteArray{
        var bytes:ByteArray = new ByteArray();
        /*for (var i:int = 0; i < brVec.length; i++){
            if (brVec[i]._break == true){
                bytes.writeObject(brVec[i]);
            }
        }*/
        bytes.writeObject(brVec);
        return bytes;
    }

有人看看我是不是做错了什么,还是不理解?

【问题讨论】:

  • ByteArray 并将该数据保存到文本文件中”将二进制数据写入文本文件有什么意义?
  • 你的byteArrayToBrickArray函数在哪里?
  • @Neal Davis 在二进制文件中,显然。二进制数据不会产生任何有用的文本。
  • @null,哦……哈哈。多么耐人寻味。我会尝试一下,看看我的结果是否有任何改善。
  • @null 似乎文本文件或 bin 文件具有相同的结果。由于我并不关心它是文本文件还是 bin 文件,因此我将使用 bin 文件,因为它看起来更整洁,但似乎没有优势。

标签: actionscript-3


【解决方案1】:

如果您正确编写了readExternalwriteExternal 方法,任何实现IExternalizable 或不是DisplayObject 的对象都可以保存在ByteArray 中并从其中恢复。如果一个对象没有实现IExternalizable,Flash 将尝试使用对代码可见的公共组件来写入它,并通过以相同顺序将读取的值分配给公共属性来读取它。通常你应该使用比Vector.&lt;int&gt; 更复杂的接口。因此,您需要在您的LineTestBlock 类中实现IExternalizable,只写入和读取那些需要的属性。此外,您只能将此方法用于具有空构造函数的对象,因为在IDataInput.readObject 中,首先构造对象,然后分配值。

The manual on IExternalizable。出于某种原因,您无法从普通的类树中访问它,但它在那里并且界面正在工作。

我会改变你的方法,将所有向量,SoundTransforms 等封装到一个类中,比如Level,然后在其中实现IExternalizable,然后它将按顺序编写所有简单的数据类型(请记住在数据之前写入向量的长度!)当被要求时,然后从字节数组中读取自身并同时重建所有内部数据结构。一个例子:

import flash.utils.*;
public class Level implements flash.utils.IExternalizable
{
private var blocks:Vector.<LineTestBlock>;
// something extra
public function writeExternal(output:IDataOutput):void {
    var l:int=blocks.length;
    output.writeInt(l);
    for (var i:int=0;i<l;i++) {
        //write all the blocks[i]'s properties in order to output
    }
    // same approach to write all the extra properties
}
public function readExternal(input:IDataInput):void {
    var l:int=input.readInt();
    blocks=new Vector.<LineTestBlock>();
    for (var i:int=0;i<l;i++) {
        // first read all the properties into any local variables in the VERY SAME order they were written
        // then create an instance of LineTestBlock
        var block:LineTestBlock=new LineTestBlock(...); 
        // required parameters for the constructor should be read before creating object
        // then fill remaining properties to the created instance
        blocks.push(block); // and reconstruct the array
    }
    // same approach to every property that was saved
    // reconstruct everything else that's depending on the data read
}
}

最后,您可能需要在应用初始化的某处执行flash.net.registerClassAlias() 调用,以使您的Level 被识别为可序列化类。

【讨论】:

  • 谢谢。我现在正在尝试这种方法。尝试添加诸如 Point 对象和 SoundTransforms 之类的属性时会遇到任何麻烦吗?你在哪里说“写入所有块[i]的属性以输出”?
  • 我不太了解如何从主文档类中使用此方法。你能证明这种语法吗?我很困惑,因为即使输出函数也返回 void ...?很抱歉,我在阅读所有这些我无法在互联网上理解的不同示例时让我的大脑在流血。
  • 只用一个 byteArray 就可以读取和写入外部文件? fileStream.writeBytes(Level)?或类似的东西?
  • 写入文件流是从 ByteArray 完成的,是的。但是写一个对象是用writeObject() 完成的。关于语法,这里有一个例子stackoverflow.com/questions/30125221/… 就足够了。恕我直言,编写Point 应该更好地完成属性,但是由于它是一个所有属性都是公共的类型,并且是一个接受零参数的构造函数,因此您可以使用内置序列化并调用writeObject(point)
  • 好吧,IExternalizable 是一个允许stream.writeObject(level) 的工具,一旦编写了序列化和反序列化方法。无论如何,流在引擎盖下都有一个字节数组。如果您只是以正确的顺序书写/阅读简单类型,那么水下不应该有任何石头。
【解决方案2】:

bytes.readObject() 返回一个对象。 所以问题是关于将Object 转换为Vector.&lt;LineTestBlock&gt; 所以你必须自己转换它

private function byteArrayToBrickArray(bytes:ByteArray):Vector.<LineTestBlock>{
    bytes.position = 0;

    // Edit : readObject() only presents an Object
    var blocks:Object = bytes.readObject();
    trace(bytes.position);

    /* you have to convert all step by step
     at first we have to assume blocks as a vector
     best way to searching its items is using _for key in_
    */
    var converted:Vector.<LineTestBlock> = new Vector.<LineTestBlock>(blocks.length);
    for (var key:String in blocks) {
        converted.push(objectToLineTestBlock(blocks[key]));
    }
    return converted;
}

由于我不知道您的 LineTestBlock 类的结构,我无法准确提供“objectToLineTestBlock”功能


这是一个模拟 LineTestBlock 类的示例

我自己的LineTestBlock Class

public class LineTestBlock
{
    public var w:int;
    public var loc:Point;
    public var stf:SoundTransform;

    public function LineTestBlock(_w:int, _loc:Point, _stf:SoundTransform)
    {
        w = _w;
        loc = _loc;
        stf = _stf;
    }
}

测试解决方案的主类。

我所做的只是将所有对象转换为它们真正的样子 bytearray.readObject() 将所有类转换为纯对象

public class ByteTest extends Sprite
{
    public function ByteTest()
    {
        var save_vector:Vector.<LineTestBlock> = new Vector.<LineTestBlock>();
        var block_item1:LineTestBlock = new LineTestBlock(200, new Point(-1, 1), new SoundTransform(0.5));
        var block_item2:LineTestBlock = new LineTestBlock(400, new Point(-2, 2), new SoundTransform(0.25));
        save_vector.push(block_item1);
        save_vector.push(block_item2);
        var load_vector:Vector.<LineTestBlock>;

        var bytes:ByteArray = new ByteArray();

        bytes.writeObject(save_vector);
        // trace(bytes.position);
        load_vector = objectToLineTestVector(bytes);
        // now test to check if everything is OK
        trace(load_vector[1].stf.volume); // must print 0.25
    }
    public function objectToLineTestVector(bytes:ByteArray):Vector.<LineTestBlock> {
        bytes.position = 0;
        var loadedObject:Object = bytes.readObject();
        var blocks:Vector.<LineTestBlock> = new Vector.<LineTestBlock>();
        for (var key:String in loadedObject) {
            blocks.push(objectToLineTestBlock(loadedObject[key])); // loadedObject[key] is a block_item1 and could be converted
        }
        return blocks;
    }
    public function objectToLineTestBlock(obj:Object):LineTestBlock {
        return new LineTestBlock(obj.w, objectToPoint(obj.loc), objectToSoundTransform(obj.stf));
    }
    public function objectToPoint(obj:Object):Point {
        return new Point(obj.x, obj.y);
    }
    public function objectToSoundTransform(obj:Object):SoundTransform {
        return new SoundTransform(obj.volume);
    }

}

【讨论】:

  • 我看到了这个问题。您是在建议(如果我理解正确的话)我制作了我的砖块的矢量(投射为对象),但 Object 类与我的 LineTestBlock 类没有相同的属性。我看到这会导致错误说 Object 类没有属性“foo”。
  • @NealDavis 我通过自定义 LineTestBlock 类对其进行测试,该类包含一些公共属性并将其保存并将其加载到 bytearray 然后从 readObject() 返回 Object 也包含具有相同的所有属性键名
  • 谢谢,我试试看!
  • var blocks:Vector.&lt;Object&gt; = bytes.readObject() as Vector.&lt;Object&gt;; 抛出我显示的所有错误。
  • 你能把你说你测试过的整个例子贴出来,这样我就可以在我的系统上运行它并看到它工作了吗?谢谢!
猜你喜欢
  • 2023-03-19
  • 2015-03-01
  • 1970-01-01
  • 2010-11-30
  • 2023-04-01
  • 1970-01-01
  • 2017-06-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多