第三章:角色控制
本篇博客主要对人物移动及其相关操作进行分析,主要包括主角以及镜头的移动。
在游戏界面中,我们使用Camera作为视角。为了方便之后判断当前tag,我们新建一个Tag脚本,存入一些tag信息,之后调用就不容易出错
using UnityEngine; using System.Collections; public class Tags : MonoBehaviour { public const string ground = "Ground"; public const string player = "Player"; //新建角色与地面的tag信息,之后还会添加物品等信息 }
3.1 点击地板
Map中的Terrain可以判断鼠标是否点击到地面,因此我们向Map中拖入一个Magician,为它添加一个脚本PlayerDirection,实现点击地板产生效果的功能。代码如下
using UnityEngine; using System.Collections; public class PlayerDirection : MonoBehaviour { public GameObject effect_click_prefab; // Update is called once per frame void Update () { if (Input.GetMouseButtonDown (0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); //创建一个射线,将在Camera中点击的点转化为一条射线 RaycastHit hitInfo; //创建碰撞信息 bool isCollider = Physics.Raycast(ray,out hitInfo); //isCollider检测是否碰撞,其中Physics.Raycast中的两个形参表示射线以及碰撞信息 if(isCollider && hitInfo.collider.tag == Tags.ground) //发生碰撞且碰撞的物体其Tag为ground { showClickEffect(hitInfo.point); //实例化点击效果 } } } void showClickEffect(Vector3 hitPoint) { hitPoint = new Vector3 (hitPoint.x, hitPoint.y + 0.1f, hitPoint.z); //将碰撞点的y向上移动一些,以完整显示 GameObject.Instantiate (effect_click_prefab, hitPoint, Quaternion.identity); //创建一个实例,显示点击信息 } }
即可
之后将点击效果导入,即可实现
运行后,点击地面时即可产生碰撞效果。
3.2 角色朝向
在获取点击目标后,我们需要控制角色朝向目标区域。对PlayerDirection修改如下
private bool isMoving = false; public Vector3 targetPosition = Vector3.zero; // Update is called once per frame void Update () { if (Input.GetMouseButtonDown (0)) //这里是判断是否按下 { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; bool isCollider = Physics.Raycast(ray,out hitInfo); if(isCollider && hitInfo.collider.tag == Tags.ground) { isMoving = true; ShowClickEffect(hitInfo.point); LookAtTarget(hitInfo.point); //将朝向设置为一个方法 } } if(Input.GetMouseButtonUp(0)) //是否抬起 { isMoving = false; } if (isMoving) //是否持续按下 { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; bool isCollider = Physics.Raycast(ray,out hitInfo); if(isCollider && hitInfo.collider.tag == Tags.ground) { LookAtTarget(hitInfo.point); //持续朝向hitInfo.point } } } void LookAtTarget(Vector3 hitPoint) { targetPosition = hitPoint; //点击的位置信息传给 targetPosition targetPosition = new Vector3(targetPosition.x,transform.position.y,targetPosition.z); //不改变y轴信息,别的信息参照 targetPosition this.transform.LookAt(targetPosition); //改变朝向 } }
3.3 角色移动
为角色添加一个角色控制器,用以控制移动
对Magician添加一个脚本PlayerMove控制移动
using UnityEngine; using System.Collections; public class PlayerMove : MonoBehaviour { public float speed = 3f; private PlayerDirection dir; private CharacterController controller; void Start(){ dir = this.GetComponent<PlayerDirection> (); //获取目标位置 controller = this.GetComponent<CharacterController> (); //为controller赋值 } // Update is called once per frame void Update () { float distance = Vector3.Distance (dir.targetPosition, transform.position); //获取目标位置与当前位置的距离 if (distance >= 0.1f) { controller.SimpleMove(transform.forward*speed); //这里的速度应带有方向 } } }
即可,但是刚开始时的targetPosition处于(0,0,0),会直接移动,需要在PlayerDirection初始化targetPositio为初始人物的位置
即
targetPosition = transform.position;
实现移动效果如下
3.4 动画效果的加入
角色移动时应当有动画,在Animation中,添加所有素材
现在需要在PlayerMove中控制角色的状态
3.5 角色状态,站立,运动
在Magician下新建一个脚本PlayerAnimation控制运动播放,首先在PlayerMove中指定站立或者运动的状态信息
using UnityEngine; using System.Collections; public enum PlayerState{ Moving, Idle } public class PlayerMove : MonoBehaviour { public float speed = 3f; public PlayerState state = PlayerState.Idle; //指定默认的Animation状态是Idle private PlayerDirection dir; private CharacterController controller; // Update is called once per frame void Update () { float distance = Vector3.Distance (dir.targetPosition, transform.position); if (distance >= 0.05f) { state = PlayerState.Moving; //运动时state状态变为Moving } else { state = PlayerState.Idle; //不运动时状态为Idle } } }
利用上述状态的初始化,之后根据状态变化在PlayerAnimation控制角色移动
using UnityEngine; using System.Collections; public class PlayerAnimation : MonoBehaviour { private PlayerMove move; // Use this for initialization void Start () { move = this.GetComponent<PlayerMove> (); } // Update is called once per frame void LateUpdate () { if (move.state == PlayerState.Moving) { PlayAnim ("Run"); //对应下图Run } else if (move.state == PlayerState.Idle) { PlayAnim("Idle"); //对应下图Idle } } void PlayAnim(string animName) { animation.CrossFade (animName); //播放对应的Animation } }
3.6 让相机跟随主角移动以及镜头的拉近拉远
对Camera添加一个脚本PlayerFollow
using UnityEngine; using System.Collections; public class PlayerFollow : MonoBehaviour { private Transform player; private Vector3 offsetPosition; // Use this for initialization void Start () { player = GameObject.FindGameObjectWithTag (Tags.player).transform; //得到角色信息 this.transform.LookAt (player.position); //使player处在Camera中心 offsetPosition = transform.position - player.position; //判断相机与人物的偏移量,之后用这个偏移量作为参考控制相机移动 } // Update is called once per frame void Update () { transform.position = offsetPosition + player.position; //使用偏移量作为相机位置的更新 } }
即可
为了提高可玩性,需要添加鼠标滑轮的功能实现拉远与缩进的功能。
在Edit——Project Setting中有一个鼠标滚轮的控制
在Camera中的脚本PlayerFollow中加入一个方法ScrollView()控制滚轮的拉近拉远操作
public float scrollSpeed = 10f; //滚轮速度 public float distance = 0f; void ScrollView() { //print (Input.GetAxis("Mouse ScrollWheel")); //Input.GetAxis("Mouse ScrollWheel")表示鼠标滚轮的滑动值,滑的速度越快,值越大 distance = offsetPosition.magnitude; //用位置偏移表示镜头与角色的距离 distance -= Input.GetAxis ("Mouse ScrollWheel") * scrollSpeed; //通过distance的减少拉近(远)视野 distance = Mathf.Clamp(distance,4f,14f); //限定拉近拉远的最值 offsetPosition = offsetPosition.normalized * distance; //取得offsetPosition的单位向量,再乘distance表示改变视野后Camera与角色的距离 }
与上类似,我们在Camera中的脚本PlayerFollow中加入一个方法RotateView()函数控制鼠标右键的左右、上下移动
private bool isRotation = false; //是否旋转视野的标志位 public float rotateSpeed = 2f; //旋转速度 void RotateView() { if (Input.GetMouseButtonDown (1)) //如果鼠标右键(1表示右键,0表示左键,2表示滚轮)按下,旋转开启 { isRotation = true; } if (Input.GetMouseButtonUp (1)) //若右键抬起,旋转关闭 { isRotation = false; } if (isRotation) { transform.RotateAround(player.position,player.up,rotateSpeed*Input.GetAxis("Mouse X")); //RotateAround表示绕着第一个参数进行旋转,其中第一个参数表示围绕player进行旋转,第二个参数表示旋转围绕的轴,在这里表示垂直主角的轴,第三个表示旋转速度 transform.RotateAround(player.position,transform.right,-rotateSpeed*Input.GetAxis("Mouse Y")); //围绕的轴改为水平方向 } offsetPosition = transform.position - player.position; //旋转后更新offsetPosition }
旋转角度的限定,即Camera中的Rotation对应的x,在 if (isRotation)中修改
if (isRotation) { transform.RotateAround(player.position,player.up,rotateSpeed*Input.GetAxis("Mouse X")); Vector3 originalPos = transform.position; //保存原始位置信息 Quaternion originalRotation = transform.rotation; //保存原始角度信息 transform.RotateAround(player.position,transform.right ,-rotateSpeed*Input.GetAxis("Mouse Y")); float x = transform.eulerAngles.x; //对应Camera的Rotation中的x if(x<10 || x>80) //如果超过限制,返回原始值 { transform.position = originalPos; transform.rotation = originalRotation; } }
总结:角色控制的大致功能都已实现,有许多细节需要注意,需要花时间研究。之后将对任务、道具等系统进行创建。