如果要做一个场景精美的手游,需要用到各种复杂的场景地形,而不仅仅是平地上的自动寻路。今天我们将通过一个完整的复杂的实例,来贯穿各个细节。我们将实现一个复杂的场景,角色可以在里面攀爬,跳跃,爬坡。是不是感觉很像当年的CS游戏呢?本案例将会用得一些基本的动画函数,大家可以先结合文档有个大概的了解。本实例是在官方的范例上加工而成。
1.在场景中摆放各种模型,包括地板,斜坡,山体,扶梯等
2.为所有的模型加上Navigation Static和OffMeshLink Generatic(这个根据需要,例如地板与斜坡相连,斜坡就不需要添加OffMeshLink)
3.特殊处理扶梯,需要手动添加Off Mesh Link,设置好开始点和结束点
4.保存场景,烘焙场景
5.添加角色模型,为其加Nav Mesh Agent组件
6.为角色添加一个新脚本,AgentLocomotion.cs,用来处理自动寻路,已经角色动画变换。代码比较长,大家可以结合注释来理解
- 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;
- private Vector3 linkEnd;
- private Quaternion linkRotate;
- private bool begin;
-
-
- void Start()
- {
- agent = GetComponent();
-
- 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()
- {
-
- while (Application.isPlaying)
- {
- yield return StartCoroutine(locoState);
- }
- }
-
-
- IEnumerator Locomotion_Stand()
- {
- do
- {
- UpdateAnimationBlend();
- yield return new WaitForSeconds(0);
- } while (agent.remainingDistance == 0);
-
- locoState = "Locomotion_Move";
- yield return null;
- }
-
- IEnumerator Locomotion_Move()
- {
- do
- {
- UpdateAnimationBlend();
- yield return new WaitForSeconds(0);
-
- if (agent.isOnOffMeshLink)
- {
- locoState = SelectLinkAnimation();
- return (true);
- }
- } while (agent.remainingDistance != 0);
-
- 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);
-
- anim.Play("Idle");
- agent.CompleteOffMeshLink();
- agent.Resume();
-
- 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;
-
-
- 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);
-
- anim.Play("Idle");
- transform.position = linkEnd;
- agent.CompleteOffMeshLink();
- agent.Resume();
-
- locoState = "Locomotion_Stand";
- yield return null;
- }
-
- private string SelectLinkAnimation()
- {
-
- OffMeshLinkData link = agent.currentOffMeshLinkData;
-
- 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;
- }
-
- Vector3 alignDir = linkEnd - linkStart;
-
- alignDir.y = 0;
-
- linkRotate = Quaternion.LookRotation(alignDir);
-
-
- if (link.linkType == OffMeshLinkType.LinkTypeManual)
- {
- return ("Locomotion_Ladder");
- }
- else
- {
- return ("Locomotion_Jump");
- }
- }
-
- private void AnimationSetup()
- {
- anim = GetComponent();
-
-
- 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;
-
-
- anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);
- }
-
- private void UpdateAnimationBlend()
- {
-
- float walkAnimationSpeed = 1.5f;
-
- float runAnimationSpeed = 4.0f;
-
- float speedThreshold = 0.1f;
-
-
- Vector3 velocityXZ = new Vector3(agent.velocity.x, 0.0f, agent.velocity.z);
-
- float speed = velocityXZ.magnitude;
-
- anim["Run"].speed = speed / runAnimationSpeed;
-
- anim["Walk"].speed = speed / walkAnimationSpeed;
-
-
- 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