zoukankan      html  css  js  c++  java
  • Torque2D MIT 学习笔记(21) 名称空间和脚本函数入口的用法

    前言

      如果你选择了Torque,那么TorqueScript对于你来说就变得非常重要,因为为了直接跨平台和与最新版本同步,你就要尽量少的修改源代码,多多的用脚本开发.

      使用脚本,不是光有一些变量支持,语法支持就行的,它最重要的作用是与引擎通信,通过接口调用和事件的捕获来完成各种各样的任务,而对于接口调用这里不再多说,你只要搜索工程(Console*****)就会看到.

      我在这里分享一下脚本中对象方法的创建,引擎事件的捕获以及内部原理.

    举例

      举个例子先:

    function Test()
    {
       %set = new SimSet() { class="SetClass"; };
       Game.ActiveStage.add( %set );
       
       %set.onCall( "test set namespace entry :)" );
    }
    
    function SetClass::onCall( %this, %data )
    {
       echo( "setclass received call:" @ %data );
    }
    

     上面的代码有三个重要点:

      1. 创建SimSet: 继承自SimObject

      2. 设置"class": SimObject的域名

      3. 声明"SetClass::onCall": 自定义回调

    问题

      引擎是如何调用到SetClass::onCall的? 举一反三,引擎内部的一些事件抛出到脚本也是一样的原理,只不过消息名不一样,我们要如何做才能捕获到他们?

    Namespace and Entry

      在Torque中,链表被大量的使用,Namesapce也是其中之一,他很重要,可以理解为是引擎和脚本的桥梁,它管理了所有的通信接口和脚本数据,方法.

      一个Namspace就可以看做是一个方法类,或者功能包.

      Entry是具体某个子功能的封装结构,比如OnCall这个方法在引擎内部就是一个Entry,而SetClass就是一个namespace.两者是一对多的关系.

      在SimObject的域初始化方法中有:

    void SimObject::initPersistFields()
    {
    	.....
    	// Namespace Linking.
    	addGroup("Namespace Linking");
    	addProtectedField("superclass", TypeString, Offset(mSuperClassName, SimObject), &setSuperClass, &defaultProtectedGetFn, &writeSuperclass, "Script Class of object.");
    	addProtectedField("class",      TypeString, Offset(mClassName,      SimObject), &setClass,      &defaultProtectedGetFn, &writeClass, "Script SuperClass of object.");
    	endGroup("Namespace Linking");
    }
    

       有两个域和名称空间有关系,而他们的具体实现方法:

    void SimObject::setClassNamespace( const char *classNamespace )
    {
    	mClassName = StringTable->insert( classNamespace );
    	linkNamespaces();
    }
    
    void SimObject::setSuperClassNamespace( const char *superClassNamespace )
    {  
    	mSuperClassName = StringTable->insert( superClassNamespace );
    	linkNamespaces();
    }
    

       linkNamespaces中,会对superclas,class两个域进行判定,如果存在则会链接到namspace的链表中,在引擎调用脚本对象方法的时候,能够找到.

    bool linkNamespaces(const char *parent, const char *child)
    {
       Namespace *pns = lookupNamespace(parent);
       Namespace *cns = lookupNamespace(child);
       if(pns && cns)
          return cns->classLinkTo(pns);
       return false;
    }
    
    Namespace *Namespace::find(StringTableEntry name, StringTableEntry package)
    {
       for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
          if(walk->mName == name && walk->mPackage == package)
             return walk;
    
       Namespace *ret = (Namespace *) mAllocator.alloc(sizeof(Namespace));
       constructInPlace(ret);
       ret->mPackage = package;
       ret->mName = name;
       ret->mNext = mNamespaceList;
       mNamespaceList = ret;
       return ret;
    }
    

       再结合编译过程中的一段代码:

             case OP_FUNC_DECL:
                if(!noCalls)
                {
                   fnName       = U32toSTE(code[ip]);
                   fnNamespace  = U32toSTE(code[ip+1]);
                   fnPackage    = U32toSTE(code[ip+2]);
                   bool hasBody = bool(code[ip+3]);
                   
                   Namespace::unlinkPackages();
                   ns = Namespace::find(fnNamespace, fnPackage);
                   ns->addFunction(fnName, this, hasBody ? ip : 0, curFNDocBlock ? dStrdup( curFNDocBlock ) : NULL );// if no body, set the IP to 0
                   if( curNSDocBlock )
                   {
                      if( fnNamespace == StringTable->lookup( nsDocBlockClass ) )
                      {
                         char *usageStr = dStrdup( curNSDocBlock );
                         usageStr[dStrlen(usageStr)] = '\0';
                         ns->mUsage = usageStr;
                         ns->mCleanUpUsage = true;
                         curNSDocBlock = NULL;
                      }
                   }
                   Namespace::relinkPackages();
    
                   // If we had a docblock, it's definitely not valid anymore, so clear it out.
                   curFNDocBlock = NULL;
    
                   //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip);
                }
                ip = code[ip + 4];
                break;
    

       我们可以看出,Namspace的名字不能够重复,在脚本编译阶段,如果遇到ClassName::Function这样的字段,编译器会自动寻找ClassName的名称空间,找不到直接创建个新的,然后加入一个Function的Entry,对象创建时的class设置于class本身的方法定义没有强制性的先后关系.

    总结

      Torque提供了很多的脚本对象事件,如果你有工程,那么直接搜executef就可以看到大部分,结合上面的方法可以在脚本中捕获,处理. :)

  • 相关阅读:
    Serialize and Deserialize Binary Tree
    sliding window substring problem汇总贴
    10. Regular Expression Matching
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第七章 链接
    程序员如何写一份合格的简历?(附简历模版)
    9个提高代码运行效率的小技巧你知道几个?
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第六章 存储器层次结构
    24张图7000字详解计算机中的高速缓存
    《深入理解计算机系统》(CSAPP)实验四 —— Attack Lab
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第五章 优化程序性能
  • 原文地址:https://www.cnblogs.com/KevinYuen/p/2977059.html
Copyright © 2011-2022 走看看