GameObject Sequencer. Bosses made simple.

For every general enemy type in the game I simply code up its actions, at the same time building a library of commonly used functions, for instance animation, path following, or wall collision.

Getting on to bosses though, it makes sense to expand the system a bit to allow for (slightly) more complex behaviours.

So what does a boss do in this game? Generally they all follow the same sort of pattern.. follow a sequence of mini-actions, attacking the player, moving around, becoming vulnerable or invulnerable to attack, etc.

This needs to be easily editable at a high level, too. so iterating on gameplay is efficient.

Cheese Boss

Here's the Cheese Boss, and below is his sequence in 'code'

BossCheese_Sequence1:
.loop:
    SEQ_IMMEDIATE BossCheeseSeq_StartScreenShake,0
    SEQ_IMMEDIATE BossCheeseSeq_SetInvisible,0
    SEQ_PAUSE 100
    SEQ_STATE BossCheeseSeq_ScaleIn,0
    SEQ_IMMEDIATE BossCheeseSeq_StopScreenShake,0
    
    SEQ_BULLETPATTERN EnemyBossCheese_BulletPattern2
    SEQ_STATE BossCheeseSeq_Hole,300
    SEQ_BULLETPATTERN EnemyBossCheese_BulletPattern1
    SEQ_STATE BossCheeseSeq_Face,300
    SEQ_BULLETPATTERN EnemyBossCheese_BulletPattern2
    SEQ_STATE BossCheeseSeq_Hole,300
    SEQ_BULLETPATTERN 0

    SEQ_IMMEDIATE BossCheeseSeq_StartScreenShake,0
    SEQ_IMMEDIATE BossCheeseSeq_SetInvisible,0
    SEQ_STATE BossCheeseSeq_ScaleOut,0
    SEQ_IMMEDIATE BossCheeseSeq_StopScreenShake,0

    SEQ_PAUSE 50
    SEQ_BULLETPATTERN EnemyBossCheese_BulletPattern3_Cheesebits
    SEQ_PAUSE 500
    SEQ_BULLETPATTERN 0
    SEQ_PAUSE 100

    SEQ_GOTO .loop

The commands (in CAPITALS) are actually 68000 assembler macros, while the labels (BossCheeseSeq_StartScreenShake) are memory addresses, containing code or data, depending on the command. 

This works essentially like Coroutines in Unity or similar. The GameObject controller schedules calling different bits of code, following the pattern in this sequence. 

SEQ_IMMEDIATE means 'call this function'

SEQ_STATE means 'call this function every frame, with this parameter, until it returns a signal that the state has come to an end'

SEQ_PAUSE, SEQ_GOTO are self explanatory, and 

SEQ_BULLETPATTERN sets up another sequencer / state machine specifically to control the firing of bullets etc.

BulletPatterns

Bulletpatterns are essentially the same code as sequences. They are totally separate from normal sequences, and can be used on other GameObjects which don't contain Sequencers. Here are a couple of the sequences from the Cheese boss:

EnemyBossCheese_BulletPattern1:
.1  BP_SETVEL 0,0
    BP_SETANG 0
    BP_BULLETTYPE GOTYPE_LARGE_BULLET,1
    BP_WAIT 100
    BP_FOR 8
    BP_SINGLE BP_TARGET_NONE,0
    BP_ADDANG 32
    BP_NEXT
    BP_STOP

EnemyBossCheese_BulletPattern2:
.1  BP_SETVEL 0,0
    BP_SETANG 0
    BP_BULLETTYPE GOTYPE_LARGE_BULLET,2
    BP_FOR 5
    BP_SINGLE BP_TARGET_PLAYER,0
    BP_PAUSE 6
    BP_NEXT
    BP_WAIT 50
    BP_GOTO .1


Another simple control language using macros. 
The first one waits for 100 frames, then fires a burst of 8 bullets at once, looping round a circle (I use 256 'degrees' in a circle in this code)
The second one fires patterns of 5 bullets at the player, with a 6 frame gap between them, followed by a 50 frame pause, then repeat.

Here's an example of the actual code which the sequencer calls. This one scales up the cheese at the start of the level. The sequencer actually calls the sequence's Init function once, while the Init function sets up it's own Update function.


BossCheeseSeq_ScaleIn:
    move.l #0,go_renderfunc(a0)            ; don't use special render code
    move.l #.update,go_sequencefunc(a0)    ; call '.update' every frame from now on
    move.l #0,go_scale(a0)                 ; reset scale
    rts
.update:
    add.l #$4000,go_scale(a0)               ; add 0.25 to the scale (scale is a 32 bit value, integer in the upper 16 bits)
    cmp #32,go_scale(a0)                    ; has it reached max
    blt .no1                                ; no? then render it
    moveq #1,d0                             ; yes? return with the zero flag cleared, telling the sequencer to move on to the next command.
    rts
.no1:
    bsr BossCheeseSeq_Scale_DoBlit           ; actually blit the cheese, scaled
    moveq #0,d0                            ; clear the zero bit, telling the sequencer to carry on
    rts

Comments

Popular posts from this blog

Converting Gunslugs to Megadrive / Genesis

Mega Palettes