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就可以看到大部分,结合上面的方法可以在脚本中捕获,处理. :)

  • 相关阅读:
    springboot定时任务框架Quartz
    Linux中安装Erlang
    prometheus常用函数详解
    Prometheus+Grafana+SpringBoot业务埋点可视化监控
    Prometheus+Grafana可视化监控SpringBoot项目
    prometheus的数据类型介绍
    DS:顺序栈
    DS:顺序队列
    Linux:06进程
    primer5:chap09顺序容器
  • 原文地址:https://www.cnblogs.com/KevinYuen/p/2977059.html
Copyright © 2011-2022 走看看