如果要做一個場景精美的手遊,需要用到各種複雜的場景地形,而不僅僅是平地上的自動尋路。今天我們将通過一個完整的複雜的執行個體,來貫穿各個細節。我們将實作一個複雜的場景,角色可以在裡面攀爬,跳躍,爬坡。是不是感覺很像當年的CS遊戲呢?本案例将會用得一些基本的動畫函數,大家可以先結合文檔有個大概的了解。本執行個體是在官方的範例上加工而成。
- 步驟
1.在場景中擺放各種模型,包括地闆,斜坡,山體,扶梯等
2.為所有的模型加上Navigation Static和OffMeshLink Generatic(這個根據需要,例如地闆與斜坡相連,斜坡就不需要添加OffMeshLink)
3.特殊處理扶梯,需要手動添加Off Mesh Link,設定好開始點和結束點
4.儲存場景,烘焙場景
5.添加角色模型,為其加Nav Mesh Agent元件
6.為角色添加一個新腳本,AgentLocomotion.cs,用來處理自動尋路,已經角色動畫變換。代碼比較長,大家可以結合注釋來了解
[csharp] view plain copy
- 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
(轉載請注明原文位址 http://blog.csdn.net/janeky/article/details/17598113 )
- 頂
- 11
- 踩
(轉載請注明原文位址 http://blog.csdn.net/janeky/article/details/17598113 )