RPG遊戲《黑暗之光》流程介紹與代碼分析之(十三):角色攻擊系統的實現

十三章:角色攻擊系統

角色攻擊是殺怪時的核心功能,攻擊模式又可細分爲普通攻擊和技能攻擊,其中技能攻擊的信息存儲在SkillInfoInList,本章節只涉及普通攻擊部分。
爲Magician添加一個PlayerAttack腳本,控制攻擊
public enum PlayerState{
    normalWalk,
    normalAttack,
    skillAttack
}

    PlayerState state = PlayerState.normalWalk;    //默認爲Walk狀態,在PlayerMove腳本中也要通過Walk狀態控制普通狀態下的移動,當狀態爲Walk時,才能進行移動。
    public string aniName_normalAttack;    //攻擊動畫,這些屬性與小狼類似
    public float normalPlayerAttackTime;
    private float playerAttackTimer = 0;
    public float minPlayerAttackDistance = 5;
    private Transform normalAttackTarget;    //攻擊目標

    void Update()
    {
        if (Input.GetMouseButtonDown (1))    //點擊右鍵攻擊
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);    //與主角移動類似,參考(鏈接)
            RaycastHit hitInfo;
            bool isCollider = Physics.Raycast(ray,out hitInfo);
            if(isCollider && hitInfo.collider.tag == Tags.enemy)    //目標爲enemy
            {
                normalAttackTarget = hitInfo.collider.transform;    //傳遞目標
                state = PlayerState.normalAttack;    //切換攻擊狀態
            }
            else    //若點擊別的地方,則切換爲Walk狀態
            {
                state = PlayerState.normalWalk;    
                normalAttackTarget = null;
            }
        }

    }

13.1 普通攻擊狀態

清楚了基本的角色攻擊狀態後,本節實現當state == PlayerState.normalAttack時的攻擊狀態。
首先當主角進行攻擊時,要判斷主角和怪物的距離。若超出攻擊距離,主角進行移動並攻擊;若未超出,直接攻擊。在PlayerAttack腳本中
        if(normalAttackTarget != null)    //選定目標後,判斷距離
        {
            float distance = Vector3.Distance(transform.position,normalAttackTarget);
            if(distance > minPlayerAttackDistance)
            {
                 PlayerMove._instance.SimpleMove(normalAttackTarget.position);    //從PlayerMove中調用移動方法

            }
            else
            {
                  //直接攻擊
            }
        }
其中SimpleMove()函數實現如下
    public void SimpleMove(Vector3 target)
    {
        transform.LookAt (target);
        controller.SimpleMove (transform.forward * speed);
    }
在攻擊時,我們也有不同的動畫,包括處在攻擊狀態下的移動、攻擊以及攻擊休息時間的動畫。因此需要對PlayerAttack腳本再添加AttackState狀態,這三種狀態對應三種不同的動畫。
public enum AttackState{
    Moving,
    Idle,
    Attack
}
並在控制角色動畫的PlayerAnimation腳本中通過當前AttackState的狀態,分配不同的動畫
   if (attack.state == PlayerState.normalWalk)
        {
            if (move.state == PlayerWalkState.Moving)    
            {
                PlayAnim ("Run");    //普通狀態下的"Run"
            }
            else if (move.state == PlayerWalkState.Idle)
            {
                PlayAnim ("Idle");
                        
            }
        }
        else if (attack.state == PlayerState.normalAttack)
        {
            if(attack.atkState == AttackState.Moving)
            {
                PlayAnim("Run");    //攻擊狀態下的"Run"
            }
        }
在PlayerAttack下的if(distance > minPlayerAttackDistance)中加入攻擊狀態下的移動狀態即可
if(distance > minPlayerAttackDistance)
            {
                atkState = AttackState.Moving;    //移動的過程中播放動畫
                PlayerMove._instance.SimpleMove(normalAttackTarget.position);
            }
就實現了攻擊狀態下移動時的動畫播放。

接下來實現攻擊時的休息狀態,添加兩個string,用以切換攻擊狀態和休息狀態
    public string aniName_idleOfRest;    //攻擊間隔的休息動畫
    public string aniName_now;    //當前攻擊狀態下的動畫
當distance <= minPlayerAttackDistance,即進入攻擊範圍時,我們添加代碼
                if(distance <= minPlayerAttackDistance)
                {
                transform.LookAt(normalAttackTarget);
                atkState = AttackState.Attack;    //切換到攻擊狀態下的攻擊模式
                playerAttackTimer += Time.deltaTime;    //計時器開啓
                animation.CrossFade(aniName_now);    //播放當前動畫,默認爲aniName_normalAttack
                if(playerAttackTimer >= normalPlayerAttackTime)
                {
                    aniName_now = aniName_idleOfRest;    //攻擊效果結束後,當前狀態爲aniName_idleOfRest
                }
                if(playerAttackTimer >= 1f/attackRate)    //攻擊間隔達到後,進入下一次攻擊
                {
                    playerAttackTimer = 0;
                    aniName_now = aniName_normalAttack;
                }
            }
即可實現AttackState下的三種狀態。

13.2 攻擊指針與攻擊特效

13.2.1 攻擊指針

