【ARPG/MOBA類游戲游戲系列】技能系統設計


    其實技能系統有很多種設計方式,暫且列出一種以前項目的設計。
    目標:
    1.使用盡可能少的類,實現游戲里所有種類的技能。

    2.滿足打擊感的可調節性、可配置性。 
    3.能基本滿足絕大部分角色扮演類游戲(比如ARPG、卡牌、Moba、ACT等),更換項目只需要對代碼做微調。 ACT類,連擊技能需要在技能類調用之前做計數,剩余的代碼一樣。

    需求分析:
        根據上面的目標,我們把攻擊分為幾個模塊  

       1.目標選擇   2.攻擊准備  3. 開始攻擊  4.受擊特效與動作  5.傷害結算、飆血浮動條、血條減少。  6.技能打斷

       注意。技能打斷,可配置該技能在XXX秒--XXX秒之間可被打斷。

       就不詳細解釋了,具體實現看代碼,有注釋。



   [mw_shl_code=applescript,true]public abstract class SkillBase 
{
    //技能狀態
    public SkillState State { get; protected set; }
    public int SkillID { get { return dataVo == null ? 0 : dataVo.ID; } }
    public SkillDataVo dataVo { get; protected set; }

    //特效
    protected GameObject effect;
    protected bool ifInitEffect = false; //是否已經播放了特效
    protected float effectBeginTime;
    protected float effectTime = 5f;     //特效時長

    //傷害
    protected bool damaged = false;     //是否已經計算了傷害


    //動畫
    protected float curAnimalLength;//當前動畫時長
    protected string animalName;
    protected float CurCd;
    protected SpriteController owner;
    protected float timer;

    //回收動作
    protected bool IfUseActionReturn;
    protected float ActionReturnTime;


    //攻擊目標、角度范圍、距離
    protected bool InForward(Transform trans, float _angle, float _dis)
    {
        if (null == trans)
        {
            return false;
        }



        // 獲得從當前Obj到目標Obj的方向,然后求和當前Obj的朝向夾角。
        Vector2 tarPos = new Vector2(trans.position.x, trans.position.z);
        Vector2 myPos = new Vector2(owner.transform.position.x, owner.transform.position.z);
        Vector2 mydir = new Vector2(owner.transform.forward.x, owner.transform.forward.z);
        Vector2 dir = tarPos - myPos;
        float angle = Vector2.Angle(mydir.normalized, dir.normalized);


if (angle < _angle / 2f)
        {

            float toPosDis = Vector2.Distance(tarPos, myPos);
            if (toPosDis <= _dis)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        return false;
    }


    public SkillBase(SpriteController obj, int skillID)
    {
        State = SkillState.None;
        this.owner = obj;
        this.CurCd = 0f;

        dataVo = ConfigDataManager.GetSkillDataByID(skillID);
        if (owner.ActionMng != null && owner.ActionMng.TryGetAnimationLength(dataVo.actionName, out curAnimalLength))
        {
            //curAnimalLength -= 0f;
        }
        else
            curAnimalLength = 1f;

        if (dataVo.BufferID.Length > 0)
        {
            dataVo.bufferEntity = new BufferDataVo[dataVo.BufferID.Length];
            for (int i = 0; i < dataVo.bufferEntity.Length; i++)
            {
                dataVo.bufferEntity = ConfigDataManager.GetConfigBufferData(dataVo.BufferID);
            }
        }
        animalName = dataVo.actionName;

        //加載特效
        GameObject effectPre = LoadCache.LoadEffect(dataVo.effectName);
        if (effectPre != null)
        {
            effect = GameObject.Instantiate(effectPre) as GameObject;
            effect.SetActive(false);
        }

    }

    //  Interrupts
    virtual public void SetInterrupts() { State = SkillState.Interrupts; }


    virtual public void ProcessBuffer(int index) {  }


    virtual public bool IsCanInterrupts() 
    {
        if (State == SkillState.None)
            return true;

        if (dataVo.minInterrupt != -1 && timer <= dataVo.minInterrupt)
            return true;

        if (dataVo.maxInterrupt != -1 && timer >= dataVo.maxInterrupt)
            return true;

        return false;
    }

    virtual public void Execute()  
    {
        switch (State)
        {
            case SkillState.Start:
                break;
            case SkillState.Execution:
                break;
            case SkillState.Interrupts:
                break;
            case SkillState.Finish:
                break;
        }
    
    }

    virtual public void Begin()
    {
        State = SkillState.Start;
    }

    //這是一個模
    virtual public void DoUpdate() 
    {
        if (State == SkillState.None)
        {
            return;
        }

      //  if (owner.mAIState == AIState.Dead)
         //   return;

        Execute();
        CalcCd();
    }


    public bool IsCanUse()
    {
        return State == SkillState.None;
    }

    //CD計時
    public void CalcCd()
    {
        CurCd += RealTime.deltaTime;
        if ( CurCd >= dataVo.CD)
        {
            CurCd = 0f;
        }
    }


}[/mw_shl_code]



我們編寫了一個超類,規定了技能的實現過程和方式、以及保存了技能的公有信息。
然后我們實現一個子類,能涵蓋絕大部分的技能。

[mw_shl_code=applescript,true]public class Skill_Default : SkillBase 
{

    private bool isMove;

    SkillEzMoveData moveData;

    Vector3 moveDir;

    public Skill_Default(SpriteController obj, int skillID)
        : base(obj, skillID)
    {
    }

    //遍歷場景所有怪物
    void ForALLEnmy()
    {
        Dictionary<long, SpriteController> dic = BattlerDataManager.Instance.DicMonsters;
        //List<long> tmpList = new List<long>();
        foreach (SpriteController controller in dic.Values)
        {
            if (controller.mAIState == AIState.Dead)

                continue;

            //技能的傷害判斷和處理
            bool inforward = InForward(controller.transform, dataVo.attackFanAngle, dataVo.attackFanRange);
            if (inforward)
            {
                int damageValue = dataVo.harm;
                owner.AttackTo(controller, damageValue);
Util.CallMethod("FightingPanel", "UpdateHeroHpMp", BattlerDataManager.Instance.SelfPlayer.SpiritVO.CurHp, BattlerDataManager.Instance.SelfPlayer.SpiritVO.CurMp);   //刷新角色血條

                //            if (BattlerDataManager.Instance.SelfPlayer.mAIState  == AIState.Dead)
                //            {
                //                //玩家死亡
                //
                //            }
                if (BattlerDataManager.Instance.SelfPlayer.SpiritVO.CurHp <= 0)
                {
                    //玩家死亡
                    // Util.CallMethod("FightingPanel", "Lose");
                }
            }
        }
    }



    public override void Execute()
    {

        switch (State)
        {
            case SkillState.Start:
                moveDir = owner.MoveToPostion; 
                timer = 0f;
                effectBeginTime = 0;
                owner.MoveMng.StopMove();
                isMove = false;
               // IfUseActionReturn = false;
                damaged = false;
                ifInitEffect = false;
                //執行
                owner.mAIState = AIState.Attack;
                owner.ActionMng.PlayAnimation(animalName);
                State = SkillState.Execution;

                Debug.Log("skill ID "+dataVo.ID);

                break;
            case SkillState.Interrupts:
                {
                    Debug.Log("被打斷");
                    State = SkillState.None;
                }
                break;
            case SkillState.Execution:
                {
                    timer += Time.deltaTime;

                    //觸發傷害
                    if (!damaged && timer > dataVo.damageTime)
                    {
                        damaged = true;
                        //傷害結算
                        ForALLEnmy();
                    }

                    //觸發特效
                    if (!ifInitEffect && timer > dataVo.fxStartTime)
                    {
                        if (effect != null)
                        {
                            effect.transform.position = owner.transform.position + new Vector3(0, 0.5f, 0)+owner.transform.forward*2;
                            effect.transform.rotation = owner.transform.rotation;
                            effect.SetActive(false);
                            effect.SetActive(true);
                        }

                        ifInitEffect = true;
                    }

                    //動畫播放結束,並且觸發了特效,觸發了傷害
                    if (timer >= curAnimalLength && damaged && ifInitEffect)
                    {
                        State = SkillState.Finish;
                    }
                    
                }
            break;

            case SkillState.Finish:
                {
                    Debug.Log("Finish");
  if (effect != null)
   effect.SetActive(false);
                    owner.mAIState = AIState.Idle;
                    State = SkillState.None;

                }
                break;
        }

    }
}
[/mw_shl_code]

有注釋的情況下,應該不難看懂,拋磚引玉。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM