The Making of Sirène


Idea by Justin Cyr

Code by JO3RI

Sprites, banner & tittlescreens by Justin Cyr


The idea for making Sirene came about after working on the last game for Arduboy Trolly Fish. Justin really liked the aquatic theme and even though Trolly Fish is more of an endless runner (swimmer?) there's a shooting power up that got him thinking a proper shmup could be possible. He had also done a 1bit mockup and some sprites from his twitter, so it was easier to pitch the idea. With the confidence of having worked together on 1 game behind us, we pushed forward with Sirène!

 

Justin Cyr: " I really love making games on the Arduboy. There's just something really appealing about the simplicity of 1bit tiny resolution graphics. The Arduboy itself is so tiny and basic it gives you no choice but to make the most distilled version of a game you'd like to make. Seeing the game come to life is always so satisfying. Team A.R.G. are really good at pushing the limits of what can be done and getting the most out of every bit of memory."


When we (Justin & JO3RI) both started working on this game, I (JO3RI) really didn't have a clue on how to make enemies show up in a certain order, but I never told Justin. None of the games I had done before were shmups. Justin just came with such a nice idea and mockups, I had to say yes and I was confident I would learn along the line, which I did.


In this technical I would like to describe, what technique we used to accomplish different enemy attack paterns or "waves" as we started calling those. It was actually when thinking about our menus, someone in the arduboy community talked about function pointers. You can google it, but it comes down to this: you first create functions. Then you create a list of those functions and with 1 variable you can pick the function needed to execute.


We came to the conclusion a "wave" should do 3 things


- if the wave starts, set the fish in a certain pattern

- move the fish (following a certain pattern)

- if the wave ends, continue to the next wave


void wave001()

{

  if (checkStartWave())

  {

    enemySetInLine(ENEMY_FISHY, 0, 3, 128, 12, 192, 0);

    enemySetInLine(ENEMY_FISHY, 3, 6, 256, 32, 192, 0);

    enemySetInLine(ENEMY_FISHY, 5, 9, 192, 48, 192, 0);

  }

  enemySwimRightLeft(0, 9, 3);

  checkEndWave();

}

And so, stages are just a list of those waves. As an extra bonus we put this list in ROM, so it doesn't use any of the valuable RAM of which we only have 2500 bytes.


typedef void (*FunctionPointer) ();

const FunctionPointer PROGMEM stages[TOTAL_AMOUNT_OF_STAGES][TOTAL_AMOUNT_OF_WAVES] =

{

  //PART 1

  { //STAGE 1

    wave001,

    wave001,

    wave002,

     ...

  },

  {

     ...

  },

  ...

};


Now we only have to execute the right function in this list and we do this by changing 1 variable: currentWave. Because we made this a global variable, anything can change it. In our game the function checkEndWave(); adds 1 to currentWave when all fish are dead or offscreen and so automatically switches to the next wave in the list.


((FunctionPointer) pgm_read_word (&stages[currentStage][currentWave]))();


This function pointer approach eliminates to use of a very big switch cases or an endless use of if statements.