一、概述

游戏中角色移动分为了主动和被动,主动主要由玩家通过摇杆或者键盘进行操作,被动移动主要应用于自动寻路,通过寻路点行动。

二、主动移动

移动组件

移动组件主要用到Unity的移动组件Character Controller,主要属性如下

  • Height角色的高度,通常和现实中的人物一样设置为2米左右。
  • Radius角色的半径,用于控制人物的胖瘦。
  • Center设置角色中心点的位置。
  • Slope Limit限制角色能爬的最大坡度。通常设置为90度以下,这样角色就不会走到墙上。
  • Step Offset移动步长。通常2米左右的人移动步长设置在0.1到0.4米.
  • Skin Width皮肤厚度。如果这个值太小角色容易被卡住,太大角色容易抖动。通常将这个数据设为0.01到角色半径的10%之间。
  • Min Move Distance最小移动距离。官方推荐把这个值设为0。
  • isGrounded可以获取角色当前是否在地面。
  • velocity可以获取角色当前的速度向量。

移动方法主要用到Move,Move方法需要自己实现重力的效果,看具体项目需求

//用摇杆控制方向,当按下空格键时跳起。
using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    public float speed = 6.0F;
    public float jumpSpeed = 8.0F;
    public float gravity = 20.0F;
    private Vector3 moveDirection = Vector3.zero;

    void Update() {
        CharacterController controller = GetComponent<CharacterController>();
        if (controller.isGrounded) {
            moveDirection = GetTargetPos(nowPos) - nowPos;

            // 跳跃
            if (Input.GetButton("Jump"))
                moveDirection.y = jumpSpeed;
        }

        //Move方法需要自己写重力效果
        moveDirection.y -= gravity * Time.deltaTime;

        //移动控制器
        controller.Move(moveDirection * Time.deltaTime);
    }
}

每帧目标点的计算

速度模拟模拟现实中人物移动的特征,起步时加速,计算代码如下

Vector3 GetTargetPos(Vector3 pos)
{
	//计算实时速度,addSpeed为加速度
	curMoveSpeed = Mathf.MoveTowards(curMoveSpeed, MaxSpeed, addSpeed * deltaTime);
	//计算终点,curMoveDir由输入决定
	pos += curMoveSpeed * curMoveDir * deltaTime
}

动画状态机

移动时需要动作配合,动作主要由Animator组件实现,通过设置不同的参数,实现状态之间的转换,还可以细化,加上准备跳跃,跳跃落地等等。

通过SetTrigger进行不同运动状态的切换,通过SetBool进行移动和静止状态间的切换。

管理组件

主动移动的管理组件主要功能如下

  • 接收输入方向
  • 状态机维护当前角色状态(普通,跳跃),设置对应动画状态
  • 计算下一帧移动方向

三、自动寻路

  • 自动寻路主要是计算出起始点到目标点的一连串的中间点,每次走一小段直线,从而完成寻路的过程,逻辑比较简单,
  • 如果使用Unity自带的Nav Mesh Agent 可以使用参考https://bbs.huaweicloud.com/blogs/303788

计算寻路点

计算寻路点主要用到Unity的NavMesh类中的CalculatePath方法,得到寻路点以后只要逐个进行寻路即可。

public static bool CalculatePath(Vector3 sourcePosition, Vector3 targetPosition, int areaMask, AI.NavMeshPath path);

逐点寻路

计算出寻路点以后,在Update中执行

private void UpdateMove(float deltaTime)
{
	float time = Vector3.Distance(_nowPos, _destList[index])/speed;
	If(deltaTime > time)
	{
			//如果到最后一个,结束

			//没有的话,index++
			//多出的时间留给下个寻路点
			UpdateMove(deltaTime - time);
	}
	else
	{
			//插值
			_nowPos = Vector3.Lerp(_nowPos, _destList[index], deltaTime / time);
	}
}