My Platformer Framework

A place where you can keep others updated about your NES-related projects through screenshots, videos or information in general.

Moderator: Moderators

Post Reply
User avatar
wonder
Posts: 26
Joined: Sat Aug 31, 2019 2:12 pm

My Platformer Framework

Post by wonder » Tue Sep 17, 2019 2:40 am

Hi!

Based on my Physics Demo, I've been writing an object oriented platformer framework in C.
My goal is to achieve the highest possible performance while at the expense of having a high level of human-readable and maintainable code.
It's too early to open-source it, but I will do so as soon as it's stable enough.

Image Image Image

Please test it out and let me know how the controls feel... :)
The collision detection is not perfect yet, but I hope there won't be any problems.

PS: I did the sprites as well. :mrgreen:

Roadmap:

Code: Select all

[X] Object-oriented code
[X] Text-based game menus
[X] Collision detection with simplified physics
[X] Smooth player movement
[X] Sprite collision detection
[X] Animated sprites
[ ] Enemy AI
[ ] Boss Fights
[ ] Text engine
[ ] Menus
[ ] Background scrolling
[>] Support metatiles and metasprites
Controls: L/R Move, A Jump, B Run

The latest version features:
- Player movement
- Sprite animation
- Actor/background collision detection
- Actor/actor collision detection
Attachments
platformer4.nes
Work in progress
(288.02 KiB) Downloaded 16 times
Last edited by wonder on Sat Jan 25, 2020 10:49 am, edited 3 times in total.
Image

User avatar
gauauu
Posts: 673
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Unnamed Platformer Framework

Post by gauauu » Tue Sep 17, 2019 8:27 am

Good work so far!
Please test it out and let me know how the controls feel...
The jump physics do feel a little odd to me. Maybe a little too fast vertically in comparison to the horizontal velocity? I'm not sure.

pwnskar
Posts: 112
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: My Platformer Framework

Post by pwnskar » Thu Sep 19, 2019 10:16 am

This looks really cool! Have you found any issues in performance or rom space developing in C as opposed to assembly?

How do you do your collision detection? With so many objects and platforms on screen, the fact that it stays at 60 fps is what I find most impressive. :)

BTW, How do you even debug your C code? I've only ever coded for NES in assembly..

User avatar
wonder
Posts: 26
Joined: Sat Aug 31, 2019 2:12 pm

Re: Unnamed Platformer Framework

Post by wonder » Sat Sep 21, 2019 4:18 am

gauauu wrote:Good work so far!
The jump physics do feel a little odd to me. Maybe a little too fast vertically in comparison to the horizontal velocity? I'm not sure.
Thanks!! Indeed, I'm still not fully satisfied with the current velocity/acceleration values, there's still a lot of room for improvement. :)

pwnskar wrote:This looks really cool! Have you found any issues in performance or rom space developing in C as opposed to assembly?
Thank you so much! I'm really glad you like it! Well, I've never coded in assembly so I'm not sure how much of a performance hit is the fact that I am writing it in C.
Nevertheless, I believe my C code is as tight as it can possible be, while keeping an high level of abstraction.
pwnskar wrote:How do you do your collision detection? With so many objects and platforms on screen, the fact that it stays at 60 fps is what I find most impressive.
It took me around one month to come-up with this algorithm and make it work fast in the NES. Normally I would use sin(), cos() and atan2(), but since the <math.h> is not really available in this context, I had to wrap my head around it and come up with an entirely different solution. I'm attaching my collision detection module, but bear in mind that it's still a work in progress.
pwnskar wrote:BTW, How do you even debug your C code? I've only ever coded for NES in assembly...
Well, first of all, I wrote my own printf(), so I can do stuff like print_at(3, 3, "Vx: %d", actor.vx);.
Second, I always check the generated assembly code in the compiler listings (both locally and on 8bitworkshop).
Finally, for benchmarking, I'm using the methods suggested in this thread: How can I benchmark my C code?
Attachments
collision.c
(7.01 KiB) Downloaded 270 times
Image

