Completed Nerdy Nights Advanced Tutorials - Next Step To Develop A Platformer?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
Posts: 12
Joined: Sun Jan 10, 2021 1:12 pm

Completed Nerdy Nights Advanced Tutorials - Next Step To Develop A Platformer?

Post by justin-rwx » Sun Feb 28, 2021 4:22 pm

I've just finished the advanced portions of the Nerdy-Nights tutorial, and I think that they provide a great set of tools to start developing my own platforming game. Unfortunately, it doesn't go much into specific concepts of platforming. I'm wondering if there is any other tutorials or in detailed examples that exist that bridge the gap from Nerdy-Nights to a basic platformer? Specifically, some of my biggest questions at the moment are how to scroll based on a players movement, how a player's sprite interacts with floating platforms, and other basic platforming concepts.

Posts: 22333
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)

Re: Completed Nerdy Nights Advanced Tutorials - Next Step To Develop A Platformer?

Post by tepples » Sun Feb 28, 2021 4:27 pm

How to scroll within the 512 pixel box: Set scroll X to player X, clamped to 0-255

How to scroll bigger: Form a data structure for map data, then draw columns as they're about to scroll into view, as illustrated in Nova the Squirrel

Using the +32 increment mode in $2004 helps with updating columns, as it causes nametable writes to go down rather than across.

User avatar
Posts: 12054
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Completed Nerdy Nights Advanced Tutorials - Next Step To Develop A Platformer?

Post by tokumaru » Sun Feb 28, 2021 9:18 pm

Scrolling consists in watching the movement of the camera and drawing a new column of tiles/metatiles whenever it crosses specific boundaries. If your game uses 16x16-pixel metatiles, then ideally you'd draw a new column of them every time the camera crosses a 16-pixel boundary. To detect the camera's movement, you need to save it position before moving it, and then compare the new value to the previous one. You only need the lower byte for this:

Code: Select all

	lda OldCameraX+0 ;get the low byte of the old position
	eor CameraX+0 ;XOR will turn bits that are the same into 0s and bits that changed into 1s
	and #%00010000 ;bit 4 is the one that changes whenever a 16-pixel boundary is crossed
	beq ScrollXDone
	jsr DrawColumn
The next step is to figure out *which* column to draw. To decide this you need to look at the direction of the camera's movement. Depending on how you moved the camera you already have the answer to this in some variable, but if you don't, you can just use the low bytes again to calculate the sign of the movement (as long as the movement is less than 128 pixels, 8 bits is enough):

Code: Select all

	lda CameraX+0
	cmp OldCameraX+0
	bpl ScrolledRight
If the new position is larger (N = 0), the camera scrolled right, if the new position is smaller (N = 1), the camera scrolled left. From there you can tell that your new column starts at the coordinates indicated by (CameraX, 0) when scrolling left, or (CameraX + 256, 0) when scrolling right. With that information, you can calculate where that column is in your level map so you can read it (the actual calculation depends on how you're encoding your maps) and decode the tiles and attributes to a buffer in RAM.

The last thing you need is to calculate the target address for your tiles and attributes in VRAM. This is also based on the coordinates of the column that you calculated before. Assuming that the column will be drawn at the very top of the name table (i.e. Y = 0), you need to take the X coordinate of the column and rearrange it in name table format:

ColumnX: FEDCBA9[8] [7654]3210 (the bits in brackets are the ones that matter)
Name table address: 00100[8]00 000[7654]0 (they need to be rearranged like this)

Here's one way to do it:

Code: Select all

	lda ColumnX+0
	and #%11110000 ;keep only the necessary bits
	lsr ;move them to the correct position
	sta TileAddress+0
	lda ColumnX+1
	and #%00000001 ;keep only the necessary bit
	asl ;move it to the correct position
	ora #%00100000 ;add the base address ($2000)
	sta TileAddress+1
Then there's the attribute table address:

ColumnX: FEDCBA9[8] [765]43210
Attribute table address: 00100[8]11 11000[765]

You can modify the tile address for this, since much of the work is the same:

Code: Select all

	lda TileAddress+0
	ora #%11000000
	sta AttributeAddress+0
	lda TileAddress+1
	ora #%00000011
	sta AttributeAddress+1
Now all you have to do is blast the buffered data to VRAM when the NMI fires, starting at the calculated addresses.

As for floating platforms, there are countless ways to go about it, but one way to do it is to treat floating platforms like any other game object (monsters, items, etc.) and have they take care of updating themselves, moving in the air little by little each frame. Once you detect a collision between the platform and the player (and possibly other objects), and the player is coming from above, the player has to "stick" to the top of the platform and follow it. You probably need a special field to indicate that the player is standing on another object, and what object that is. In my projects, objects that can be stood on will have a field indicating how much they moved each frame, and if the player is "attached" to one of those objects, the same displacement is applied to the player. For this to work correctly, you need to make sure that the platform is moved before the player is, otherwise the animation may look jerky. On top of that displacement, you need to add the player's own movement as well. You constantly need to check whether the player is still colliding with the platform, and if it isn't, you simply "detach" it from the platform.

Post Reply