U3D多点触控框架实例(下)
Posted konglingbin66
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了U3D多点触控框架实例(下)相关的知识,希望对你有一定的参考价值。
1.概述
上次我们一口气讲到下层组件讲多点触控的消息根据具体的需求,在每个时刻将其抽象画成单一指令。
接下来我们要聊聊,上层组件中如何将单一的指令应用在角色上,让角色在移动,普通攻击和释放技能中间做出正确的行为。
上次我们也说过了,上层组件是个优先级队列,队列中分别有对移动,普通攻击,技能释放的判定检测组件,按照 技能> 普攻 > 移动的优先级,对下层组件传递过来的消息依次,逐个进行处理。
按照这种设计思路,我们首先还是应该有一个接口,统一组建的操作,然后在响应事件的时候对组件for循环处理
2.队列组件接口
public enum TouchEventType { down, press, up, moveEnd, } public class ManualControllerCom { protected Units self;//绑定的角色 bool pause = true; protected Units lastTarget;//选定的地方目标 public virtual void OnInit() { } public virtual void OnStart() { } public virtual void OnExit() { } public virtual void OnStop() { } public virtual void OnDown(TriggerParamTouch param)//响应按下事件 { } public virtual void OnPress(TriggerParamTouch param)//响应长按 { } public virtual void OnUp(TriggerParamTouch param)//响应弹起 { } public virtual void OnMoveEnd(TriggerParamTouch param)//响应移动结束,这个消息是别人加的,暂时忽略, { } public virtual void OnSkill(ITriggerDoActionParam param)//点击UI按下 { } public virtual void OnNavigateEnd()//寻路结束,也就是寻路到目标位置了 { } protected float GetSkillRange(Skill skill)//获取技能攻击范围(其他人添加,不应该放在基类中) { if (skill.IsAttack) { return self.GetAttackRange(null); } else { return skill.distance; } } protected Units GetSkillTarget(Skill skill, Units enemyTarget)//获取技能目标<span style="font-family: Arial, Helvetica, sans-serif;">(其他人添加,不应该放在基类中)</span> { if (skill.needTarget) { if (enemyTarget == null) { if (skill.data.targetCamp == SkillTargetCamp.Self) { return self; } else if (skill.data.targetCamp == SkillTargetCamp.Partener) { return self; } else if (skill.data.targetCamp == SkillTargetCamp.AllWhitOutPartener) { return self; } } else { if (skill.data.targetCamp == SkillTargetCamp.Self) { return self; } else if (skill.data.targetCamp == SkillTargetCamp.Partener) { if (enemyTarget.teamType == self.teamType) { return enemyTarget; } else { return self; } } else if (skill.data.targetCamp == SkillTargetCamp.AllWhitOutPartener) { if (enemyTarget.teamType == self.teamType) return self; else return enemyTarget; } } return enemyTarget; } return null; } }
该接口响应触控事件,以及技能释放事件
3.上层组件
上层组件主要是集合所有上层操作组件(释放技能,普攻,移动)。
public class ManualController : StaticUnitComponent//该组件也是英雄角色身上的组件之一 { #region 变量 ManualControlTarget targetCom;//目标 ManualControlSignalMng signalCom;//组件通信的信号量 List<TriggerEvent2> listTrigger2 = new List<TriggerEvent2>();//消息监听 List<ManualControllerCom> listManuControllerCom = new List<ManualControllerCom>();//组件容器 TouchEventDebug touchDebugger;//DEBUG组件 bool pause = false;//是否暂停 bool initial = false;//是否初始化 bool debugFlag = false;//是否开启debug #endregion public Units ControlUnit { get { return self; } } public ManualControlTarget TargetCom { get { return targetCom; } } public ManualControlSignalMng SignalCom { get { return signalCom; } } public CoroutineManager CoroutineMng { get { return m_CoroutineManager; } } #region 构造函数 public ManualController() { } void Init() { if(debugFlag) { touchDebugger = new TouchEventDebug(); if(null!=touchDebugger) { touchDebugger.OnInit(); } } signalCom = new ManualControlSignalMng(this); targetCom = new ManualControlTarget(this); RegisterTrigger(); InvokeEveryCom("OnInit");//init组件 这是通过反射调用每个组件的对应override函数 } #endregion #region 事件处理 public override void OnInit() { base.OnInit(); if (!initial) { Init(); initial = true; } } public override void OnStart() { pause = false; base.OnStart(); InvokeEveryCom("OnStart");//组件启动 } public override void OnStop() { pause = true; base.OnStop(); InvokeEveryCom("OnStop");//停止组件 } public override void OnExit()//退出组件 { base.OnExit(); InvokeEveryCom("OnExit"); UnRegisterTrigger(); m_CoroutineManager.StopAllCoroutine();//停止协程 if (debugFlag && null != touchDebugger) { touchDebugger.OnExit(); } } #endregion void OnDown(ITriggerDoActionParam param) { if(!CanHandleControl())//这个就是因为一些情况不需要处理,比如角色死亡之类的 return; TriggerParamTouch info; if (!HandleTouchParam(param,out info))//检查传入参数,传入的是个接口 return; //if (!targetCom.UpdateTarget(info.Pos)) // return; signalCom.Init();//初始化信号量,表示现在没有组件处理这个消息,因为每个组件依次处理,所以处理前要复位,处理过程中置位,职位后后续组件无需处理 InvokeEveryCom("OnDown", new object[] { info }); } void OnPress(ITriggerDoActionParam param) { if(!CanHandleControl()) return; TriggerParamTouch info; if (!HandleTouchParam(param,out info)) return; //if (!targetCom.UpdateTarget(info.Pos)) // return; signalCom.Init(); InvokeEveryCom("OnPress", new object[] { info }); } void OnUp(ITriggerDoActionParam param) { if(!CanHandleControl()) return; TriggerParamTouch info; if (!HandleTouchParam(param,out info)) return; //if (!targetCom.UpdateTarget(info.Pos)) // return; signalCom.Init(); InvokeEveryCom("OnUp", new object[] { info }); } void OnMoveEnd(ITriggerDoActionParam param) { if(!CanHandleControl()) return; TriggerParamTouch info; if (!HandleTouchParam(param,out info)) return; //if (!targetCom.UpdateTarget(info.Pos)) // return; signalCom.Init(); InvokeEveryCom("OnMoveEnd", new object[] { info }); } //@jason void OnSkillEnd(ITriggerDoActionParam param) { if (!CanHandleControl()) return; signalCom.Init(); InvokeEveryCom("OnSkillEnd", new object[] { param }); } void OnSkill(ITriggerDoActionParam param) { if (!CanHandleControl()) return; signalCom.Init(); InvokeEveryCom("OnSkill",new object[]{param}); } void OnNavigateEnd(ITriggerDoActionParam param=null) //停止寻路回调 { signalCom.Init(); InvokeEveryCom("OnNavigateEnd"); } void InvokeEveryCom(string methodName,object[] param=null)//用反射调用每个组件的对应方法 { if (string.IsNullOrEmpty(methodName)) return; ShowEventDebug(methodName); foreach (var it in listManuControllerCom) { Type t = it.GetType(); MethodInfo mInfo = t.GetMethod(methodName); if(null!=mInfo) { mInfo.Invoke(it, param); } } } public void ShowEventDebug(string str) { if (debugFlag) { touchDebugger.AddEventInfo(str); } } public void ShowHandlerDebug(string str) { if (debugFlag) { touchDebugger.AddHandlerInfo(str); } } //---------------------------------//后面的代码就不用怎么看了,和框架没啥大关系 #region 注册回调 //切换主站英雄的时候将移动光标去掉 void RegisterTrigger() { RegisterEvent2(EEventID2_touch.eDown, OnDown); RegisterEvent2(EEventID2_touch.ePress, OnPress); RegisterEvent2(EEventID2_touch.eUp, OnUp); RegisterEvent2(EEventID2_touch.eMoveEnd, OnMoveEnd); RegisterEvent2(EEventID2_skillControl.ePressSkill, OnSkill); RegisterEvent2(EEventID2_navigation.eOnStopPath, OnNavigateEnd); //@jason RegisterEvent2(EEventID2_skillControl.eKillEnd, OnSkillEnd); MobaMessageManager.RegistMessage((ClientMsg)ClientV2V.BattleShop_shopOpened, OnControllerClose); MobaMessageManager.RegistMessage((ClientMsg)ClientV2V.BattleShop_shopClosed, OnControllerOpen); } void UnRegisterTrigger() { for (int i = 0; i < listTrigger2.Count; ++i) { TriggerManager2.Instance.RemoveListner(listTrigger2[i]); } MobaMessageManager.UnRegistMessage((ClientMsg)ClientV2V.BattleShop_shopOpened, OnControllerClose); MobaMessageManager.UnRegistMessage((ClientMsg)ClientV2V.BattleShop_shopClosed, OnControllerOpen); } void RegisterEvent2(EEventID2_touch eventID, Callback<ITriggerDoActionParam> fun_action) { TriggerCreateParamTouch param = new TriggerCreateParamTouch(); RecordTrigger(param, (int)eventID, fun_action); } void RegisterEvent2(EEventID2_skillControl eventID, Callback<ITriggerDoActionParam> fun_action) { TriggerCreateParamSkillControl param = new TriggerCreateParamSkillControl(); RecordTrigger(param, (int)eventID, fun_action); } void RegisterEvent2(EEventID2_navigation eventID, Callback<ITriggerDoActionParam> fun_action) { TriggerCreateParamNavigation param = new TriggerCreateParamNavigation(); List<TriggerCondition<ITriggerDoActionParam>> list = new List<TriggerCondition<ITriggerDoActionParam>>(); list.Add(IsPlayer); RecordTrigger(param, (int)eventID, fun_action,list); } void RecordTrigger(ITriggerCreatorParam param, int eventID, Callback<ITriggerDoActionParam> fun_action, List<TriggerCondition<ITriggerDoActionParam>> conditions = null) { if (null != param) { param.EventID = (int)eventID; param.TriggerID = TriggerManager2.assign_trigger_id(); param.Func_actions = fun_action; param.Func_conditions = conditions; TriggerEvent2 trigger = TriggerManager2.CreateTriggerEvent2(param); listTrigger2.Add(trigger); TriggerManager2.Instance.AddListener(trigger); } } bool IsPlayer(ITriggerDoActionParam param=null) { TriggerParamNavigation info = param as TriggerParamNavigation; return null != info && info.IsPlayer; } #endregion bool HandleTouchParam(ITriggerDoActionParam param,out TriggerParamTouch info) { info = param as TriggerParamTouch; return info != null; } bool CanHandleControl() { if (controllerUnlockTime > 0) { if (DateTime.Now.Ticks - controllerUnlockTime < 100 * 10000) { return false; } controllerUnlockTime = 0; } return null!=self && self.isLive && !self.IsLockCharaControl && !self.LockInputState && controllerLock == 0; } public void ShowDebugMsg(string s) { #if UNITY_EDITOR ClientLogger.Debug(LogTagNames.TAG_NULL, "==>>ManualController:" + s); #endif }
4.组件实现
a.技能组件
每个组件不仅处理事件,而且会保存角色移动,释放技能,普通攻击的状态。以释放技能为例,其实也有很多状态,可以考虑用状态机,不过有点复杂。技能显示范围指示,技能释放过程中,技能释放结束,技能释放结束的结果,是成功,失败,还是打断,这些状态下,都可能收到对应的消息,那么要对其进行处理。如果不处理,那么信号量就是闲置,接下来普攻组件和移动组件会处理。如果处理了,后面的组件优先级较低,就不会再处理了。由于这个功能完成之后没有及时做笔记,被其他人改的面目全非,所以现在仅仅保留一个影子,可以看到一个大概。
public class ManualControlSkillNormal : ManualControllerCom { List<VTrigger> listTrigger = new List<VTrigger>(); ManualController controller; ManualControlTarget targetCom; ManualControlSignalMng signalCom; Skill tmpLastSkill; float tmpLastSkillTm; Skill readySkill; //已经按出指示器,准备要放的技能 Skill lastReadySkill; //已经按出指示器,准备要放的技能 float readyTm; void ClearReadySkill() { if (readySkill != null) { TriggerManager.Instance.SendUnitSkillPointerEvent(UnitEvent.UnitSkillCmdHidePointer, self, readySkill, Vector3.zero); readySkill = null; SkillView.Instance.ClearSelectState(); SkillView.Instance.HideSkillWarn(); } } void SetReadySkill(Skill skill, Units targetUnits) { ClearReadySkill(); readySkill = skill; lastReadySkill = skill; readyTm = Time.realtimeSinceStartup; SkillView.Instance.SetSelectState(); SkillView.Instance.ShowSkillWarn("请选择目标"); TriggerManager.Instance.SendUnitSkillPointerEvent(UnitEvent.UnitSkillCmdShowPointer, self, readySkill, GetSkillPointerPos(skill, targetUnits)); } void DragSkillPointer(Vector3 targetPos) { TriggerManager.Instance.SendUnitSkillPointerEvent(UnitEvent.UnitSkillCmdDragPointer, self, readySkill, targetPos); } bool ReadySkillOld() { return readySkill == null || Time.realtimeSinceStartup - readyTm > 0.6f; } #region 构造函数 public ManualControlSkillNormal(ManualController c) { controller = c; self = c.ControlUnit; targetCom = controller.TargetCom; signalCom = controller.SignalCom; } #endregion //----------------------------------------------------------------------- #region 事件处理 public override void OnInit() { //YJLog.log("OnInit"); base.OnInit(); VTrigger tr = null; tr = TriggerManager.CreateUnitEventTrigger(UnitEvent.UnitSkillOver, null, OnSkillOver, self.unique_id); listTrigger.Add(tr); tr = TriggerManager.CreateUnitEventTrigger(UnitEvent.UnitSkillFailed, null, OnSkillFailed, self.unique_id); listTrigger.Add(tr); tr = TriggerManager.CreateUnitEventTrigger(UnitEvent.UnitDeath, null, OnUnitDeath, self.unique_id); listTrigger.Add(tr); tr = TriggerManager.CreateUnitEventTrigger(UnitEvent.UnitSkillCmdStart, null, OnSkillStart, self.unique_id); listTrigger.Add(tr); //tr = TriggerManager.CreateUnitEventTrigger(UnitEvent.UnitSkillEnd, null, OnSkillEnd, self.unique_id); //listTrigger.Add(tr); //added by shaoming self.ClearBornPowerObjSkillData(); //@added by shaoming } public override void OnStop() { //YJLog.log("OnStop"); base.OnStop(); targetCom.ClearSkillFlag(); targetCom.ClearMoveFlag(); } public override void OnExit() { //YJLog.log("OnExit"); foreach (var it in listTrigger) { TriggerManager.DestroyTrigger(it); } base.OnExit(); } public override void OnDown(TriggerParamTouch param) { //YJLog.log("OnDown"); //Debug.LogError("Control OnDown"); if (GlobalSettings.Instance.isLockView) { if (readySkill == null) { OnTrySkillOrMove(param, TouchEventType.down); } } } public override void OnPress(TriggerParamTouch param) { //YJLog.log("OnPress"); //Debug.LogError("Control OnPress"); if (GlobalSettings.Instance.isLockView) { targetCom.ClearTarget(); targetCom.UpdateTarget(param.Pos); Vector3 targetPos = targetCom.GroundPoint; Units targetUnits = targetCom.TargetUnit; if (targetUnits != null && !targetUnits.CanBeSelected) { targetUnits = null; } if (!self.CanManualControl()) { return; } if (readySkill != null) { targetCom.ClearMoveFlag(); DragSkillPointer(targetPos); } else { OnTrySkillOrMove(param, TouchEventType.press); } } } public override void OnUp(TriggerParamTouch param) { //YJLog.log("OnUp"); //Debug.LogError("Control OnUp"); if (GlobalSettings.Instance.isLockView) { if (readySkill != null) OnTrySkillOrMove(param, TouchEventType.up); } else { OnTrySkillOrMove(param, TouchEventType.up); } } public virtual void OnMoveEnd(TriggerParamTouch param) { //YJLog.log("OnMoveEnd"); //Debug.LogError("Control OnMoveEnd"); if (GlobalSettings.Instance.isLockView) {//判断移动 或者 攻击 if (readySkill != null) { OnTrySkillOrMove(param, TouchEventType.moveEnd); } else { targetCom.ClearTarget(); targetCom.UpdateTarget(param.Pos); Units targetUnits = targetCom.TargetUnit; if (targetUnits != null && !targetUnits.CanBeSelected) { targetUnits = null; } if (!self.CanManualControl()) { return; } if (targetUnits == null) { OnTrySkillOrMove(param, TouchEventType.moveEnd); } } } else {//可以滑动视窗时,什么都不操作 } } void OnTrySkillOrMove(TriggerParamTouch param, TouchEventType type) { targetCom.ClearTarget(); targetCom.UpdateTarget(param.Pos); Vector3 targetPos = targetCom.GroundPoint; Units targetUnits = targetCom.TargetUnit; bool needForceSend = type == TouchEventType.press ? false : true; if (targetUnits != null && !targetUnits.CanBeSelected) { targetUnits = null; } if (!self.CanManualControl()) { return; } if (readySkill == null) { if (targetUnits == null) { DisableAIAutoAttackMove(); targetCom.DrawMoveFlag(); self.mCmdCacheController.EnqueueMoveCmd(targetPos, needForceSend); lastTarget = null; } else { if (type == TouchEventType.down || (type != TouchEventType.down && lastTarget != targetUnits)) { lastTarget = targetUnits; DisableAIAutoAttackMove(); targetCom.ClearMoveFlag(); self.mCmdCacheController.EnqueueSkillCmd(self.attacks[0], targetPos, targetUnits); } } } else { targetCom.ClearTarget(); targetCom.UpdateTargetAll(param.Pos, readySkill.data.targetCamp); targetCom.ClearMoveFlag(); DragSkillPointer(targetCom.GroundPoint); LaunchSkill(readySkill, targetCom.TargetUnit, targetCom.GroundPoint); ClearReadySkill(); lastTarget = null; } } void OnSkillOver() { } void OnSkillFailed() { } void OnUnitDeath() { //YJLog.log("OnUnitDeath"); ClearReadySkill(); } void OnSkillStart() { Skill skill = self.GetSkillContext(); if (skill != null && skill == readySkill) { ClearReadySkill(); } } public override void OnSkill(ITriggerDoActionParam param) { TriggerParamSkillControl info = param as TriggerParamSkillControl; //YJLog.log("OnSkill:" + info.SkillID); Skill skill = self.getSkillOrAttackById(info.SkillID); Units targetUnits = FindTargetHelper.FindAutoSkillTarget(self, self.trans.position, TargetTag.Hero, self.atk_type == 1 ? 6.5f : GetSkillRange(skill) + 2); if(targetUnits == null) targetUnits = PlayerControlMgr.Instance.GetSelectedTarget(); targetCom.ClearMoveFlag(); if (!self.CanManualControl()) { return; } if (self.IsSkillCanTriggerBornPowerObj(info.SkillID)) { ClearReadySkill(); LaunchSkill(skill, targetUnits); return; } if (DoseSkillNeedDoubleClick(skill)) { if (skill == readySkill) { if (ReadySkillOld())//过期时 再次点击是清楚上次选择 { readyTm = Time.realtimeSinceStartup; ClearReadySkill(); } else { ClearReadySkill(); LaunchSkill(skill, targetUnits); } return; } if (readySkill == null && lastReadySkill == skill && Time.realtimeSinceStartup - readyTm < 0.5f) { ClearReadySkill(); LaunchSkill(skill, targetUnits); return; } SetReadySkill(skill, targetUnits); return; } else { ClearReadySkill(); LaunchSkill(skill, targetUnits); } } public override void OnNavigateEnd() { //YJLog.log("OnNavigateEnd"); self.mCmdRunningController.OnMoveEnd(); targetCom.ClearMoveFlag(); } #endregion Vector3 GetSkillPointerPos(Skill skill, Units targetUnits) { if (targetUnits != null) return targetUnits.mTransform.position; else { return self.mTransform.position + self.mTransform.forward * 10f; } } void LaunchSkill(Skill skill, Units targetUnits) { Vector3 targetPos = Vector3.zero; if (!skill.needTarget) { if (targetUnits != null && !targetUnits.CanBeSelected) { targetUnits = null; } if (targetUnits == self) { targetUnits = null; } targetPos = self.mTransform.forward; if (targetUnits != null) { targetPos = targetUnits.mTransform.position; } else { targetPos.Normalize(); if (skill.distance == 0) targetPos = self.mTransform.position + targetPos; else targetPos = self.mTransform.position + targetPos * skill.distance; } LaunchSkill(skill, targetUnits, targetPos); } else { targetUnits = GetSkillTarget(skill, targetUnits); if (targetUnits != null) { LaunchSkill(skill, targetUnits, targetPos); } else { UIMessageBox.ShowMessage("未选择目标,不能释放!"); } } } void LaunchSkill(Skill skill, Units targetUnits, Vector3 targetPos) { if (skill.needTarget) { targetUnits = GetSkillTarget(skill, targetUnits); if (targetUnits != null && !targetUnits.CanBeSelected) { targetUnits = null; } if (targetUnits != null) { DisableAIAutoAttackMove(); targetCom.ClearMoveFlag(); self.mCmdCacheController.EnqueueSkillCmd(skill.realSkillMainId, targetUnits.mTransform.position, targetUnits); } else {//如果指向性技能没选择目标 该怎么默认处理 } } else { if (targetUnits != null && !targetUnits.CanBeSelected) { targetUnits = null; } if (targetUnits == self) { targetUnits = null; } DisableAIAutoAttackMove(); targetCom.ClearMoveFlag(); self.mCmdCacheController.EnqueueSkillCmd(skill.realSkillMainId, targetPos, targetUnits); } } bool DoseSkillNeedDoubleClick(Skill skill) { return !skill.IsInstance; } void DisableAIAutoAttackMove() { if (self.aiManager != null) { self.aiManager.EnableSearchTarget(false); } self.SetAIAutoAttackMove(false); } }
以上是关于U3D多点触控框架实例(下)的主要内容,如果未能解决你的问题,请参考以下文章
朝花夕拾Android自定义View篇之多点触控(下)实践出真知
有啥方法可以在 Android Emulator 上测试多点触控吗?