對金璽曾版《Unity3D手機遊戲開發》第三章“第一人稱射擊遊戲”修改,使支持殭屍連續攻擊

我個人覺得這本書寫的至少很和我口味,而且他的光盤資料也很詳盡,比如,一個實例,不僅有一個完整的實現工程,還有一份供作練習的工程(該工程中沒有要練習的部分,而資源啥的都有),讓人感覺很好。

這本書下載電子版也很方便,比如: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次,直到主角死亡,或者不再攻擊範圍而追擊主角。

發佈了61 篇原創文章 · 獲贊 34 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章