Slopes

Slopes replace the hard tile edges with diagonal surfaces the player slides up and down. The visual is a triangle tile; the physics are a surface height that changes linearly across the tile’s width.

Loading editor…

Slope Tile Types

Only two diagonal variants are needed to build any terrain shape:

SLOPE_UP  (value 2, / shape): bottom-left corner to top-right corner
SLOPE_DOWN(value 3, \ shape): top-left corner to bottom-right corner

In PixiJS v8, draw these as filled polygons using .poly(). The coordinate list is [x1, y1, x2, y2, x3, y3], relative to the tile’s top-left corner:

const TS = TILE_SIZE;

// SLOPE_UP /  — bottom-left, top-right, bottom-right
new PIXI.Graphics().poly([0, TS, TS, 0, TS, TS]).fill(0x5A8A3A);

// SLOPE_DOWN \ — top-left, bottom-right, bottom-left
new PIXI.Graphics().poly([0, 0, TS, TS, 0, TS]).fill(0x5A8A3A);

Surface Height Calculation

The surface Y at a given pixel X within a slope tile changes linearly from one corner to the other. Given localX = x - (col * TILE_SIZE):

function slopeSurface(x, y) {
    const col = Math.floor(x / TILE_SIZE);
    const row = Math.floor(y / TILE_SIZE);
    const t   = map[row][col];

    if (t !== SLOPE_UP && t !== SLOPE_DOWN) return null;

    const localX      = x - col * TILE_SIZE;
    const tileBottomY = (row + 1) * TILE_SIZE;

    // SLOPE_UP (/): left end at tile floor, right end at tile ceiling
    if (t === SLOPE_UP)   return tileBottomY - localX;

    // SLOPE_DOWN (\): left end at tile ceiling, right end at tile floor
    if (t === SLOPE_DOWN) return tileBottomY - (TILE_SIZE - localX);
}

At localX = 0 on a SLOPE_UP tile, surfaceY = tileBottomY — floor level. At localX = TILE_SIZE, surfaceY = tileTopY — ceiling level. Values in between are proportional, giving the smooth slope.

Collision: Flat-to-Slope Transition

The standard falling check compares the player’s next foot position against solid tiles. Slopes require an additional step: if no solid floor is found, look for a slope surface at foot level. Also check one tile-height above the foot to handle the transition from flat floor onto the base of a slope:

if (player.velocityY >= 0) {
    const footX    = player.x + player.width / 2;
    const newFeetY = player.y + player.height + player.velocityY;

    // 1. Solid floor takes priority
    if (isSolid(footX - 2, newFeetY) || isSolid(footX + 2, newFeetY)) {
        player.y         = Math.floor(newFeetY / TS) * TS - player.height;
        player.velocityY = 0;
        player.onGround  = true;
    } else {
        // 2. Slope at foot level; or one tile up (flat→slope transition)
        let surf = slopeSurface(footX, newFeetY);
        if (surf === null) surf = slopeSurface(footX, newFeetY - TS + 1);

        if (surf !== null && newFeetY >= surf) {
            player.y         = surf - player.height;
            player.velocityY = 0;
            player.onGround  = true;
        } else {
            player.y       += player.velocityY;
            player.onGround = false;
        }
    }
}

The slopeSurface(footX, newFeetY - TS + 1) call checks whether there is a slope tile in the row immediately above the foot’s current tile. Without it, a player walking from a flat floor onto the bottom of a slope would briefly “fall through” by one frame because the foot-level tile is still the flat floor, not the slope.

Next: Scrolling