Category: Uncategorized

  • Devlog 4: Colliders Part 2 (Tilemap Cont’d)

    So that was a doozy to figure out. I never really considered that every single entity might have different reactions to collisions, especially if there’s not a true “physics” system. I don’t have any interest in making a physics engine for this game, everything can be handled at the object level.

    SO COLLISIONS. Let’s look at some of the code.

    Tilemap Check

    function TileMap:getTileAt(point)
        local row = math.floor((point.y - self.__origin.y) / self.__cell_size) + 1
        local col = math.floor((point.x - self.__origin.x) / self.__cell_size) + 1
    
        local cell_origin = Point(
            (col - 1) * self.__cell_size + self.__origin.x,
            (row - 1) * self.__cell_size + self.__origin.y)
    
        if self.__debug_draw then
            gfx.drawRect(
                cell_origin.x, cell_origin.y,
                self.__cell_size, self.__cell_size)
        end
    
        return self[row][col], cell_origin
    end

    This makes the tile a big target. Say our tile size is 8×8, that gives 64 different points that will return the same tile. Simple arithmetic, super fast. You can see I made a flag called “debug_draw” that will come in handy in a minute. That lets me see what tiles are being requested by any entity. It just draws an empty rectangle if that tile is polled.

    Points to check

    Each object is going to have slightly different rules, but lets look at something that might be a character or an enemy. I want to check for x movement, y movement, and then x/y movement. What do I mean by this?

    The movement above is exaggerated, we’re typically moving just a few pixels each frame. So I loop through the vertical line, and place points uniform tile-length apart. Then move those points in the direction of the velocity, and then remove points that wouldn’t be in the “green”

    Then loop through the horizontal, place points uniformly, move, cull what wouldn’t be magenta.

    Finally just take the velocity distance from the “moving” corner.

    When I do a final write up I’ll make this all much prettier with equations and stuff, but hopefully that makes sense.

    Resolving a Collision

    So we do our check. There’s 4 possible outcomes.

    • There was no collision, move the object.
    • There was a collision on the x-axis, wait to resolve
    • There was a collision on the y-axis, wait to resolve
    • There was no collision on the x or y axis and there was a diagonal collision.

    If there will be a collision on the x axis, for this example we got a little bit of work to do. We need to find the distance between our object and our tile. This has two possible collisions was the object moving left, or right? If it’s moving left, and our origin is in the top left, we need to move

    x of the tile origin + width of the tile – current x (before moving)

    tile origin = (10, 10)
    tile width = 8
    object origin = (20, 20)
    object width = 16
    object speed = 5
    
    10 + 8 - 20 = 2
    
    We need to move only 2 pixels to the left, not 5

    if we’re moving right…

    x of the tile origin – width of the object – current x (before moving)

    tile origin = (20, 20)
    tile width = 8
    object origin = (2, 2)
    object width = 16
    object speed = 5
    
    20 - 16 - 2 = 2
    
    We need to move only 2 pixels to the right, not 5

    The same logic applies for y movement. The real key is to resolve the collisions AFTER you check. Don’t move X then check Y. Check one, check the other, and then resolve.

    If there is a diagonal collision, apply both the X and the Y logic.

    If you want to peek at the code, this is the specific folder in the repository: https://github.com/lodomo/PlaydateResources/tree/main/Colliders/TileMapPart2b

    So far this only handles solid vs not-solid blocks. If there were say “spikes” that whole detection, resolve loop would need to be changed. This has turned out to be much trickier than I anticipated. The cool thing though is that it works.

    Here’s a little recording I did of it in action. The barriers are placed at random every time the program starts so I could test it.

    Legend:

    Dots: Where the player is polling to move
    Hollow Squares: Activated Tiles
    Solid Squares: Barriers
    Big Square: Player

    I think I need to turn this into a nice formal lesson/video/code along thing. This is mostly just a stream of consciousness again.

    Until Next Time,

    Lodomo