dark        
Flegma blog header photo
Flegma's c-blog
Fronts 3Posts 760Blogs 79Following 0Followers 18


 
 

LONG BLOG

Fledgling retrodev: controller and VDP constraints

   0

Caveat: as the title suggests, I'm a beginner in creating games for old hardware. This is more or less my journal as I try to get the game done before a contest deadline flies by.

Last time, I got the engine up and running at a decent speed and not take ridiculous amounts of memory. There's still work in there, but it'll need to wait until I get on holidays and can write more code.

Instead, I had to rethink what kind of game I was making.

One of the lessons I've learned from Extra Credits is that you should aim for the minimum viable product and then focus on the extra stuff. But what is the minimum viable product in this case?

Believe it or not, building an on-rails shooter on this foundation isn't the simplest thing to do. The simplest thing would be to make it a flying game against the clock, like the AiRace titles on DS and 3DS. The turns would be handled like in old racing games (Outrun, Pole Position, Hang-On) where the vehicle would drift to the side in a curve. In this case, there'd just be eight directions the dragon could drift in instead of the regular two.

If I added opponents/enemies/competitors to the mix, I'd need to have the game store the enemy locations and figure out how to move them so that they would move as smoothly as the player sprite, without expending all too many CPU cycles for the calculations. And the same for projectiles as well.

But the controller...

MSX was unlike many 8-bit computers at the time in one sense: it supported two fire buttons. However, all joysticks I ever had were built for use with Commodore 64 and the like with only one fire button. The common lack of the extra buttons could be handled in a number of ways, none of which I consider good for this game.

The next question is what features I can map to the controller and how.

With only one fire button, I don't see how I could have both speed control and shooting, unless I made shooting automatic. Speed control could possibly be done by doing it either by "press fire and push up/down to accelerate/decelerate", or "press fire to give the dragon a speed boost" -- a flappy dragon. With two buttons, it'd be easy to map speed up and speed down to the two buttons.

I've noticed the mentality that those who have and use their MSX today have a two-button joystick and an MSX2, so why support the older hardware standard, since the next hardware iteration has been out for 32 years now? Well, I have neither a two-button joystick or an MSX2.

In the end, I chose to go with the on-rails shooter route, since I doubt I could get the screen redraw rate high enough to work for a racing game without dropping the screen resolution further, and two, because the one-button joysticks appeal to me for nostalgic reasons, I'm going with that and both of the control schemes I described for such joysticks are far from ideal. As long as I can get the enemies and the player to move reasonably smoothly, I'm 'good'.

Refresh rates again

Back in 1980s having PAL and NTSC releases run at the same speed was more of an exception than a rule. Of course, these are not the 1980s but the late 2010s, and that's something I should keep in mind.

With 50Hz refresh rate for PAL and 60Hz for NTSC, every 5th frame in PAL and 6th in NTSC happen at the same time. Currently, my renderer can do at best one full redraw every 10th frame in PAL. Once I add enemies and such to the mix, I expect to be lucky if I can redraw the background every 15th PAL frame (or every 18th NTSC frame).

The "easy" way is to allocate the refreshes in a static fashion for different tasks. For instance:

  1. Clear doublebuffer, render the tiles furthest from the player (depth 8) to double buffer
  2. Render depth 7 to doublebuffer
  3. Render depth 6 to doublebuffer 
  4. Render depth 5 to doublebuffer
  5. Render depth 4 to doublebuffer
  6. ?
  7. Render depth 3 to doublebuffer
  8. Update enemy positions 
  9. Redraw enemies
  10. Render depth 2 to doublebuffer
  11. Render depth 1 to doublebuffer
  12. ?
  13. ?
  14. Update enemy positions in memory
  15. Copy doublebuffer to video memory, redraw enemies

This isn't all, not by a longshot: every frame will need to have the player input read, the player moved and possibly player bullets moved and collisions checked. For the NTSC version, I'd need to replan this schedule, but that's an issue for later, if I get around to doing that.

We also get back to timing here. If the enemies move towards the player the same speed as the player flies forward, the game gets too fast. Since it would take (8 * 15/50)=2.4 seconds for the player to traverse the whole visible area in the cave, the enemy would reach the player 1.2 seconds after appearing on screen. That might be a bit too much, so I might make the screen scroll even slower or make the enemies do similar detours on the screen as the enemies in Space Harrier do. Or not have the enemies switch their depth every time they move. Or redraw the ground frames twice at different intervals (first at distances 0, 2, 4, 6, 8, 10, 12, 14, and the next redraw at distances 0, 1, 3, 5, 7, 9, 11, 13 before really moving the frames forward one step), effectively keeping the screen update rate the same but halving the speed the dragon flies forward and taking another half a kilobyte of ROM.

