跳到主要内容

标题:[Unity 3D] 1天开发闯关游戏_Part2_玩家交互 链接:Yew's Blog 日期:2024年1月8日

游戏操作

操作键盘手柄
移动WASDL
加速ShiftLB
跳跃空格Y
旋转视角鼠标R

玩家功能部分

设置角色行走功能

利用网络上的素材,简单搭建场景并添加 Animator Controller 用来测试 Collider 和角色行走的功能

picture 1

切换到侧视图,给小骷髅设置 Capsule Collider,给草方块添加 Mesh Collider

picture 2

新建脚本:PlayerMovement.cs 在课堂上脚本的基础上,优化一下代码结构:利用 Character Controller 来控制玩家,可以用 SimpleMove 来代替 Translate 和一堆参数,还可以省去 RigidBody ,并且可以像 AI Navigation 中的 Agent 一样让角色能够走一定程度的障碍。

搭建如下障碍,并给角色添加 Character Controller。

picture 9

优化后的代码如下:

public class PlayerMovement : MonoBehaviour
{
public float moveSpeed;
public float rotationSpeed;
private CharacterController chracterController;

private void Start()
{
chracterController = GetComponent<CharacterController>();
}
private void FixedUpdate()
{
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");

Vector3 movementDirection = new Vector3(horizontalInput, 0, verticalInput);
float magnitude = movementDirection.magnitude;
movementDirection.Normalize();
chracterController.SimpleMove(movementDirection * magnitude * moveSpeed);

if (movementDirection != Vector3.zero)
{
Quaternion toRotation = Quaternion.LookRotation(movementDirection);

transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
}

只使用 Character Controller 效果如下:

picture 10

行走改进:修复 Normalize 导致手柄无法线性控速

在获取输入的代码中,我们采用了以下代码来修复斜向速度超过1的问题:

movementDirection.Normalize();

但是这样也导致了小数值被格式化为 1,所以,在对数值格式化之前,应该先将 magnitude 存储起来,以确保游戏能在手柄上使用。

修改代码如下:

float magnitude = movementDirection.magnitude;
movementDirection.Normalize();
transform.Translate(movementDirection * magnitude * moveSpeed * Time.deltaTime, Space.World);

修改效果对比:

修改前修改后
picture 8picture 7

使用 CinemaMachine 添加跟随摄像机

在菜单栏 -- Window -- Package Manager,右上角搜索 Cinemachine,再点击 Install 安装。 在 Hierarchy 面板右键 -- Cinemachine -- Virtual Camera

picture 11

这时,Main Camera 会出现一个 Cinemachine 的 logo,代表 Main Camera 被 Cinemachine 控制。在右侧的 Inspector 面板配置 Cinemachine:

picture 12

效果如下:

picture 13

镜头改进:Cinemachine 第三人称动画

在固定视角中,当玩家在某些场景下可能需要观察周围的环境,所以需要动鼠标或者右摇杆来旋转视角,而使用 Cinemachine 的固定跟随视角很明显不能做到,所以在这里,将镜头更改为第三人称动画:

修改相机焦点 在玩家角色下,新建一个空物体 CamFocus 作为子物体。待会儿将这个子物体作为相机焦点,高度差不多放在嘴巴高度。

picture 11

新建摄像机 最开始创建的虚拟摄像机默认是固定视角的,所以我们需要新建一个 FreeLook Camera 作为第三人称摄像机。 将刚才新建的 CamFocus 拖到 FreeLookCam 的 Follow 和 LookAt

picture 12

修改 Cinemachine 设置 在 Scene 视图中,我们可以看到 Cam 有 3 个圈,分别代表了 3 个不同高度的摄像机运动路径。我们按照“站得高看得远” 的原则,设置 3 个圈的半径如下:

picture 13

修改脚本,设置视角旋转 接下来,修改脚本,以支持鼠标旋转视角。 为了让角色的朝向和视角的朝向一致,我们要先引入摄像机组件,并且将摄像机组件的 y 轴旋转值传递给角色朝向:

[SerializeField]
private Transform cameraTransform; //引入摄像机位置组件

注意,上方代码的 [SerializeField] 是序列化的意思,序列化了之后,你就可以在 Inspector 面板看到 cameraTransform (即使是 private 属性),这样可以方便开发者传参,也有效避免了被其他类访问导致代码出错。

接下来,将相机的 y 轴旋转值赋值给 moveDirection:

// 将移动方向转换为相机的方向
movementDirection = Quaternion.AngleAxis(cameraTransform.rotation.eulerAngles.y, Vector3.up) * movementDirection;
movementDirection.Normalize();

镜头改进:细节设置

隐藏光标 还没完,要真正实现鼠标转换视角总得把光标隐藏了吧? 在脚本中添加如下方法,代码很简单,字面意思,懒得解释了:

// 锁定焦点、隐藏光标
private void OnApplicationFocus(bool focus)
{
if (focus)
{
Cursor.lockState = CursorLockMode.Locked;
}
else
{
Cursor.lockState = CursorLockMode.None;
}
}

接下来,将 Main Camera 拖到 Camera Transform 中:

picture 14

自动同步朝向 在 FreeLookCam 中,继续进行以下设置:

设置作用
Orbits - Bingding ModeLock To Target On Assign自动对齐相机与角色同向
Recenter To Target HeadingEnable: True同上

自动躲避障碍 在 Inspector 面板中, 找到 Extension,添加 Cinemachine Collider:

设置作用
StrategyPull Camera Forward遇到障碍,相机向前运动
Damping2相机向前视角的速度
Damping When Occluded2视角恢复的速度

反转轴向

设置作用
Axis Control -- Y AxisInvert: false取消反转 Y 轴
Axis Control -- X AxisInvert: false取消反转 X 轴

摇杆视角 接下来,给手柄的右摇杆添加控制视角功能: Edit -- Project Setting -- Input Manager,找到 Mouse X: Duplicate Array Element,参数如下:

picture 15

再找到 Mouse Y,同样的操作,不过选择 5th axis。

最终,镜头改进之后,我们实现了如下功能:

  • 相机可以聚焦角色头部
  • 遇到障碍后自动向前运动
  • 角色转向后自动与角色朝向同步
  • 摇杆可以控制视角

效果如下:

picture 16

使用 Character Controller 添加跳跃功能

分析跳跃逻辑如下:

picture 4

根据跳跃逻辑,我们可以设置变量 jumpSpeed 和 ySpeed 计算角色的高度,并赋值给 Character Controller 的 Move 方法。 首先,新增如下变量:

public float jumpSpeed;//修改跳跃高度
private float originalStepOffset; //跳跃前的偏倚
private float ySpeed;

在 Start() 方法中存储原始的步长偏移

originalStepOffset = characterController.stepOffset;// 存储原始偏移

在 FixedUpdate() 方法中计算 y 轴高度:

// 计算y高度
ySpeed += Physics.gravity.y * Time.deltaTime;
if (characterController.isGrounded)
{
characterController.stepOffset = originalStepOffset; // 恢复原始偏移
ySpeed = -0.5f;// 跳跃后给y固定一个稳定的值

if (Input.GetButtonDown("Jump"))
{
ySpeed = jumpSpeed;
}
}
else
{
characterController.stepOffset = 0; // 跳跃时将原始偏移设置为0
}

修改原来的 SimpleMove() 方法,将跳跃高度加入到 Character Controller 中

// 添加了y轴的速度
Vector3 velocity = movementDirection * magnitude;
velocity.y = ySpeed;
characterController.Move(velocity * Time.deltaTime);

效果如下:

picture 0

因为跳跃的动画幅度较大,直接跳跃有可能会让动画跟不上(别问,问就是调一天也没调好),所以我们将跳跃拆成3个阶段:跳起、滞留、落地。 去 Adobe 的动作库网站: mixamo,将我们的小骷髅建模上传上去,分别找到这三个动作,然后下载。

picture 5

将三个动作导入到Unity中,并对刚才的三个动作进行设置根动画,其中:Falling Idle 需要勾选 Loop Time,确保滞空的时候动作时间够长。

picture 6

跳跃改进:增加缓冲区

由于玩家是存在反应时间的,如果没有缓冲区,在游玩过程中很容易出现以下情况:

Bug体验原因
刚落地就跳跃跳跃不起来isGrounded还没有为true,还差一点接触到地面碰撞箱
从平台上跳起失效玩了一点时间点击跳跃按钮,已经离开地面碰撞箱,isGrounded = false
各种跳起失效控制器、键盘的电气特性造成的延迟

改进的跳跃逻辑如下:

picture 1

首先,增加变量用于检测跳跃按钮按下的时间:

public float jumpingBufferTime; //设置跳跃误差时间
private float? lastGroundedTime; //上一次接触地面的时间,?表示可以为空值(null)
private float? lastJumpPressedTime; //上次点击跳跃的时间

在 Update() 方法中,更新如下代码用于记录时间:

// 每次刷新,更新落地时间和跳跃按钮时间
if (characterController.isGrounded)
{
lastGroundedTime = Time.time;
}
if(Input.GetButtonDown("Jump"))
{
lastJumpPressedTime = Time.time;
}

在记录了时间之后,判断是否满足缓冲区并给予 ySpeed

// 误差时间内,如果跳跃按钮被按下,则跳跃
if (Time.time - lastGroundedTime <= jumpingBufferTime)
{
characterController.stepOffset = originalStepOffset; // 恢复原始偏移
ySpeed = -0.5f;// 跳跃后给y固定一个稳定的值

if (Time.time - lastJumpPressedTime <= jumpingBufferTime)
{
ySpeed = jumpSpeed;
//清空数值,以免重复跳跃
lastJumpPressedTime = null;
lastGroundedTime = null;
}
}
else
{
characterController.stepOffset = 0; // 跳跃时将原始偏移设置为0
}

这里我们将 JumpSpeed 设置为 5,JumpBufferTime 设置为 0.2 秒,更方便观察连跳效果。 改进后的跳跃效果如下: (可能看不出来的,但是游玩过程中能明显发现起跳更容易了)

picture 2

标题:[Unity 3D] 1天开发闯关游戏_Part2_玩家交互 链接:Yew's Blog 日期:2024年1月8日