unity3d教程:如何控制人物移动(RPG游戏黑暗之光)

发表于2017-03-12
评论0 5.3k浏览
在游戏开发中要怎样实现控制人物移动?下面以RPG游戏黑暗之光的人物移动为例,给大家讲讲使用Unity3D开发人物移动问题,一起来看看吧。

角色的移动分为两步实现:①角色的移动;②角色播放移动动画。
一、角色的移动
前期工程已经实现了朝向点击方向,则现在只需要让角色向前移动即可。脚本如下:
[csharp] view plain copy
 
  1. "font-size:12px;">private CharacterController playercontroller; //生命角色控制器,并调用角色的CharacterController组件  
  2. private PlayerDirection dir; //上期工程中控制角色朝向的脚本  
  3. private float speed; //设定角色移动速度  
  4. void Start( )  
  5. {  
  6.     playercontroller = GetComponent(); //获取角色的控制器组件    
  7.     dir = GetComponent(); //获取角色的PlayerDirection脚本组件  
  8.     speed = 4; //设定角色移动速度为4  
  9. }  
  10. void Update()  
  11. {  
  12.     float distance = Vector3.Distance( dir.targetPosition,tranform.position ); //获取目标地点与当前位置的距离  
  13.     if( distance > 0.5f ) //设定距离判断范围,注:范围需要设定精准  
  14.     {  
  15.         playercontroller.SimpleMove( transform.forward.*speed); //这样角色就可以正常移动了  
  16.     }  
  17. }  
二、角色移动动画的播放
上述脚本实现了角色模型的平移,现在需要给角色加入移动动画的控制。
首先在角色移动脚本中加入移动状态控制标示位,更新脚本如下:

[csharp] view plain copy
 
  1. public enum PlayerState //声明枚举,为枚举添加角色状态成员  
  2. {  
  3.     Moving,Idle   
  4. }  
  5. private CharacterController playercontroller;  
  6. private PlayerDirection dir;  
  7. private float speed;  
  8. public PlayerState state; //调用枚举,作为角色状态标示位  
  9. void Start( )  
  10. {  
  11.     playercontroller = GetComponent();  
  12.     dir = GetComponent();  
  13.     speed = 4;  
  14.     state = PlayerState.Idle; //开始状态赋值为Idle  
  15. }  
  16. void Update()  
  17. {  
  18.     float distance = Vector3.Distance( dir.targetPosition,tranform.position );  
  19.     if( distance > 0.5f )      
  20.     {     
  21.         state = PlayerState.Moving; //满足移动条件时,切换为Moving状态  
  22.         playercontroller.SimpleMove( transform.forward.*speed);  
  23.     }  
  24.     else      
  25.     {  
  26.         state = PlayerSate.Idle; //不满足移动条件时,切换为Idle状态  
  27.     }  
  28. }  



上述脚本尽管确定了标示位的状态,但是还是没有实现播放动画的功能,那么我们只需要创建动画播放脚本驱动角色改进就可以了,脚本如下:
[csharp] view plain copy
 
  1. private Animation playerAnimation;  
  2. private PlayerMove playermove;  
  3. void Start( )  
  4. {  
  5.     playerAnimation = GetComponent( );  
  6.     playermove = GetComponet( );  
  7. }  
  8. void PlayAnim( string animName )  
  9. {  
  10.     playerAniamtion.CrossFame( animName );  
  11. }  
  12. void LateUpdate( )   //LateUpdate是在所有Update函数调用后被调用  
  13. {  
  14.     if( playermove.state == PlayerState.Moving )      
  15.     {  
  16.         PlayAnim("Run");      
  17.     }  
  18.     else if( playermove.state == PlayerSatete.Idle )  
  19.     {  
  20.         PlayAnim("Idle");  
  21.     }  
  22. }  

这样就实现了角色的移动及动画的播放。

经过上述工程,实现了角色的移动功能。但是经过测试,发生了BUG,当角色因地形或其他遮蔽无法顺利抵达TargetPosition时,角色就会一直朝面向方向走下去。
这明显是不符合要求的。
我们把角色运动过程进行分解如下:
鼠标按下 → 确定目标点 → 鼠标松开 → 角色转向 → 计算角色与目标点距离 → 角色前进 → 角色到达目标范围 → 角色停止
明显,角色到达目标范围永远返回false,因为角色的朝向在鼠标松开那一刻便已经决定了,如果沿此方向不能达到目标点,那么角色就会超过最小范围圈,一直运动下去。
那么,需要在鼠标松开后,对角色运动状态进行检测,如果角色处于移动中,那么要求角色始终朝向目标点。
我们对PlayerDirection类进行优化,如下:

[csharp] view plain copy
 
  1. "font-size:12px;">using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class playerDir : MonoBehaviour {  
  5.   
  6.     public GameObject effect_click_prefab;  
  7.     public Vector3 targetPosition = Vector3.zero;  
  8.   
  9.     private bool isMoving = false;  //表示鼠标是否按下  
  10.     private playerMove playerMove;  
  11.       
  12.   
  13.     void Start()  
  14.     {  
  15.         targetPosition = transform.position;  
  16.         playerMove = this.GetComponent();  
  17.     }  
  18.       
  19.   
  20.     void Update ()  
  21.     {  // && UICamera.hoveredObject == null  
  22.         if (Input.GetMouseButtonDown(0))    
  23.         {  
  24.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
  25.             RaycastHit hitInfo;  
  26.             bool isCollider = Physics.Raycast(ray, out hitInfo);  
  27.             if(isCollider && hitInfo.collider.tag==Tags.ground)  
  28.             {  
  29.                 isMoving = true;  
  30.                 //实例化点击效果  
  31.                 ShowEffectClick(hitInfo.point);  
  32.                 LookAtTarget(hitInfo.point);  
  33.             }  
  34.         }  
  35.   
  36.         if(Input.GetMouseButtonUp(0))  
  37.         {  
  38.             isMoving = false;  
  39.         }  
  40.   
  41.         if (isMoving)  
  42.         {  
  43.             //得到移动的目标位置并让主角朝向那儿     
  44.            //人在移动过程中 要不断更新人物的朝向 要让它一直朝向点击的目标  
  45.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
  46.             RaycastHit hitInfo;  
  47.             bool isCollider = Physics.Raycast(ray, out hitInfo);    
  48.             if (isCollider && hitInfo.collider.tag == Tags.ground)  
  49.             {  
  50.                 LookAtTarget(hitInfo.point);  
  51.             }  
  52.         }  
  53.         else  
  54.         {  
  55.             if(playerMove.isMoving)  
  56.             LookAtTarget(targetPosition);  
  57.         }  
  58.     }  
  59.   
  60.     void ShowEffectClick(Vector3 hitPoint)   //点击效果  
  61.     {  
  62.         hitPoint.y += 0.1f;  
  63.         GameObject.Instantiate(effect_click_prefab, hitPoint, Quaternion.identity);  
  64.     }  
  65.   
  66.     void LookAtTarget(Vector3 hitPoint)    //看向点击目标  
  67.     {  
  68.         targetPosition= hitPoint;  
  69.         targetPosition.y = transform.position.y;  
  70.         this.transform.LookAt(targetPosition);  
  71.     }  
  72. }  

以上,便解决了该BUG。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引