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