Devlog: Vixx (bullet hell game)

All aspects of programming on the Commander X16.
VincentF
Posts: 75
Joined: Mon Jun 29, 2020 8:22 pm

Devlog: Vixx (bullet hell game)

Post by VincentF »


Hello Everybody !

I wanted to make a post to share a project I started several days ago. I wanted to create a "Bullet Hell" type of game (it's a sub-genre of shoot'em ups games) for the X16, following a concept idea I got to control several sprites on screen.

This post will act as a devlog for this project. For the history, I discovered the genre two weeks ago and wanted to try making one for fun.

So here we go with the first issue of the devlog ! (it'll be long since I got a lot of code done already) Note that I'm not this good at programming in assembly, My only knowledge comes from some youtube videos (Game Mechanics Explained, 8-bit Show And Tell, ...) so I may not be aware of most best practices. (I would like to hear from you if you got some ! ? )

#1 26/01/2021: Game framework and first contact

So, before I start with the thing, here's some info on my working environment :

I work with ACME Assembler, because it's the first one I learned and I like the way it works so that's what I'm using.

I have some "libraries" I wrote for myself to declare every Kernal addresses, petscii special characters, some vera addresses, masks, macros, a "basic_startup" macro, some math macros (adc16, inc16, multiply, divide, ...) etc...

I usually start a project by creating a folder and some files in it, I like to start with "main.asm" and "paper.asm". The "main" file will contain the actual program, and the "paper" will contain the variables, constants and some comments on how the game will be structured.

 

Once I got my working environment ready, I started to think of how I'll handle the bullets on-screen. So at first I needed to design the bullet's datas.

To keep things simple I wanted to classify every moving object on screen as an "object". Each objects has 4 properties: a type, x and y coordinates and a parameter. The type will determine which code will handle the movements. The parameter will allow the code to know how to behave and also modify it if needed. The x and y coordinates are ... used as coordinates ¯\_(ツ)_/¯.

Okay, I got a structure for my objects. that's 6 bytes per objects counting the coordinates as 16 bits values. Let's see how I'll configure the VERA ... 640x480 is quite overkill so I downgrade it to 320x240. Playing with the coordinates I see that the low byte of x and y coordinates takes around 3/4 of the screen horizontally and the entire screen vertically. So I can cut off 2 bytes per objects. VERA allows up to 128 sprites on screen, so I note a hard limit of 128 objects. 128 objects means I'll take 512 bytes if the screen is full of bullets.

 

I drew a small 8x8 bullet sprite and on startup I initialize all VERA's sprites as this one. Now, how to handle the movements ?

I said earlier that I started this project with an idea in mind, so here it is : I have an array of addresses, and using JMP ($ABCD, X), I can access all of them. So that mean I can only store 128 types of objects in my game. That's enough for what I want.

So I have 512 bytes for the objects, and 256 bytes for the movement table. I can place them at the end of the fixed RAM ($9C00 to $9FFF). I also use one more byte ($9BFF) to store a count of how much bullets I got in my list (so I don't need to parse it in whole each time I need to).

I came with two loops to handle the objects : one that will move them, and one that'll refresh their position on screen. I wanted to move the objects on every frame but didn't want to bloat the VSYNC interrupt with it. So I created a wait_frame variable that'll be set once the movement has been handled, and will be reset when the VSYNC occured.

Here's the pseudo-code for the "move objects" loop :


Quote




    Load Object structure into a fixed RAM location

    Shift the type to the right once (to align with the 16bit addresses)

    Place the type into the X register

    Jump (movement_table, X) ; <- we will assume the movement routine selected will always return to movement_ended

movement_ended:

    Save Object structure changes from the fixed RAM location back into the Object Array

    Repeat until object_count



For the "display object" loop, it's even simpler : we take the coordinates and output them directly into VERA.

 

I implemented some simple movement functions : the first one is a "Null" movement, nothing moves. The second one is a "reset" movement, we force the position out of the screen and set its type as "Null".

The third one is a player controlled movement. We get the joystick buttons and move accordingly.

The next ones are basic constant movements. Since the parameter is only one byte, and the bullets does not need to move like a blue hedgehog, I assumed the left hex part will be the x movement and the right hex part will be the y movement. I firstly assumed that the value is pixels per frames, but I decided later to take the lowest bit of each hexadecimal number to make half-pixel movements (we move the bullet every odd frames).

You can't imagine how much I had to debug the thing and the weird behaviors I got because of some not-properly-set flag before a critical operation (I knew the carry was used for ADC and SBC, but not for ROL and ROR). Also the hard-to-reverse crashes because you exited a loop without pulling away a value you pushed on the stack. But the best reward was the system working properly at the end, and was even very generic at the end !

 

The next thing I decided to work with was the collisions. Bullet Hell games are easy to make on this side because pretty much everything has small round hitboxes. So after moving the bullets the next thing I do is checking for collisions against the player (I assumed the player will always be the first object on the array). So I take the player's coordinates, and loop through every other objects.

I first subtract the player's x coordinate with the current object's, check the carry to know if I have to check < 3 or > 252. If I match, I do the same check for the y coordinates and if there is a second match, the player has been hit ! (I still need to implement lives)

I later decided to award the player some points if he takes risks and go near bullets. So I added a little scoreboard on the right (discovered by the way the wonderful decimal mode of the 6502) and added a check for larger distances in my collision code to give the player points for each frame passed near a bullet.

 

On the way, I started to create some basic subroutines to add objects to the array (this will check for empty spaces and place the object in it, with a limit of 128 objects (objects inserted beyond the limit will be skipped), optimize the object's array by setting the count to the lowest value possible (freeing the processor as much as possible). I also added some boundary check for the movement functions that'll reset the bullets when they get offscreen. I also imported a RNG subroutine found on the web.

 

While playing with the insert routine, I went through a new problem : how to handle the bullets ? I'll need to create some on the fly, but I can't just use some random code for this. I need some sort of conductor to keep things clean and ease the creation of levels.

So I started to create the "Choreography" module. It'll live on the game loop (not the vsync loop), and instead of being run once per frame (as the movements), it'll be run as much as possible, but leaving the priority to movement's and collision's code.

The Choreography code will be an interpreter, with its own Program Counter, X and Y position, two A and B registers and a "mode" variable that'll just sit here doing nothing but will eventually get a meaning later.

The program counter will start at a fixed location in the code ($9000) and will travel it reading each bytes of opcodes and their parameters. The X and Y refers to a "cursor", it's the position where objects will be inserted next. it can move to absolute values as well as relative ones. It has two registers that'll be used mainly for looping and doing conditional jumps.

I created some opcodes : SLEEP to skip some frames, INSERT to insert object into the game, SETPOS to set the position of the cursor, MOVPOS to move it in a relative way, LOADA to load some value to the "A" register, LOADB for the "B" register, INCA and INCB to increment them, JMP to jump, JAZ "Jump when A is Zero", JAN "Jump when A is Not zero", and so on ...



I wrote some of this custom code and got a very nice result. I wanted to share this with you :

bullet_hell_1.gif.d29d2a94ffd62f18f863a7d79bfe6166.gif

 

And that's All for this first loooooong devlog ! I'm a bit tired by writing all of this ^^' (it was around 1am when this was finished) so I'm not sure I'll double-check now if I got mistakes on this. I also wanted to share this with you in case you can get some ideas for your own projects. Another reason is I tend to easily drop projects by not being motivated in them anymore. I'm considering making this project open-source so you'll be able to review (if you're courageous) and continue it (for the courageous++) just in case. Tell me what you're thinking of this project, I'll be happy to talk with you further about it ^^

User avatar
DusanStrakl
Posts: 134
Joined: Sun Apr 26, 2020 9:15 pm
Location: Bay Area, California
Contact:

Devlog: Vixx (bullet hell game)

Post by DusanStrakl »


Great start. Looking forward to your progress.

VincentF
Posts: 75
Joined: Mon Jun 29, 2020 8:22 pm

Devlog: Vixx (bullet hell game)

Post by VincentF »


Hello Everybody !

Here's the second issue of my devlog, I made some progress and thanks to x16-edit's source code, I was able to setup my file saving/loading code ^^

#2 - 28/01/2021: Gamemodes and High-scores

These last few days I worked mainly on creating a gamemode system to handle menus and different game states (initializing game, game loop, game over screen ...). When the gamemode is changed, we need to jump the code to a "change_gamemode" routine that acts as an intersection between every gamemodes. Its purpose is just to jump to the right routine based on the gamemode. Then, the routine manages itself (calling subroutines, changing stuff, waiting some frames, looping around...) until the gamemode is changed again.

I then created a "title screen" and "game over screen" gamemode, a "init_game" mode that changes directly to the last one called "game_loop".

- "Title_screen" just displays a "Press Enter" and waits for an input. It then switch to "init_game".

- "Init_game", sets up the variables for a new game and switch to "game_loop".

- "Game_loop" is the main loop, with all the bullets and collisions and choreography, etc. When the player loses it switch to "game_over"

- Finally, "Game_over" just displays a "Game Over" text for a few seconds and return back to "title_screen".

 

By the way, I implemented some lives for the player, with an invincibility cooldown (with no visual effects for now) and decided to start implementing a high-score system.

This system was quite hard to create, because I have never saved anything with the X16. So starting from scratch, I looked on the Ultimate C64 Reference how to use the Kernal functions to save things but wasn't able to do much. After looking on the IO errors, I found out that we need to provide a SD card to the emulator in order to save files. Okay, so let's add an SDcard ! Getting more troubles trying to OPEN anything, I finally given up and searched some example code of a saving routine. I knew the existence of X16-edit so I decided to read some assembly code (kinda "exotic", due to an assembler language that differs from ACME). Taking examples from this code (and resetting the sdcard file multiple times because the emulator has corrupted it many times probably because I didn't have closed the file properly), I finally got my score file saved on the card ! It took me hours to get it to work properly but now that this part is done, I'm happy with it.

 

Since last devlog, I also improved the Choreography Interpreter, making it able to print text on the screen, execute external code, and other opcodes to use when composing levels.

 b_hell_chor_opcodes.png.5b90198ddba79498576ff18176814e35.png b_hell_chor_loop.png.c5e76b5380a6bba8e6d9cf7ad34d5f60.png

(The first image shows the different Choreography opcodes, and the second image shows an example of choreography code, the demo gif below shows the result of it)

 

Here's a gif of the progress so far, the choreography demonstrates almost the maximum amount of bullets that can run simultaneously on screen (there are ~120 object displayed over the maximum 128)

recording.gif.ca5165b09c69af342bdf29883e7cb90a.gif

 

And that wraps up my progress of these last two days. The progress is satisfying and I can't wait to have some alpha version to share with you all. Thanks for reading ! ^^

User avatar
desertfish
Posts: 1125
Joined: Tue Aug 25, 2020 8:27 pm
Location: Netherlands

Devlog: Vixx (bullet hell game)

Post by desertfish »


interesting read and nice results so far. Keep m coming

VincentF
Posts: 75
Joined: Mon Jun 29, 2020 8:22 pm

Devlog: Vixx (bullet hell game)

Post by VincentF »


Hello Everybody !

This time I'm not writing a devlog (or kinda) but I want some feedback from you.

First, I came with some lore for the game and a name !

Vixx16

Your Commander X16 got a virus ! Thankfully you have an antivirus called Vix that'll get rid of the evil software.

You play as the Vix Antivirus, scanning the computer trying to locate and destroy the virus.


 

I got some gameplay footage with more visual things and also a demo for you to test (the boss is a placeholder that loops until you lose) :

WARNING: you must use an EMPTY sdcard to store the high scores, for some reason the program sometimes corrupts the file ! (If by the way you know why the file corrupts so easily I would be grateful)


vixx16.prg



recording.gif.97eb58108301bdfbcb729f050bfe782f.gif

User avatar
desertfish
Posts: 1125
Joined: Tue Aug 25, 2020 8:27 pm
Location: Netherlands

Devlog: Vixx (bullet hell game)

Post by desertfish »


With the virus theme combined with "bullet hell" style gameplay I get strong Nier Automata vibes, which is a good thing (I adore that game). Were you inspired by that?

Not a big fan though of the game name to be honest.  Repeating "x16" in everything is not my preference.  "Vixx" is good enough to me ?

VincentF
Posts: 75
Joined: Mon Jun 29, 2020 8:22 pm

Devlog: Vixx (bullet hell game)

Post by VincentF »



7 minutes ago, desertfish said:




With the virus theme combined with "bullet hell" style gameplay I get strong Nier Automata vibes, which is a good thing (I adore that game). Were you inspired by that?



Hmm no, I love nier automata as well but the idea does not comes from it... maybe indirectly ? ?


7 minutes ago, desertfish said:




Not a big fan though of the game name to be honest.  Repeating "x16" in everything is not my preference.  "Vixx" is good enough to me ?



The original name was "Vix", but when drawing the title screen the "x16" came by itself. It made sense to me since in the game story we are playing in a X16 ... Maybe I need to rethink this part.

Stefan
Posts: 465
Joined: Thu Aug 20, 2020 8:59 am

Devlog: Vixx (bullet hell game)

Post by Stefan »


It looks nice, all simultaneously moving sprites.

I have experienced SD card corruption if the SD card is mounted in the local file system at the same time it's used by the emulator. There's nothing stopping you from doing that, but you should avoid it.

VincentF
Posts: 75
Joined: Mon Jun 29, 2020 8:22 pm

Devlog: Vixx (bullet hell game)

Post by VincentF »



1 hour ago, Stefan said:




I have experienced SD card corruption if the SD card is mounted in the local file system at the same time it's used by the emulator. There's nothing stopping you from doing that, but you should avoid it.



I didn't have mounted the file before he corrupted itself. Playing around with it I found it's related to the fact I tried to "OPEN" a file that doesn't exist. For some reason the "LED indicator" keeps blinking even after I "CLOSE" the file (that didn't exist in the first place). Is that an emulator bug maybe ? ?‍♂️

Stefan
Posts: 465
Joined: Thu Aug 20, 2020 8:59 am

Devlog: Vixx (bullet hell game)

Post by Stefan »


I haven't had that problem myself in my program X16 Edit.

My program always reads the disk status after accessing a file, the equivalent of entering DOS at the BASIC prompt. Maybe reading the disk status is necessary, clearing the error state before accessing other files.

It's not likely an emulator bug. More likely a "feature" of the Kernal that you stumbled upon.

Post Reply