Ascent代码分析3-World之对象与脚本

 

事件管理器(EventMgr)

 

Ascent的每个事件是一个TimedEvent对象,其中包含了一个回调函数对象、间隔时间、回调次数和标志信息。

EventMgr只是封装了添加事件的操作(并不负责运行事件的回调函数),真正执行事件回调函数的是EventableObjectHolderupdate()函数。当使用EventMgr::AddEvent的时候,需要指定目标对象(一个EventableObject对象)、一个回调函数和14个参数,类型、间隔时间、回调次数(0不限)和标志。函数原型:

void AddEvent(Class *obj, void (Class::*method)(P1), P1 p1, uint32 type, uint32 time, uint32 repeats, uint32 flags)

事件会加入到EventableObject对象的m_eventsmultimap)中,并会加入到EventableObjectHolder对象中。在EventableObjectHolder::update()函数中将会执行到时间的事件回调函数。

EventableObjectHolder中,活动事件都在表m_events中,但是对事件的添加删除各添加了一个缓冲链表(m_insertPoolm_deletePool),并对两个链表单独加锁。当再次回调update()的时候才真正的添加删除事件。因为活动的事件比较多,这样就可以避免对m_events的频繁锁竞争,或者其他线程添加事件被阻塞的情况以及线程死锁的情况。

关系如图1的上半部分。附两段注释:

/**

  * @class EventableObject

  * EventableObject means that the class inheriting this is able to take

  * events. This 'base' class will store and update these events upon

  * receiving the call from the instance thread / WorldRunnable thread.

*/

/**

  * @class EventableObjectHolder

  * EventableObjectHolder will store eventable objects, and remove/add them when they change

  * from one holder to another (changing maps / instances).

  *

  * EventableObjectHolder also updates all the timed events in all of its objects when its

  * update function is called.

  *

*/

AI与脚本管理器(ScriptMgr):

与大多管理器一样,ScriptMgr也是一个singleton类。主要负责AI和脚本的加载注册及管理。

脚本加载

关于脚本,都是以动态库的形式实现,放在配置的脚本目录中(默认为script_bin)。ScriptMgr加载脚本时会遍历该目录下的所有dll文件,并显式加载。

脚本动态库需要有三个导出函数:_exp_get_version

typedef void(*exp_script_register)(ScriptMgr * mgr); 

typedef uint32(*exp_get_script_type)();

typedef uint32(*exp_get_version)();

这三个导出函数将被ScriptMgr用来判断该脚本动态库的版本和类型(脚本引擎,脚本函数。),并注册该脚本动态库。

exp_script_register需要提供ScriptMgr的对象指针给脚本动态库,由脚本动态库调用ScriptMgr的注册函数注册EntryId和对应的创建函数。注册的创建函数以EntryIdKey分类放在hashmap中。

脚本使用

       1中的下半部分列出了脚本类与Ascent类的关系,针对不同的类(需要用到脚本的类)有一个对应的脚本类基类(图中只列出了相关的3种,还有一些script类目前没有涉及到,但思想是一样的),该基类提供了各种虚函数接口,会在对应的地方调用。当需要一个脚本对象时,会使用在ScriptMgr中注册的创建函数来生成一个脚本对象。而该脚本对象一般就是从脚本类基类派生实现的(可参考脚本目录中的moon等项目,有c++lua的实现)。

以类Creature为例, Creature属性中有CreatureAIScript的对象指针_myScriptClass,该指针指向一个CreatureAIScript对象或一个其的派生类对象。脚本就是通过从CreatureAIScript派生并实现其相应接口来实现逻辑扩展。当脚本动态库加载后,脚本动态库会向ScriptMgr注册一个EntryID和一个创建函数指针。该创建函数可以创建并返回一个脚本中实现的派生类对象,并返回一个基类对象指针。当Creature对象加入到游戏世界时,会调用Creature::LoadScript加载脚本对象:

void Creature::LoadScript()

{

     _myScriptClass = sScriptMgr.CreateAIScriptClassForEntry(this);

}

其中调用了ScriptMgr::CreateAIScriptClassForEntry创建脚本对象:

              CreatureAIScript* ScriptMgr::CreateAIScriptClassForEntry(Creature* pCreature)

{

     //查找注册的函数指针

     CreatureCreateMap::iterator itr = _creatures.find(pCreature->GetEntry());

     if(itr == _creatures.end())

         return NULL;

     //调用函数指针创建对象

     exp_create_creature_ai function_ptr = itr->second;

     return (function_ptr)(pCreature);

}

CreateAIScriptClassForEntry中以EntryID查找注册的创建函数(该函数的一般实现就是return new CLASSNAME;),并调用创建函数返回一个派生的脚本类对象。该对象指针将赋给_myScriptClass,在合适的时候通过宏 CALL_SCRIPT_EVENT调用其虚函数接口。

AI实现:

基本的AI实现都在类AIInterface中,AIInterface是一个基于事件的有限状态机,在HandleEvent中完成状态转换,在Update时刷新当前状态的行为动作。每个Unit对象都会有一个AIInterface的指针(如图1),在需要的时候通过GetAIInterface获取指针并调用相应的函数接口。将AI单独分离出来而作为成员指针,降低了状态机的实现复杂度,也使脚本对象一样可以派生扩展,从而也可以动态替换AI

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值