Vigilante Ninja 2, Post-Mortem

Hello.

 

It’s been a while since I finished my big NES game. While it took me 2 years to complete, it could have been done in 1 year, if I focused on it 100% of the time. The game is still available in a limited release, btw. But enough time has passed to reflect on the development process.

 

VIGD2 Purple Cape Man.
My first game I made, “Purple Cape Man, Vigilante Ninja”, was programmed with NESASM. Someone suggested I make a sequel, which became Vigilante Ninja 2, which is entirely unlike the first game in every way.

Vigilante Ninja 2 was programmed with ASM6 (except the music engine, was programmed in cc65/ca65). Graphics were YY-CHR and level design in Tiled (with a custom python script I wrote to compress each level). The design process could have been much better, since it still required lots of cutting and pasting. So, level editing was very slow.

vig2_lv4-1 Vigilante Ninja 2.

What went right?

The music.

Probably the best part of the game. Originally, I wrote a temporary song for the demo, and had planned to write a song for every level. Luckily, estlib was kind enough to write songs for me, which are excellent.

He used more sound effects than any famitone version could handle, so I had to use the full famitracker driver…and again…lucky for me that Shiru wrote a version of famitone to compliment the full famitracker driver.

There is still a small, rare bug that I was unable to diagnose and solve. The main downside to using an unfamiliar sound engine. Occasionally, when you start level 4, the main melody doesn’t play for a few seconds. But the bass line plays. It doesn’t sound “wrong”, but it was.

Some of the graphics.

At the start, I had no idea what I was doing, or even what style I wanted. I actually redid the level 1 graphics 3-4 times, and completely redrew the hero. A fellow homebrewer informed me that the graphics of my game were crap, so I kept trying to improve them.

vigN_Old

vigN_Old2
vigN_Old3
Vig2New

By the end, especially level 4, I had my flow, and the graphics actually started to click. Many of the enemies in level 5 (fire, ghost) are my favorites.

Vig2-Lv5

The process of sketching on paper, and drawing in GIMP, then YY-CHR didn’t change. But, I got better at it over time. I wish I had put more effort into animating. Nearly every enemy only has 2 animation frames. And I had plenty of blank tiles I could have used.

Special thanks again to Nicolas Betoux for drawing the title screen graphics. Paired with the excellent title screen music (by estlib), it really made my little homebrew feel like a real NES game.

The throwing weapons.

One of the more fun parts of the game is collecting different weapons and using them. Oddly, I got complaints that you had unlimited shots, so I (very late) added a power bar, which drains if you use too many weapons. I’m still not satisfied with this hacky solution, since it just slows the game down (especially fighting a boss).

Most of the game engine.

Vigilante Ninja 2 scrolls pretty smoothly, without bugs. The NMI code got a little spaghetti like at the end, when I added bank switching. All the level testing was done with NROM sized roms, even through level 5. I made 2 separate level 5 test ROMs, 1 for level, 1 for boss.

In the end, when I finally switched to MMC3, I put in what I called “trampolines” in the main fixed bank, which switched (for example) the music code and data banks in and started the music engine. But it wasn’t so easy, and the bank switching code is ugly and hacky. But I suppose everything is working… so it’s good enough… just don’t expect to reuse the code in a year and understand what everything is doing.

What didn’t work?

The Sprite system.

I didn’t plan the sprite object system well. Pretty much just used static addresses in the OAM buffer, which made the size and shape of enemies very inflexible and difficult to change. And, I used an entirely different 256 bytes of RAM just for sprite shuffling, which was a huge waste of memory. (NES has only about 2000 bytes of usable RAM).

The worst part of it, was I didn’t use variable names in the original code, just address numbers. And, so much time passed before I revisited the object code, that I barely knew what the numbers meant. I wanted to change things, but I would have had to rewrite a thousand lines of code, so I kept the original terrible system.

I rewrote part of it for the hero, just so I could layer sprites (the sword). I wanted to change the look of coins and make them bigger, but I painted myself into a corner, and couldn’t change a little thing without rewriting everything. So, my biggest regret is not starting with a better sprite code.

The gameplay.

