Related Posts Plugin for WordPress, Blogger...

2-17 Player Melee Damage

今天要來製作玩家的範圍技!哈哈,不過還沒有動畫特效啦,先把程式碼的部分實作好。首先請大家下載課程提供的武器:

自己匯入。

匯入之後放在場景中。

然後請將武器物件放到Player中,結構如下圖。Player/Armature/Hips/Spine/Shoulder_R/Upper_Arm_R/Lower_Arm_R/Hand_R底下,簡言之就是右手下面啦。

然後請自己調整武器的位置,就像是握在手上。執行看看吧,開始遊戲後武器就會跟人物的動畫綁在一起。

以下提供程式碼,註解也寫在程式碼中嘍:
Player.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour, IDamageable {

 [SerializeField] float maxHealthPoints = 100f;
 [SerializeField] float damagePerHit = 50f;
 [SerializeField] float minTimeBetweenHits = 0.5f;
 [SerializeField] float maxAttackRange = 5f;

 GameObject currentTarget;
 float currentHealthPoint;
 float lastHitTime;
 CameraRaycaster cameraRaycaster;

 public float healthAsPercentage{
  get{ return currentHealthPoint / maxHealthPoints; }
 }

 void Start(){
  // 初始化血量
  currentHealthPoint = maxHealthPoints;

  cameraRaycaster = FindObjectOfType ();
  cameraRaycaster.notifyMouseClickObservers += OnMouseClick ;
 }

 void OnMouseClick(RaycastHit raycastHit, int layerHit){
  const int enemyLayer = 9;
  if (layerHit == enemyLayer) {
   GameObject enemy = raycastHit.collider.gameObject;

   // 確認敵人是否在範圍技的攻擊範圍內
   if ((enemy.transform.position - transform.position).magnitude > maxAttackRange) {
    return; // 超過攻擊範圍就跳出
   }

   // 控制攻擊頻率,若本次攻擊時間離上一次攻擊時間大於minTimeBetweenHits,才允許攻擊
   // 相當於範圍技的CD時間,不能一直發動技能!
   if (Time.time - lastHitTime > minTimeBetweenHits) {
    currentTarget = enemy;
    IDamageable enemyDamageable = enemy.GetComponent (typeof(IDamageable)) as IDamageable;
    // 讓敵人損血
    enemyDamageable.TakeDamage (damagePerHit);
    // 紀錄目前攻擊的時間
    lastHitTime = Time.time;
   }

  }
 }

 public void TakeDamage(float damage){
  // Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值
  currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints);
 }
}


Enemy.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityStandardAssets.Characters.ThirdPerson;

public class Enemy : MonoBehaviour, IDamageable {

 [SerializeField] float maxHealthPoints = 100f;
 [SerializeField] float attackRadius = 3.0f;
 [SerializeField] float chaseRadius = 10.0f;
 [SerializeField] float damagePerShot = 9f;
 [SerializeField] float secondsBetweenShots = 0.5f;
 [SerializeField] GameObject projectileToUse;
 [SerializeField] GameObject projectileSocket;
 [SerializeField] Vector3 aimOffset = new Vector3(0, 1f, 0);

 bool isAttacking = false;
 float currentHealthPoint = 100f;
 AICharacterControl aiCharacterControl = null;
 GameObject player;
 IEnumerator coroutine;

 public float healthAsPercentage{
  get{ return currentHealthPoint / maxHealthPoints; }
 }

 void Start(){
  aiCharacterControl = GetComponent ();
  player = GameObject.FindGameObjectWithTag ("Player");
 }

 void Update(){
  // 計算Player跟Enemy的距離
  float distanceToPlayer = Vector3.Distance (player.transform.position, transform.position);
  // 若彼此之間的距離小於attackRadius,就讓Enemy開始攻擊Player
  if (distanceToPlayer <= attackRadius && !isAttacking) {
   isAttacking = true;
   coroutine = SpawnProjectile ();
   StartCoroutine (coroutine);
  } 
  if (distanceToPlayer > attackRadius && isAttacking) {
   isAttacking = false;
   StopCoroutine (coroutine);
  }
  // 若彼此之間的距離小於chaseRadius,就讓Enemy開始追蹤Player
  if (distanceToPlayer <= chaseRadius) {
   aiCharacterControl.SetTarget (player.transform);
  } else {
   aiCharacterControl.SetTarget (transform);
  }
 }

 IEnumerator SpawnProjectile(){
  while (true) {
   yield return new WaitForSeconds (secondsBetweenShots);

   // Instantiate可以生成GameObject,Quaternion.identity為方向不進行任何轉向
   GameObject newProjectile = Instantiate(projectileToUse, projectileSocket.transform.position, Quaternion.identity);
   // 取得Projectile
   Projectile projectileComponent = newProjectile.GetComponent ();
   // 設定Projectile的攻擊威力
   projectileComponent.SetDamage(damagePerShot);
   // 計算發射Projectile到Player之間的單位向量
   Vector3 unitVectorToPlayer = (player.transform.position + aimOffset - projectileSocket.transform.position).normalized;
   float projectileSpeed = projectileComponent.projectileSpeed;
   // 將單位向量乘以發射速度,透過velocity將Projectile發射出去吧!
   newProjectile.GetComponent ().velocity = unitVectorToPlayer * projectileSpeed;
  }
 }

 public void TakeDamage(float damage){
  // Math.Clamp可以確保數值運算後不會低於最小值或者高於最大值
  currentHealthPoint = Mathf.Clamp (currentHealthPoint - damage, 0f, maxHealthPoints);
  // 敵人血量低於0就會消失
  if (currentHealthPoint <= 0) {
   Destroy (gameObject);
  }
 }

 void OnDrawGizmos(){
  //繪製攻擊範圍
  Gizmos.color = new Color(255f, 0f, 0f, 0.5f);
  Gizmos.DrawWireSphere (transform.position, attackRadius);

  //繪製移動範圍
  Gizmos.color = new Color(0f, 0f, 255f, 0.5f);
  Gizmos.DrawWireSphere (transform.position, chaseRadius);
 }
}


開始執行遊戲後,玩家點擊敵人就會走到敵人前面,並且在進入玩家的範圍攻擊內(玩家的紅色框框)才可以讓敵人損血,而且攻擊敵人時有我設定的CD時間0.5秒。

將敵人打死後,敵人就會消失。

留言