Over the past ~4 months as a side project I have been programming an engine for an NES platformer. Here it is: https://bitbucket.org/Light-Dark/nes-platformer/src. The up-to-date source code and other data is included as well for anyone interested .
EDIT:
Here's a video of it in action:
Made a semi-more-interesting level and made the player shoot a useless projectile lawl.
Thanks man! Also background collision may seem like an easy task at first but do not underestimate it for it was a frustrating task indeed!
Re: NES Platformer Engine
Posted: Sat Feb 15, 2014 4:43 pm
by Light-Dark
If anyone at all is interested in this: I have made an update to the repository linked above which includes a fix for the very wonky background collision detection and makes it less wonky (You no longer go through the bottom of the floor when jumping). As of right now I just got a more flexible sprite drawing system to work(something kinda similar to vertex buffering actually), it's way better than just hardcoding sprite data into the OAM. I'm also considering making a knockoff of my level editor that I made for my RPG engine that will output level data in my Level Data format
example of my format:
MetaTileDefine:
.db $00,$00,$00,$00 ; The 4 8x8 tiles that compose the 16x16 metatile
Level:
.word MetaTileDefine ;Pointer to metatile definition
.db $01 ;How many times it is repeated (repeated 1 time)
New sprite drawing routine:
;; Takes:
;;; Sprite_Pointer - Index of the current location in the OAM buffer in ram (OAM_PAGE = $0200) to write to
;;; Temp_Sprite - Temporary holder for sprite data, 4 bytes (y , attr, tile, x)
Load_Sprite:
LDY Sprite_Pointer
LDA Temp_Sprite
STA OAM_PAGE, y
INY
LDA Temp_Sprite+1
STA OAM_PAGE, y
INY
LDA Temp_Sprite+2
STA OAM_PAGE, y
INY
LDA Temp_Sprite+3
STA OAM_PAGE, y
INY
TYA
STA Sprite_Pointer
RTS
I'm going to replace all the hard coded sprites when I get home and then upload it. Lastly, if anyone out there wants to use this Engine,improve upon it, learn from it or critique me for my poor 6502 programming skills please feel free to do so as it is open source for just those purposes !
Re: NES Platformer Engine
Posted: Sat Mar 08, 2014 6:02 pm
by Light-Dark
So I scrapped the NESASM3 build and rewrote the Engine for the ASM6 assembler (superior to NESASM3). One new thing is that the level format is now waaaaay more versatile:
; Meta tile defines, 4 8x8 tiles
Black:
.db $00,$00,$00,$00
Sky:
.db $44,$44,$44,$44
...
; Tile_Set table
Tile_Set_1:
.word Sky ;0
.word Dirt_Top ;1
.word Dirt_Center ;2
.word Dirt_Top_L ;3
...
; Level information
Level_1:
.db $01,$01 ; header, uses Song1
.word Tile_Set_1 ;Tile set used
.word Map_Data_1 ;Pointer to map data
.word Palette_1 ;Pointer to color pal used
.word Attribute_1 ; Pointer to attribute table (palette selected for 32x32 region)
.word Collision_1 ; Pointer to collision table
...
; Map data, uses run length encoding scheme for compression
Map_Data_1:
.DB $00 ;Tile#
.db 162 ; Repeat value
.DB $03
.db 0
.DB $01
.db 11
.db $00
.db 2
....
The screen no longer flashes garbage when a new area is loaded, which is great (solving that issue gave me a nasty headache by the way). I've integrated the public domain FamiTone music driver into the engine because it is compatible with FamiTracker and is really easy to setup and use. You may notice that at the top of the screen there is this black bar, that is the status bar. It used to be able to output text however integrating FamiTone fucked it up somehow and I'm in the process of fixing it right now.
Some parts of the old engine were recycled like collision detection. However I made collision more versatile in that you can give certain tiles different properties like reduced gravity (water), solid(ground), low friction (ice),etc,etc. In the video I demonstrate some water areas where you may notice that the character falls slower and moves up faster in the "water" (excuse my awful artistic skills).
Re: NES Platformer Engine
Posted: Sun Aug 03, 2014 11:13 pm
by Light-Dark
So I've made more progress on this engine. A major change is the incorporation of the UNROM mapper which allows me to have 8 16KB PRGROM banks with CHR RAM (Basically makes the tile/sprite pattern table writable). I've also given the 2nd byte in the header word of levels a purpose: the upper 4 bits is which PRGROM bank the tile data for that level is located, the 1st bit of the lower 4-bits determines which half of the 16KB PRGROM bank has the tile data ($8000-$9FFF or $A000-$BFFF).
Now that the invisible stuff is out of the way let's get on to the stuff you have seen in the video: AI, better jumping and movement mechanics and palette swapping. Each screen or level as I call it can have a maximum of 3 AI. Each AI is given 9 bytes in RAM which determine the following:
-X coordinate
-Y coordinate
-Sprite Attributes
-Sprite index(used to determine which graphics to use)
-HP
-X Velocity
-Y Velocity
-Action (used to determine which 'script' will be executed for the AI)
-Frame Counter
The Action byte and the NPC script jump-table are what makes the AI in the engine somewhat versatile. You can pretty much do anything you can think of with the AI's properties in a 'script'. What I've done in the video is give the ai different movement patterns (one moves up and down, another moves left and right and the last one moves from left to right in a zig zag pattern).
Now onto the improved jumping and movement mechanics. As you can see in the video the player jumps a lot more smoothly and it has a kind of arc to it. That is because I've made the movement mechanics more sophisticated adding things like resistance (friction), gravity and acceleration into the fray. Resistance is determined by the properties of a collider on the collision layer that you are colliding with for example there is more resistance to movement in water than air. Each collider can be solid or non solid as determined by the most significant bit in the byte, the rest of the bits in the byte that represents the collider are used to to determine what type it is (e.g water, air, conveyor belt,etc).
Lastly I wanted to make things a little more flashier so I made it so levels can be animated via the age old trick of palette swapping. The way it currently goes about doing that is ugly and inefficient and I'm probably going to revamp it once I get into animating the background legitimately by modifying the tile graphics at run time in VRAM/CHR-RAM however it's going to be limited to only a few 16x16 tile blocks at a time because you only get so many cycles during VBlank. Well that's all for now I think. I'm sure half of this doesn't make a lick of sense to a lot of people but hey I guess that's what the video is for!