天天看點

Unity 自動尋路Navmesh之跳躍,攀爬,斜坡

如果要做一個場景精美的手遊,需要用到各種複雜的場景地形,而不僅僅是平地上的自動尋路。今天我們将通過一個完整的複雜的執行個體,來貫穿各個細節。我們将實作一個複雜的場景,角色可以在裡面攀爬,跳躍,爬坡。是不是感覺很像當年的CS遊戲呢?本案例将會用得一些基本的動畫函數,大家可以先結合文檔有個大概的了解。本執行個體是在官方的範例上加工而成。

  • 步驟

1.在場景中擺放各種模型,包括地闆,斜坡,山體,扶梯等

2.為所有的模型加上Navigation Static和OffMeshLink Generatic(這個根據需要,例如地闆與斜坡相連,斜坡就不需要添加OffMeshLink)

3.特殊處理扶梯,需要手動添加Off Mesh Link,設定好開始點和結束點

4.儲存場景,烘焙場景

5.添加角色模型,為其加Nav Mesh Agent元件

6.為角色添加一個新腳本,AgentLocomotion.cs,用來處理自動尋路,已經角色動畫變換。代碼比較長,大家可以結合注釋來了解

[csharp]  view plain  copy

  1. using UnityEngine;  
  2. using System.Collections;  
  3. public class AgentLocomotion : MonoBehaviour  
  4. {  
  5.     private Vector3 target;//目标位置  
  6.     private NavMeshAgent agent;  
  7.     private Animation anim;//動畫  
  8.     private string locoState = "Locomotion_Stand";  
  9.     private Vector3 linkStart;//OffMeshLink的開始點  
  10.     private Vector3 linkEnd;//OffMeshLink的結束點  
  11.     private Quaternion linkRotate;//OffMeshLink的旋轉  
  12.     private bool begin;//是否開始尋路  
  13.     // Use this for initialization  
  14.     void Start()  
  15.     {  
  16.         agent = GetComponent<NavMeshAgent>();  
  17.         //自動移動并關閉OffMeshLinks,即在兩個隔離障礙物直接生成的OffMeshLink,agent不會自動越過  
  18.         agent.autoTraverseOffMeshLink = false;  
  19.         //建立動畫  
  20.         AnimationSetup();  
  21.         //起一個協程,處理動畫狀态機  
  22.         StartCoroutine(AnimationStateMachine());  
  23.     }  
  24.     void Update()  
  25.     {  
  26.         //滑鼠左鍵點選  
  27.         if (Input.GetMouseButtonDown(0))  
  28.         {  
  29.             //錄影機到點選位置的的射線  
  30.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
  31.             RaycastHit hit;  
  32.             if (Physics.Raycast(ray, out hit))  
  33.             {  
  34.                 //判斷點選的是否地形  
  35.                 if (hit.collider.tag.Equals("Obstacle"))  
  36.                 {  
  37.                     begin = true;  
  38.                     //點選位置坐标  
  39.                     target = hit.point;  
  40.                 }  
  41.             }  
  42.         }  
  43.         //每一幀,設定目标點  
  44.         if (begin)  
  45.         {  
  46.             agent.SetDestination(target);  
  47.         }  
  48.     }  
  49.     IEnumerator AnimationStateMachine()  
  50.     {  
  51.         //根據locoState不同的狀态來處理,調用相關的函數  
  52.         while (Application.isPlaying)  
  53.         {  
  54.             yield return StartCoroutine(locoState);  
  55.         }  
  56.     }  
  57.     //站立  
  58.     IEnumerator Locomotion_Stand()  
  59.     {  
  60.         do  
  61.         {  
  62.             UpdateAnimationBlend();  
  63.             yield return new WaitForSeconds(0);  
  64.         } while (agent.remainingDistance == 0);  
  65.         //未到達目标點,轉到下一個狀态Locomotion_Move  
  66.         locoState = "Locomotion_Move";  
  67.         yield return null;  
  68.     }  
  69.     IEnumerator Locomotion_Move()  
  70.     {  
  71.         do  
  72.         {  
  73.             UpdateAnimationBlend();  
  74.             yield return new WaitForSeconds(0);  
  75.             //角色處于OffMeshLink,根據不同的地點,選擇不同動畫  
  76.             if (agent.isOnOffMeshLink)  
  77.             {  
  78.                 locoState = SelectLinkAnimation();  
  79.                 return (true);  
  80.             }  
  81.         } while (agent.remainingDistance != 0);  
  82.         //已經到達目标點,狀态轉為Stand  
  83.         locoState = "Locomotion_Stand";  
  84.         yield return null;  
  85.     }  
  86.     IEnumerator Locomotion_Jump()  
  87.     {  
  88.         //播放跳躍動畫  
  89.         string linkAnim = "RunJump";  
  90.         Vector3 posStart = transform.position;  
  91.         agent.Stop(true);  
  92.         anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);  
  93.         transform.rotation = linkRotate;  
  94.         do  
  95.         {  
  96.             //計算新的位置  
  97.             float tlerp = anim[linkAnim].normalizedTime;  
  98.             Vector3 newPos = Vector3.Lerp(posStart, linkEnd, tlerp);  
  99.             newPos.y += 0.4f * Mathf.Sin(3.14159f * tlerp);  
  100.             transform.position = newPos;  
  101.             yield return new WaitForSeconds(0);  
  102.         } while (anim[linkAnim].normalizedTime < 1);  
  103.         //動畫恢複到Idle  
  104.         anim.Play("Idle");  
  105.         agent.CompleteOffMeshLink();  
  106.         agent.Resume();  
  107.         //下一個狀态為Stand  
  108.         transform.position = linkEnd;  
  109.         locoState = "Locomotion_Stand";  
  110.         yield return null;  
  111.     }  
  112.     //梯子  
  113.     IEnumerator Locomotion_Ladder()  
  114.     {  
  115.         //梯子的中心位置  
  116.         Vector3 linkCenter = (linkStart + linkEnd) * 0.5f;  
  117.         string linkAnim;  
  118.         //判斷是在梯子上還是梯子下  
  119.         if (transform.position.y > linkCenter.y)  
  120.             linkAnim = "Ladder Down";  
  121.         else  
  122.             linkAnim = "Ladder Up";  
  123.         agent.Stop(true);  
  124.         Quaternion startRot = transform.rotation;  
  125.         Vector3 startPos = transform.position;  
  126.         float blendTime = 0.2f;  
  127.         float tblend = 0f;  
  128.         //角色的位置插值變化(0.2内變化)  
  129.         do  
  130.         {  
  131.             transform.position = Vector3.Lerp(startPos, linkStart, tblend / blendTime);  
  132.             transform.rotation = Quaternion.Lerp(startRot, linkRotate, tblend / blendTime);  
  133.             yield return new WaitForSeconds(0);  
  134.             tblend += Time.deltaTime;  
  135.         } while (tblend < blendTime);  
  136.         //設定位置  
  137.         transform.position = linkStart;  
  138.         //播放動畫  
  139.         anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);  
  140.         agent.ActivateCurrentOffMeshLink(false);  
  141.         //等待動畫結束  
  142.         do  
  143.         {  
  144.             yield return new WaitForSeconds(0);  
  145.         } while (anim[linkAnim].normalizedTime < 1);  
  146.         agent.ActivateCurrentOffMeshLink(true);  
  147.         //恢複Idle狀态  
  148.         anim.Play("Idle");  
  149.         transform.position = linkEnd;  
  150.         agent.CompleteOffMeshLink();  
  151.         agent.Resume();  
  152.         //下一個狀态Stand  
  153.         locoState = "Locomotion_Stand";  
  154.         yield return null;  
  155.     }  
  156.     private string SelectLinkAnimation()  
  157.     {  
  158.         //獲得目前的OffMeshLink資料  
  159.         OffMeshLinkData link = agent.currentOffMeshLinkData;  
  160.         //計算角色目前是在link的開始點還是結束點(因為OffMeshLink是雙向的)  
  161.         float distS = (transform.position - link.startPos).magnitude;  
  162.         float distE = (transform.position - link.endPos).magnitude;  
  163.         if (distS < distE)  
  164.         {  
  165.             linkStart = link.startPos;  
  166.             linkEnd = link.endPos;  
  167.         }  
  168.         else  
  169.         {  
  170.             linkStart = link.endPos;  
  171.             linkEnd = link.startPos;  
  172.         }  
  173.         //OffMeshLink的方向  
  174.         Vector3 alignDir = linkEnd - linkStart;  
  175.         //忽略y軸  
  176.         alignDir.y = 0;  
  177.         //計算旋轉角度  
  178.         linkRotate = Quaternion.LookRotation(alignDir);  
  179.         //判斷OffMeshLink是手動的(樓梯)還是自動生成的(跳躍)  
  180.         if (link.linkType == OffMeshLinkType.LinkTypeManual)  
  181.         {  
  182.             return ("Locomotion_Ladder");  
  183.         }  
  184.         else  
  185.         {  
  186.             return ("Locomotion_Jump");  
  187.         }  
  188.     }  
  189.     private void AnimationSetup()  
  190.     {  
  191.         anim = GetComponent<Animation>();  
  192.         // 把walk和run動畫放到同一層,然後同步他們的速度。  
  193.         anim["Walk"].layer = 1;  
  194.         anim["Run"].layer = 1;  
  195.         anim.SyncLayer(1);  
  196.         //設定“跳躍”,“爬樓梯”,“下樓梯”的動畫模式和速度  
  197.         anim["RunJump"].wrapMode = WrapMode.ClampForever;  
  198.         anim["RunJump"].speed = 2;  
  199.         anim["Ladder Up"].wrapMode = WrapMode.ClampForever;  
  200.         anim["Ladder Up"].speed = 2;  
  201.         anim["Ladder Down"].wrapMode = WrapMode.ClampForever;  
  202.         anim["Ladder Down"].speed = 2;  
  203.         //初始化動畫狀态為Idle  
  204.         anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);  
  205.     }  
  206.     //更新動畫融合  
  207.     private void UpdateAnimationBlend()  
  208.     {  
  209.         //行走速度  
  210.         float walkAnimationSpeed = 1.5f;  
  211.         //奔跑速度  
  212.         float runAnimationSpeed = 4.0f;  
  213.         //速度閥值(idle和walk的臨界點)  
  214.         float speedThreshold = 0.1f;  
  215.         //速度,隻考慮x和z  
  216.         Vector3 velocityXZ = new Vector3(agent.velocity.x, 0.0f, agent.velocity.z);  
  217.         //速度值  
  218.         float speed = velocityXZ.magnitude;  
  219.         //設定Run動畫的速度  
  220.         anim["Run"].speed = speed / runAnimationSpeed;  
  221.         //設定Walk動畫的速度  
  222.         anim["Walk"].speed = speed / walkAnimationSpeed;  
  223.         //根據agent的速度大小,确定animation的播放狀态  
  224.         if (speed > (walkAnimationSpeed + runAnimationSpeed) / 2)  
  225.         {  
  226.             anim.CrossFade("Run");  
  227.         }  
  228.         else if (speed > speedThreshold)  
  229.         {  
  230.             anim.CrossFade("Walk");  
  231.         }  
  232.         else  
  233.         {  
  234.             anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);  
  235.         }  
  236.     }  
  237. }  

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

Unity 自動尋路Navmesh之跳躍,攀爬,斜坡
  • 總結

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

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

11

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

繼續閱讀