目录
在游戏开发的世界里,战斗连招系统是动作游戏的核心魅力之一,它能为玩家带来畅快淋漓的战斗体验。今天,咱们就来深入探讨一下在 Unity 引擎中,如何打造一套包含轻重击结合的战斗连招系统。
一、前期准备
(一)工程选择
你可以选择使用上期已经搭建好基础的工程,这样能基于已有成果进行开发,节省时间。要是没有上期工程,新建一个工程也没问题。
(二)素材导入
本次开发提供了资产素材包,其中包含上期视频的基础资产以及连招动作库。如果需要完整版资产,记得前往官方购买。获取素材包后,在 Unity 中直接导入.unitypackage
文件。当然,你也可以根据自己的喜好,用其他动作素材进行替换。
二、理论基础
连招本质上是连续播放战斗动作的游戏机制。不同动作依据前一个动作的播放依次衔接,形成连贯的动作链条。这种动作间的连接结构可以是链形,即简单的依次顺序播放;也可以是树形,从某个动作节点出发,根据不同条件延伸出多种后续动作分支。而这些动作播放的组合方式,其实就是游戏的数据结构,再通过特定算法将它们组织起来。
在连招实现中,指令输入的时机至关重要。输入过早,后一个动作指令会覆盖前一个,导致连招错乱;输入过晚,连招看起来就会卡顿不流畅。所以,我们会采用预输入的方式,也就是在一个合适的时间窗口期内完成按键指令的处理,确保连招的顺畅性。
三、Unity 中的具体实现步骤
(一)输入设置改造
- 找到并双击打开
input
配置文件,它既关联在角色身上,也能在资产中找到。 - 打开配置界面后,右键添加一个
action
,命名为roll
。接着,点击输入区域,输入shift
,在筛选出的可绑定按键中选择left shift
,并将控制方案设置为鼠标键盘。 - 把原预设的
fire
改名为fire1
,作为轻击输入;再添加一个fire2
作为重击输入。将fire1
绑定到鼠标左键,fire2
绑定到鼠标右键,同时分别为它们增加键盘输入J
键和K
键,方便操作。 - 完成设置后,点击保存资产并关闭窗口。
(二)翻滚脚本改造
双击打开上期编写的翻滚脚本,修改代码,使其符合新的input
调用方式。这一步主要是让脚本能够正确响应新设置的输入指令。修改完成后保存脚本,回到 Unity 编辑器运行一下,检查是否能正常工作。player input
会根据命名调用同一物体上的对应函数,这是一种方便的按键输入方案。
(三)新建战斗控制脚本
- 变量声明:在新建的脚本中,首先声明两个
Animation Clip
类型的动画数组,分别用于存放轻击和重击的动画。同时,声明之前编写的第三人称移动组件和Animator
组件的成员变量,以便在脚本中对它们进行操作。在Start
函数中,通过代码从同级对象获取这些组件。
using UnityEngine;
public class CombatController : MonoBehaviour
{
public AnimationClip[] lightAttackClips;
public AnimationClip[] heavyAttackClips;
private ThirdPersonMovement movementComponent;
private Animator animator;
private int inputType;
private int currentAttackIndex;
private float invalidTime;
void Start()
{
movementComponent = GetComponent<ThirdPersonMovement>();
animator = GetComponent<Animator>();
}
}
- 播放攻击函数实现:编写播放攻击函数,在函数开始时,先让角色的移动失效,避免在攻击时角色意外移动。然后根据传入的类型参数(区分轻击还是重击)和当前攻击索引,从对应的动画数组中找到合适的动画剪辑。对于轻击,攻击索引可以采用累加的方式,这样就能按顺序播放不同的轻击动画;而对于重击(终结技),则让连招索引归零,重新开始连招计数。最后,使用
Animator
直接播放获取到的动画剪辑,并记录动画的时长。
void PlayAttackAnimation(int type, float transitionTime)
{
movementComponent.enabled = false;
AnimationClip clip;
if (type == 1) // 假设1代表轻击
{
clip = lightAttackClips[currentAttackIndex];
currentAttackIndex = (currentAttackIndex + 1) % lightAttackClips.Length;
}
else // 假设其他值代表重击
{
clip = heavyAttackClips[currentAttackIndex];
currentAttackIndex = 0;
}
animator.Play(clip.name, 0, 0);
invalidTime = clip.length;
}
Update
函数实现:在Update
函数中,首先让失效时间不断减少,当失效时间减少到零时,恢复移动组件的生效状态,同时将连招计数归零,让角色回到可正常操作的状态。接着,判断是否有预存的输入数值。如果有,再判断当前时间是否在动画结束前的 0.4 秒内(这个 0.4 秒就是预输入的窗口时间,可根据实际需求调整),并且输入对应的动画剪辑在数组范围内,满足这些条件就调用攻击动画函数。
void Update()
{
if (invalidTime > 0)
{
invalidTime -= Time.deltaTime;
if (invalidTime <= 0)
{
movementComponent.enabled = true;
currentAttackIndex = 0;
}
}
if (inputType != 0)
{
float remainingTime = animator.GetCurrentAnimatorStateInfo(0).length - animator.GetCurrentAnimatorStateInfo(0).normalizedTime * animator.GetCurrentAnimatorStateInfo(0).length;
if (remainingTime <= 0.4f && remainingTime >= 0)
{
PlayAttackAnimation(inputType, 0.2f);
inputType = 0;
}
}
}
- 获取输入函数编写:为了获取
input system
中的输入,我们还需要编写OnFire1
和OnFire2
函数,分别对应轻击和重击的输入。在函数中,将输入类型保存到inputType
变量中。
public void OnFire1()
{
inputType = 1;
}
public void OnFire2()
{
inputType = 2;
}
(四)脚本与动画配置
- 将编写好的战斗控制脚本添加到主角对象上。
- 在脚本的属性面板中,把准备好的轻击和重击动画剪辑分别添加到对应的动画数组中。例如,准备了四个轻击动画和四个重击动画,就依次将它们配置到
lightAttackClips
和heavyAttackClips
数组里。 - 打开主角的
Animator Controller
,把用到的所有动作都拖进去,然后逐个将这些动作与站立移动状态连接起来。这样设置后,攻击动画播放完成后,角色就能回归到正常的站立移动融合状态。
四、优化与拓展思考
目前在自由控制模式下,角色在攻击时方向不能改变,这是因为直接禁用了移动组件。优化方法可以通过改造禁用逻辑,利用布尔值变量来控制是否同步动画参数,从而实现攻击时角色方向的灵活调整。不过,这部分内容就留给大家根据实际项目需求去探索和实现啦。
战斗连招的实现方式多种多样,不同的游戏项目有不同的需求,所以并没有放之四海而皆准的完美方案。希望通过这篇博客,能让你对战斗连招系统的开发有更清晰的认识,帮助你在游戏开发的道路上迈出坚实的一步!要是你在开发过程中遇到了其他难题,欢迎随时交流。