Coincidentally, Jeremy Parish uploaded a new episode on Capcom's 1942 in his NES Works. And one of the many gripes he has with 1942 is that the background doesn't update on the same frames as the enemies. I don't see myself managing to avoid that problem.

Enemy visual constraints

One of the major limitations of the video chip MSX1 has is that each scanline can have at most four hardware sprites without flickering. The fifth would not be displayed. If you didn't already check the link to Jeremy Parish's video above, please do. If you did, then do it again. He mentions the horizontal sprite limitations on the NES. See the planes disappear on the same height as the score? This is the exact same thing but with even stricter limits.

By hardware sprite, I mean an 8x8 or 16x16 pixel monochrome image that can be located on top of the background. These are easy to move -- just specify the location where the sprite should be next, the colour it should take and which image it should represent, and the sprite is moved there.

By comparison, software sprites change the background and moving the sprite to a new location means restoring its previous location manually. If you use an image manipulation software like GIMP, software sprites are like painting directly into the background layer whereas the hardware sprite is more like moving a bitmap on a layer on top of the background. In case you want to see software sprites in action, go see just about any ZX Spectrum game video on YouTube. The computer had no hardware sprite support, so all its games used software sprites.

In a nutshell, the more I can use hardware sprites the better, since moving them is 1) quick (for the CPU), 2) easy (just send four bytes to VRAM telling the position, colour and pattern), 3) smooth and responsive for the player (they're free to ignore 8-pixel horizontal steps and colour bleeding). I choose to represent the player as a sprite and limit the number of bullets the player can shoot to three, and render these as sprites as well. Even Megaman could have only three shots in air at the same time, so it probably isn't that big a loss.

 Strange, I remember being able to shoot the bullets closer to each other when I was a kid... oh, right, this is the US version and back then I had the slower PAL version.

But as for the enemies, it's software sprites only. Even more so, I'll be using only full characters, so the enemies will move in multiples of 8-pixel steps.

And now we get back to the limited amount of video memory. We need to fit everything in the status display, the terrain tiles and the prescaled enemies into 256 characters. To display numbers 0-9 and the texts "level", "score", "top", "lives", we need 20 characters. Another 9 goes for the actual rendering view (one for each depth and one for empty). We still got a nice amount of tiles left, and after making a quick mockup of drawing one enemy at four different distances, the enemy will take 15 characters. For once, I don't think we're in a sore need of extra memory and don't even need to change character sets on the fly. I'll probably try and write the enemy rendering code versatile enough that I can easily add extra levels of definition to them if it's feasible.

Thankfully, I think I understood how the "high-resolution" graphics mode in TMS9918A works. The main difference to the graphics mode I used before is that I can specify foreground and background colours for 8x1 pixel areas, not 8x8, despite everything still being essentially character-based. This means I can add more detail to the software sprites. Here's one enemy as it would be flying towards the enemy at the speed I've planned:

 

The sudden jumps are the consequence of having the first phases as 8x8 pixel character, then 16x16 and finally 24x24 pixels. The stutter is because I'm reusing the image for that frame. That's still too fast unless the player is not intended to destroy them. The blue parts are what will not be obscured by the enemy character and can show the cave walls, unlike the black opaque background.

Again, we need to remember the ROM size constraints: while the graphics won't take up any RAM, I still need to copy them to video RAM from ROM, and my Huffman coding implementation is a very poor fit for this purpose. 256*8 bytes for all characters, 256*8 bytes for the colours. That's 4kB, max. I hope RLE will do wonders to compress the colours into smaller space. 

What next?

The next big things I need to do are the routines needed for handling enemies. I need to be able to spawn them, move them, render them, repaint the background after they've left, and define their movement patterns.

Once that's done, there are the player's bullets, moving them and all sorts of collision detection. Once that's done, the only execution-time-intensive part I can imagine being needed is the possible music or sound effects. If the framerate's stable, I can properly garnish the game screen and start creating content for the game, namely the levels. And possibly jump back to designing more enemies if necessary.

That's a lot of coding I need to get to now that I finally got on a holiday.

Login to vote this up!

LOOK WHO CAME:


Flegma   
Joel Peterson   34
Wes Tacos   19
Uber Mashu   8
Kerrik52   6


 
 

  0 COMMENTS

Please login (or) make a quick account (free)
to view and post comments.



 Login with Twitter

 Login with Dtoid

Three day old threads are only visible to verified humans - this helps our small community management team stay on top of spam

Sorry for the extra step!

 

About Flegmaone of us since 11:34 PM on 01.17.2015

Very much unprofessional writer, don't take anything I write without a truckload of salt.

On a hopefully long-term break from saying anything.