Sunday, October 26, 2014

Getting stuff done...

So I got a lot done this weekend (not counting the work I did on my back porch, but this isn't a home improvement blog, so....)

  • I got the basics of the scripting system working for cutscenes in Robo-Ninja. I reworked my original design slightly (to put the entire script in a single file, instead of a different script for each "puppet" in the scene). I still need to flesh out a number of the script commands, but I have a very ugly basic cutscene working, where Robo-Ninja stands there as Prof. Treeblot walks away from him, and then a buggy half-broken Dr Squidbrain shows up.

  • On Atari Anguna, I did some thinking, and realized the way I had written 3 completely separate kernels was dumb. I managed to rework it so that the the smaller, simpler kernels were just subsets of the bigger more complicated kernel. They are a couple cycles slower, as I had to add a branch to possibly jump out of the kernel loop for the smaller kernels, but I managed to squeeze the branch into a spot where the timing wasn't as critical. So that saved me a good 500 bytes (1/8 of a bank, or 1/16 of my total rom space). If nothing else, that frees up space for at least 6 more room layouts. (to allow for more total number of rooms, I'll reuse the layout data across multiple rooms in the game)

  • I also got my bankswitching scheme all set up, so that the main game logic happens in one bank, and the display kernel and graphics data lives in the other bank. This was a little messy because I originally divided my code into separate files, based on what sort of game element each file was about. So I had a file for enemies, a file for backgrounds, a file for the main player, etc. But by default, the assembler just lays out everything sequentially in memory. So I had to do a little bit of rearranging to make sure that everything was declared in the right bank. (There was one small set of data, used for positioning sprites, that I ended up having to include in both banks, so I hardcoded its location, so that it was in the same place in both banks).  The fact that I mostly use macros to break up my code (instead of actual subroutines) make it a bit easier, as the macros get substituted inline where you call them, instead of existing where you declare them.

The big thing that annoyed me this weekend is realizing, once again, how slow development is, when you don't have much time to put into it. Often I've only got about 30-60 minutes of useful brain function left in my spare time in the evenings, before I get too tired to do anything useful. It's just hard to get anything done in that time. (which is partially why I like blogging about it -- it takes less brainpower, so I can do this during that weird gap of time where I'm not quite ready for bed, but my brain refuses to produce more code.) I guess if I only had 1 project instead of 2, it would move faster. Although I've really enjoying the fact that I have 2 options. If I get bored of one project, I just swap to the other.

Ok, my brain is fully shutting down now....

Tuesday, October 21, 2014

Kernels, enemies dying, and bankswitching

So for the past couple weeks, I've been putting off working on Atari Anguna because I've been up against a bug that sounded difficult and boring to solve: whenever I killed an enemy, it messed up my kernel logic, and everything went haywire.

What's supposed to happen, is that every frame, I sort my enemies by Y position, and then compare all their Y values, select the right kernel mode, then draw them in order. If an enemy is dead, I'd sort it to the end, and select kernel modes that ignored that enemy. But it wasn't working.

I sat down to tackle this today, and happily, it only took about 10 minutes. It was a simple logic error in my sorting routine, which occasionally caused a dead enemy to get sorted to the front of the list.