pwnskar
Posts: 112
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Unnamed Platformer Framework

Post by pwnskar » Sat Sep 21, 2019 12:39 pm

wonder wrote:It took me around one month to come-up with this algorithm and make it work fast in the NES. Normally I would use sin(), cos() and atan2(), but since the <math.h> is not really available in this context, I had to wrap my head around it and come up with an entirely different solution. I'm attaching my collision detection module, but bear in mind that it's still a work in progress.
Thanks for sharing the attachment! I had a look at it but I'm ashamed to admit most of it went above my head! :D Will try to read through it some more though, as I'm sure there's lots I can learn from it.

Do each entity check it's collision against whatever tiles are loaded into the nametables behind them? Is that what you're doing when using your P2B() function? Or do you keep a buffer of collision boxes that gets updated with every nametable change?

I do a buffer kind of solution myself, but I only load in platforms that you can land on. For collision between entities I think maybe we're doing some things similarly. What I do is I take the coordinates for the collision box of entity_1, add all the sides to the collision box of entity_2, but mirrored and flipped upside down, then I compare the result of that to the pivot (or "hot spot" if you will) of entity_2. The result would be the same as comparing all corners of entity_1 to each side of entity_2 to see if they intersect, but this way I only check one point instead of four.

Code: Select all

Screen space collision boxes:
(p = pivot)
                        +-------------+
       +----------+     |  entity_2   |
       | entity_1 |     |       :     |
       |     :    |     |       :     |
       |-----p----|     |-------p-----|
       |     :    |     +-------------+
       +----------+
-------------------------------------------

Screen space collision boxes added together:

+------------------------+ entity_2 pivot
|     +----------+       |      |
|     | entity_1 |       |      |
|     |     :    |       |      V
|-----|-----p----|-------|      p
|     |     :    |       |
|     +----------+       |
|                        |
|  enity_1 padded with   |
| dimensions of entity_2 |
+------------------------+

-------------------------------------------

Does entity_2's pivot overlap the combined collision box?
Anyways, since you shared so much of your code I thought I'd share the principle of mine. The actual assembly code is even more confusing even to myself, so I'll spare you that.. :)

I'm looking forward to see how your engine comes along!

Cheers! :beer:

User avatar
wonder
Posts: 26
Joined: Sat Aug 31, 2019 2:12 pm

Re: My Platformer Framework

Post by wonder » Sun Sep 22, 2019 2:16 am

Hello again!
Do each entity check it's collision against whatever tiles are loaded into the nametables behind them?
Yes, but I'm not using the nametable itself. I'm using a grid object char matrix[x][y] = {{...}, {...}, ...};.
Is that what you're doing when using your P2B() function?
P2B means "pixel-to-block", it gives me the result of the given pixel-coordinate divided by 8 (block size).
Or do you keep a buffer of collision boxes that gets updated with every nametable change?
I'm not sure what you mean, but I'm guessing that the answer is no.

I've drafted a PDF on which I try to explain my reasoning in more detail, see attached. :)
Attachments
Collision detection algorithm on the NES.pdf
(129.67 KiB) Downloaded 260 times
Image

pwnskar
Posts: 112
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: My Platformer Framework

Post by pwnskar » Sun Sep 22, 2019 9:17 am

wonder wrote:
Or do you keep a buffer of collision boxes that gets updated with every nametable change?
I'm not sure what you mean, but I'm guessing that the answer is no.
Perhaps buffer wasn't the right word. What I meant was if you were keeping a representation of all the collision boxes in RAM and then update them accordingly whenever there's a change to the nametables. If your char matrix[x,y] is kept in RAM, then this would be that. But I'm guessing you're keeping it in ROM, since it should need 960 bytes for each full screen and keeping it in RAM would take up almost half the available RAM on NROM.

User avatar
wonder
Posts: 26
Joined: Sat Aug 31, 2019 2:12 pm

Re: My Platformer Framework

Post by wonder » Sun Sep 22, 2019 1:28 pm

