TAML 编译
当你要将一个对象写入到TAML时,并不意味着之写入单一的对象,写入操作同样将作用于他的子对象.
在写入的时候会遵循若干个步骤:
- 查找所有静态和动态的属性Field进行写入,保存.
- 查找所有的子对象进行写入,保存.
- 查找所有自定义状态进行写入,保存.
静态和动态属性的编译
这个过程实际上是由两部分组成,首先是遍历所有的静态属性域,并检测他们是否应该被写入文件.要注意的是这个检测操作只在没有前置调用WriteDefaults的情况下执行.
在引擎的属性域系统中预先指定了每个属性的相关接口,有设置,获取,写入方法,如下:
addProtectedField("Angle", TypeF32, NULL, &setAngle, &getAngle, &writeAngle, "");
比如WriteAngle方法,如果谁想知道这个Angle域是否能被写入都可以进行调用来询问,用代码说明:
// 添加image属性域 addProtectedField("image", TypeImageAssetPtr, Offset(mImageAsset, ImageFont), &setImage, &getImage, &writeImage, ""); // writeImage的实现 static bool writeImage( void* obj, StringTableEntry pFieldName ) { // 检测资源是不是为空(空为默认,证明没有改变过) return static_cast<ImageFont*>(obj)->mImageAsset.notNull(); }
TAML快速遍历所有的静态属性域并检测他们,形成一张需要写入的静态属性域列表.
接下来,TAML快速遍历所有的动态属性域,并直接将他们加入写入列表.因为动态属性是在脚本中创建的,没有相关的检测接口,所以无法决定是否需要写入.
子对象编译
TAML先对写入对象进行检测是不是继承于TamlChildren,如果是则会查询包含多少子对象.
参照代码可见:
// 直接继承于TamlChild的类有三个(也有二次继承的,这里省略) class ParticleAsset : public AssetBase, public TamlChildren class SimSet: public SimObject, public TamlChildren class Scene : public BehaviorComponent,
public TamlChildren,
public PhysicsProxy,
public b2ContactListener,
public b2DestructionListener, public virtual Tickable
// 编译代码中 void Taml::compileChildren( TamlWriteNode* pTamlWriteNode ) { ..... // 编译对象 SimObject* pSimObject = pTamlWriteNode->mpSimObject; // 类型转换为TamlChildren. TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject ); // 这里要判断是不是转换成功以及有没有子对象 if ( pChildren == NULL || pChildren->getTamlChildCount() == 0 ) return; }
如果存在子对象,TAML将遍历所有的子对象,并递归的进行它们属性域,自定义状态,子对象的写入.
如上所示,SimSet继承于TamlChildren,可以包含很多的SimObject,同样适用于包含多个SceneObject的Scene,如下代码:
%list = new SimSet(); // Add some objects. %list.add( new ScriptObject() ); %list.add( new ScriptObject() ); %list.add( new ScriptObject() ); // Write out the list. TamlWrite( %list, "list.taml" );
写入的内容:
<SimSet> <ScriptObject/> <ScriptObject/> <ScriptObject/> </SimSet>
XML 格式
与XML格式相比,想要阅读或者编辑二进制格式的文件是很困难的,而XML格式在开发和编辑过程中容易使用.
下面是一个比较具体的脚本例子:
// 创建一个SimSet对象实例 %bookList = new SimSet() { Title="My Book List"; // 增加动态域Titile }; // 创建几个Script对象,并设置相关的动态域 %book1 = new ScriptObject() { Title="Torque 3D Game Development Cookbook"; Author="Dave Wyand"; }; %book2 = new ScriptObject() { Title="Graphics Programming Black Book (Special Edition)"; Author="Michael Abrash"; }; %book3 = new ScriptObject() { Title="Programming with Graphics"; Author="Garry Marshall"; }; // 加入simSet %bookList.add( %book1 ); %bookList.add( %book2 ); %bookList.add( %book3 ); // 写入TAML文件 TamlWrite( %bookList, "booklist.taml" );
文件结果如下:
<SimSet Title="My Book List"> <ScriptObject Title="Torque 3D Game Development Cookbook" Author="Dave Wyand"/> <ScriptObject Title="Graphics Programming Black Book (Special Edition)" Author="Michael Abrash"/> <ScriptObject Title="Programming with Graphics" Author="Garry Marshall"/> </SimSet>
XML的布局方式非常的简洁.
每一个XMl节点代表着一种能够被引擎认可并创建的类型.
每一个XMl属性都是一个属性域.这个属性域可以是静态或者动态属性域名.
TAML支持同一个对象在同一个TAML文件中多处存在,比如:
%list = new SimSet(); %obj = new ScriptObject(); %list.add( %obj ); %list.add( %obj ); %list.add( %obj ); TamlWrite( %list, "list.taml" );
产生的结果是:
<SimSet> <ScriptObject TamlId="1"/> <ScriptObject TamlRefId="1"/> <ScriptObject TamlRefId="1"/> </SimSet>
命名对象
TAML支持Torque对象的命名,不过要特别注意的是相同名字重复而产生的丢失问题.
所以使用全局唯一的对象名是必要的,尤其是GUI文件.使用方法如下:
TamlWrite( new ScriptObject(Fred), "test.taml" ); // 输出 <ScriptObject Name="Fred"/>
TAML事件回调
TAML提供了一个TamlCallbacks的监听器来执行对象的读写通告,SimObject继承了这个监听器,因此每一个对象都可以处理TAML的事件回调.具体回调如下:
- onTamlPreWrite() - 对象写入前调用.
- onTamlPostWrite() - 对象写入后调用.
- onTamlPreRead() - 对象状态读取前调用.
- onTamlPostRead() - 对象状态读取后调用.
- onTamlAddParent() - onTamlPostRead()调用后并添加到父对象后调用.
- onTamlCustomWrite() - 对象自定义属性写入期间调用.
- onTamlCustomRead() - 对象自定义属性读取期间调用.