Looking back, there is very little difference between the different sub-levels. Sure, the graphics and enemies change from level to level, but I could have done 100 little things to make each sub-level feel interesting. Hidden things. Changes in graphics. More interactive elements. A specific goal (collect all X, kill all X’s, avoid X). A special throwing weapon only found in room 23. A secret 1 up, if you do X.

Basically, just run from the left to the right, and don’t die. Not great.

Enemy AI.

Probably the weakest point of the game. Most enemies walk left and right. A few fly in right to left. Shoot them, get a standard reward.

Level 3 has the most interesting enemies. The spider can climb any wall, and always follows you. I think (at least in early versions) if you slowly walk right, the spider can climb on top of the ceiling and follow you pretty far.

The bat (not the boss), was an attempt at a more aggressive enemy, who loops around attacking you. However, he’s a bit buggy if you move a certain way. Never quite got him right.

Vig2-Bat

The bees (level 4) chase you, fly just above you, and try to sting you. It’s all timed a certain way, that it pretty much always misses you trying to sting. I had to slow them down, as they were damn hard to get away from or hit.

But, nearly all the rest just slowly walk around, and disappear if they wander just a bit too far left. Also a failure, not keeping off screen coordinates, so he could walk back on screen after that.

The Ending.

I wish I had done a more elaborate ending. It’s just 1 screen of text, and then the credits. I had room for more graphics, I could have done some more graphic story…maybe related to that magic sword I kept talking about.

Marketing.

I really dropped the ball here. I barely made an effort to advertise the game development. I had a thread over at nesdev forums, and mentioned it on my blog. But, almost no one from the nesdev forum asked me for a copy of the game. I guess buyers don’t visit there.

I didn’t make boxes, or print booklets, etc. I could have printed T-shirts. I could have traveled to game conventions. Or tried to contact all the used game stores within an hour drive of my house. But, I didn’t.

When I finally did mention the game to buyers, it was right after the release of Ninja II, a completely different homebrew game with a similar name…which I think caused some confusion.

Actually, the entire concept… making a sequel to a lousy game that no one played, and making it completely different, was confusing. Several people asked me “where is Vigilante Ninja 1?” Well, it’s called ‘Purple Cape Man’, and it’s not the same game. I had a few people ask me, “if he’s a vigilante, why is he fighting squirrels and bunnies?” I think a different name and concept would have helped.

Conclusions…

In 2 years, I learned a lot. Made a full sized NES game. Put it on a cartridge (thanks infinite NES lives), and gamers around the world are playing it. I’ll say partial success. There have been so many great NES homebrew releases lately, that have really raised the bar. Perhaps people would be better off buying Haunted Halloween, or Twin Dragons, or Lizard. I’d be ok with that.

And I’m still making games. Maybe the next game will be the one everyone remembers. Only time will tell. Back to the work I go.

Advertisements

NES Podcast

Just a shout out to KHAN Games (Kevin Hanley, @atonofglaciers ) and Sole Goose Productions (Beau, @SoleGoose ) for their recent and ongoing Podcasts.

The Assembly Line discusses all aspects of NES homebrewing, and live interviews with homebrewers. I’ve listened to them, and rate it a “must listen” broadcast for anyone programming the NES, or just anyone interested in games and game production.

I’ve found multiple links…I think these all work.

https://itunes.apple.com/us/podcast/the-assembly-line-an-nes-homebrew-podcast/id1247222547

https://m.soundcloud.com/user-463768680

https://player.fm/series/the-assembly-line-an-nes-homebrew-podcast

https://play.google.com/music/m/Il7yhrcfqbuejnfyh24a2dlpp7y?t=The_Assembly_Line_An_NES_Homebrew_Podcast

Good stuff guys. Looking forward to more.

 

How to make a coloring book page from any image.

Using GIMP. Open the image.

Gnome

Color / Desaturate

Gnome2

Filters / Edge-Detect / Edge…

I just used whatever was standard settings = Sobel, 2.0, Smear.

Gnome3

Color / Invert

Gnome4

Color / Curves

Put a dot near the middle and then drag the right side up.

