你说得对,但是EmiteInnaACTSystemFramework是一款由EmiteInna自主研发,适用于PC平台的Unity第三人称上帝视角3D动作游戏代码框架。您将在游戏里扮演一个名叫游戏程序的角色,通过跳跃,冲刺和各种各样的丝滑小连招击败你的对手,修复程序中的bug,找回失散多年的代码能力,发掘未知的设计模式和模拟算法,揭开游戏行业无法入行、毕业即失业的真相。

本篇是ACT系列之一,github仓库位于:https://github.com/EmiteInna/EmiteInnaActSystem

简述

今天主要是边写边反思地想怎么写一个更完善的技能模块。

考虑到技能的一些其它特性,我来写一个文档:

目前技能的构造大概和上一篇的类设一样,主要是靠Configure驱动,然后角色的Configure下会有一个list可以放入技能configure。

然后技能Configure如类射图所示,它包含一些可以填入分配的信息。

这里做一个示例:

图片

这是一个叫旋转的技能的configure,如上所示,这是一个旋转技能,它持续20秒,会使使用的角色在第0秒先触发名为EnterCasting事件,大部分技能都可以通过这个事件来进入到Casting状态,因为我们并不希望玩家在释放技能的时候能够像在Idleground状态下一一样自由操作。

然后,与此同时但是在之后,玩家会触发Spin事件,这个事件的代码如下。

代码
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

public void Spin()
{
CharacterStartInterruptableCoroutine(Dospin());
}
IEnumerator Dospin()
{
yield return new WaitForEndOfFrame();
float timer = 0;
int frame=0;
while (timer<20f)
{
timer += Time.deltaTime;
int frameCount = (int)(timer / Time.fixedDeltaTime);
if (frameCount > frame)
{
frame++;
transform.Rotate(Vector3.up, 15f);
}
if (Input.GetKeyDown(KeyCode.V))
{
//延后一帧是为了避免刚停止又继续释放。
yield return new WaitForEndOfFrame();
CharacterCancelSpellWithCommand("finish");
}
yield return null;
}
CharacterCancelSpellWithCommand("finish");
}

第一个函数Spin()是挂载事件的函数,随后这个DoSpin是它触发的协程,我首先解释一下CharacterStartInterrupableCoroutine是什么意思,众所周知技能是可以被打断的,但是技能中的一些持续行为是不一定会被打断的。

就比如我放下一个魔法阵,然后这个魔法阵里持续以某种模式发射弹幕,这个时候哪怕我被打断了,魔法阵也会继续发射弹幕。但是如果是我需要持续施法的一个发射弹幕的魔法,被打断了之后相应的弹幕也不会发射了。

这就是设置这个方法的目的,在注册技能时,我希望将所有会被打断的协程全都用这种方法来执行,这样一来在技能被打断时,这些协程也会被打断,它的实现是一个栈,当然这好像不是什么值得一提的事情。

每个技能的结尾最好都放上一个类似finish的方法,这决定了在技能释放完毕之后角色会回到哪个状态。

因此你添加一个新技能的全过程是:新建一个SpellConfigure并在里面写上自己希望播放的动画、音频、特效和逻辑事件,将这个Configure拖进角色Configure的spellList,在Controller脚本中实现对应的逻辑方法——然后在合适的位置启用这个技能,在合适的时间使用它。

后续(杂谈)

然后我们会发现,光实现这些东西是没用的,技能是为了干嘛,为了打伤害,为了发射弹幕,为了加buff和加debuff等等一大堆东西(误)。

所以这些的组件都是需要一个一个设计的,或许到后面我们会把它们全都作为结构体,放一个list到spellConfigure里,然后这个configure可能需要你在inspector界面从上翻到下才能遍历所有的东西。

但至少这提高了效率,不是吗?

所以我试做型地添加了一个攻击范围伤害的模块。

图片

如你所想,它的功能是在特定时间造成一个定制形状的范围伤害,但你勾选了开启debug之后,在测试场景里你能看到一个gizmos显示这个事件造成的伤害范围。

为此用我弱鸡的计算几何功底写了个实时的debug,爽到。

代码
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

