我個人覺得這本書寫的至少很和我口味,而且他的光盤資料也很詳盡,比如,一個實例,不僅有一個完整的實現工程,還有一份供作練習的工程(該工程中沒有要練習的部分,而資源啥的都有),讓人感覺很好。
這本書下載電子版也很方便,比如:http://download.csdn.net/detail/u012337666/8402165。
我接下來具體說說自己對 第三章“第一人稱射擊遊戲”中的改動。
先上改動前的源代碼:
using UnityEngine;
using System.Collections;
[AddComponentMenu("Game/Enemy")]
public class Enemy : MonoBehaviour {
// Transform組件
Transform m_transform;
//CharacterController m_ch;
// 動畫組件
Animator m_ani;
// 尋路組件
NavMeshAgent m_agent;
// 主角
Player m_player;
// 角色移動速度
float m_movSpeed = 0.5f;
// 角色旋轉速度
float m_rotSpeed = 120;
// 計時器
float m_timer=2;
// 生命值
int m_life = 15;
// 成生點
protected EnemySpawn m_spawn;
// Use this for initialization
void Start () {
// 獲取組件
m_transform = this.transform;
m_ani = this.GetComponent<Animator>();
m_agent = GetComponent<NavMeshAgent>();
// 獲得主角
m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
}
// 初始化
public void Init(EnemySpawn spawn)
{
m_spawn = spawn;
m_spawn.m_enemyCount++;
}
// 當被銷燬時
public void OnDeath()
{
//更新敵人數量
m_spawn.m_enemyCount--;
// 加100分
GameManager.Instance.SetScore(100);
// 銷燬
Destroy(this.gameObject);
}
// Update is called once per frame
void Update () {
// 如果主角生命爲0,什麼也不做
if (m_player.m_life <= 0)
return;
// 獲取當前動畫狀態
AnimatorStateInfo stateInfo = m_ani.GetCurrentAnimatorStateInfo(0);
// 如果處於待機狀態
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.idle") && !m_ani.IsInTransition(0))
{
m_ani.SetBool("idle", false);
// 待機一定時間
m_timer -= Time.deltaTime;
if (m_timer > 0)
return;
// 如果距離主角小於1.5米,進入攻擊動畫狀態
if (Vector3.Distance(m_transform.position, m_player. m_transform.position) < 1.5f)
{
m_ani.SetBool("attack", true);
}
else
{
// 重置定時器
m_timer=1;
// 設置尋路目標點
m_agent.SetDestination(m_player. m_transform.position);
// 進入跑步動畫狀態
m_ani.SetBool("run", true);
}
}
// 如果處於跑步狀態
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.run") && !m_ani.IsInTransition(0))
{
m_ani.SetBool("run", false);
// 每隔1秒重新定位主角的位置
m_timer -= Time.deltaTime;
if (m_timer < 0)
{
m_agent.SetDestination(m_player. m_transform.position);
m_timer = 1;
}
// 追向主角
MoveTo();
// 如果距離主角小於1.5米,向主角攻擊
if (Vector3.Distance(m_transform.position, m_player. m_transform.position) <= 1.5f)
{
//停止尋路
m_agent.ResetPath();
// 進入攻擊狀態
m_ani.SetBool("attack", true);
}
}
// 如果處於攻擊狀態
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.attack") && !m_ani.IsInTransition(0))
{
// 面向主角
RotateTo();
m_ani.SetBool("attack", false);
// 如果攻擊動畫播完,重新進入待機狀態
if (stateInfo.normalizedTime >= 1.0f)
{
m_ani.SetBool("idle", true);
// 重置計時器
m_timer = 2;
m_player.OnDamage(1);
}
}
// 死亡
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.death") && !m_ani.IsInTransition(0))
{
if (stateInfo.normalizedTime >= 1.0f)
{
OnDeath();
}
}
}
// 轉向目標點
void RotateTo()
{
// 當前角度
Vector3 oldangle = m_transform.eulerAngles;
// 獲得面向主角的角度
m_transform.LookAt(m_player.m_transform);
float target = m_transform.eulerAngles.y;
// 轉向主角
float speed = m_rotSpeed * Time.deltaTime;
float angle = Mathf.MoveTowardsAngle(oldangle.y, target, speed);
m_transform.eulerAngles = new Vector3(0, angle, 0);
}
// 尋路移動
void MoveTo()
{
float speed = m_movSpeed * Time.deltaTime;
m_agent.Move(m_transform.TransformDirection((new Vector3(0, 0, speed))));
}
// 傷害
public void OnDamage(int damage)
{
m_life -= damage;
// 如果生命爲0,銷燬自身
if (m_life <= 0)
{
m_ani.SetBool("death", true);
}
}
}
我發現以書中的邏輯:殭屍每次到達攻擊範圍後,只會發起一次攻擊,然後就轉爲IDEL狀態,然後再檢測是否有攻擊,沒有則繼續追有則再發起攻擊。
感覺運行起來的效果不是太好,因爲殭屍只攻擊一次(也就是揮一次手臂),然後就恢復站立狀態,等下再揮,很不像殭屍的作風。至少也得連續揮舞吧。
連續揮舞有兩種實現方式:
1.設置Attack的動畫爲Loop,然後將上述代碼中的這一句if(stateInfo.normalizedTime > 1.0f),將1.0f稍微該大些,這樣殭屍就會揮動很多次手臂。
但我覺得這種方式直接改掉了Attack這個動畫本來的意義,不採用
2.對Animator中的Attack狀態做些變化,目前Attack這個狀態有下面的3個狀態變化,
那麼目前我就需要 一個attack -> attack的狀態變化,以實現attack的連續執行。
設置如下:
這樣就建立了attack->attack的動畫過渡效果,然後還需要代碼控制這一狀態,於是再設置:
這樣,當bool值attack的值爲False時,Attack這個動畫就不再重複了。
我改動的代碼如下:
if (stateInfo.fullPathHash == Animator.StringToHash ("Base Layer.attack")
&& !m_ani.IsInTransition(0)) {
RotateTo ();
if (m_timer > 0) {
m_timer -= Time.deltaTime;
return;
}
m_ani.SetBool ("attack", false);
if (stateInfo.normalizedTime >= 1.0f) {
m_ani.SetBool ("idle", true);
m_timer = 0.1f;
}
}
這裏,我將在RotateTo後面等待m_Timer的時間,這個時間也就是動畫重複播放的時間,這個時間過之後,我就會設置Attack的值爲False,然後當再播放完一次時,就設置idle爲true,然後程序就會轉而執行idle的部分了。
總體效果就是:殭屍到達攻擊範圍,攻擊N次,然後停止,再繼續攻擊N次,直到主角死亡,或者不再攻擊範圍而追擊主角。