[ANN] the beginning of a platformer

Hi,
Here is the beginning of a platformer game that I made for my 3 years old son:
http://decapode314.free.fr/ant/platform7.3/platform.html

If the avatar falls into the water it changes its color, I made this because it was easy to do and it made my son laugh.

On my computers MariOCaml is slow, so I was happy to see that my version is not slow, even with a bigger canvas size. It’s not slow even on a modest laptop.

I didn’t found the algorythm by myself, I found it in an article but I didn’t kept the link. I should have kept it, so that I could have cite it in the source code.

This algorithm is very simple, it simplifies the process with 2 loops, one for the Y axis, and another one for the X axis, so the move of the avatar is not a real diagonal, but it’s not visible when we play.

Also it uses int calculations, so that it matches the pixel of the screen, and the pixel of the platform that it collides.

I don’t know how MariOCaml’s physics works, I tried to read the source code to locate the physics, but I haven’t been able to find it.

There is curently only one screen in the level. I would take any advice about how to make a level that is larger than one screen, which algorithm to use to check collisions with only the nearest blocks.

9 Likes

I am also working on a platformer with ocaml, and the early versions of my code looked a lot like your platform.ml! It has grown into this monstrosity, which is ~10k lines of code now.

This algorithm is very simple, it simplifies the process with 2 loops, one for the Y axis, and another one for the X axis, so the move of the avatar is not a real diagonal, but it’s not visible when we play.

I don’t know how MariOCaml’s physics works, I tried to read the source code to locate the physics, but I haven’t been able to find it.

My code for updating position is here and it basically does the same thing that your code does - applies the current velocity, accelerates y for gravity, and adjusts for collisions. There are a few differences though:

  • It multiplies the velocity by dt, which is the “time elapsed for this frame”. This ensures the entity moves at a constant rate if the framerate changes.
  • Instead of checking x and y collisions separately, it moves the entity in both directions and then adjusts the position based on the direction that the entity collided from. That is calculated here, but it doesn’t work perfectly when colliding with corners.

There is curently only one screen in the level. I would take any advice about how to make a level that is larger than one screen

I am using Raylib (through the raylib-ocaml library), which has the concept of a “camera” - basically “which screen-sized subset of the room is visible now”. If you don’t care about supporting zoom or rotation, it’s just an offset x/y that you need to keep track of. So everything in the room would have an “absolute” position, and after applying the camera offset to the absolute positions, you know what is on-screen. You would update the camera offset based on the smiley position - there are examples of different cameras here.

which algorithm to use to check collisions with only the nearest blocks

I looked this up a while ago because I was worried about this too, and “spatial partitioning” may be the search term you’re looking for. Instead of keeping all collidable blocks in an array, you divide them into “regions”. Then you only need to check blocks that are in the same region as the smiley. It’s a little more complicated than that because you need to handle blocks and the smiley being in multiple regions (although you can define regions so that blocks can’t be in more than one).

I ended up solving this in a lazier way though - instead of having one collision per floor tile, I manually define a single rectangle to cover groups of tiles. I am using the Tiled map editor to make my maps, so I use tile layers for rendered tiles, then use an object layer to draw rectangle objects that define collisions. The largest room (the abyss climb) has ~200 rectangles, and I check them all every frame because it’s not even close to being a performance bottleneck (profiled with the landmarks library).

4 Likes

but it doesn’t work perfectly when colliding with corners.

I also used this method in previous tests/attemps and also got problems with corners. If you switch to the method I use you will not have this problem anymore.

Thank you for your advice to group collidable things in regions, I will follow your advice.

I would also recommand you to group all the code that depends on raylib, so that it will be easier later if you or some else decide to switch to an html/canvas front-end.