//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// dota中的道具/技能及动作状态机 相关原型 设计备忘::
/*
商店用拥有很多kToyItem供购买,角色通过购买也可以得到很多kToyItem。
kToyItem用于kToy物品类的显示封装,对应有一个物品类kToy以及储存在哪个格子中,价格多少等信息。
kToy中包含3种类型:1.kEquip(装备),2.kUse(使用), 3.kSkill(技能)
这3个类中各自包含一个listAction(动作列表),列表中存放的是kAction动作基类。
kAction类中有Select()和IsValid()以及Execute()函数;
1. kEquip中的Update()中调用listAction中每一个IsValid()函数来判断是否执行对应的Execute();
2. kUse中的Use()函数中调用listAction中每一个IsValid()函数来判断是否执行对应的Execute();
3. kSkill中的select()函数遍历listAction中每一个Select()函数来判断是否能选择该技能,都通过才选择。
kSkill中的Start()函数中调用listActionStart中每一个IsValid()函数来判断是否执行对应的Execute();
kSkill中的Cast()函数中调用listActionCast中每一个IsValid()函数来判断是否执行对应的Execute();
说明:
kSkill设计时考虑到施放前摇,故用了start()函数来播放前摇动作/特效,然后等待前摇时间完成再调用cast()函数来播放对应的施放动作/特效。
当特效完成后再处理listAction(动作列表)。dota中还有后摇,我理解成技能冷却时间(不知道对否),当cast()时,skill就可以开始冷却了。
[说明]
角色属性有:基础属性,附加属性(直接+),加成属性(*(1+比例)),而
kAction的派生类kActionProperty专用于处理kEquip(装备)提高属性:
它用VARY_TYPE类型指明针对某种属性, 如: str, int, dex, HP/MP(min/max), atk(min/max)。。。等.
它用PROPERTY_METHOD区别是直接加还是比例乘:add/rate。
这样角色如果拥有kToyItem的话,那么在更新中就会调用每个物品的kEquip的Update,从而把属性更新到角色的add_data[类型]和rate_data[类型]中,
接着角色的更新就会用(base_data[]+add_data[])*(1+rate_data[])的公式来处理之(当然,dota的一些特殊的属性使用另外的计算方法)
kAction可以方便地扩充很多普通派生处理类,只要在相应的Execute()进行处理即可实现想要的功能,比如可能派生一个名为kActionHurt类,
然后把它加入到kUse/kSkill中的listAction列表中,这样只要点击使用,即可Execute中处理加血/扣血。
另外从kAction中派生的还有一些特殊类型:
1. kActionEffect(包含listAction成员):
kActionEffect执行Execute()时会调用全局的kEffectManager(特效管理器)的PlayEffect(effect_class_name)函数来产生一个kEffect的派生类对象, 并转让listAction给它。
kEffectManager用来管理listEffect列表,更新处理其中每一个kEffect。
kEffect类中包含一个listAction(动作列表)指针,它是由kActionEffect在产生它时传递过来的,这样在特效完成并且条件成立时(比如命中敌人)调用listAction中每一个IsValid()函数来判断是否执行对应的Execute();
2. kActionState(包含listAction成员):
kActionState执行Execute()时会调用全局的kStateManager(状态管理器)的PlayState(state_class_name)函数来产生一个kState的派生类对象, 并转让llistAction给它。
kStateManager用来管理listState列表,更新处理其中每一个kState。
kState类中包含一个listAction(动作列表)指针, 它是由kActionState在产生它时传递过来的,这样在该状态完成时会调用listAction中每一个IsValid()函数来判断是否执行对应的Execute();
注意: kState队列的执行优先于kAIState队列,只有kState列表为空时,kAIState才有机会执行.
关于kState和kAIState的区别:
a. 动作触发的状态机:
kState主要是kAction(动作)触发引起角色的一些临时被动行为,比如kState被击退状态,被晕状态,状态之间可以并行,或者串行(通过kState中的listAction列表挂接,在kState完成时遍历调用listAction)。
比如:
可以将kActionHurt加入到kStateStun(被晕)状态中的listActon中,然后再把kStateStun加到kStateThrustBack(被击退)中,然后再把kStateThrustBack加入到kShotEffect的listAction中,接着给kActionEffect设置对应的kShotEffect
并把kActionEffect加入到kToy中的kSkill的listActon中.这样,当技能使用时就会触发一个kActionEffect播放kShotEffect,这个kShotEffect播放完成时会触发kStateThrustBack将角色击退到一边,退到一边完成后接着触发kStateStun让角色晕上一会。
而如果前面的kShotEffect是范围特效的话,那在特效伤害范围的角色都被击退后再晕上一会(由于击退和晕是动作状态优先于角色的AI状态,所以这时角色的AI是不运行的,只有等击退后晕完了AI才醒过一继续)。
b. AI行为状态机:
kAIState主要是由kAIBrain(大脑)思考引起的一些主动的AI行为,比如kAIRoam漫步, kAIPursue追捕等,各AI状态之间不能并行或串行只能切换。在任何时候包括在kAction的处理中也可以进行角色的kAIState切换。
3. kActionTrigger:
kActionTrigger中拥有一个事件名称列表,当该action被execute时会通过kEventManager->PostEvent(Event_Name)来发出事件消息。这时事件监听列表中的对应事件号的触发器先判断kCondition是否成立,是则调用相应的触发器的kAction动作。
关于触发器:
kTrigger触发器,拥有listEventName, kCondition, kAction。事件管理器kEventManager可以创建kTrigger,并添加触发器监听的事件列表(listEventName),条件(kCondition),以及动作(kAction)。
当事件发生时可随时调用kEventManager->PostEvent(Event_Name)来发出消息,事件的监听者kTrigger先判断kCondition是否成立,是则调用kAction。
调用的kAction即前面所说的动作基类,当动作完成时可以再次PostEvent(...)以便触发另外的触发器。
4. kActionPose:
这个Action只是简单地调用一下角色的动作播放。
5. kActionSleep(包含listAction成员):
这个kAction只是延迟一段时间,时间到了再调用listAction各成员的execute()函数.
由以上设计可以知道每一个kToy道具均可以由不同的处理函数并行及串行(注意:这里说的串/并行跟多线程无关,概念不一样)组合而成,这样就可以实现动作或特效的串行/并发执行,以及触发相应的处理。
*/
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx