今天试着自己实现了简单的连招系统,先来介绍一下该系统的逻辑:角色按左键是轻攻击、右键是重攻击,重攻击四组、轻攻击三组动画。根据轻攻击连按次数+右键的点击打出不同的重攻击。
技能编辑器
首先做一个最基础的属性编辑器,用ScriptableObject做,为每一个攻击动画配置好我们需要的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using UnityEngine; [CreateAssetMenu(fileName = "ComboData", menuName = "Scriptable Objects/ComboData")] public class ComboData : ScriptableObject { [SerializeField, Header("招式名称")] private string comboName; [SerializeField, Header("招式伤害")] private int comboDamage; [SerializeField, Header("被招式受击名称")] private string hitName; [SerializeField, Header("被招式格挡名称")] private string parryName; [SerializeField, Header("招式冷却时间")] private float coldTime; public string ComboName => comboName; public int ComboDamage => comboDamage; public string HitName => hitName; public string ParryName => parryName; public float ColdTime => coldTime; }
上面就是我们给每个攻击动作配置的属性了,光有属性还不够,需要创建技能编辑器来进行组合。我们对外提供一些方法,可以根据索引来快速获取每个动作的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 using NUnit.Framework; using System.Collections.Generic; using Unity.Burst.Intrinsics; using UnityEngine; [CreateAssetMenu(fileName = "Combo", menuName = "Scriptable Objects/Combo")] public class Combo : ScriptableObject { //显示可编辑的招式 [SerializeField]private List<ComboData> _combo = new List<ComboData>(); public string TryGetComboName(int index) { if (_combo.Count == 0 || _combo.Count < index + 1) { return null; } else { return _combo[index].ComboName; } } //获得受伤动画名称 public string TryGetHitName(int index) { if (_combo.Count == 0 || _combo.Count < index + 1) { return null; } else { return _combo[index].HitName; } } //获得格挡动画名称 public string TryGetParryName(int index) { if (_combo.Count == 0 || _combo.Count < index + 1) { return null; } else { return _combo[index].ParryName; } } //获得招式伤害 public int TryGetComboDamage(int index) { if (_combo.Count == 0 || _combo.Count < index + 1) { return 0; } else { return _combo[index].ComboDamage; } } public float TryGetColdTime(int index) { if(_combo.Count == 0 || _combo.Count < index + 1) { return 0; } else { return _combo[index].ColdTime; } } //获得招式动画数量 public int TryGetComboCount() => _combo.Count; }
之后我们就可以将不同动作进行组合、打出不同的组合技了。
编写核心代码
我的思路是,给出基础组合技和变招表,声明两个索引值分别记录轻攻击索引和重攻击索引。首先声明一个变量canAttck来帮助我们控制角色能否攻击,比如我们在打出第一段攻击后、有0.4秒时间我们是不希望角色进行第二段攻击的(怕点击次数太快造成抽搐),再写一个函数CanAttack()来判断能不能进行攻击。 我们默认使用基础组合技,当按下鼠标左键,comboIndex和changeComboIndex都会递增,动画播放后canAttack = false;之后通过时间管理器 进行延迟调用函数,延迟调用ResetCombo(); 对于重攻击,默认是不能连按右键打出重攻击组合技的,需要配合轻攻击,那就每次打出一个重攻击就把它的索引值归为0。具体参考以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 using UnityEngine; public class PlayerComboController : MonoBehaviour { [SerializeField, Header("基础组合技")] private Combo baseCombo; [SerializeField, Header("变招表")] private Combo ChangeCombo; private Combo currentCombo; private Animator animator; private int comboIndex = 0; private int changeComboIndex = 0; private int currentIndex = 0; private bool canAttack = true; private float coldTime = 0f; private void Awake() { animator = GetComponent<Animator>(); currentCombo = baseCombo; _mainCamera = Camera.main.transform; currentIndex = comboIndex; } private void Update() { ExcuteAttack(); EndAttack(); } private void FixedUpdate() { DetectDirection(); } #region 连招逻辑 private bool CanAttack() { if (!canAttack) return false; if (animator.AnimationAtTag("Parry")) return false; if (animator.AnimationAtTag("Hit")) return false; return true; } private void ExcuteAttack() { if (!CanAttack()) return; if (GameInputManager.Instance().leftAttack) { currentIndex = comboIndex; currentCombo = baseCombo; animator.CrossFade(currentCombo.TryGetComboName(comboIndex), 0.15f); coldTime = currentCombo.TryGetColdTime(comboIndex); canAttack = false; TimerManager.Instance().TryGetOneTimer(coldTime, ResetCombo); } if(GameInputManager.Instance().RightAttack) { currentIndex = changeComboIndex; currentCombo = ChangeCombo; animator.CrossFade(currentCombo.TryGetComboName(currentIndex),0.1f); coldTime = currentCombo.TryGetColdTime(currentIndex); canAttack = false; changeComboIndex = 0; comboIndex = 0; TimerManager.Instance().TryGetOneTimer(coldTime, ResetHeavy); } } private void ResetCombo() { comboIndex++; changeComboIndex++; canAttack = true; coldTime = 0f; if(comboIndex == currentCombo.TryGetComboCount()) { comboIndex = 0; } //关于这点我解释一下,如果角色打完一套轻击、不停下来又开始打轻击,此时就得重置一下changeComboIndex。你问为什么打重击或者停止时?我们都已经重置了。 //至于为什么是1,因为我们设置的changeComboIndex == 0 时,那个动画只有静止时、或者打完一个重击再打才能用。这里轻击我们采用从1开始的重置 if(changeComboIndex == ChangeCombo.TryGetComboCount()) { changeComboIndex = 1; } } private void ResetHeavy() { canAttack = true; coldTime = 0f; } private void EndAttack() { if (animator.AnimationAtTag("Move") || animator.AnimationAtTag("Idle")) { comboIndex = 0; changeComboIndex = 0; } } #endregion }