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);
        }
    }