Unity基础连招思路(索引版)

今天试着自己实现了简单的连招系统,先来介绍一下该系统的逻辑:角色按左键是轻攻击、右键是重攻击,重攻击四组、轻攻击三组动画。根据轻攻击连按次数+右键的点击打出不同的重攻击。

技能编辑器

首先做一个最基础的属性编辑器,用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
}