Omega Chase - An arcade style shooter

OMEGA CHASE

Omega_Chase_title

Omega Chase is an Arduboy port of the classic arcade style shooter game Omega Race. In this game, you control a spaceship and must navigate through an arena, shooting down enemy ships and avoiding obstacles. The player must destroy and avoid drone ships, commander ships, death ships and space mines. The death ships fire back and are very accurate. The ship bounces off an invisible barrier on the edges of the screen that briefly appears when hit. Wall bouncing can be used to your advantage when hunting enemies. Extra lives are awarded at 500 and 1000 points. The objective of the game is to survive as long as possible while racking up a high a score. The levels increase in difficulty as the game proceeds.

recording_20230522221029

screenshot_20230522221401

Controls:
Left: Rotate Left
Right: Rotate right
Up: Thrust
Down: Negative thrust
“A” button: single shot
“B” button: Full auto

controls
points

Download the entire project: https://github.com/Karl-Williams/OmegaChase/

Download the .hex file here: OmegaChase/build at main · Karl-Williams/OmegaChase · GitHub

11 Likes

Looks great. I used to love Omega Race on Colecovision. This is the perfect sort of game for the AB. Thanks for sharing. I still need to repair my DIY arduboy, but I’ll be sure to download this one.

1 Like

It looks great. Cannot wait to play it.

1 Like

A few things regarding the sound library…

  • You shouldn’t call arduboy.audio.on() in setup(). arduboy.audio.on() forces the sound on regardless of a user’s EEPROM settings. arduboy.audio.begin() enables or disables sound based on a user’s EEPROM settings, but you don’t need to call that manually because arduboy.begin() already calls that.
  • You don’t need sound_state to track whether the sound is enabled, you can just use arduboy.audio.enabled(). E.g. printText(arduboy.audio.enabled() ? "Sound On" : "Sound Off", 48, 46, 1).
  • arduboy.audio.on() and arduboy.audio.off() are indeed how you turn the sound on and off, but if you just want to change to the opposite (i.e. turn the sound off if it’s on and of if it’s off) then you can use arduboy.audio.toggle().

With those things in mind you could scrap your sound_state variable and simplify a lot of your sound handling code.

(There’s a few other issues here and there if you’re interested in improvements (e.g. formatting images so you don’t need drawSlowXYBitmap, using switch, moving code into separate functions, making text printing more efficient, avoiding delay), but I started with the thing that stood out the most. I usually have my Arduboy muted to avoid disturbing others, especially late at night, so I’m in the habit of checking whether sound is being forced on before I load a game.)

2 Likes

Thanks for the feedback about the audio! I’ll make those changes in the next version. I’m open to any advice on improvements too. This is my first game and it has been a lot of fun figuring it out!

1 Like

There’s a number of minor issues and one or two larger structural issues. I won’t go into everything now, but if you’re already planning to do another version then I may as well mention a few things that shouldn’t be too much effort to fix…

  • The drawSlowXYBitmap function is something you should generally avoid because (as its name suggests) it’s quite slow. It exists primarily to deal with images that aren’t in Arduboy’s native format. Generally you should aim to make sure all your images are kept in Arduboy’s native format so you can use the faster functions like Sprites::drawOverwrite (as you have already done in a few places). To fix this issue all you need to do is put the images in the right format and replace the drawSlowXYBitmap calls with the equivalent function from the Sprites class.
  • If you wrap the strings you pass to arduboy.print/arduboy.println in the F macro it will typically save RAM. E.g. arduboy.print(F("SHIP DESTROYED!"));. A bare string ends up in both progmem (a bit like ROM) and RAM, whereas a string wrapped in F stays in progmem only.
    • Also a different version of print and println are used for each, so be sure to do it for all strings that are passed to print and println, so you only end up using the one kind of function rather than both.
    • Your printText function won’t be able to deal with these as it is. If you want to make it compatible with F-wrapped strings you can change the const char * to const __FlashStringHelper *, but if you do that you must make sure that every string passed to it is wrapped in F.
  • All of your printText function calls are passing 1 for textSize, and you’re not changing the text size anywhere else, so you may as well drop the setTextSize functionality from it. Doing so would save you a few bytes of progmem, and possibly a bit of RAM too.

Some things that would take more effort to deal with, but I’ll mention anyway:

  • You’ve got a lot of stuff going on in a few large functions. If you were to break that up into smaller functions it would be more manageable and potentially might make your compiled code size a bit smaller.
  • Using delay effectively pauses the entire program, which upsets your framerate, so you won’t actually be getting a proper 60 FPS framerate. Unfortunately fixing that would probably take more effort than you’d likely want to expend because you’d have to restructure your code a fair bit.

(There’s a number of minor issues too (e.g. and versus &&, int and signed short versus int16_t, returns at the end of void functions), mainly stylistic issues or redundancies, but I held back on those to avoid overwhelming you and because they’re more about readability and code quality than things that would actually affect the compiled code. If you’re interested in those let me know.)

(Your code would also benefit from using some structs, but I’m presuming you just haven’t learnt about those yet.)

I definitely get the impression from your code that you’ve learnt some better ways of doing things as you’ve gone along, but possibly not gone back to replace the older stuff after learning about the better option. (Perhaps simply because you didn’t realise which was the better option?)

1 Like

Thanks for the code review! That’s all great feedback. I was looking into using structs and that will make it easier to work with the direction vectors and ship movements. The function that returns the wall bounce directions is huge so I’m working on a compact and faster way to do that.

1 Like