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).