无论是RPG
、ACT
、AVG
还是TPS
游戏,都有大量的以第三人称为人物视角的游戏作品,尤其是TPS
,直接以第三人称为特点来命名
游戏类型:
RPG
:角色扮演游戏ACT
:动作游戏AVG
:冒险游戏SRPG
:模拟类角色扮演游戏TPS
:第三人称设计游戏
而对于第三人称而言,人物角色的所有状态全部暴露在玩家的视角之下,关于角色的所有的动画、逻辑、相机表现都要能够让玩家感受到最完美的体验
在开始使用Unity
设计开发第三人称游戏之前,我们需要先获得合适的角色模型。Unity
支持多种格式的模型,而当我们使用具有动画的模型时,往往选择fbx格式的模型
选择好模型后,就可以将模型导入到Unity
中,然后我们需要对模型进行一些参数调整,点击模型后,就可以在Inspector
面板看到这些参数:
也许大家看到这些英文参数有些蒙,没关系,可以点击右上角的问号查阅官方文档来了解这些参数的意义,同时我也在下面列出了几个关键点来帮助大家完成设置:
1,如果角色模型导入场景过大:
2,对于Rig
选项下的Animation Type
选项的选择:
3,关于第三个选项Animation
为了使得角色可以根据玩家的操作做出相应的动画,可以使用动画控制器来对一系列的状态动画进行管理,这里可以查看我写的关于角色动画的博客:
Unity人物动画:
- unity人物动画配置
但是本次案例,我们讲一个全新的动画配置效果:动画混合树
通过动画混合树,可以使得我们的角色动画过渡的更加平滑,使得玩家的游戏体验更加真实,而创建动画混合树的具体步骤为:
2.1,创建动画混合树:
1,在Animator
面板右击选择Create state
,并在其中选择From New Blend Tree
即可完成创建,
创建完Blend Tree
后,我们点击即可进入动画控制树配置面板:
可以看到,创建该State
同时会自动创建一个参数,这个参数类似于我们添加一个float
参数,可以通过调整这个值来平滑的切换几种 状态:下面我们就为该动画混合树添加几种状态:
右键刚刚创建Blend Tree
,或者在右侧点击加号,即可为该树添加状态,也就是动画,
完成创建后,主要需要调整的几个地方在下面的红框里面:
关于Blend Type
,即动画混合类型,通过点击倒角发现有下面 几种混合方式:
因为本案例采用速度控制动画切换的方式,因此直接选择1D来作为动画混合类型,即只需要通过最右边的一个数值框的参数来控制动画切换即可,当我们把动画拖入后,选择好三个状态对应的参数,当参数权值慢慢靠近某种状态的对应参数时,动画也会慢慢的转换,如图:
从图中可以看出,三种动画经过混合后,可以实现这种由慢到快的平滑的系列动画
2.2,如何通过脚本使用混合树:
和Unity人物动画配置那一章类似,我们可以直接使用Blend
来实现动画平滑移动,比如说,人物从静止到移动的动画切换,可以通过一个插值来获取动画需要切换的速度:
private float Blend;
private Animator ani;
private void Start()
{
ani = this.GetComponent<Animator>();
}
private void Update()
{
if(Input.GetKey(KeyCode.W))
{
Blend = Mathf.Lerp(Blend, 0.5f, 0.01f);
}
if (!Input.GetKey(KeyCode.W))
{
Blend = Mathf.Lerp(Blend, 0, 0.01f);
}
ani.SetFloat("Blend", Blend);
}
这样当我们按下W时,控制动画混合树的参数就会插值增加,直到0.5(对应移动状态)而不按W时,动画插值回到0,人物慢慢停止移动
3.1,为角色添加组件
在进行脚本编写之前,我们需要为场景中模型添加一些组件来帮助我们实现角色移动、旋转,以及角色动画控制等功能:
添加角色控制器:
添加刚体组件:
因为我们控制物体时旋转时,仅仅需要其在Y轴上的效果,而X轴或者Z轴的旋转往往会导致我们的角色倾倒在地,这显然不是我们需要的,所以我们可以通过在
Constraints
中的Freeze Rotation
中勾选X和Z来冻结其旋转功能
添加碰撞体:
3.2、脚本控制角色运动
第一种方案:
移动:
rigidbody.MovePosition()
方法来控制角色移动到该位置旋转:
Vector3.Slerp()
方法来进行插值旋转动画:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
private Transform m_transform;
private Rigidbody rigidbody;
private Animator ani;
//移动速度与旋转速度
private float transloatSpeed=2.5f;
private float rotateSpeed=5;
private Vector3 targetPos;
private float Blend;
//获取前面添加的组件
private void Start()
{
m_transform = this.transform;
rigidbody = this.GetComponent<Rigidbody>();
ani = this.GetComponent<Animator>();
}
private void Update()
{
//引入动画参数
ani.SetFloat("Blend", Blend);
//通过监控用户输入,判定角色移动向量,并在FixUpdate中控制角色移动到目标位置
Vector3 v3 = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")).normalized;
targetPos = v3 * Time.deltaTime * transloatSpeed * Blend;
//通过角色移动目标向量与角色自身的正方向插值决定旋转角度,并使用rotation进行旋转
Vector3 targetDir = Vector3.Slerp(m_transform.forward, v3, rotateSpeed * Time.deltaTime);
m_transform.rotation = Quaternion.LookRotation(targetDir);
//监控用户输入,控制动画参数
if (Input.GetKey(KeyCode.W)||Input.GetKey(KeyCode.S)|| Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
{
if(Input.GetKey(KeyCode.LeftShift))
{
Blend = Mathf.Lerp(Blend, 1f, 0.03f);
return;
}
Blend = Mathf.Lerp(Blend, 0.5f, 0.03f);
}
if (!(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)))
{
Blend = Mathf.Lerp(Blend, 0, 0.03f);
}
}
private void FixedUpdate()
{
//通过目标向量与自身位置控制角色移动
rigidbody.MovePosition(targetPos + m_transform.position);
}
}
这样就可以有一个比较好的旋转与启动的效果,但是当我们启动后,发现角色的移动是基于世界坐标,我们按下W时,永远都是面向世界坐标的Z轴移动,而不是向着当前状态角色面对的方向移动,所以这种设计比较适合视角固定的第三人称游戏手游,比如说王者荣耀,可以得到一个较为顺滑的体验
产生 这种情况是因为:
rigidbody.MovePosition()
是相对于世界坐标来计算的,而产生旋转后,角色的世界坐标方向不发生改变,所以再次移动时,依旧会以坐标轴计算
第二种方案:
大致的显示效果(类似于原神的第三人称控制效果):
角色控制脚本:
为了可以和相机有一个更好的融合,通过对于相机方向的判断,然后决定角色的正方向,这样可以直接伴随相机来进行设计,为了有一个更好的效果,可以使用一个相机插件Cinemachine
来使得游戏体验更好:
由于我们相机镜头可以自由围绕Y轴自由旋转,所以可以让相机来承担旋转人物,角色的角度可以根据相机的角度来改变,这样可以有一更好的体验:当我们按下W时,角色就会背向相机移动,给玩家更真实的体验:
大概解释一下代码:
- 1,首先当我们输入控制变量时(
WASD
),动画参数blend
会插值增大,同时速度也会根据该参数的变化而变化,角色会产生向前移动的效果- 2,接下来要控制角色的转向,我们角色转向的参照物是依据与相机,当玩家按下W时,我们要保证角色背对相机来移动,要实现这样的效果,首先通过
Vector3.ProjectOnPlane(cam.transform.forward, Vector3.up)
方法来获取相机Z轴在世界坐标中X轴与Y轴的投影,即水平面,而如果角色forword
在与相机的forword
X轴与Z轴的投影不一致,则插值旋转到相机Z轴的投影方向,这样就可以保证玩家按下W的时候,真的会有向前走的感觉,如图,让两个粉色向量方向保持一致
- 3,同理对于按下
ASD
键时,分别对应相机不同轴在水平面(X轴与Z轴组合的面)的投影,让角色分别旋转到对应的角度即可- 4,通过一个
Vector3
向量来控制角色移动,并加入影响速度的moveSpeed
、blend
两个变量
下面就是第二个控制脚本的代码,需要注意,除了之前角色添加的组件,还需要获取要跟随的相机
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerTest : MonoBehaviour
{
///
///获取角色的跟随相机
///
public Camera cam;
///
/// 获取角色相关组件
///
private Transform m_transform;
private Rigidbody rig;
private Animator anim;
///
/// 定义几个移动参数
///
private Vector3 targetPos;
private Vector3 movePos;
private Vector3 camPos;
public float moveSpeed=10f;
private float blend;
private void Start()
{
m_transform = this.transform;
rig = this.GetComponent<Rigidbody>();
anim = GetComponent<Animator>();
}
private void Update()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
anim.SetFloat("Blend", blend);
cam.transform.LookAt(m_transform);
camPos = m_transform.position + new Vector3(0, camy, camZ);
cam.transform.position = Vector3.Lerp(cam.transform.position, camPos, 0.1f);
if (Input.GetKey(KeyCode.W))
{
blend = Mathf.Lerp(blend, 0.5f, 0.02f);
targetPos.z = 1;
Vector3 targetDir = Vector3.Slerp(m_transform.forward, Vector3.ProjectOnPlane(cam.transform.forward, Vector3.up), 0.02f);
m_transform.rotation = Quaternion.LookRotation(targetDir, Vector3.up);
}
if (Input.GetKey(KeyCode.S))
{
blend = Mathf.Lerp(blend, 0.5f, 0.02f);
targetPos.z = 1;
Vector3 targetDir = Vector3.Slerp(m_transform.forward, -Vector3.ProjectOnPlane(cam.transform.forward, Vector3.up), 0.02f);
m_transform.rotation = Quaternion.LookRotation(targetDir,Vector3.up);
}
if (Input.GetKey(KeyCode.A))
{
blend = Mathf.Lerp(blend, 0.5f, 0.02f);
targetPos.z = 1;
Vector3 targetDir = Vector3.Slerp(m_transform.forward, -Vector3.ProjectOnPlane(cam.transform.right, Vector3.up), 0.02f);
m_transform.rotation = Quaternion.LookRotation(targetDir, Vector3.up);
}
if (Input.GetKey(KeyCode.D))
{
blend = Mathf.Lerp(blend, 0.5f, 0.02f);
targetPos.z = 1;
Vector3 targetDir = Vector3.Slerp(m_transform.forward, Vector3.ProjectOnPlane(cam.transform.right, Vector3.up), 0.02f);
m_transform.rotation = Quaternion.LookRotation(targetDir, Vector3.up);
}
if (!Input.GetKey(KeyCode.W)&&!Input.GetKey(KeyCode.S)&& !Input.GetKey(KeyCode.A) && !Input.GetKey(KeyCode.D))
{
targetPos.z = 0;
blend = Mathf.Lerp(blend, 0f, 0.02f);
}
movePos = targetPos * Time.deltaTime * moveSpeed* blend;
transform.Translate(movePos);
}
}
相机插件:
在完成角色控制脚本的编写后,需要为角色添加一个跟随相机,为了有一个比较好的效果,就不自己编写脚本来实现这种跟随功能, ,而是使用一个官方的相机组件Cinemachine
来完成相机的跟随自由视角的效果:
首先点击Windows
中选择Package Manager
打开资源管理器,然后再输入框输入Cinemachine找到该相机插件,下载后导入到项目中,由于我已经下载导入了,所以显示为Up to date
:
当我们导入该插件后,就可以在上面的导航栏中看到Cinemachine的选项,点击后选择Create FreeLook Camera
创建一个视角可以自由移动的相机,关于Cinemachine
的具体细节大家可以查阅官方文档,这里我仅对要用的参数做一些说明:
最先调整的是Look At
和Follow
参数,即相机看着谁与相机要跟随谁,很明显,都是我们的角色,所以可以将角色(或者角色的一部分,比如眼睛更合适)拖入其中
接下来我们观察人物的周围,发现有三个红色的圈,标明了相机可以围绕角色移动的范围,我们同样可以在虚拟相机上面调整其参数:
完成后,我们发现在移动相机时会有一些画面卡顿的问题,这个时候,我们可以点击我们的MainCamera
,来修改其中一些参数和图片中一致即可:
完成这些设置,你可能得到的结果并不尽人意,但是不要灰心,并不是你做的不好,而是最终的效果是受到多个参数影响的,你可以花一些时间来慢慢调整这些参数,打磨你的游戏,最终会有一个好的结果的
在花费一定时间后,如果你还是觉得效果不尽人意,那你可能需要换一种思路来设计你的第三人称控制器。没有最好的,只有最合适的,加油