Clouds

Cloud platforms are one-way surfaces: the player can jump through them from below, pass through them from the sides, but lands on top when falling. This tutorial adds that behaviour to the jumping system from the previous tutorial.

Loading editor…

How Cloud Platforms Work

A solid tile blocks movement from every direction. A cloud tile applies collision only when the player is falling downward: the player can jump through from below, pass through from the sides, but lands on top when descending.

This selective collision is what creates the layered platform structures common in platformers — the player can reach higher platforms from below without needing a separate entry path.

Normal solid wall blocks hero from all directions

Cloud platform allows entry from sides/below, stops from above

Tile Types

Define tile type constants rather than sprinkling literal numbers through the code:

// Define tile types as constants
const TILE_TYPES = {
    EMPTY: 0,
    SOLID: 1, 
    CLOUD: 2
};

const tileProperties = {
    [TILE_TYPES.EMPTY]: {
        walkable: true,
        solid: false,
        cloud: false
    },
    [TILE_TYPES.SOLID]: {
        walkable: false,
        solid: true,
        cloud: false  
    },
    [TILE_TYPES.CLOUD]: {
        walkable: true,    // Can move through from sides
        solid: false,      // Not solid like normal walls
        cloud: true        // Special cloud behavior
    }
};

// Helper functions for tile checking
function getTileType(x, y) {
    const col = Math.floor(x / TILE_SIZE);
    const row = Math.floor(y / TILE_SIZE);
    
    if (row < 0 || row >= map.length || col < 0 || col >= map[0].length) {
        return TILE_TYPES.EMPTY;
    }
    
    return map[row][col];
}

function isCloudTile(x, y) {
    return getTileType(x, y) === TILE_TYPES.CLOUD;
}

function isSolidTile(x, y) {
    return getTileType(x, y) === TILE_TYPES.SOLID;
}

Helper functions (isCloudTile, isSolidTile) keep the collision code readable, and adding a new tile type is a matter of adding one entry to TILE_TYPES and one to tileProperties.

Cloud Platform Collision

A cloud tile is only solid when the player is falling onto it from above:

function canLandOnCloudPlatform(player) {
    // Only check when player is falling down
    if (player.velocityY <= 0) {
        return null; // Not falling, can't land
    }
    
    // Check both feet positions
    const leftFootX = player.x + 5;  // Slight inset from edge
    const rightFootX = player.x + player.width - 5;
    const feetY = player.y + player.height + 1; // Just below player
    
    // Are either feet touching cloud platforms?
    const leftOnCloud = isCloudTile(leftFootX, feetY);
    const rightOnCloud = isCloudTile(rightFootX, feetY);
    
    if (leftOnCloud || rightOnCloud) {
        // Calculate exact platform surface
        const platformRow = Math.floor(feetY / TILE_SIZE);
        const platformY = platformRow * TILE_SIZE;
        
        // Only land if falling onto platform from above
        if (player.y + player.height <= platformY + 5) {
            return platformY; // Return platform surface Y position
        }
    }
    
    return null; // No valid cloud platform landing
}

The three conditions for landing: velocityY > 0 (falling, not rising), the player’s feet are at the platform’s tile row, and the player’s feet are at or above the platform surface (not already embedded inside it). Checking both feet handles the case where the player is partially over the edge.

Integrating Cloud Collision

The collision function checks solid tiles first, then cloud tiles, then ceiling. Cloud tiles are never checked for upward movement:

function checkCollisions() {
    // Normal solid tile collision first
    if (player.velocityY > 0) { // Falling
        const solidGroundHit = checkSolidGroundCollision();
        if (solidGroundHit) {
            landOnSurface(solidGroundHit);
            return;
        }
        
        // Check cloud platform collision
        const cloudLanding = canLandOnCloudPlatform(player);
        if (cloudLanding) {
            landOnSurface(cloudLanding);
            return;
        }
    }
    
    // Ceiling collision — cloud tiles never block upward movement
    if (player.velocityY < 0) {
        const ceilingHit = checkCeilingCollision(); // Only solid tiles
        if (ceilingHit) {
            hitCeiling(ceilingHit);
        }
    }
}

function landOnSurface(surfaceY) {
    player.y = surfaceY - player.height;
    player.velocityY = 0;
    player.onGround = true;
}

Putting It All Together: Cloud Platform Movement

Now we need to update our movement and falling logic to handle cloud platforms correctly:

// In your main game loop
function updateMovementAndCollision() {
    handleInput();        // Process player controls
    updatePhysics();      // Apply gravity and velocity
    checkCollisions();    // Handle all collision types
    checkStillOnGround(); // See if player walked off platform
}

function checkStillOnGround() {
    if (!player.onGround) return; // Already falling
    
    // Check if still standing on solid ground OR cloud platform
    const leftFoot = player.x + 5;
    const rightFoot = player.x + player.width - 5;
    const groundLevel = player.y + player.height + 1;
    
    const onSolid = isSolidTile(leftFoot, groundLevel) || 
                   isSolidTile(rightFoot, groundLevel);
    
    const onCloud = isCloudTile(leftFoot, groundLevel) || 
                   isCloudTile(rightFoot, groundLevel);
    
    // If not on any platform, start falling
    if (!onSolid && !onCloud) {
        player.onGround = false;
        // Gravity will take over next frame
    }
}

The edge-detection check (checkStillOnGround) must test for cloud tiles as well as solid tiles, otherwise the player will float in mid-air after walking off a cloud platform. When neither foot is over a solid or cloud tile, player.onGround becomes false and gravity resumes.

Next: Ladders