pwnskar wrote:If your char matrix[x,y] is kept in RAM, then this would be that. But I'm guessing you're keeping it in ROM, since it should need 960 bytes for each full screen and keeping it in RAM would take up almost half the available RAM on NROM.
Yep! That's right, I'm keeping it in ROM (in C all I had to do was declare it as const char). :)
Image

User avatar
wonder
Posts: 26
Joined: Sat Aug 31, 2019 2:12 pm

Re: My Platformer Framework

Post by wonder » Sat Nov 09, 2019 5:06 am

New version:
Image
Note: The gif framerate is sped-up.
The game runs at 60fps, nonetheless.


So I managed to achieve a huge performance upgrade by writing a customized unrolled memcpy in assembly.
While I recognize that using plain arrays (or an struct of arrays) is super-fast, using this method on an array of structs turns out to be really fast as well.

It makes me really happy being able to write this framework in C while maintaining an object-oriented approach without sacrificing a lot of performance. :mrgreen:
The rom is available on the first post. :)

Algorithm:
- Size of actor is 16 bytes
- Actor group is an array of structs
- Load actor index and multiply it by 16 to get the offset from the beginning of the array
- Copy the data in the structure into a temporary working object called t_actor
- Working on t_actor is super fast!!

Code: Select all

#define SELECT_ACTOR_NUMBER(index, group) { \
  t_actor_index = index; \
  __asm__("lda %v", index); \
  __asm__("asl a"); \
  __asm__("asl a"); \
  __asm__("asl a"); \
  __asm__("asl a"); \
  __asm__("tay"); \
  __asm__("lda %v+0, y", group); \
  __asm__("sta %v+0", t_actor); \
  __asm__("lda %v+1, y", group); \
  __asm__("sta %v+1", t_actor); \
  __asm__("lda %v+2, y", group); \
  __asm__("sta %v+2", t_actor); \
  __asm__("lda %v+3, y", group); \
  __asm__("sta %v+3", t_actor); \
  __asm__("lda %v+4, y", group); \
  __asm__("sta %v+4", t_actor); \
  __asm__("lda %v+5, y", group); \
  __asm__("sta %v+5", t_actor); \
  __asm__("lda %v+6, y", group); \
  __asm__("sta %v+6", t_actor); \
  __asm__("lda %v+7, y", group); \
  __asm__("sta %v+7", t_actor); \
  __asm__("lda %v+8, y", group); \
  __asm__("sta %v+8", t_actor); \
  __asm__("lda %v+9, y", group); \
  __asm__("sta %v+9", t_actor); \
  __asm__("lda %v+10, y", group); \
  __asm__("sta %v+10", t_actor); \
  __asm__("lda %v+11, y", group); \
  __asm__("sta %v+11", t_actor); \
  __asm__("lda %v+12, y", group); \
  __asm__("sta %v+12", t_actor); \
  __asm__("lda %v+13, y", group); \
  __asm__("sta %v+13", t_actor); \
  __asm__("lda %v+14, y", group); \
  __asm__("sta %v+14", t_actor); \
  __asm__("lda %v+15, y", group); \
  __asm__("sta %v+15", t_actor); \
}

#define UPDATE_ACTOR_NUMBER(index, group) { \
  ... \
}
// The same as above, but in reverse.
Image

sugarnaut
Posts: 1
Joined: Sun Jan 19, 2020 4:55 pm

Re: My Platformer Framework

Post by sugarnaut » Sun Jan 19, 2020 4:58 pm

I'm a new dev, doing as much research as I can into NES dev, and this seems like just the sort of thing I'd love to have a look at! Any updates?

User avatar
wonder
Posts: 26
Joined: Sat Aug 31, 2019 2:12 pm

Re: My Platformer Framework

Post by wonder » Fri Jan 24, 2020 10:26 am

Hi!

Yes, I'm still actively working on it. There's still a lot to do, I would say the current progress is around 30%-40%.

Here's the current build. :beer: :D
Attachments
platformer4.nes
(288.02 KiB) Downloaded 21 times
Image

Post Reply