【Unity】动作游戏开发实战详细分析-07-连续技与组合技功能设计
基本思路
在一些动作游戏中,存在着连续技这一功能,具体来说就是连续按下规定的按键会触发能力的功能,或者是长按触发等等。
实现解析
ComboCmd
这是命令结构体,它代表单个连续技命令
我们来逐一分析他的代码,首先,我们要明确,我们对于命令的监听基于协程,因此该命令类需要有最基本的两个方法,那就是重置和更新。
重置方法很简单,就是重置长按倒计时与限制倒计时
public ComboCmd Reset()//重置
{
mLimitTime_Timer = LimitTime;
mHoldTime_Timer = HoldTime;
return this;
}
我们通过委托从外部输入检测条件以及时间帧长
我们来看重点的更新函数
首先他会输出一个整形变量state,用于表示当前的命令状况
每次更新时,默认初始值为等待值。如果监听条件成立,则进行时间判定,并减少长按时间,当长按时间归零,则赋予state变量成功值
这里还有一个限制时间的变量监听,它用于组合技的按键时间间隔限制
例如,目前具有组合技C+J触发行为,但是要求必须在0.2秒内完成输入。因此就需要限制时间的存在。如果连续技的两个单独命令的按键输入超过限制时间,则返回失败值
public ComboCmd Tick(out sbyte state)//每一次更新
{
state = STATE_WAIT;
if (Conditional())//监测条件是否成立
{
if (mHoldTime_Timer > 0)//是否为长按
mHoldTime_Timer -= DeltaTime();
else//不为长按且条件进入,则命令完成
state = STATE_SUCCESS;
}
if (state != STATE_SUCCESS && mLimitTime_Timer > COUNTDOWN_INVALID)//更新限制时间
{
mLimitTime_Timer -= DeltaTime();//更新倒计时
if (mLimitTime_Timer <= 0)//超过时间则失败
{
state = STATE_FAILURE;
mLimitTime_Timer = 0;
}
}
return this;
}
完整代码
using System;
using UnityEngine;
namespace ACTBook
{
public struct ComboCmd
{
const sbyte COUNTDOWN_INVALID = -1;//无效倒计时
const sbyte STATE_SUCCESS = 1;//成功ID
const sbyte STATE_FAILURE = -1;//失败ID
const sbyte STATE_WAIT = 0;//等待ID
float mLimitTime_Timer;//限制时间倒计时
float mHoldTime_Timer;//长按时间倒计时
public float LimitTime { get; set; }//限制时间
public float HoldTime { get; set; }//长按时间
public Func<bool> Conditional { get; set; }//检测条件(鼠标、键盘、手柄按下等)
public Func<float> DeltaTime { get; set; }//两帧时间差
public ComboCmd Reset()//重置
{
mLimitTime_Timer = LimitTime;
mHoldTime_Timer = HoldTime;
return this;
}
public ComboCmd Tick(out sbyte state)//每一次更新
{
state = STATE_WAIT;
if (Conditional())//监测条件是否成立
{
if (mHoldTime_Timer > 0)//是否为长按
mHoldTime_Timer -= DeltaTime();
else//不为长按且条件进入,则命令完成
state = STATE_SUCCESS;
}
if (state != STATE_SUCCESS && mLimitTime_Timer > COUNTDOWN_INVALID)//更新限制时间
{
mLimitTime_Timer -= DeltaTime();//更新倒计时
if (mLimitTime_Timer <= 0)//超过时间则失败
{
state = STATE_FAILURE;
mLimitTime_Timer = 0;
}
}
return this;
}
}
}
WaitForComboInput
连续技命令监听类,我们需要通过协程来完成监听,因此该类需要实现
IEnumerator
接口
字段指令数组存储连续技指令
重点来看更新逻辑函数
首先初始化result与state
然后更新当前的命令(从index=0开始)
如果命令为失败值,则对所有命令进行重置
如果是成功值,则继续监听下一个命令,直到最后一个命令也成功时,result赋予false,表示协程暂停结束。
如果是等待值,则继续监听
bool IEnumerator.MoveNext()//更新逻辑
{
var result = false;
var state = STATE_FAILURE;//初始化状态
mCmdArray[mCurrentIndex] = mCmdArray[mCurrentIndex].Tick(out state);//更新命令
switch (state)
{
case STATE_FAILURE://失败的情况
mCurrentIndex = 0;
for (int i = 0, iMax = mCmdArray.Length; i < iMax; i++)
mCmdArray[i] = mCmdArray[i].Reset();//失败重置
result = true;
break;
case STATE_SUCCESS://成功的情况
mCurrentIndex++;
if (mCurrentIndex == mCmdArray.Length)
result = false;//若为最后一个指令协程结束
else
result = true;
break;
case STATE_WAIT://继续等待
result = true;
break;
}
return result;//若返回值为True则继续更新
}
完整代码
using System.Collections;
namespace ACTBook
{
public class WaitForComboInput : IEnumerator
{
const sbyte COUNTDOWN_INVALID = -1;//无效倒计时
const sbyte STATE_SUCCESS = 1;//成功ID
const sbyte STATE_FAILURE = -1;//失败ID
const sbyte STATE_WAIT = 0;//等待ID
int mCurrentIndex;//当前指令索引
ComboCmd[] mCmdArray;//指令数组
public WaitForComboInput(ComboCmd[] comboCmdArray)//初始化
{
mCmdArray = comboCmdArray;
}
object IEnumerator.Current { get { return null; } }//接口实现
void IEnumerator.Reset() { mCurrentIndex = 0; }//接口实现
bool IEnumerator.MoveNext()//更新逻辑
{
var result = false;
var state = STATE_FAILURE;//初始化状态
mCmdArray[mCurrentIndex] = mCmdArray[mCurrentIndex].Tick(out state);//更新命令
switch (state)
{
case STATE_FAILURE://失败的情况
mCurrentIndex = 0;
for (int i = 0, iMax = mCmdArray.Length; i < iMax; i++)
mCmdArray[i] = mCmdArray[i].Reset();//失败重置
result = true;
break;
case STATE_SUCCESS://成功的情况
mCurrentIndex++;
if (mCurrentIndex == mCmdArray.Length)
result = false;//若为最后一个指令协程结束
else
result = true;
break;
case STATE_WAIT://继续等待
result = true;
break;
}
return result;//若返回值为True则继续更新
}
}
}
最后我们可以使用一个测试类,对功能进行测试
using System;
using System.Collections;
using UnityEngine;
namespace ACTBook
{
class Foo : MonoBehaviour
{
IEnumerator Start()
{
while (true)
{
Func<float> deltaTime = () => Time.deltaTime;//时间间隔函数
yield return new WaitForComboInput(new ComboCmd[]
{
new ComboCmd(){Conditional=()=>Input.GetKey(KeyCode.X), DeltaTime=deltaTime, HoldTime=-1, LimitTime=0.2f },//0.2秒内按下x
new ComboCmd(){Conditional=()=>Input.GetKey(KeyCode.Y), DeltaTime=deltaTime, HoldTime=-1, LimitTime=0.2f },//0.2秒内按下y
});
Debug.Log("Triggered!");//触发
yield return null;
}
}
void OnGUI()
{
GUILayout.Box("请在0.2秒内按下键盘'X'和'Y'按键,以触发日志打印");
}
}
}