8Bit Rogue

Next Steps

Next Steps


Now, I suppose, I should make the game do something.

I can move, I can attack, and when I walk to the edge of the screen, I can use an LFSR to generate pseudo-random numbers from which I can proceedurally generate each new screen.

Each new screen will have two things (either items or monsters), one on the top floor, one on the bottom; as well as a top-floor ladder, bottom-floor ladders, neither, or both. The idea is that you’re climbing a tower, so you’ll need to limit the number of screens you can visit horizontally. I’m thinking 4 at this stage, as the maths will be probably easier (if I stick to multiples of 2). The tower is a maze of ladders and obstacles, and your goal is to reach the top.

To limit the horizontal movement, I need walls. Because I only need them on one side at a time, I can’t use the playfield. The playfield either mirrors or repeats, but is always symmetrical; to make it asymmetrical, you need to update it while the row is being drawn, and my kernel is too tight to add that sort of thing. So I’ll use a player graphic.

This means that I’m sacrificing an item or enemy on both floors on a screen with a wall, but it lets me fit them inside what I’m already doing.

I threw in a graphic that looks like a column, make it as tall as each floor, and get this:

first attempt at walls

Looks good enough, but there’s a few problems here. Firstly, normally the sprite tables have a row of 0 at the end, so that you don’t have to unset the GRP1 graphics register every time. However, since each P1 character has a fixed Y position, that would make the walls appear to float. Not clearing them, however, means that last line keeps repeating into the top of the next frame:

P1 Graphic artefacts at top of frame

Since this is a problem only when I’m drawing a wall, rather than changing the P1 Y position, I can just clear GRP1 at the end of my kernel.

Next, the walls are sitting out from the edge:

There’s a gap to the left of the wall that needs to be fixed

The trouble here is that the walls are at X position 0. The Horizontal positioning algorithm I’m using has a padding of 9 pixels. It’ll mean re-factoring all my other x-positioning, but I’ll take that padding out.

Next, the tops of the walls don’t meet the ceiling:

Gap at the top of the wall

This is because in my two-line kernel, I don’t start drawing the P1 graphics until the second line. So I need to start drawing them in the ceiling. The ceiling lines are a lot looser in my kernel, so that’s an easy fit.

Walls aligned

And now I just colour them the same as the playfield to they blend in:

Walls coloured

That looks good. Time to check the alignment for the right-hand side walls…

The kernel is broken again

My kernel is broken again… It doesn’t like aligning the walls and the player too far to the right.

So I threw out everything I’ve done here, and started again.

Fixed the kernel, make the ceiling pre-loading graphics neater, and also replaced my wall graphics with something a lot plainer, which I think has a better look (the original was my attempt at a sort of fluted column, which was lost once I stretched the graphic).

New kernel, new walls

This time I did’t remove the 9-pixel padding to align the wall with the edge of the screen. Instead I placed it past the right of the screen. This makes the position loop around to the start again.

Now I need to make them unpassable. When I set up collisions, I made the quick assumption that if the P1 and P0 graphics collided (and you weren’t swinging the sword), then the enemy had hit you. But that means now walls and items will kill the player.

So, there are three things that can collide with the player: an enemy, an item, or a wall. Which gives us three collision modes: HURT, COLLECT, and BOUNCE.

Bouncing is a little tricky. You need to keep track of the player’s previous position each frame, and restore that if you collide with a wall. But I’ve got a problem: he moves 1 pixel per frame, so that’s how far the previous position is; but when the character animates, his knee goes two pixels forward. This gets can get him stuck in the wall. And, if you’re hard against the wall and turn around, now his hat is inside it, so you can’t move away. I tried changing the wall sprite so it would hit different parts of the graphic (while still looking right), but to no avail.

Instead, I went with a different route. Instead of keeping track of the players pervious position, I instead use which direction the player is facing. The VCS has a register to flip the player graphics, so I am already keeping track of which way I’m drawing him. When I detect a collision and we’re in BOUNCE mode, I just load my reflectP1 variable. If it’s 0, he’s facing right, decrement the X pos 2 pixels; if it’s not 0, increment by 2.

2 pixels is enough to push him back past his animation sprite, and turn around. He no longer gets stuck, and I free up 2 bytes of RAM by not needing the previous position any more.

Making a complete floor

Until now, I’ve just been hard-coding each screen, so that I can test things. Now it’s time to dynamically load things proceedurally. I’ve got my walls, and I’ve got a LFSR pseudo-random number generator. Time to put these together.

Each level of the tower needs a fixed number of rooms, and I’m gonna make that divisible by 2, so that the maths is simple and predictable on the 6502. I’ll start with 4 and see how I go. This means we’re gonna have two outer rooms — one on the left, one on the right – and two inner rooms, with items, enemies, or obstacles.

If the room number is divisible by 4, draw a right wall. If the room number - 1 is divisible by 4, draw a left wall. Otherwise, pick entities for the top and bottom floor by bitmasking the LFSR Random variable; and again to decide whether to draw the ladders on the top and bottom floors.

For now I’m just gonna shuffle some numbers around until I get some variety. In the future, I’m going to be a bit more precise so the game unfolds how I want it to (and to make sure that you can actually solve the maze.)

A full level of the tower, with 4 rooms aligned horizontally, completed


Share: