【Unity】动作游戏开发实战详细分析-26-组合攻击

【Unity】动作游戏开发实战详细分析-26-组合攻击


基本思想

在游戏中,有些攻击方法必须要等待条件触发后才可以执行,并且根据情况的不同会发挥不同的效果,这是一种有前置条件要求的组合攻击方式。

脚本逻辑结构

我们先看一下这个模块的脚本结构

在这里插入图片描述

ComposeAttackController脚本中存放着不同的攻击组合类型,通过Update事件函数每帧更新当前可触发的组合攻击,并信息存储在索引字段中。上下文结构信息存放了组合攻击所需要的角色自身组件,例如Animator、Transform等。

模块代码实现

首先这是组合攻击的抽象类,定义了基本的方法

  • CanTrigger,用于判断组合技是否达到了触发条件,他的输入参数要求信息上下文
  • Trigger,用于处理触发逻辑
public abstract class ComposeAttackBase : ScriptableObject
{
  public abstract bool CanTrigger(ComposeAttackContext context, bool prepareTrigger);
  public abstract IEnumerator Trigger(ComposeAttackContext context);
}
public struct ComposeAttackContext
{
  public Transform CasterTransform { get; set; }
  public Animator Animator { get; set; }
}

然后就是组合攻击控制器

该控制器用于控制当前控制器下所有的攻击组件,并且通过Update进行每帧判断,并将可使用的攻击索引存储在字段中。并提供了外部的触发接口对对应的攻击组件进行触发

public class ComposeAttackController : MonoBehaviour
{
  [SerializeField] Animator animator = null;//上下文所需接口,面板暴露参数
  [SerializeField] ComposeAttackBase[] composeAttackArray = null;//组件列表面板暴露参数
  public int TriggerableComposeAttackIndex { get; private set; }//当前已触发的组合技能索引
  public ComposeAttackBase[] GetComposeAttackArray()//对外提供组合技能数组列表
  {
    return composeAttackArray;
  }
  public void Update()//每一帧更新组合技能是否触发逻辑,但可修改.enabled关闭脚本更新
  {
    var context = new ComposeAttackContext() { Animator = animator, CasterTransform = transform };
    for (int i = 0; i < composeAttackArray.Length; i++)
    {
      var item = composeAttackArray[i];
      if (item.CanTrigger(context, true))//触发条件检测
      {
        TriggerableComposeAttackIndex = i;
        break;
      }
    }
  }
  public IEnumerator TriggeredComposeSkill(int index)//组合技能的触发接口
  {
    if (index > composeAttackArray.Length - 1)
      throw new ArgumentOutOfRangeException();
    var context = new ComposeAttackContext() { Animator = animator, CasterTransform = transform };
    yield return composeAttackArray[index].Trigger(context);
  }
}

测试代码

public class ComposeAttackController_Test : MonoBehaviour
{
  public ComposeAttackController composeAttackController;
  public Animator[] animators;


  void Update()
  {
    if (InputCache.AttackButton)
    {
      StartCoroutine(composeAttackController.TriggeredComposeSkill(composeAttackController.TriggerableComposeAttackIndex));
      for (int i = 0; i < animators.Length; i++)
      {
        animators[i].enabled = false;
        animators[i].enabled = true;
        animators[i].Play("LightHit", 0, 0);
      }
    }
  }
}
public class InputCache : MonoBehaviour
{
  public static bool AttackButton { get; set; }


  void Update()
  {
    AttackButton = Input.GetKeyDown(KeyCode.J);
  }
}
[CreateAssetMenu(fileName = "ComposeAttack1", menuName = "ComposeAttacks/Attack1")]
public class ComposeAttack1 : ComposeAttackBase
{
  public float yOffset = 1f;//检测碰撞的y轴偏移
  public Vector3 size = new Vector3(1f, 2f, 1f);//检测碰撞的大小
  public Vector4 aroundOffset = new Vector4(0.5f, 0.5f, 0.5f, 0.5f);//前后左右检测距离偏移
  public LayerMask layerMask;
  bool mIsForwardAndBackword;

  public override bool CanTrigger(ComposeAttackContext context, bool prepareTrigger)
  {
    var upAxis = -Physics.gravity.normalized;//垂直轴
    var right = Vector3.ProjectOnPlane(context.CasterTransform.right, upAxis);//投影的右侧方向
    var forward = Vector3.ProjectOnPlane(context.CasterTransform.forward, upAxis);//投影的前方
    var upAxisOffset = upAxis * yOffset;//垂直轴偏移
    var forwardFlag = Physics.CheckBox(context.CasterTransform.position
                                       + upAxisOffset + forward * aroundOffset.x
                                       , size
                                       , Quaternion.identity
                                       , layerMask);
    var backwardFlag = Physics.CheckBox(context.CasterTransform.position
                                        + upAxisOffset + (-forward) * aroundOffset.y
                                        , size
                                        , Quaternion.identity
                                        , layerMask);

    if (forwardFlag && backwardFlag)//前后都有敌人
    {
      if (prepareTrigger) mIsForwardAndBackword = true;
      return true;
    }
    var leftFlag = Physics.CheckBox(context.CasterTransform.position
                                    + upAxisOffset + (-right) * aroundOffset.z
                                    , size
                                    , Quaternion.identity
                                    , layerMask);
    var rightFlag = Physics.CheckBox(context.CasterTransform.position
                                     + upAxisOffset + right * aroundOffset.w
                                     , size
                                     , Quaternion.identity
                                     , layerMask);

    if (rightFlag && leftFlag)//左右都有敌人
    {
      if (prepareTrigger) mIsForwardAndBackword = false;
      return true;
    }
    return false;
  }

  public override IEnumerator Trigger(ComposeAttackContext context)
  {
    Debug.Log("Trigger!");

    yield return null;
    if (mIsForwardAndBackword)//前后都有敌人的情况
    {
      context.Animator.Play("Range_Attack", 0, 0);
    }
    else//左右都有敌人的情况
    {
      context.CasterTransform.forward = context.CasterTransform.right;
      //先将角色朝向切至右边
      context.Animator.Play("Range_Attack", 0, 0);
    }
  }
}

猜你喜欢

转载自blog.csdn.net/qq_52324195/article/details/125978790