Sunday, November 16, 2014

Moving Right Along

Both Robo-Ninja and Atari Anguna have been moving forward the past few days, although I haven't had much interesting to say about them.

On Robo-Ninja, I've finally gotten far enough along in the mapping where I can finally assume that, by this point, the player has most or all of the powerups from the first 2/3 of the game. Meaning I can finally switch up a lot of the level designs to force all sorts of difficult combinations and toggling of abilities.  For example, in room "cave_10":

If you can follow my horrible paintbrush start dropping down at the purple arrow on the right. You have you double-jump at the blue part, and while in the air, change the "ground action" ability to slide instead of jump, so you can slide under the wall (orange). Then immediately toggle back to jump (and make sure your wall ability is set to wall jump) to handle the yellow jumps. Then you have a very short stretch to toggle your in-air ability from double-dump to the "slam down" ability, so you can slam and avoid the spikes (red).  This only took me about 25 or 30 tries in testing, so I figure the challenge level is about right :-)

(Of course, I was purposely avoiding using the slow motion powerup, which I'd expect somebody to use at this point. Especially because I put one at the gray square there near the beginning, to make sure you have at least one, and to remind they player that it exists)

I'm still putting off my work on the title cutscene though. I just keep thinking it's going to be a lot of work for what amounts to be a really ugly-looking cutscene.

In Atari Anguna land, I've been working on the transitions between rooms. This means fleshing out my room definition code (which previously only had the wall definitions) to also include color scheme, what enemies are in a room, any special state for doors (requiring a key, requiring all enemies to be killed, fake walls, etc), and what item rewards might be in a room.  So far, I'm only as far as colors and enemies. Just tonight I got it rigged up so enemies spawn semi-correctly when you move from room to room.

The part I need to figure out next, is the best way to randomly distribute the enemies in a room, but without spawning them in a wall. Do I randomly drop them in, and if it's a collision, try again? This could take a long time in rooms with a lot of walls. Or do I drop them in, and try nudging them around until they don't collide? That could give really weird results, if I nudge them the wrong way. And what's the best way to detect collisions as I'm spawning them? Trying to compute the mapping between screen coordinates to wall collisions is hard on the atari, it's WAY easier to let the collision detection do it, but that only happens once per frame. So do I do the math myself, or do I waste a bunch of frames trying to arrange my enemies before the player is allowed to play the level?

I'm sure I'll figure it out, but my brain is too tired to properly answer those questions right now. (although I'm leaning toward repeatedly random dropping them in, but giving up after 10-15 tries. And I think I can make the background, walls, and enemies all be the same color as I'm trying to position the enemies, so if there's a 1/4 or 1/2 second loading per room, at least it won't look stupid with the enemies bouncing around trying to get positioned.  (But I'm not 100% sure that that will work))

Monday, November 10, 2014

Rumination on design and global variables

I had an interesting observation the other night. Switching back and forth between Robo-Ninja and Atari Anguna makes me much more aware of the differences in how I write code.

For example, when writing code for enemies in both games, I often need to know about various pieces of state that live outside the enemy -- such as the position of the player. (But not limited to the state of the player, which is what makes this tricky). In a game designed with a modern language and architecture, you have to make a lot of decisions about how you both provide and limit access to the state that the enemy would need. How do you expose certain state to the enemy, without giving it access to all sorts of things it shouldn't have access to?  There are lots of solutions to that problem, but it's a big part of thinking through the design of a system. (Tangent: I find this to be tricky in hobby-style video games, because I'm always dreaming up new enemies and mechanics that require access to things I hadn't planned on. I then sometimes get in a hurry and provide access in the easiest way possible, instead of taking the time to rethink what might be the best design for the future)

For Atari Anguna, on the other hand, everything is fair game. 6502 assembly by default makes all your variables global. In theory you could probably use some discipline to localize variable use, but you really don't have processor time to waste using any sort of accessor mechanisms anyway. Practically, everything has access to everything else.  With these constraints, many of the rules of good design get tossed out the window by necessity.  Which is strangely fun and freeing. An enemy needs to know the player's state? Just read it directly.  Who cares about loose coupling, orthogonality, and all that other stuff?  It feels dirty (and would be a disaster in a larger, more complex program), but for something small and compact like this, just adds to the fun.

Friday, November 7, 2014

Sound effects

The past couple nights have been devoted to getting sound working in Anguna 2600. Unlike video, sound is actually pretty easy, as long as you aren't too picky and don't want it to sound like much. There's 2 sound channels, each one having 3 registers: one for volume, one for waveform (one of 7 or 8 presets), and one that controls frequency. Trying to get any particular notes or scales out of it is near impossible, but getting rough sound effects is pretty easy.

Although I spent way too long last night fiddling with it before realizing that I couldn't set the sound registers before the first video frame for some reason. When I did that, everything locked up. I'm still not sure why, I couldn't find any reference to that in the documentation. Oh well.

My first successful test was to pick a semi-random waveform (#4, which is basically a simple square wave), and tie the frequency to the player's X position, to test it out. This ended up accidentally producing the exact sound effect used in Yars Revenge when the Qotile starts spinning and is about to jump out and kill you. (see this google+ post where I have videos comparing the two).

Tonight I sat down to actually build the sound system for the game. I wanted to be able to do a simple "PlaySound #SOUND_SWING_SWORD" type of command, fire and forget, and have the sound engine do the rest (select which channel to play based on which was busy, start playing the correct sound, and turn off that channel after the right amount of time). Oh, and I wanted to have the sound engine use up no more than 2 bytes of RAM.

Which actually, ended up being not too difficult. I use 1 byte of ram for each channel. The upper 3 bits are used to track which sound ID I'm playing (so I only get 8 sounds possible), and the bottom 5 bits are a timer to track when it should be shut off. Each frame I decrement the timer, then go through the assembly equivalent of a big switch-case block to see what wave and frequency values should be set for that sound. If I want to later set it up so that one sound leads into another (ie one sound effect that has two tones in sequence) I can just build that into the giant switch statement, without having to take up more RAM. And if I really need more than 8 sounds, I can designate certain sounds that would belong to each channel. That would mean certain sound effects could never play at the same time, but if I really come up with that many sounds that I want to play, I think I can deal with that limitation.

Right now I have unpleasant sounds for when the player swings his sword, when you take a hit from an enemy, and when an enemy takes a hit. That's the majority of what I need, so for now, I just need to play around and pick better waveform/frequency values for those so they don't sound stupid, and I should be good to go.

Then it's on to building doors that open and close based on game conditions (# of enemies alive, keys in inventory, etc) as well as secret doors that look like walls but that you can walk through.

Wednesday, November 5, 2014

Enemy Wall Collisions Working

After much mucking about, I finally have the enemy wall collisions working. My general algorithm that I mentioned before was mostly correct, but it needed a little tweaking. The way it works now is:

  1. Check if there was a wall collision last frame. (which would only occur if the enemy was actually drawn!) If so, reset to the "last drawn" position.
  2. If no collision and enemy shown, copy the current position to the "last" position
  3. Move the enemy
  4. Determine which enemies will be shown this frame. If shown, copy "last" position to "last shown" position
  5. Clear the collision flags
  6. Redraw (and capture changes to the collision flags)

The part that really threw me off was the bold part in step 2. I was copying the current to the "last" position even if the enemy wasn't shown, which, once I worked it out on paper, turned out to let the enemy advance slowly through a wall. But it all works now!

Once that was finished, I also managed to nearly recreate my slime "AI" code from the GBA version, so that was a good test to make sure my design is holding up well. ("AI" is in quotes as the there's no intelligence about it, but that's the best label I have for the bit of code that tells the slime how to move around)

So I think I'm finally done with the very core of the game, and it's time to move to other fun things, like transitioning through rooms, adding sound, allowing for alternate items, etc.

And lest I forget Robo-Ninja, I've cleaned up the cutscene a little, but I'm stuck trying to figure out how to animate, with my limited art skills and free internet art, Dr Squidbrain capturing Prof Treeblot. I need to think of a way that doesn't look stupid, but doesn't really require more frames of animation than I have.

I also started back into level design, which is always a nice diversion when I'm stuck. The past couple of rooms felt a little uninspired, but I'm almost to the point where I can expect the player to have a large set up powerups, so I can start designing new sorts of challenges that require quickly switching between powers. That should be fun.

NES Anguna

Well, I had a little bit of time still, while Frankengraphics is finishing up her game Project Blue, to have a little downtime on Halcyon, s...