Every skill animation so far had the same shape: each cycle, ask whether the skill is running, work out which frame it is on, compute the position for that frame, and create one effect. For a simple animation that is right. It falls apart once the animation is “dash forward, pause, burst into three, then pull back” - every stage grows its own condition, state has to be handed between stages, and changing one stage shifts the frame numbers of everything after it.
There is another shape. At the moment of the cast, compute the whole animation and write it into a queue; from then on, all you ever do is pop the entries whose time has come. The corpus splits it into two functions, Plan and Exec.
// 같은 길이의 큐 네 개를 나란히 씽니다 / four parallel queues, same length
var qX = EUDQueue(1000); // x
var qY = EUDQueue(1000); // y
var qT = EUDQueue(1000); // 절대 프레임 / absolute frame
var qF = EUDQueue(1000); // 단계 / phase
function Plan(epd, px, py, dir) {
const now = Frame;
foreach(i : py_range(12)) { // 진짜 전반 / the dash
const x, y = lengthdir_256(i * 10, dir);
qX.append(px + x); qY.append(py + y);
qT.append(now + i); qF.append(1);
}
foreach(k : py_range(3)) { // 세 갈래 / the three-way burst
const x, y = lengthdir_256(120, dir + k * 64 - 64);
qX.append(px + x); qY.append(py + y);
qT.append(now + 14); qF.append(2);
}
Stun(epd, 126); // 시전자를 잡아 둡니다
}function Exec() {
const n = qT.length; // 두 번 읽지 않습니다 / read ONCE
foreach(i : py_range(n)) {
const x = qX.popleft(), y = qY.popleft();
const t = qT.popleft(), f = qF.popleft();
if (t <= Frame) {
setloc(loc, x, y);
TrgUnit("Nuclear Missile").groundWeapon = WEAPON_OF[f];
CreateUnit(1, "Nuclear Missile", loc, P1);
} else {
qX.append(x); qY.append(y); // 아직 아니면 뒤로 다시 / not yet: re-append
qT.append(t); qF.append(f);
}
}
}Read the loop bound exactly once, before the loop. Put qT.length in the loop condition itself and the entries you re-appended get popped again in the same pass, so the queue never drains. It is the standard mistake with this structure and the symptom is the game simply stopping.
| Stages become data | “three-way at frame 14” is three rows in a queue, not an if. Changing the animation becomes changing numbers in Plan. |
|---|---|
| A different weapon per stage | Carrying a phase column lets Exec attach that stage's weapon as it pops. The dash hits softly and the burst hits hard, with no branch. |
| The time is absolute | You store the actual Frame, not “n frames after the start”. Because of that, overlapping casts all run through the same one queue. |
| The caster is held | Stun(epd, 126) holds the hero still for the duration. If the caster could move mid-animation, precomputed coordinates would be wrong. |
With lengthdir_256, +64 is exactly a quarter turn: a full revolution is 256 steps, so a quarter is 64. That is all dir + k * 64 - 64 above is saying - left, centre, right.
It is equally clear when not to use it. A skill that must stop when it meets an enemy, or bounce off terrain, does not fit: a timetable written in advance knows nothing about what happens in between. Those skills want the per-cycle version.
Precomputing the whole thing at compile timeFive ways to decide who is in range