B站Unity學習筆記
連結:https://www.bilibili.com/video/BV12s411g7gU
敵人
策劃
需求分析
代碼實作
-
敵人馬達EnemyMotorEnemyMotor類
-定義朝向目标點旋轉的方法
-定義尋路的方法
-定義向前移動的方法
/// <summary>
/// 敵人馬達類
/// 提供移動、旋轉、尋路功能
/// </summary>
public class EnemyMotor : MonoBehaviour
{
public WayLine line;
//目前路點的索引預設是0
private int currentPointIndex;
public float moveSpeed = 5;
/// <summary>
/// 向前移動
/// </summary>
public void MovementForward()
{
this.transform.Translate(0,0,moveSpeed*Time.deltaTime);
}
/// <summary>
/// 注視旋轉
/// </summary>
/// <param name="point">需要注視的目标點</param>
public void LookRotation(Vector3 targetPoint)
{
//目前物體注視目标點旋轉
this.transform.LookAt(targetPoint);
}
/// <summary>
/// 尋路,沿路線(Vector3[])移動
/// </summary>
/// <returns>傳回尋路結果</returns>
public bool Pathfinding()
{
//return false; 到達終點,無需尋路
//return true; 需要尋路
if (line.WayPoints == null ||currentPointIndex>= line.WayPoints.Length) return false;
//如果到達目标點(判斷 目前位置 和 目标點之間的距離(Vector3.Distance))
//更新目标點(向下一個路點移動)
//朝向目标點
LookRotation(line.WayPoints[currentPointIndex]);
//向前移動
MovementForward();
if (Vector3.Distance(this.transform.position, line.WayPoints[currentPointIndex])<0.1f)
{
currentPointIndex++;
}
return true;
}
2.敵人狀态資訊EnemyStatusInfo類
- 定義變量:目前生命值,目前生命值
- 定義方法:受傷,死亡。
/// <summary>
/// 敵人狀态資訊
/// 定義敵人資訊,提供受傷、死亡功能;
/// </summary>
public class EnemyStatusInfo : MonoBehaviour
{
public EnemySpawn spawn;
//目前血量
public float enemyHP = 200;
//最大血量
public float maxHP = 200;
/// <summary>
/// 受傷
/// </summary>
/// <param name="amount">需要扣除的血量</param>
public void Damage(float amount)
{
//扣血
enemyHP -= amount;
//血量為0時,調用死亡方法
if (enemyHP <= 0)
{
Deadth();
}
}
//延遲銷毀時間
public float deathDelay = 5;
/// <summary>
/// 死亡
/// </summary>
public void Deadth()
{
//播放死亡動畫
var anim = GetComponent<EnemyAnimation>();
anim.action.Playing(anim.deathAnimName);
//銷毀目前物體
Destroy(this.gameObject,deathDelay);
Debug.Log("我死了");
//設定路線狀态
GetComponent<EnemyMotor>().line.IsUsable = true;
//産生下一個敵人
spawn.GenerateEnemy();
}
}
3.敵人動畫EnemyAnimation類
- 定義各種動畫名稱的變量,如跑步、閑置、攻擊。
- 定義AnimationAction類,提供有關動畫的行為。
/// <summary>
/// 敵人動畫
/// 定義需要播放的動畫名稱
/// </summary>
public class EnemyAnimation : MonoBehaviour
{
//跑步動畫名稱
public string runAnimName;
//攻擊動畫名稱
public string attackAnimName;
//閑置動畫名稱
public string idleAnimName;
//死亡動畫名稱
public string deathAnimName;
//行為類
public AnimationAction action;
public void Awake()
{
action = new AnimationAction(GetComponentInChildren<Animation>());
}
}
4.動畫行為類 AnimationAction
/// <summary>
/// 動畫行為類
/// 提供有關動畫的行為
/// </summary>
public class AnimationAction
{
//附加在敵人模型上的動畫元件引用
private Animation anim;
/// <summary>
/// 建立動畫行為類
/// </summary>
/// <param name="anim">附加在敵人模型上的動畫元件引用</param>
public AnimationAction(Animation anim)
{
this.anim = anim;
}
/// <summary>
/// 播放動畫
/// </summary>
/// <param name="animName">需要播放動畫的名稱</param>
public void Playing(string animName)
{
anim.CrossFade(animName);
}
/// <summary>
/// 判斷指定動畫是否在播放
/// </summary>
/// <param name="animName">動畫名稱</param>
/// <returns></returns>
public bool IsPlaying(string animName)
{
return anim.IsPlaying(animName);
}
}
5.敵人人工智能EnemyAI
- 擷取[敵人馬達、敵人動畫]腳本對象引用。
- 定義枚舉:敵人狀态State
- 每幀根據狀态,執行尋路或攻擊
/// <summary>
/// 敵人AI
/// </summary>
[RequireComponent(typeof(EnemyAnimation))]
[RequireComponent(typeof(EnemyMotor))]
[RequireComponent(typeof(EnemyStatusInfo))]
public class EnemyAI : MonoBehaviour
{
//定義敵人狀态的枚舉類型
public enum State
{
Attack, //攻擊狀态
Pathfinding //尋路狀态
}
private State currentState = State.Pathfinding;
private EnemyAnimation anim;
private EnemyMotor motor;
private void Start()
{
anim = GetComponent<EnemyAnimation>();
motor = GetComponent<EnemyMotor>();
}
//攻擊計時器
private float atkTimer;
//攻擊間隔
private float atkInterval = 2;
private void Update()
{
//判斷
switch (currentState)
{
case State.Pathfinding:
Pathfinding();
break;
case State.Attack:
Attack();
break;
}
}
private void Pathfinding()
{
//播放跑步動畫
anim.action.Playing(anim.runAnimName);
//執行尋路 調用motor類中的尋路方法
//如果尋路結束,修改狀态為攻擊
if (motor.Pathfinding() == false) { currentState = State.Attack; };
}
private void Attack()
{
//如果攻擊動畫沒有播放
if (anim.action.IsPlaying(anim.attackAnimName) == false)
{
//播放閑置動畫
anim.action.Playing(anim.idleAnimName);
}
if (atkTimer <= Time.time)
{
//執行攻擊
//播放攻擊動畫
anim.action.Playing(anim.attackAnimName);
atkTimer = Time.time + atkInterval;
}
}
}
敵人生成器
策劃
需求分析
1.敵人生成器EnemySpawn類
-
定義變量
WayLine[] lines用于存儲所有路線
GameObject[] enemyTyoes用于記錄敵人預制件
int startCount 用于記錄開始時需要建立的敵人數量
int spawnedCount 用于記錄已經産生的敵人數量
int maxCount 用于記錄産生敵人數量的上限
int maxDelay 用于記錄産生敵人的最大延遲時間
-
定義方法
CalculateWayLines() 用于計算所有路線及坐标
GenerateEnemy(),用于生成一個敵人
/// <summary>
/// 敵人生成器
/// </summary>
public class EnemySpawn : MonoBehaviour
{
public GameObject[] enemyType;
//需要建立的敵人最大數目
public int maxCount = 5;
//開始同時建立的敵人數目
public int startCount = 2;
//已經建立的敵人數量
private int spawnedCount;
public int maxDelay = 10;
/// <summary>
/// 生成一個敵人
/// </summary>
public void GenerateEnemy()
{
spawnedCount++;
if (spawnedCount>=maxCount)
{
return;
}
//延遲時間随機
Invoke("CreateEnemy",Random.Range(1,maxDelay));
}
private void CreateEnemy()
{
//選擇一條可以使用的路線
//選擇所有可以使用的路線
WayLine[] usableWayLines = SelectUsableWayLine();
//随機選擇一條
WayLine line = usableWayLines[Random.Range(0, usableWayLines.Length)];
int randomIndex = Random.Range(0, enemyType.Length);
//建立敵人
//Instantiate(敵人預制件,位置,旋轉角度);
GameObject go = Instantiate(enemyType[randomIndex], line.WayPoints[0], Quaternion.identity) as GameObject;
//配置資訊
EnemyMotor motor = go.GetComponent<EnemyMotor>();
motor.line = line;
line.IsUsable = false; //不可以使用
//傳遞生成器對象引用[建議使用委托代替]
go.GetComponent<EnemyStatusInfo>().spawn = this;
}
private void Start()
{
CalculateWaylines();
}
private WayLine[] lines;
private void CalculateWaylines()
{
//路線數組
lines = new WayLine[this.transform.childCount];
for (int i = 0; i < lines.Length; i++)
{
//每一個路線
//路線變換元件的引用
Transform wayLineTF = this.transform.GetChild(i);
//路點數量
int count = wayLineTF.childCount;
//建立路線對象
lines[i] = new WayLine(count);
lines[i].IsUsable = true;
//建立路點數組對象
lines[i].WayPoints = new Vector3[count];
for (int pointIndex = 0; pointIndex < count; pointIndex++)
{
lines[i].WayPoints[pointIndex] = wayLineTF.GetChild(pointIndex).position;
}
}
}
/// <summary>
/// 選擇所有可以使用的路線
/// </summary>
/// <returns></returns>
private WayLine[] SelectUsableWayLine()
{
List<WayLine> result = new List<WayLine>(lines.Length);
//周遊所有路線
foreach (var item in lines)
{
//如果可以使用,添加到result中
if (item.IsUsable) result.Add(item);
}
return result.ToArray();
}
}
2.Wayline類
public class WayLine
{
/// <summary>
/// 目前路線所有路點坐标
/// </summary>
public Vector3[] WayPoints { get; set; }
/// <summary>
/// 是否可用
/// </summary>
public bool IsUsable { get; set; }
public WayLine(int wayPointCount)
{
WayPoints = new Vector3[wayPointCount];
IsUsable = true;
}
}