Gnome6

Gnome5

Lets put a border on it.

-Select All

-Select / shrink, 3 pixels

-Select / invert selection

-Pencil Tool, choose black, draw over the edge

Gnome7

Change print size. Image / print size / 8″ wide (or 10.5″ tall).

And print your coloring book page.

 

Scrolling

By request, I made a scrolling game engine, with BG collisions. I was supposed to use neslib, but I made so many changes, and basically did 90% of the meat in ASM, that it’s not really a lesson in C. Sorry.

I’m going to have to come back and explain lots of things. I made it way too complicated…so basically, I created a huge top-down all-direction scrolling game engine.

I think I’m going to have to make a simpler one, for my next page (a little later).

Here’s the mess of code, that probably still has bugs in it. I recommend speeding up the emulation, it’s a bit slow.

http://dl.dropboxusercontent.com/s/wapes71qfp3gvxx/lesson28.zip

lesson28

NOTE: I haven’t had time to properly test/debug this code…or even comment about it here. Some people on the nesdev board have had bugs/problems, so I suggest you don’t use this code until further notice.

 

Add Music, Famitracker

I wrote some (terrible) songs and sound effects in Famitracker. So, first the songs. Put all songs in the same Famitracker file (adding songs in the module properties). I wrote another page on the limitations of the famitone music engine. Basically, no effects allowed except looping backwards (the Bxx effect, where xx is the frame number).

Export all the songs as a text export, and then process the .txt with text2data (command line program). Make sure to include the -ca65 tag.

Sound effects were put on another Famitracker file. Again, each sound effect on a unique song, using module properties to add another song. You can use any kind of sound effect (I think). But there is an annoying limitation on the length of sound effects. You can actually get around that limitation, by rewriting the famitone.s source (but I can’t show you that rewrite, because it’s not open source code. Sorry.) I have a habit of ending each sound effect with — cut sound and then C00 end of song (on separate lines). I believe this properly ends the sound effect.

Anyway, save all the sound effects as 1 FTM file. Process it with ftm2data (with the -ca65 tag).

Include the music data, in crt0, right after the label music_data: and include the SFX data in crt0 after sounds_data. The crt0 I have, will automatically initialize (set up some pointers) the data, during startup.

Now…use music_play(0); to start the first song. You pass it the song number, starting with 0, of course. If the song number is too high, music will stop. (That’s not the rigtht way to make music stop, btw).

To pause the music, use music_pause(1). To unpause, use music_pause(0). To stop the music (and lose your place in the song) use music_stop().

To trigger a sound effect, use sfx_play(unsigned char sound,unsigned char channel). I didn’t enable (in crt0) more than 1 sound effect channel, but you can, if you want some sound effects to have priority over the others. Notice, I have Square Channel 2 blank in the songs, and exclusively use Square Channel 2 for sound effects. That’s so they don’t collide. If you put song parts on the same channel as the sound effect, make sure you use a less important song part, so the main melody doesn’t cut out every time a sound effect plays.

I just remembered. I have previously used a modified version of famitone.s…but I think I’m using the original verion here. I may change that in the future. No worries. Just make sure the volume of sound effects stays at least as loud as the music channel…or else it won’t play. In a modified version (not here) sound effects always had priority over music, regardless of volume.

That’s it. Easy. The song’s suck, but I didn’t want to spend much time on this. Here’s the example code. Pong, with sound and music (but still no scoreboard).

http://dl.dropboxusercontent.com/s/m02sq4hfod15dct/lesson27.zip

 

Sprite BG Collision, Pong

So, I wrote the simplest game possible. Pong with walls. The ball bounces off the walls (and the paddles)

The first thing I did was make everything in NES Screen Tool. Saved the CHR, saved the BG as a RLE .h file. Saved the Paddles as Metatiles.

Then I made a collision MAP. I just did this by hand. I could have used Tiled and saved the map as a .csv, but it was quicker just to type the map out as an array of zeroes and ones. 1 = the ball will collide with the BG. 0 = the ball will pass through it. Each entry represents a 16×16 block on the screen.