/// <summary>
/// 技能debug用的框框
/// </summary>
public void OnDrawGizmos()
{
if (spell != null)
{
if(spell.spells != null)
{
foreach(KeyValuePair<string,SpellBase> sp in spell.spells)
{
if (sp.Value.config.attackEvents != null)
{
foreach(SpellAttackEvent e in sp.Value.config.attackEvents)
{
if (e.showDebugGizmos == false) continue;
if (e.areaType == AreaType.CUBE)
{
Vector4 center = e.centerOffset;
Vector4 p1 = center + new Vector4(e.extends.x, e.extends.y, e.extends.z,1);
Vector4 p2 = center + new Vector4(-e.extends.x, e.extends.y, e.extends.z, 1);
Vector4 p3 = center + new Vector4(e.extends.x,- e.extends.y, e.extends.z, 1);
Vector4 p4 = center + new Vector4(e.extends.x, e.extends.y, -e.extends.z, 1);
Vector4 p5 = center + new Vector4(-e.extends.x,- e.extends.y, e.extends.z, 1);
Vector4 p6 = center + new Vector4(-e.extends.x, e.extends.y,- e.extends.z, 1);
Vector4 p7 = center + new Vector4(e.extends.x, -e.extends.y, -e.extends.z, 1);
Vector4 p8 = center + new Vector4(-e.extends.x,- e.extends.y,- e.extends.z, 1);
p1 = transform.localToWorldMatrix * p1;
p2 = transform.localToWorldMatrix * p2;
p3 = transform.localToWorldMatrix * p3;
p4 = transform.localToWorldMatrix * p4;
p5 = transform.localToWorldMatrix * p5;
p6 = transform.localToWorldMatrix * p6;
p7 = transform.localToWorldMatrix * p7;
p8 = transform.localToWorldMatrix * p8;
Gizmos.color = Color.green;
Gizmos.DrawLine(p1, p2);
Gizmos.DrawLine(p1, p3);
Gizmos.DrawLine(p1, p4);
Gizmos.DrawLine(p2, p6);
Gizmos.DrawLine(p2, p5);
Gizmos.DrawLine(p3, p5);
Gizmos.DrawLine(p3, p7);
Gizmos.DrawLine(p4, p6);
Gizmos.DrawLine(p4, p7);
Gizmos.DrawLine(p5, p8);
Gizmos.DrawLine(p6, p8);
Gizmos.DrawLine(p7, p8);
}else if (e.areaType == AreaType.ELLIPSE)
{
Gizmos.color = Color.green;
int delta = 10;
Vector4 center = e.centerOffset;
if (e.angle.y >= 180)
{
for (int i = 0; i < 360; i+=delta)
{
float degnow = (float)i / 360 * 2 * Mathf.PI;
float degnext = (float)(i + delta) / 360 * 2 * Mathf.PI;
Vector4 pnow = center + new Vector4(-e.extends.x * Mathf.Sin(degnow), e.extends.y, e.extends.z * Mathf.Cos(degnow), 1);
Vector4 pnext = center + new Vector4(-e.extends.x * Mathf.Sin(degnext), e.extends.y, e.extends.z * Mathf.Cos(degnext), 1);
pnow = transform.localToWorldMatrix * pnow;
pnext = transform.localToWorldMatrix * pnext;
Gizmos.DrawLine(pnow, pnext);
pnow = center + new Vector4(-e.extends.x * Mathf.Sin(degnow), -e.extends.y, e.extends.z * Mathf.Cos(degnow), 1);
pnext = center + new Vector4(-e.extends.x * Mathf.Sin(degnext), -e.extends.y, e.extends.z * Mathf.Cos(degnext), 1);
pnow = transform.localToWorldMatrix * pnow;
pnext = transform.localToWorldMatrix * pnext;
Gizmos.DrawLine(pnow, pnext);
}
}
else
{
delta = (int)(e.angle.y/18);
if (delta == 0) delta++;
Vector4 pcBtm = center + new Vector4(0, -e.extends.y, 0, 1);
Vector4 pcTop = center + new Vector4(0, e.extends.y, 0, 1);
pcBtm = transform.localToWorldMatrix * pcBtm;
pcTop = transform.localToWorldMatrix * pcTop;
bool flg = false;
Vector4 psBtm=Vector4.one, psTop = Vector4.one, peBtm = Vector4.one, peTop = Vector4.one;
for (int i =(int)(e.angle.x-e.angle.y); i < (int)(e.angle.x + e.angle.y); i+=delta)
{
// float down = e.angle.x - e.angle.y;
//// if (down < 0) down += 360;
// float up = e.angle.x + e.angle.y;
//// if (up >= 360) up -= 360;
// if ((i < down || i + delta >up)&&(i-360<down||i+delta-360>up)) continue;


float degnow = (float)i / 360 * 2 * Mathf.PI;
float degnext = (float)(i + delta) / 360 * 2 * Mathf.PI;


Vector4 pnow = center + new Vector4(-e.extends.x * Mathf.Sin(degnow), e.extends.y, e.extends.z * Mathf.Cos(degnow),1);
Vector4 pnext = center + new Vector4(-e.extends.x * Mathf.Sin(degnext), e.extends.y, e.extends.z * Mathf.Cos(degnext),1);
pnow = transform.localToWorldMatrix * pnow;
pnext = transform.localToWorldMatrix * pnext;
if (!flg)
{
psTop = pnow;
}
peTop = pnext;
Gizmos.DrawLine(pnow, pnext);
pnow = center + new Vector4(-e.extends.x * Mathf.Sin(degnow), -e.extends.y, e.extends.z * Mathf.Cos(degnow),1);
pnext = center + new Vector4(-e.extends.x * Mathf.Sin(degnext), -e.extends.y, e.extends.z * Mathf.Cos(degnext),1);
pnow = transform.localToWorldMatrix * pnow;
pnext = transform.localToWorldMatrix * pnext;
if (!flg)
{
psBtm = pnow;
flg = true;
}
peBtm = pnext;
Gizmos.DrawLine(pnow, pnext);

}
//Debug.Log(psTop + " " + psBtm + " " + peTop + " " + peBtm);
Gizmos.DrawLine(pcTop, pcBtm);
Gizmos.DrawLine(pcTop, psTop);
Gizmos.DrawLine(psTop, psBtm);
Gizmos.DrawLine(pcTop, peTop);
Gizmos.DrawLine(pcBtm, psBtm);
Gizmos.DrawLine(pcBtm, peBtm);
Gizmos.DrawLine(peTop, peBtm);
}

}
}
}
}
}
}
}