在攻擊時,我們需要改變鼠標指針,並且在攻擊動畫播放完成後顯示攻擊特效。
我們參照4.3節(鏈接)使用繼承的方法,在鼠標放在小狼身上時出現不同的效果,有攻擊圖標和技能圖標。
新建一個AttackCursor腳本,輸入
    void OnMouseEnter()
    {
        MouseSetting._instance.SetNormalAttackCursor ();
    }
    
    void OnMouseExit()
    {
        MouseSetting._instance.SetNormalCursor ();
    }
並在MouseSetting中設置SetNormalAttackCursor(),如下
    public Texture2D cursor_attack;
    public void SetNormalAttackCursor()
    {
        Cursor.SetCursor (cursor_attack, hotspot, mode);
    }
之後讓WolfBaby繼承自AttackCursor腳本,即可實現鼠標的變換功能。

補充:考慮到使用技能時的攻擊圖標與普通攻擊衝突,我們可以不用繼承的方式,在WolfBaby上用
    void OnMouseEnter()
    {
        MouseSetting._instance.SetNormalAttackCursor ();
    }
    
    void OnMouseExit()
    {
        MouseSetting._instance.SetNormalCursor ();
    }
直接替換。

13.2.2 攻擊特效

我們使用RPG——>Effect——>Prefabs中的Effect_Slash作爲攻擊時的閃光特效,在PlayerAttack中創建一個GameObject並導入特效的Prefab

並在攻擊結束時創建
    private bool effectFlag = false;
                if(playerAttackTimer >= normalPlayerAttackTime)
                {
                    aniName_now = aniName_idleOfRest;
                    if(effectFlag == false)
                    {
                        effectFlag = true;
                        GameObject.Instantiate(effect,normalAttackTarget.position,Quaternion.identity);    //添加特效
                    }
                }
即可顯示攻擊閃光,如下圖所示

在顯示特效的同時造成傷害,需要訪問PlayerStatus的人物攻擊屬性和Equipment中的裝備加成屬性。
    private PlayerStatus ps;
    void Awake()
    {
        ps = this.GetComponent<PlayerStatus> ();
    }
    public int GetAttack()
    {
        return Equipment._instance.attack + ps.attack + ps.attack_plus;    //裝備、基礎和加點攻擊力
    }
之後在產生特效之後加入如下代碼即可
normalAttackTarget.GetComponent<WolfBaby>().BeDamaged(GetAttack());
即可看到效果

13.3 野怪的自動生成

我們在Hierarchy中新建一個空的遊戲物體,用作小狼的巢穴,命名爲BabySpawn,爲其添加腳本BabySpawn,源源不斷地孵化小狼。
public static BabySpawn _instance;
    private int maxNumber = 5;    //最大數量與當前數量,控制是否繼續添加小狼
    public int currentNumber = 0;
    private float createTimer = 3;    //創建的時間間隔
    private float timer = 0;
    public GameObject prefab;    //小狼的prefab

    void Awake()
    {
        _instance = this;
    }

    void Update()
    {
        if (currentNumber < maxNumber)    //滿足創建條件
        {
            timer += Time.deltaTime;
            if(timer >= createTimer)
            {
                Vector3 pos = transform.position;    //創建時隨機生成一個位置。
                pos.x += Random.Range(-3,3);
                pos.z += Random.Range(-5,5);
                GameObject.Instantiate(prefab,pos,Quaternion.identity);
                timer = 0;
                ++currentNumber;    
            }
        }
    }
當小狼死亡時,即WolfBaby中hp<0時,調節currentNumber,即
BabySpawn._instance.currentNumber -= 1;
即可實現野怪的刷新功能。

13.4 怪物與狀態的交互

殺死小狼時,我們要添加經驗值等信息,並更新任務信息
我們調用PlayerStatus中的GetExp()函數和BarNPC中的PlusWolfKilledNumber()函數,實現如下
    public void GetExp(float exp)
    {
        this.expCurrent += exp;
        int totalExp = 100 + this.level * 30;
        while (this.expCurrent >= totalExp)
        {
            ++level;
            expCurrent -= totalExp;
            point_remain += 5;
            totalExp = 100 + this.level * 30;
        }
        EXPBar._instance.SetValue (this.expCurrent / totalExp);
    }

    public void PlusWolfKilledNumber()
    {
        if (isOnTask)
        {
            killWolfNumber += 1;    
        }
    }
並在WolfBaby腳本中進行調用
if (hp <= 0)
            {
                UpdatePara();    //更新這些數據參數
                state = WolfBabyState.Death;
                Destroy (this.gameObject, 2);
                GameObject.Destroy(HUDTextGO);
                        
            }
其中 UpdatePara()實現如下
    void UpdatePara()
    {
        BabySpawn._instance.currentNumber -= 1;
        ps.GetExp(exp);
        BarNPC._instance.PlusWolfKilledNumber();
    }
當任務完成後,我們需要獲得獎勵,如上右圖所示,即點擊上左圖的OK按鈕,提交任務,領取獎勵。
    public void OnAcceptQuest()
    {
        Inventory._instance.EarnMoney (1000);
        isOnTask = true;
        ShowTaskProgress ();
    }
其中EarnMoney()函數實現如下
    public void EarnMoney(int money)
    {
        coinCount += money;
        coinNumberLabel.text = coinCount.ToString();    //更新顯示
    }
至此,角色攻擊系統以及與任務系統的交互就大體實現了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章