程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> Unity手游之路<十>自動尋路Navmesh之跳躍,攀爬,斜坡,

Unity手游之路<十>自動尋路Navmesh之跳躍,攀爬,斜坡,

編輯:C#入門知識

Unity手游之路<十>自動尋路Navmesh之跳躍,攀爬,斜坡,


 轉載

Unity手游之路<十>自動尋路Navmesh之跳躍,攀爬,斜坡

分類: unity2013-12-27 00:50 6545人閱讀 評論(5) 收藏 舉報 unity3dNavmesh手游自動尋路

在之前的幾篇Blog總,我們已經系統學習了自動尋路插件Navmesh的相關概念和細節。然而,如果要做一個場景精美的手游,需要用到各種復雜的場景地形,而不僅僅是平地上的自動尋路。今天我們將通過一個完整的復雜的實例,來貫穿各個細節。我們將實現一個復雜的場景,角色可以在裡面攀爬,跳躍,爬坡。是不是感覺很像當年的CS游戲呢?本案例將會用得一些基本的動畫函數,大家可以先結合文檔有個大概的了解。本實例是在官方的范例上加工而成。

(轉載請注明原文地址http://blog.csdn.net/janeky/article/details/17598113)

  • 步驟
1.在場景中擺放各種模型,包括地板,斜坡,山體,扶梯等
2.為所有的模型加上Navigation Static和OffMeshLink Generatic(這個根據需要,例如地板與斜坡相連,斜坡就不需要添加OffMeshLink)
3.特殊處理扶梯,需要手動添加Off Mesh Link,設置好開始點和結束點
4.保存場景,烘焙場景
5.添加角色模型,為其加Nav Mesh Agent組件
6.為角色添加一個新腳本,AgentLocomotion.cs,用來處理自動尋路,已經角色動畫變換。代碼比較長,大家可以結合注釋來理解
[csharp] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. using UnityEngine;  
    using System.Collections;  
      
    public class AgentLocomotion : MonoBehaviour  
    {  
        private Vector3 target;//目標位置  
        private NavMeshAgent agent;  
        private Animation anim;//動畫  
        private string locoState = "Locomotion_Stand";  
        private Vector3 linkStart;//OffMeshLink的開始點  
        private Vector3 linkEnd;//OffMeshLink的結束點  
        private Quaternion linkRotate;//OffMeshLink的旋轉  
        private bool begin;//是否開始尋路  
      
        // Use this for initialization  
        void Start()  
        {  
            agent = GetComponent<NavMeshAgent>();  
            //自動移動並關閉OffMeshLinks,即在兩個隔離障礙物直接生成的OffMeshLink,agent不會自動越過  
            agent.autoTraverseOffMeshLink = false;  
            //創建動畫  
            AnimationSetup();  
            //起一個協程,處理動畫狀態機  
            StartCoroutine(AnimationStateMachine());  
        }  
      
        void Update()  
        {  
            //鼠標左鍵點擊  
            if (Input.GetMouseButtonDown(0))  
            {  
                //攝像機到點擊位置的的射線  
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
                RaycastHit hit;  
                if (Physics.Raycast(ray, out hit))  
                {  
                    //判斷點擊的是否地形  
                    if (hit.collider.tag.Equals("Obstacle"))  
                    {  
                        begin = true;  
                        //點擊位置坐標  
                        target = hit.point;  
                    }  
                }  
            }  
            //每一幀,設置目標點  
            if (begin)  
            {  
                agent.SetDestination(target);  
            }  
        }  
      
        IEnumerator AnimationStateMachine()  
        {  
            //根據locoState不同的狀態來處理,調用相關的函數  
            while (Application.isPlaying)  
            {  
                yield return StartCoroutine(locoState);  
            }  
        }  
      
        //站立  
        IEnumerator Locomotion_Stand()  
        {  
            do  
            {  
                UpdateAnimationBlend();  
                yield return new WaitForSeconds(0);  
            } while (agent.remainingDistance == 0);  
            //未到達目標點,轉到下一個狀態Locomotion_Move  
            locoState = "Locomotion_Move";  
            yield return null;  
        }  
      
        IEnumerator Locomotion_Move()  
        {  
            do  
            {  
                UpdateAnimationBlend();  
                yield return new WaitForSeconds(0);  
                //角色處於OffMeshLink,根據不同的地點,選擇不同動畫  
                if (agent.isOnOffMeshLink)  
                {  
                    locoState = SelectLinkAnimation();  
                    return (true);  
                }  
            } while (agent.remainingDistance != 0);  
            //已經到達目標點,狀態轉為Stand  
            locoState = "Locomotion_Stand";  
            yield return null;  
        }  
      
        IEnumerator Locomotion_Jump()  
        {  
            //播放跳躍動畫  
            string linkAnim = "RunJump";  
            Vector3 posStart = transform.position;  
      
            agent.Stop(true);  
            anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);  
            transform.rotation = linkRotate;  
      
            do  
            {  
                //計算新的位置  
                float tlerp = anim[linkAnim].normalizedTime;  
                Vector3 newPos = Vector3.Lerp(posStart, linkEnd, tlerp);  
                newPos.y += 0.4f * Mathf.Sin(3.14159f * tlerp);  
                transform.position = newPos;  
      
                yield return new WaitForSeconds(0);  
            } while (anim[linkAnim].normalizedTime < 1);  
            //動畫恢復到Idle  
            anim.Play("Idle");  
            agent.CompleteOffMeshLink();  
            agent.Resume();  
            //下一個狀態為Stand  
            transform.position = linkEnd;  
            locoState = "Locomotion_Stand";  
            yield return null;  
        }  
        //梯子  
        IEnumerator Locomotion_Ladder()  
        {  
            //梯子的中心位置  
            Vector3 linkCenter = (linkStart + linkEnd) * 0.5f;  
            string linkAnim;  
            //判斷是在梯子上還是梯子下  
            if (transform.position.y > linkCenter.y)  
                linkAnim = "Ladder Down";  
            else  
                linkAnim = "Ladder Up";  
      
            agent.Stop(true);  
      
            Quaternion startRot = transform.rotation;  
            Vector3 startPos = transform.position;  
            float blendTime = 0.2f;  
            float tblend = 0f;  
      
            //角色的位置插值變化(0.2內變化)  
            do  
            {  
                transform.position = Vector3.Lerp(startPos, linkStart, tblend / blendTime);  
                transform.rotation = Quaternion.Lerp(startRot, linkRotate, tblend / blendTime);  
      
                yield return new WaitForSeconds(0);  
                tblend += Time.deltaTime;  
            } while (tblend < blendTime);  
            //設置位置  
            transform.position = linkStart;  
            //播放動畫  
            anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);  
            agent.ActivateCurrentOffMeshLink(false);  
            //等待動畫結束  
            do  
            {  
                yield return new WaitForSeconds(0);  
            } while (anim[linkAnim].normalizedTime < 1);  
            agent.ActivateCurrentOffMeshLink(true);  
            //恢復Idle狀態  
            anim.Play("Idle");  
            transform.position = linkEnd;  
            agent.CompleteOffMeshLink();  
            agent.Resume();  
            //下一個狀態Stand  
            locoState = "Locomotion_Stand";  
            yield return null;  
        }  
      
        private string SelectLinkAnimation()  
        {  
            //獲得當前的OffMeshLink數據  
            OffMeshLinkData link = agent.currentOffMeshLinkData;  
            //計算角色當前是在link的開始點還是結束點(因為OffMeshLink是雙向的)  
            float distS = (transform.position - link.startPos).magnitude;  
            float distE = (transform.position - link.endPos).magnitude;  
      
            if (distS < distE)  
            {  
                linkStart = link.startPos;  
                linkEnd = link.endPos;  
            }  
            else  
            {  
                linkStart = link.endPos;  
                linkEnd = link.startPos;  
            }  
            //OffMeshLink的方向  
            Vector3 alignDir = linkEnd - linkStart;  
            //忽略y軸  
            alignDir.y = 0;  
            //計算旋轉角度  
            linkRotate = Quaternion.LookRotation(alignDir);  
      
            //判斷OffMeshLink是手動的(樓梯)還是自動生成的(跳躍)  
            if (link.linkType == OffMeshLinkType.LinkTypeManual)  
            {  
                return ("Locomotion_Ladder");  
            }  
            else  
            {  
                return ("Locomotion_Jump");  
            }  
        }  
      
        private void AnimationSetup()  
        {  
            anim = GetComponent<Animation>();  
      
            // 把walk和run動畫放到同一層,然後同步他們的速度。  
            anim["Walk"].layer = 1;  
            anim["Run"].layer = 1;  
            anim.SyncLayer(1);  
      
            //設置“跳躍”,“爬樓梯”,“下樓梯”的動畫模式和速度  
            anim["RunJump"].wrapMode = WrapMode.ClampForever;  
            anim["RunJump"].speed = 2;  
            anim["Ladder Up"].wrapMode = WrapMode.ClampForever;  
            anim["Ladder Up"].speed = 2;  
            anim["Ladder Down"].wrapMode = WrapMode.ClampForever;  
            anim["Ladder Down"].speed = 2;  
      
            //初始化動畫狀態為Idle  
            anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);  
        }  
        //更新動畫融合  
        private void UpdateAnimationBlend()  
        {  
            //行走速度  
            float walkAnimationSpeed = 1.5f;  
            //奔跑速度  
            float runAnimationSpeed = 4.0f;  
            //速度閥值(idle和walk的臨界點)  
            float speedThreshold = 0.1f;  
      
            //速度,只考慮x和z  
            Vector3 velocityXZ = new Vector3(agent.velocity.x, 0.0f, agent.velocity.z);  
            //速度值  
            float speed = velocityXZ.magnitude;  
            //設置Run動畫的速度  
            anim["Run"].speed = speed / runAnimationSpeed;  
            //設置Walk動畫的速度  
            anim["Walk"].speed = speed / walkAnimationSpeed;  
      
            //根據agent的速度大小,確定animation的播放狀態  
            if (speed > (walkAnimationSpeed + runAnimationSpeed) / 2)  
            {  
                anim.CrossFade("Run");  
            }  
            else if (speed > speedThreshold)  
            {  
                anim.CrossFade("Walk");  
            }  
            else  
            {  
                anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);  
            }  
        }  
    }  

     

效果圖如下,點擊任何一個地點,角色都可以自動尋路過去。中間可能經過不同的障礙物,我們可以看到角色如我們所預料的一樣,可以跳躍下來,可以爬樓梯,最終到達目標點。


 

  • 總結

 

今天的這個例子比較復雜,要根據尋路網格的類型,來處理角色的動作是普通尋路,還是攀爬,抑或跳躍。這個例子應該是比較接近真實項目了。大家在實際項目中如果還有更加復雜的尋路,歡迎探討。[email protected]

 

  • 源碼

 

http://pan.baidu.com/s/1i35cVOD

 

  • 參考資料
1.http://www.xuanyusong.com/
2.http://liweizhaolili.blog.163.com/
3.http://game.ceeger.com/Components/class-NavMeshAgent.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved