生活随笔
收集整理的這篇文章主要介紹了
Unity手游之路十自动寻路Navmesh之跳跃,攀爬,斜坡
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
轉(zhuǎn)載
Unity手游之路<十>自動(dòng)尋路Navmesh之跳躍,攀爬,斜坡
分類:?unity2013-12-27 00:50?6545人閱讀? unity3dNavmesh手游自動(dòng)尋路
在之前的幾篇Blog總,我們已經(jīng)系統(tǒng)學(xué)習(xí)了自動(dòng)尋路插件Navmesh的相關(guān)概念和細(xì)節(jié)。然而,如果要做一個(gè)場(chǎng)景精美的手游,需要用到各種復(fù)雜的場(chǎng)景地形,而不僅僅是平地上的自動(dòng)尋路。今天我們將通過(guò)一個(gè)完整的復(fù)雜的實(shí)例,來(lái)貫穿各個(gè)細(xì)節(jié)。我們將實(shí)現(xiàn)一個(gè)復(fù)雜的場(chǎng)景,角色可以在里面攀爬,跳躍,爬坡。是不是感覺(jué)很像當(dāng)年的CS游戲呢?本案例將會(huì)用得一些基本的動(dòng)畫函數(shù),大家可以先結(jié)合文檔有個(gè)大概的了解。本實(shí)例是在官方的范例上加工而成。
(轉(zhuǎn)載請(qǐng)注明原文地址http://blog.csdn.net/janeky/article/details/17598113)
1.在場(chǎng)景中擺放各種模型,包括地板,斜坡,山體,扶梯等
2.為所有的模型加上Navigation Static和OffMeshLink Generatic(這個(gè)根據(jù)需要,例如地板與斜坡相連,斜坡就不需要添加OffMeshLink)
3.特殊處理扶梯,需要手動(dòng)添加Off Mesh Link,設(shè)置好開(kāi)始點(diǎn)和結(jié)束點(diǎn)
4.保存場(chǎng)景,烘焙場(chǎng)景
5.添加角色模型,為其加Nav Mesh Agent組件
6.為角色添加一個(gè)新腳本,AgentLocomotion.cs,用來(lái)處理自動(dòng)尋路,已經(jīng)角色動(dòng)畫變換。代碼比較長(zhǎng),大家可以結(jié)合注釋來(lái)理解
[csharp]?view plaincopy
using UnityEngine;
using System.Collections; public class AgentLocomotion : MonoBehaviour
{ private Vector3 target;//目標(biāo)位置 private NavMeshAgent agent; private Animation anim;//動(dòng)畫 private string locoState = "Locomotion_Stand"; private Vector3 linkStart;//OffMeshLink的開(kāi)始點(diǎn) private Vector3 linkEnd;//OffMeshLink的結(jié)束點(diǎn) private Quaternion linkRotate;//OffMeshLink的旋轉(zhuǎn) private bool begin;//是否開(kāi)始尋路 // Use this for initialization void Start() { agent = GetComponent<NavMeshAgent>(); //自動(dòng)移動(dòng)并關(guān)閉OffMeshLinks,即在兩個(gè)隔離障礙物直接生成的OffMeshLink,agent不會(huì)自動(dòng)越過(guò) agent.autoTraverseOffMeshLink = false; //創(chuàng)建動(dòng)畫
AnimationSetup(); //起一個(gè)協(xié)程,處理動(dòng)畫狀態(tài)機(jī)
StartCoroutine(AnimationStateMachine()); } void Update() { //鼠標(biāo)左鍵點(diǎn)擊 if (Input.GetMouseButtonDown(0)) { //攝像機(jī)到點(diǎn)擊位置的的射線 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { //判斷點(diǎn)擊的是否地形 if (hit.collider.tag.Equals("Obstacle")) { begin = true; //點(diǎn)擊位置坐標(biāo) target = hit.point; } } } //每一幀,設(shè)置目標(biāo)點(diǎn) if (begin) { agent.SetDestination(target); } } IEnumerator AnimationStateMachine() { //根據(jù)locoState不同的狀態(tài)來(lái)處理,調(diào)用相關(guān)的函數(shù) while (Application.isPlaying) { yield return StartCoroutine(locoState); } } //站立
IEnumerator Locomotion_Stand() { do { UpdateAnimationBlend(); yield return new WaitForSeconds(0); } while (agent.remainingDistance == 0); //未到達(dá)目標(biāo)點(diǎn),轉(zhuǎn)到下一個(gè)狀態(tài)Locomotion_Move locoState = "Locomotion_Move"; yield return null; } IEnumerator Locomotion_Move() { do { UpdateAnimationBlend(); yield return new WaitForSeconds(0); //角色處于OffMeshLink,根據(jù)不同的地點(diǎn),選擇不同動(dòng)畫 if (agent.isOnOffMeshLink) { locoState = SelectLinkAnimation(); return (true); } } while (agent.remainingDistance != 0); //已經(jīng)到達(dá)目標(biāo)點(diǎn),狀態(tài)轉(zhuǎn)為Stand locoState = "Locomotion_Stand"; yield return null; } IEnumerator Locomotion_Jump() { //播放跳躍動(dòng)畫 string linkAnim = "RunJump"; Vector3 posStart = transform.position; agent.Stop(true); anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll); transform.rotation = linkRotate; do { //計(jì)算新的位置 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); //動(dòng)畫恢復(fù)到Idle anim.Play("Idle"); agent.CompleteOffMeshLink(); agent.Resume(); //下一個(gè)狀態(tài)為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內(nèi)變化) 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); //設(shè)置位置 transform.position = linkStart; //播放動(dòng)畫 anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll); agent.ActivateCurrentOffMeshLink(false); //等待動(dòng)畫結(jié)束 do { yield return new WaitForSeconds(0); } while (anim[linkAnim].normalizedTime < 1); agent.ActivateCurrentOffMeshLink(true); //恢復(fù)Idle狀態(tài) anim.Play("Idle"); transform.position = linkEnd; agent.CompleteOffMeshLink(); agent.Resume(); //下一個(gè)狀態(tài)Stand locoState = "Locomotion_Stand"; yield return null; } private string SelectLinkAnimation() { //獲得當(dāng)前的OffMeshLink數(shù)據(jù) OffMeshLinkData link = agent.currentOffMeshLinkData; //計(jì)算角色當(dāng)前是在link的開(kāi)始點(diǎn)還是結(jié)束點(diǎn)(因?yàn)镺ffMeshLink是雙向的) 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; //計(jì)算旋轉(zhuǎn)角度 linkRotate = Quaternion.LookRotation(alignDir); //判斷OffMeshLink是手動(dòng)的(樓梯)還是自動(dòng)生成的(跳躍) if (link.linkType == OffMeshLinkType.LinkTypeManual) { return ("Locomotion_Ladder"); } else { return ("Locomotion_Jump"); } } private void AnimationSetup() { anim = GetComponent<Animation>(); // 把walk和run動(dòng)畫放到同一層,然后同步他們的速度。 anim["Walk"].layer = 1; anim["Run"].layer = 1; anim.SyncLayer(1); //設(shè)置“跳躍”,“爬樓梯”,“下樓梯”的動(dòng)畫模式和速度 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; //初始化動(dòng)畫狀態(tài)為Idle anim.CrossFade("Idle", 0.1f, PlayMode.StopAll); } //更新動(dòng)畫融合 private void UpdateAnimationBlend() { //行走速度 float walkAnimationSpeed = 1.5f; //奔跑速度 float runAnimationSpeed = 4.0f; //速度閥值(idle和walk的臨界點(diǎn)) float speedThreshold = 0.1f; //速度,只考慮x和z Vector3 velocityXZ = new Vector3(agent.velocity.x, 0.0f, agent.velocity.z); //速度值 float speed = velocityXZ.magnitude; //設(shè)置Run動(dòng)畫的速度 anim["Run"].speed = speed / runAnimationSpeed; //設(shè)置Walk動(dòng)畫的速度 anim["Walk"].speed = speed / walkAnimationSpeed; //根據(jù)agent的速度大小,確定animation的播放狀態(tài) if (speed > (walkAnimationSpeed + runAnimationSpeed) / 2) { anim.CrossFade("Run"); } else if (speed > speedThreshold) { anim.CrossFade("Walk"); } else { anim.CrossFade("Idle", 0.1f, PlayMode.StopAll); } }
} ?
效果圖如下,點(diǎn)擊任何一個(gè)地點(diǎn),角色都可以自動(dòng)尋路過(guò)去。中間可能經(jīng)過(guò)不同的障礙物,我們可以看到角色如我們所預(yù)料的一樣,可以跳躍下來(lái),可以爬樓梯,最終到達(dá)目標(biāo)點(diǎn)。
?
?
今天的這個(gè)例子比較復(fù)雜,要根據(jù)尋路網(wǎng)格的類型,來(lái)處理角色的動(dòng)作是普通尋路,還是攀爬,抑或跳躍。這個(gè)例子應(yīng)該是比較接近真實(shí)項(xiàng)目了。大家在實(shí)際項(xiàng)目中如果還有更加復(fù)雜的尋路,歡迎探討。ken@iamcoding.com
?
?
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
轉(zhuǎn)載于:https://www.cnblogs.com/lihonglin2016/p/4361162.html
總結(jié)
以上是生活随笔為你收集整理的Unity手游之路十自动寻路Navmesh之跳跃,攀爬,斜坡的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。