Now, I wanted a random start point and direction for the ball, but I needed a way to get a random seed. I decided to have it spin in a while loop, and count how many loops before a button is pressed on Controller 1.

while (!pad_poll(0)){
  ++Seedy; 
} // Seedy is an int, 2 bytes

set_rand(Seedy);

Now rand8() will give us some actually random numbers.

 

Each frame, the ball’s speed are added to the ball’s position. First X, then check collisions, then Y, then check collisions. If the change in X caused a collision, then I reverse the X speed. If the change in Y caused a collision, then I reverse the Y speed.

X example…

KeepIt = Ball.X; 
Ball.X += Ball_X_Speed;
index = (Ball.Y & 0xf0) + (Ball.X >> 4); 
if (MAP[index]){ 
  Ball_X_Speed = 0 - Ball_X_Speed; 
  Ball.X = KeepIt; 
}

If the ball goes past the paddle, I have it pause for 2 seconds. It counts down frames, until the pause variable is zero, then it respawns the ball with new random direction and position.

I didn’t add a scoreboard. I just wanted to keep it as simple as possible, to show how sprites can collide with the background. It, of course, isn’t really colliding with the background tiles, but rather an array (MAP) that represents the positions of the wall within the room.

Here’s the example code.

lesson26

http://dl.dropboxusercontent.com/s/1a20e1s1pd00ntg/lesson26.zip

 

Sprite Collision, and Controllers

OK, I kind of copied the idea from Shiru’s example code. Controller 1 controls the yellow sprite. Controller 2 controls the blue sprite. The background color changes when a collision is detected.

CONTROLLER READS. I changed this. Normally, with neslib, you have to pass the controller read to a variable, and do a seperate function to get new button presses (trigger mode). I feel it would save zero-page RAM if you could access those internal variables directly.

So normally you would…

pad1 = pad_poll(0); // reads controller 1

I changed it so you do this…

pad_poll(0); // reads controller 1

…then to access the read value, you use the PAD_STATE variable. And to use the new button pressed value, you use the PAD_STATET variable. Example.

if(PAD_STATE & PAD_LEFT){ }

would do something if LEFT is pressed on controller 1.

if(PAD_STATET & PAD_LEFT){ }

would do something if LEFT is pressed this frame, but not the last frame. A new press.


 

You should read the controller at the beginning of each frame. (Or not, if your game logic takes 2 frames to complete, and you want a consistent controller value across both frames.).

For 2 player, you need to make sure you read the 2nd controller.

pad_poll(1); // reads controller 2


 

For the sprite collision, I wrote some ASM code, that expects 2 pointers to structs who’s first 4 bytes are ordered like this…

struct BoxGuy {

unsigned char X;

unsigned char Y;

unsigned char width;

unsigned char height;};

It returns 0 if no collision, and 1 if collision. It’s slightly buggy at the edges of the screen. This is the function…

unsigned char CheckCollision(struct BoxGuy* one, struct BoxGuy* two);

 

NOTE – it could have been written in C. Something like this…

unsigned char CheckCollision (struct BoxGuy* one, struct BoxGuy* two){ 
  if (((one->X + one->width) > two->X) && 
  ((two->X + two->width) > one->X) && 
  ((one->Y + one->height) > two->Y) && 
  ((two->Y + two->height) > one->Y)){ 
     return 1; 
  } 
  else { 
     return 0; 
  }
}

But, I tested that, and it runs twice as slow as the ASM version (478 cycles vs. 255 cycles). I’m assuming that a much more complex game will need to do lots of sprite collision checks. Hmm. Maybe I should make this even more efficient?

So, anyway, if collision == true, change the background color, else, change it back. You can change 1 color with this function.

pal_col(unsigned char index,unsigned char color);

pal_col(0, 0x30); // 0 is the first color in the palette array, 0x30 is white.

Here’s the code.

lesson25

http://dl.dropboxusercontent.com/s/qdkz26l9n3rpx6y/lesson25.zip

 

Interesting side note. Notice how the yellow box is always in front of the blue box. That is because the yellow box was loaded first into the buffer. It has a higher priority. If the blue box was loaded first, it would be on top.