【Unity】动作游戏开发实战详细分析-07-连续技与组合技功能设计

  • Post author:
  • Post category:其他




【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'按键,以触发日志打印");
    }
  }
}



版权声明:本文为qq_52324195原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。