I usually work on several projects at a time 'cause I easily get bored and need to switch back and forth to keep the interest flowing, so I have several ideas (some of them in a pretty advanced state) for this compo. I'd like to share one of them 'cause it's me getting out of my comfort area to try new things and 'cause I have developed (probably reinvented a few wheel) a method to drive AIs which may be of interest of somebody, or maybe is something we could discuss.
The project is nothing original, just a side scrolling beat'em'up in the spirit of Spartan or Dragon Ninja (simple as Spartan, but with two height levels just like Dragon Ninja). The idea came to me after I discovered (late, I know) the good ol' Hokuto No Ken in a chinese multicart, and because I felt like pixeling some animations. I had to enhance my custom converter program to handle big metasprites properly, and designed them to take as few different patterns as possible. Thankfully, my converter tool knows how to reuse patterns as much as possible.
The first thing I put together, in some spare time a couple of weekends ago, was a nice finite state machine to control the player movement and animation. Drawing the big diagram on paper I could design all state changes so coding was a piece of cake. Right now, the main player can walk, jump, crouch, change level, punch, perform a low kick and a flying kick. Connecting cells of animation seems to work nicely and the controls feel responsibe and tight. I also managed to cram the scrolling routines to run in the first 32 raster lines, before the sprite-0 split point.
But then it came the need of having quite a number of different enemy AIs, and for some reason I am aiming for pure NROM on this one (well, maybe CNROM for the looks). I'm a C coder and C has a bigger footprint than ASM so I couldn't just throw a bunch of hardcoded behaviours, so I designed a simple language to drive enemy behaviours, and a sort of bytecode interpreter which runs on each enemy, parsing its own "program".
The language allows for loops, checks (about the player and "self"), and some basic actions. Enemies can do almost everything the player does plus throw projectiles, and have a very simmilar FSM to drive them. The interpreter consumes an instruction every frame (or a check and the next instruction in some cases) and modifies the FSM state accordingly. It seems to work great, and I was wondering if this is the method which is generally used, 'cause I have the habit of reinventing wheels.
The programs which drive enemies look like this, for example:
I have written a small compiler which produces a tight bytecode which is easy to interpret. In fact, the bytecode is even simpler than the languages, as some structures can be expressed with a number of simpler opcodes, making the interpreter smaller and simpler:
With this, I only need one function to control every type of enemies, and I can design really complex patterns, which will be great if I have enough space to add bosses.
This works great, I'm having three active enemies and I still have lots of frame time to do collision checks. I'm happy 'cause I haven't come around to optimizing so maybe I can still get better results. We'll see, lots of stuff to add, specially when I introduce projectiles at arbitrary angles.
What do you think of the approach?
P.S. this is how this game looks. The background is temporal. I know the perspective is off, but it somewhat looks nice and retro-looking, from those times back then when nobody gave a crap about perspective
The project is nothing original, just a side scrolling beat'em'up in the spirit of Spartan or Dragon Ninja (simple as Spartan, but with two height levels just like Dragon Ninja). The idea came to me after I discovered (late, I know) the good ol' Hokuto No Ken in a chinese multicart, and because I felt like pixeling some animations. I had to enhance my custom converter program to handle big metasprites properly, and designed them to take as few different patterns as possible. Thankfully, my converter tool knows how to reuse patterns as much as possible.
The first thing I put together, in some spare time a couple of weekends ago, was a nice finite state machine to control the player movement and animation. Drawing the big diagram on paper I could design all state changes so coding was a piece of cake. Right now, the main player can walk, jump, crouch, change level, punch, perform a low kick and a flying kick. Connecting cells of animation seems to work nicely and the controls feel responsibe and tight. I also managed to cram the scrolling routines to run in the first 32 raster lines, before the sprite-0 split point.
But then it came the need of having quite a number of different enemy AIs, and for some reason I am aiming for pure NROM on this one (well, maybe CNROM for the looks). I'm a C coder and C has a bigger footprint than ASM so I couldn't just throw a bunch of hardcoded behaviours, so I designed a simple language to drive enemy behaviours, and a sort of bytecode interpreter which runs on each enemy, parsing its own "program".
The language allows for loops, checks (about the player and "self"), and some basic actions. Enemies can do almost everything the player does plus throw projectiles, and have a very simmilar FSM to drive them. The interpreter consumes an instruction every frame (or a check and the next instruction in some cases) and modifies the FSM state accordingly. It seems to work great, and I was wondering if this is the method which is generally used, 'cause I have the habit of reinventing wheels.
The programs which drive enemies look like this, for example:
Code:
WALK
DO
IF P_CLOSER_THAN 1 GOTO :1
LOOP
:1
IF P_SAME_LEVEL GOTO :1b
DO
IF P_FURTHER_THAN 1 GOTO :1c
LOOP
:1c
CHANGE
GOTO :1d
:1b
JUMP
WAITIDLE
:1d
TURN
WALK
DO
IF P_HIT_RANGE GOTO :2
LOOP
:2
HIT
TURN
WALKSP 2
WALK
END
DO
IF P_CLOSER_THAN 1 GOTO :1
LOOP
:1
IF P_SAME_LEVEL GOTO :1b
DO
IF P_FURTHER_THAN 1 GOTO :1c
LOOP
:1c
CHANGE
GOTO :1d
:1b
JUMP
WAITIDLE
:1d
TURN
WALK
DO
IF P_HIT_RANGE GOTO :2
LOOP
:2
HIT
TURN
WALKSP 2
WALK
END
I have written a small compiler which produces a tight bytecode which is easy to interpret. In fact, the bytecode is even simpler than the languages, as some structures can be expressed with a number of simpler opcodes, making the interpreter smaller and simpler:
Code:
const unsigned char ep_00 [] = {
0x10, 0xd1, 0x05, 0xce, 0x01, 0xf0, 0x0e, 0xe1, 0x0b, 0xce, 0x07, 0x42, 0xce, 0x13, 0x41, 0xff,
0x13, 0xce, 0x0f, 0x40, 0x10, 0xf4, 0x19, 0xce, 0x15, 0x52, 0x40, 0x72, 0x10, 0x00
};
0x10, 0xd1, 0x05, 0xce, 0x01, 0xf0, 0x0e, 0xe1, 0x0b, 0xce, 0x07, 0x42, 0xce, 0x13, 0x41, 0xff,
0x13, 0xce, 0x0f, 0x40, 0x10, 0xf4, 0x19, 0xce, 0x15, 0x52, 0x40, 0x72, 0x10, 0x00
};
With this, I only need one function to control every type of enemies, and I can design really complex patterns, which will be great if I have enough space to add bosses.
This works great, I'm having three active enemies and I still have lots of frame time to do collision checks. I'm happy 'cause I haven't come around to optimizing so maybe I can still get better results. We'll see, lots of stuff to add, specially when I introduce projectiles at arbitrary angles.
What do you think of the approach?
P.S. this is how this game looks. The background is temporal. I know the perspective is off, but it somewhat looks nice and retro-looking, from those times back then when nobody gave a crap about perspective