Specus, a Zeldroidvania Demo

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Specus, a Zeldroidvania Demo
by on (#78341)
Hey all! I am back again with yet another iteration of my adventure game demo. I started over from scratch writing a two-way scrolling engine that supports both horizontal and vertical maps (one or the other, like Metroid).

Right now all it does is load a map and let you scroll around it. But I'll tell you, that was a big challenge! Had it not been for TheFox's debugging extensions for Nintendulator I'd still be at it :D

The next step is to implement a proper object system. Unfortunately I can't reuse most of the code from the NES Adventure Game demo because I need real 16-bit positioning.

A couple technical points:

The scrolling engine breaks up the name table updates over four frames, and updates a 16-pixel wide area during that time. That means this engine cannot handle scrolling faster than 4 pixels per frame (it actually can, and even sustain the speed for several seconds, but I digress).

The same code path is used for both horizontal and vertical maps. Branches are taken to handle the differences. I am not sure if this is the best approach, but it has worked out for me :D

The thing I am most excited about is that the worst-case execution time for the scroll handler is 1569 cycles (not including the PPU updates in VBlank). That's only 5% of my frame! I think that will leave me plenty of head room for the object system.

About the demo:

The project page is here

Screenshot:
Image

Download Link: iNES Format ROM

Switch maps with the Start button.
Scroll with the Left and Right buttons (even on the vertical map).
Hold down B to scroll 4 pixels per frame.
Hold down A to scroll 6 pixels per frame and watch it break :D

The thing that surprised me most about the scroller is how it handles scrolling too fast. Because it updates the extreme edges of the name tables, and due to how the "need to scroll" detection works, if you scroll too fast it will still handle things gracefully. You will eventually catch up to the scroll seam, but if you then slow down you can watch the updates progress off-screen. It's kinda neat to watch :D

by on (#78346)
When I did my scroll engine, it handled up to 8 pixels per frame for both the X and Y directions. It screwed up if you moved more than 8 pixels per frame.

by on (#78350)
Nice! I'm actually working on a similar style game, myself.

I handled the updates differently, updating the nametables right before they roll on screen, so it's interesting to how yours handles scrolling too fast compared to mine.

I'm looking forward to seeing how it comes out!

by on (#78351)
That is a long hallway, but it's lined with extra nice looking statues! 8) :) And just walking left without A or B takes you to the next room eventually... ....long hallway.

by on (#78366)
unregistered wrote:
That is a long hallway, but it's lined with extra nice looking statues! 8) :) And just walking left without A or B takes you to the next room eventually... ....long hallway.


The long hallway filled with statues you are seeing is a range clamp. You are scrolling into the negatives. When the scroller tries to read a collumn that is out of bounds it reads collumn 0, which happens to be a statue. You eventually wrap back around to the other side of the map :D

In the game I won't let the screen scroll past the map edges. But the scrolling engine is not concerned with that, it only does what it is told.

Thanks for the input ya'll! I will post again when you can run and jump around.

by on (#78372)
YAY! I am even more interested in your project now.

by on (#78392)
Nice! An impressive demo (gotta love the Zelda 2 influence), not to mention a really good reference for gauging scroll speed in general! Just by playing around with this, I was able to determine that 2 pixels-per-frame is the speed I consider "standard" in my mind's eye.

(I gauged 2 ppf by taking advantage of the optical illusion triggered by activating 6 ppf in the Hallway of Statues.)


Just curious- is there a reason you tackled the scroll engine before main character movement mechanics? (Or was that Demo1?) Also, was the 16-pixel-wide update area chosen for its simplicity or due to cycle limitations?

by on (#78396)
The first demo implemented everything but a scrolling engine. You can find it here. Bolting a scrolling engine onto that program was not an option as it used 8-bit positions.

I chose 16 pixel wide scroll updates due to simplicity. When the engine decides it needs to scroll to does the following actions on subsequent frames:

1. Fetch map data from main RAM into a couple of buffers for use later.
2. Generate the left-most column of tiles and stage them for the PPU udpate.
3. Generate the right-most column of tiles and stage them for the PPU update.
4. Update the attribute cache to reflect the new state, and copy it to the PPU staging buffer.

Another approach is to roll steps 1-3 into one frame and step 4 into a different frame. I think that is what Metroid is doing.

It was important for me to spread the CPU load over multiple frames to leave more room for collision detection. I know from experience that correct platformer collision detection (as in NOT what Castlevania uses) eats a lot of CPU time. I am hopeful I can squeeze in 45 degree slopes with 6 or 8 background-colliding objects, but that may not be feasible. Then there is enemy AI, object-to-object collision, object culling and spawning and a music engine that all need to run every frame.

One complication I have noticed is that in my previous game I found 6 pixels per frame to be the best maximum falling speed. That poses a problem as it breaks my scroller. I can compensate for that by making sure the player cannot fall more than two screens at once.

Yea, that might not work out so well. Anyway...
Going down, down in an earlier round
by on (#78397)
qbradq wrote:
One complication I have noticed is that in my previous game I found 6 pixels per frame to be the best maximum falling speed. That poses a problem as it breaks my scroller. I can compensate for that by making sure the player cannot fall more than two screens at once.

Or you can do what Bartman Meets Radioactive Man does and assume that if the player falls past the camera, the player has probably fallen past the body's ability to land safely.

by on (#78406)
qbradq wrote:
I know from experience that correct platformer collision detection (as in NOT what Castlevania uses) eats a lot of CPU time.


Spasms... how much time, approximately?

The handful of games I've played around with all seem to finish their MPL/rendering stuff by scanline 200 or sooner...

by on (#78590)
Wow,very smooth!
Congrats ;D
Now, It's going to be interesting thing.
I spoted a bug, though.
When you scroll to the right while holding A button and then, you change direction to Left, nametables will be still updated like you are holding right button.

by on (#78625)
Dr. Floppy wrote:
qbradq wrote:
I know from experience that correct platformer collision detection (as in NOT what Castlevania uses) eats a lot of CPU time.


Spasms... how much time, approximately?

The handful of games I've played around with all seem to finish their MPL/rendering stuff by scanline 200 or sooner...


I posted some numbers a while back but can't seem to find them. The player update was close to 6000 cycles due to input handling and complex animation. I think the base update (gravity, velocity and collision) worst-case was in the neighborhood of 1500 cycles. That does not sound so bad until you consider that you will probably want six or eight such objects on screen at once. And then there is object to object collision to think about.

Denine wrote:
Wow,very smooth!
Congrats ;D
Now, It's going to be interesting thing.
I spoted a bug, though.
When you scroll to the right while holding A button and then, you change direction to Left, nametables will be still updated like you are holding right button.


Yup, the scrolling breaks when you hit 88 miles per hour. Replace your flux capacitor and try again.

by on (#78691)
qbradq wrote:
I think the base update (gravity, velocity and collision) worst-case was in the neighborhood of 1500 cycles. That does not sound so bad until you consider that you will probably want six or eight such objects on screen at once. And then there is object to object collision to think about.


Thank you; this is very informative! (I shall count among my blessings my game taking place entirely underwater, where gravity is effectively absent.)

What's your preferred format for player-block collision detection?

by on (#78697)
My perferred method is to find the left and right extents of the bounding box, then check the bounding flags for each tile along the bottom if moving down, or along the top if moving up. A similar method is used for latteral movement.

I use an in-RAM tile map that has the collision flags pre-calculated to speed things up.

Just remeber the order of opperations:

Apply Y velocity
Do Y collisions
Apply X velocity
Do X collisions

If you try to do both updates at once things go sideways on you.

Good luck writting this piece in hex ;)

by on (#78859)
qbradq wrote:
My preferred method is to find the left and right extents of the bounding box, then check the bounding flags for each tile along the bottom if moving down, or along the top if moving up. A similar method is used for lateral movement.

I use an in-RAM tile map that has the collision flags pre-calculated to speed things up.

Just remember the order of operations:

Apply Y velocity
Do Y collisions
Apply X velocity
Do X collisions

If you try to do both updates at once things go sideways on you.

Good luck writing this piece in hex ;)


Thank you much! Truth be told, I enjoy programming in hex... I must be a purist at heart. :mrgreen:

Is collision detection typically done on a 16x16-pixel "block" basis, or an 8x8-pixel tile basis?

by on (#78861)
You usually take a object and you create a X by Y box with a X by Y margin, and you can put a hit rectangle anywhere you want on your object.