Demo:
Same base engine used in Bullet Ball:
This platformer base code is what my game Bullet Ball is built on. It is inspired by Super Mario Bros for the NES. This demo contains code for Super Mario style variable jumping, AABB collision (1 way platforms or full collision), and a very simple animation function.
Like many newbies, I struggled with platforming when I was learning to program and always found that my code was bloated with many unnecessary variables. I still see many newbies struggling with this. I'm also seeing many demos with verbose code and needless multiple variables.
Programming is difficult and optimizing even more so, but as a rule of thumb, you always want to have efficient, optimized code that's clear to read and understand. By efficiency, you want to use as little computer power as you can. Even though with today's ultra blazing fast processors, it seems not necessary, it's a good practice, because it also helps you simplify. It takes years to get there, but you learn.
The magical element in my code is the use of lookup tables for the use of movement and position for collision detection. Super Mario Bros used lookup tables as using mathematical equations (especially multiplication and division) would bog down the program on the NES. The NES ran at 1.79MHz with very limited ram. NES programmers had to optimize hardcore to get their games to run the way they wanted them too. SMB is a true marvel. In my own movement code, I only increment, decrement and use modulus (same as division, but the result is the remainder) for friction.
So, a quick look at the lookup tables and an explanation for them. Following that in the hidden tag is commented code. It is aimed at newbies with little experience coding. The code in the demo above is condensed down to 87 lines (sans the 2 dividing dashed lines) and can be condensed a bit more if you take out the block for 1-way platforms.
This is the speed look up table. This is used for both horizontal running and vertical jump movement.
speed={-4,-2,-1,-1,0,1,1,2,4} |
By using a counter variable that can increment or decrement, I can then add the result to my x or y position and voila.
For example, if you wanted the player to just run right at top speed:
hv=9 --hv is horizontal velocity x+=speed[hv] |
So by simply changing the hv variable, I can make the player not only move left or right, but also immitate acceleration and drag. Notice how the sets of numbers count up on both sides of 0. Look at the movement function in the code below to see how this all works.
For gravity, using the speed table is super handy. If vv (vertical velocity) is less than 5, my player will move up (jumping), but go past 5 and the player starts to fall. So in my movement code, I have:
if(vv<9) vv+=1 |
This line runs no matter what. When holding down my jump button, vv is set to 1 and will be reset at 1 every tick until either my jump boost (see code below) runs out or I let go before it runs out, so that the player will move up. When I let go or my jump boost has hit max, then the line above is free to count up vv. This causes an arc movement and then the player falls down and will continue to move down unless stopped by a platform. Gravity.
My 2nd look up table is simply a table of multiples of 8:
for i=-1,16 do
tile[i]=i*8
end |
This table helps in detecting local collision around the player. Refer to the code below to see how it works.
Feel free to use the code as a base for your projects. Be careful if you tinker with the values within the speed table. Collision can be easily thrown off and you might end up inside or going through blocks. I'm not easily available, but I'll answer any questions best I can, when I can. I'm also of the "there are no stupid questions" policy. Ask anything.
You can also look at the code in Bullet Ball to see more platformer functions and to see how my code works with the entire map space. At the moment BB's code is uncommented, but I am putting together a post-mortem post that will contain commented code.
FULL COMMENTED CODE:
indeed solid basis.
comments:
- vertical speed is too floaty imho, suggest a dedicated vertical speed table
- too many single letter globals, this is very error prone in a larger codebase
- use functions with parameters to handle collision, again to allow multiple entities without relying on globals
- platform collision is a bit awkward as it doesn’t take velocity nor player size into account (something that will trip beginners)
o
@LawrenceJunior thank you. :)
@freds72 Thanks for your feedback: I'll address each point you brought up:
1.Yes a vertical speed table can be implemented, but collision detection might need to be altered a bit to keep from falling through or into a block. Usually when the player falls down, the position is always multiples of 4 along tile axis'. This works perfect with the current collision detection.
2.For the purpose of a small demo, x and y for example, being separate single letter variables isn't going to hurt. For newbies reading this response, you can make a player object like this:
player={x=60,y=88,xdir=false} |
Then if you're updating x in the code, you would instead use
player.x+=speed[hv] |
I could have done this in the demo code, but for learning sake, I try to keep it simple and easy to read and understand.
3.I do this in Bullet Ball. Again, in the demo, just for simplicity sake.
You do have me thinking that perhaps I should try a Youtube tutorial or tutorial series. Then I could build a whole project, show potential pitfalls and what to do to avoid them.
4.I'm a bit confused by the last comment. Do you mean that it doesn't feel like there is friction when coming in contact with a platform? I have a little friction function that can be altered by lowering the "surface" value. I have it currently set to 2 and it allows a small bit of sliding on the platform - like Mario when running at full speed. There's 2 things that could be done there: when coming into contact with a tile - set surface to 1 (but I found that it felt more sticky). OR if moving slowly, surface can be lower, but if moving at full speed, surface can be knocked up 1. Not sure how much that would help though. When I designed this particular platformer base, it was meant for fast action movement.
The bounding box for the player is the same size as tiles (since, the original characters are almost square):
|
|
[0x0] |
|
|
[0x0] |
|
|
[0x0] |
|
|
[0x0] |
The Mario character is smaller and I might just update the code to give him a smaller bounding box.
Thank you again. :)
Hi @tjbynum, so I whipped up a quick image for ya. The following works for axis aligned bounding boxes:
I'm just explaining the X-axis here, but this is how Y works as well. It can get confusing when the X,Y coordinate of any sprite in Pico-8 is the top-left pixel. This is the way the map works as well. The map starts at 0,0 at the upper left corner.
When you give your character a bounding box, it will depend on how you position it. In the case of my chonky Mario character, the standard bounding box starts at X and goes to X + 7 (7 more pixels to the right totaling 8 pixels). So I don't have to position it, it's already centered. In the next example I shrunk the bounding box down to 6 wide and 7 high. To center it, I go by the X coordinate again, but add 1 on the left side and take away 1 on the right. So now it starts at X+1 and ends at X+6. The top of the sprite is also shrunk down 1 pixel, so the top starts at Y+1.
Let's say you have a small character that's only 4 pixels wide. You could center by X+2 to X+5 or you could draw the character to the left side of the 8x8 sprite space and set the bounding box at X to X+3.
If you look at the lines of code above, chonky mario can run back and forth on the block, and as long as the edges of the bounding boxes touch, he'll stay on top of the block. I hope this helps. :)
@tjbynum You're very welcome. Another trick you can do (can also be done digitally in paint.net for instance), is to cut out pieces of graph paper where each grid square represents a pixel. Draw your sprite on one and the other one will be the object or block you want to figure out your collision for. Then on your sprite, draw your bounding box on the grid lines. Then you can position them around and see where lines and corners intersect. Game design almost always involves you making yourself tools that help you figure things out. Sometimes the tools are very personalized. I've known designers that will even code their own map makers, so that it's more intuitive for them.
One thing to remember with collision, if you're just doing AABB collision, it's best that the blocks/tiles/objects you collide with use their full size bounding box. So in the case of the platformer demo above, the platform blocks are all 8x8. If you shrink the bounding box for blocks/tiles, you might end up producing passable gaps, if they are lined up in rows or columns. If you're doing an RPG and you have trees that you want to be able to pass between, then you can give them a tiny bounding box (or none if you do old school overworld).
I wish you the best with your project. And remember, 90% of your coding will be just problem solving, so don't let it get you down. Struggling is part of learning.
Thanks for making and sharing this! It’s nice to have something with all the basics working, and it’s small enough to be adapted (say if I wanted to avoid global variables or rename the functions, easy to do myself).
For a future game I have looked at another platformer lib (by eniko, https://www.lexaloffle.com/bbs/?tid=35821) which has a nice feature: slopes! Maybe you’re interested in adding support for these. Or maybe not and it will be a challenge for me!
@Snow_ thanks again, i think i'm getting the hang of it slowly but surely. i was having issues with my sprite "bumping" his head, but i think the help you've given will set me on the right path. thanks again for sharing with the rest of us.
@merwok yep, i've seen that and i like it. this is also really slick. https://github.com/jamesedge/pico8-physics
Well you know, I've been wanting to do slopes. I could approach the method the same way I'm using look up tables - using a sub-flag system, detect which block my sprite is coming in contact with. Then adjusting the hv and vv values to make it seem like the sprite is colliding with the slope (no matter at which point on the slope). The other method would be to try and come up with a compact polygon collision detection function.
I'm intrigued. I'll test out these methods. In the meantime, @merwok I do encourage you to try your own solution as well.
I think I'll be adding more demos to this thread for sure to make it a more complete beginner's platformer base. I had forgot to add wall jumping to the demo above (which is super easy in my code), so I'll add that to a second demo.
If I do make a demo with slopes, I'm only going as far as 3 or 4 angles per left or right direction. I'm not going to do curves (like Sonic levels), as that's more advanced and not beginner friendly. I'd say, I'll go as far as the mechanics of Super Mario Bros 3.
@tjbynum that physics lib has very impressive demos!
@Snow_ right, I’d be very happy with just one angle at 22.5° (so that two tiles bring you one tile up, see that other thread if I’m not clear). but now that you’ve said 3 or 4 angles I see the potential for variety in level design and nicer transitions, and it could be interesting to write that code!
[Please log in to post a comment]



