十三章:角色攻擊系統
角色攻擊是殺怪時的核心功能,攻擊模式又可細分爲普通攻擊和技能攻擊,其中技能攻擊的信息存儲在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(); //更新顯示
}
至此,角色攻擊系統以及與任務系統的交互就大體實現了。