(Tangent time! I ended up using a *gasp* Bubble Sort for sorting these enemies. Why? Because I realized that for N=3, which is my case, Bubble Sort only requires 3 comparisons (a/b, b/c, a/b again). So it's a really simple sort to implement, and if I'm guaranteed constant time, then hey, why not. The bug I ran into was that if the enemy in the first position of a comparison was dead, I'd always swap, but I forgot to check if the enemy in the 2nd position was dead, and refuse to swap in that case.)

So tonight I also realized that my kernel was working for 3 enemies as long as 2 of them overlapped, but I never had gotten around to implementing my kernel mode for 3 completely separate enemies. That's really simple and straightforward, but requires a good bit of compiled-code, as I'm using macros instead of subroutines (I can't afford the 6 cycles for the JSR and 6 more cycles for the RTS during my kernels!), so in the compiled code, there's tons of repetition. Well, this created a new problem: I've now almost used up my entire 4K of ROM.

Now the geniuses back in the 80's solved this problem with bankswitching, which is what I now have to figure out. Basically, there are 2 entirely separate blocks of ROM that you can switch in and out of actual ROM. This gives you double the space, but gets tricky. For example, as soon as you give the command to switch banks, it immediately switches. Meaning the instruction pointer is now pointing to the memory location of the next command, but in the other bank. So you have to make sure to line up your code just right, so that when you toggle banks, the code in the new bank picks up and runs where you left off. (And when you power on the Atari, you don't know which bank you're in. So both banks have to have identical initialization routines, so you can get yourself into a known state!)

I think I've got the basics working now:
In both banks, I have an almost-identical routine where I manually load a target address (the target address of a routine in the other bank that I want to execute) onto the stack, simulating what would happen if I had jumped to a subroutine with JSR. Then I toggle banks, and immediately RTS, which makes the Atari try to return from the subroutine, and jump to the target routine in the new bank:

    ORG $D009    ;Bank 0 uses a "pretend" address of $D000
    RORG $F009   ;but a "real" address of $F000
                 ;(The actual Atari ROM is always $F000) 

JumpToOtherBank
    LDA BANK_2   ;Reading this register switches banks
    RTS          ;and then jump to the location on the stack

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ORG $E009    ;Now the same thing for Bank 1
    RORG $F009    

JumpToOtherBank
    LDA BANK_1   
    RTS          

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; And then a macro to make it easier to set up
; and call the JumpToOtherBank routine

        MAC BankJump 
            LDA #>{1}   ;push the target's MSB onto the stack
            PHA
            LDA #<{1}-1 ;push the LSB (minus one*) onto the stack
            PHA
            JMP JumpToOtherBank ;then call the magic routine

        ENDM


* I spent a good while fighting this code until I realized I needed the "minus one" on the least-significant-bit. Turns out JSR pushes one less than the address of the next command, and then RTS pulls that from the stack, and increments it. So I was 1 byte off when I'd try to RTS, which was breaking everything horribly.

I still need to actually break my code up into the different banks now. I'm hoping to fit the kernels and graphics data in bank 1, and everything else in bank 0. But we'll see.

And I still need to actually test my full kernel for 3 separate enemies. And do the enemy-wall collisions that I've been planning for weeks. But bank switching is more fun.....

Thursday, October 9, 2014

Jetpack and death counter

Tonight was back to Robo-Ninja. First, to make Aaron happy, I went ahead and created the jetpack item. He's been wishing for that non-stop for months, so I thought it would be a fun surprise to add for him. Plus, it was one of those few times that something was really easy. It helped that it's so similar to the double-jump item in function.

Then, I felt like doing something different (the scripting stuff, while interesting, required some serious thought and design work, which I didn't feel up to tonight), so I added something I've been planning for awhile: the death counter. What good is a hard game if it doesn't regularly remind you about how bad you are at the game?

With Anguna, when I released it, I had a little contest, where the first person to win the game with 100% of the hidden items being found, won a free copy of the game. It was a fun way to drum up some interest and attention. (and was fun from my end -- the 2nd place winner was only a few hours behind the real winner!)

I'm thinking about doing something similar with Robo-Ninja if I ever finish. To have a contest to see who can win with the fewest deaths. I'm not sure what the winner will get, but hopefully it will tempt a few crazy people into actually finishing the game. We'll see. (Realistically, I'll be lucky if I can get more than 5 or 10 people to actually notice and download the game. But hey, whatever).

Monday, October 6, 2014

3 enemy kernel (getting close!)

Spent awhile tonight working on trying to get my 3-enemy kernel working for the atari game. It basically amounted to a ton of branches, checking to see which enemies were close, which frame I was in, etc.

Basically, I have to see which enemies are close to each other vertically, pick the appropriate kernel (whether to render 1, 2, or all 3 enemies), then swap around the enemies in the enemy array to make sure that the correct enemies are rendered for this frame (since enemies that overlap a row only get rendered on alternating frames).

So tonight was my first pass at it. It works in a few of the cases, but completely freaks out in a few of the arrangements. So now I get to start the fun job of isolating which cases fail, and debugging. This should be fun.

Wednesday, October 1, 2014

Robo-Ninja Puppets

So now on Robo-Ninja I have the fun little diversion of figuring out how to do the scripted sequences for the beginning and ending stories. It will consist of little more than a handful of characters (Robo-Ninja, Prof. Treeblot, and Dr ?, maybe a robot guard) moving around and having word bubbles over their heads.

But I need a nice way of scripting it.

So that's what I'm currently working on, which is fun because it's a different sort of thing. I decided to not go all out and embed a lua interpreter in my game, but I AM making a super-simple script language that I'll run for each character.

I've extended my GameCharacter class (which handles drawing characters, animated them, detecting collisions with other characters) to create a new class that follows a script. Now I just need to actually implement the script system (and parser!)

I haven't finished planning it out fully yet, but I'm envisioning something like:

2.5   moveto 132,234
1.5   say    This is text that he will say!
1.5   wait
2.5   moveto 132,234
Where the first number is the amount of time to spend doing the action, the next token (separated by whitespace) is the action to take (I'm not sure I need much more than those 3 actions, unless I need some way to specify additional animation frames that a character should do (ie facial expressions?)). The moveto command should be smart enough to move at the right speed to take the amount of time specified on the left. And be smart enough to make sure the character is facing the right direction, showing the "walk" animation, etc.

Now I just need to sit back and think about whether I'm missing anything important. (And I'm still trying to decide if instead of a "wait" command I should include absolute start times on each command. That would be more complicated for me if I had to rearrange and re-time everything, but it might be easier for keeping multiple character's script information in sync. I'll still have to think on that.)

In other news, I got my Atari enemy display kernel working with 2 enemies right now. (No wall collision detection yet though). I'll see if I can get that 3rd enemy working, then try to tackle the collision detection.


Tuesday, September 23, 2014

No, I was wrong

The morning after I posted that last post, I realized my "simple solution" was wrong. Because I can't determine if an enemy is showing on a frame until after I've moved all the enemies for that frame. So I really do need to save 3 sets of previous X/Y coordinates. That's 6 whole bytes of my 24 remaining.

I don't like it. But I'm not sure I have any options.

Sunday, September 21, 2014

Multiple Enemies

Ok, now I need to talk about getting multiple enemies on the screen in Anguna 2600. Time to write a novel here.

A little bit of background first: The atari is designed to only show 2 sprites on the screen at once. (although each one can be doubled or tripled horizontally) But, because you can change things up every scanline (if you are fast enough!) you can reset the sprites every few vertical lines, to make a number of things happening onscreen, so long as you commit to spacing them out vertically. Which is why so many games had rows of sprites in their own little lanes on the screen. Classic examples are Freeway and Space Jockey:



In each of these, the player is one sprite, and sprite 2 is reused over and over vertically (in freeway, you can see the various options for duplicating the sprite horizontally)

But things get messy if you want to try to show more sprites than that on a single line. There are a few crazy tricks (you can place both sprites next to each other, triple them, and then update the graphic data on the fly as the scanline moves across the screen. But that pretty much takes ALL your processing time, so you can't do anything else as well). Or you can do what pac-man does with the ghosts, which is draw alternating sprites on different frames. With this technique, you could theoretically draw as many sprites as you wanted, but since you're only drawing a couple of them per frame, they get more and faded the more you draw (on pac-man, I believe each ghost is only drawn every 4 frames!)

My goal with Anguna, if I can figure it out, is to allow 3 enemies onscreen at once. What I hope to do, is adaptively adjust how I draw them, depending on the current screen layout.  If they are all spaced out nicely, in different vertical locations, I'd just draw them separately, like space jockey or freeway. But as soon as 2 (or more) of them would overlap vertical positions, I'd start fading and alternate drawing them.

That way, it wouldn't be a faded mess most of the time, but I could degrade to fading when necessary. The tricky part of this is that I'd basically have to write multiple different display kernels, and choose the right one accordingly (there's not enough time during the tight display loop to branch and handle the different cases, so I'd have to write separate (but mostly similar) display loops, and pre-select the right one before drawing. I'm not sure if I'm smart enough to pull this off.

The other problem I foresee is going to be collision detection. The one relatively easy thing the Atari 2600 gives you is built-in pixel-level collision detection. You can read a register to see if your sprite collided with the playfield walls. Or with another sprite. Or a missile. Or the "ball" (the only other on-screen object that the Atari provides besides 2 sprites, 2 missiles, and a playfield). The Atari automatically sets the values of the collisions registers as it's drawing, whenever the TIA (the fancy Atari display chip) actually draws the collision.

Which causes problems when I'm fading enemies. Because an enemy-wall collision might only be detected every 3rd frame (if all 3 enemies are on the same row, so I'm having to alternate drawing them). I still want them to move at regular speed, but if they run into a wall, they have to back up to their previous location. I previously was saving the player and enemies "last location" along with the current location, so I can revert to that in the case of a wall collision.

But with my plan, with enemies, I'll have to store the last position from the last time they were actually drawn. (as that was the last time that I knew they weren't colliding with a wall) But I don't know for sure if they'll be drawn this frame, until after I move them. It's not a terribly complicated problem, but requires keeping a little bit more data in ram to sort it out. Which I don't have much of. The Atari gives you 120 bytes of ram (for both variables and your stack!). I've used up most of that already, and only have about 24 bytes remaining, which I'm greedily guarding.

Ok, actually, the process of just typing this up and explaining it made the optimal solution pretty obvious to me -- I can do this using just 2 more bytes of ram. I just need to temporarily store the last X and last Y for an enemy in a temporary variable, before I move them. Then, if I've decided I'm drawing that enemy, copy that those temp variables to the actual lastX and lastY for that enemy. I can reuse those temporary variables so that I don't have to allocate more ram than that.

Ok, well, now that I've scared off any readers that I may have still had, I think I'm done for now. It was worth it just to make me organize my thoughts enough to solve that silly problem.

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...