Stupid Enemy

Time to add some DANGER to your world! ๐Ÿ‘พ A game without enemies is like a movie without conflict - technically possible, but nowhere near as exciting! You’re about to breathe life into your levels with patrolling enemies that turn peaceful exploration into heart-pounding challenge. Even “stupid” enemies can create incredible tension and satisfaction!

Controls: Arrow Keys to move, avoid the purple and turquoise enemies!
Try this: Watch how enemies patrol back and forth - simple but effective!

Why “Stupid” Enemies Are Actually Genius! ๐Ÿง 

Before you think your game needs super-intelligent AI, let’s talk strategy! Many legendary games use beautifully simple enemy patterns:

๐Ÿ”ด Pac-Man ghosts: Simple chase/scatter patterns that create complex emergent gameplay ๐Ÿฆ” Sonic badniks: Basic back-and-forth patrolling that’s predictable yet challenging
๐Ÿ„ Mario Goombas: Walk in straight lines, but placement makes them deadly ๐Ÿ‘พ Space Invaders: Move in formation - simple rules, intense gameplay

Why simple enemies work:

The wisdom: Players don’t need enemies to surprise them with complex decisions. They need enemies that create interesting spatial puzzles, timing challenges, and satisfying patterns to overcome!

Our Enemy Types

We’ll create two fundamental enemy archetypes:

๐Ÿ”„ Horizontal Patroller

โฌ†โฌ‡ Vertical Patroller

These simple behaviors combine to create surprisingly complex level challenges!

Setting Up Enemy Types: Modern Class Design

Let’s create a clean, flexible enemy system using contemporary JavaScript patterns:

// Enemy type definitions - easy to extend!
const ENEMY_TYPES = {
    HORIZONTAL_PATROL: {
        color: 0x8A2BE2,    // Purple
        moveX: 1,           // Moves right initially
        moveY: 0,           // No vertical movement
        speed: 1,           // Pixels per frame
        size: 10            // Square size
    },
    
    VERTICAL_PATROL: {
        color: 0x00CED1,    // Dark turquoise
        moveX: 0,           // No horizontal movement
        moveY: 1,           // Moves down initially
        speed: 1,
        size: 10
    },
    
    FAST_HORIZONTAL: {
        color: 0x32CD32,    // Lime green
        moveX: 1,
        moveY: 0,
        speed: 2,           // Twice as fast!
        size: 8             // Smaller but faster
    }
};

// Enemy spawn configuration for each level
const levelEnemies = {
    1: [
        { type: 'HORIZONTAL_PATROL', tileX: 2, tileY: 1 },
        { type: 'VERTICAL_PATROL', tileX: 8, tileY: 1 },
        { type: 'FAST_HORIZONTAL', tileX: 1, tileY: 5 }
    ],
    
    2: [
        { type: 'HORIZONTAL_PATROL', tileX: 3, tileY: 3 },
        { type: 'HORIZONTAL_PATROL', tileX: 6, tileY: 4 }
    ]
};

Why this approach rocks:

Spawning Enemies: Bringing Danger to Life

Let’s create a robust enemy spawning system:

// Active enemies array
const enemies = [];
let nextEnemyId = 0;

// Spawn enemies for current level
function spawnEnemiesForLevel(levelId) {
    // Clear existing enemies
    enemies.forEach(enemy => enemy.sprite.destroy());
    enemies.length = 0;
    
    // Get enemy spawn data for this level
    const spawns = levelEnemies[levelId] || [];
    
    spawns.forEach(spawnData => {
        createEnemy(spawnData.type, spawnData.tileX, spawnData.tileY);
    });
}

// Create individual enemy
function createEnemy(typeName, tileX, tileY) {
    const type = ENEMY_TYPES[typeName];
    if (!type) {
        console.warn(`Unknown enemy type: ${typeName}`);
        return;
    }
    
    // Create enemy sprite
    const sprite = new Graphics()
        .rect(0, 0, type.size, type.size)
        .fill(type.color)
        .stroke({width: 1, color: 0x000000}); // Black outline
    
    // Create enemy object
    const enemy = {
        id: nextEnemyId++,
        sprite: sprite,
        type: typeName,
        
        // Position (convert tile coords to pixels)
        x: tileX * TILE_SIZE + (TILE_SIZE - type.size) / 2,
        y: tileY * TILE_SIZE + (TILE_SIZE - type.size) / 2,
        width: type.size,
        height: type.size,
        
        // Movement properties
        moveX: type.moveX,
        moveY: type.moveY, 
        speed: type.speed,
        
        // State
        active: true
    };
    
    // Position sprite
    sprite.x = enemy.x;
    sprite.y = enemy.y;
    
    // Add to stage and tracking
    app.stage.addChild(sprite);
    enemies.push(enemy);
    
    return enemy;
}

Smart spawning features:

Enemy AI: Simple Brains, Effective Results! ๐Ÿค–

Time to give our enemies the intelligence to patrol and threaten the player:

// Main enemy AI update function
function updateEnemies() {
    enemies.forEach(enemy => {
        if (!enemy.active) return;
        
        // Calculate next position
        const nextX = enemy.x + enemy.moveX * enemy.speed;
        const nextY = enemy.y + enemy.moveY * enemy.speed;
        
        // Check for wall collision
        if (wouldHitWall(nextX, nextY, enemy.width, enemy.height)) {
            // Hit wall - reverse direction!
            enemy.moveX = -enemy.moveX;
            enemy.moveY = -enemy.moveY;
            
            // Optional: Add turning animation or sound here
            enemy.sprite.tint = 0xFFAAAA; // Brief flash
            setTimeout(() => {
                if (enemy.active) enemy.sprite.tint = 0xFFFFFF;
            }, 100);
        } else {
            // Safe to move - update position
            enemy.x = nextX;
            enemy.y = nextY;
        }
        
        // Update sprite to match position
        enemy.sprite.x = enemy.x;
        enemy.sprite.y = enemy.y;
        
        // Check collision with player
        checkEnemyPlayerCollision(enemy);
    });
}

// Collision detection between enemy and player
function checkEnemyPlayerCollision(enemy) {
    if (!player.alive) return;
    
    const distance = getDistance(enemy, player);
    const minDistance = (enemy.width + player.width) / 2;
    
    if (distance < minDistance) {
        handlePlayerHit(enemy);
    }
}

// Handle what happens when player gets hit
function handlePlayerHit(enemy) {
    player.alive = false;
    
    // Visual feedback
    player.sprite.tint = 0xFF0000; // Flash red
    enemy.sprite.tint = 0xFFFF00;  // Enemy flashes yellow
    
    // Respawn after delay
    setTimeout(() => {
        respawnPlayer();
    }, 1500);
}

function respawnPlayer() {
    player.alive = true;
    player.sprite.tint = 0xFFFFFF;
    
    // Reset to starting position
    player.x = 60;
    player.y = 180;
    
    // Reset enemy colors
    enemies.forEach(enemy => {
        enemy.sprite.tint = 0xFFFFFF;
    });
}

// Utility: Distance between two objects
function getDistance(obj1, obj2) {
    const dx = (obj1.x + obj1.width/2) - (obj2.x + obj2.width/2);
    const dy = (obj1.y + obj1.height/2) - (obj2.y + obj2.height/2);
    return Math.sqrt(dx * dx + dy * dy);
}

The Magic of Simple AI

What makes this “stupid” AI actually brilliant:

๐Ÿ”„ Predictable patterns: Players can learn and plan around enemy movements

โšก Instant feedback: Enemies react immediately to walls with direction changes

๐ŸŽฏ Consistent threat: Always moving, always dangerous, never idle

๐ŸŽจ Visual personality: Different colors and speeds create distinct “characters”

Performance benefits:

Design wisdom: The best enemy AI doesn’t try to outsmart the player - it creates interesting spatial and temporal puzzles for them to solve!

Integration with Game Loop

// Add to your main game loop
function gameLoop() {
    handleInput();      // Player movement
    updateEnemies();    // Enemy AI and movement
    updatePhysics();    // Gravity, collisions, etc.
    render();          // Draw everything
}

app.ticker.add(gameLoop);

๐ŸŽŠ Boom! Your peaceful world is now alive with danger and challenge! These simple patrolling enemies transform static levels into dynamic puzzles. Players must now time their movements, find safe paths, and feel the thrill of narrowly avoiding threats!

Coming next: We’ll make these enemies even smarter by adding platform awareness and more sophisticated patrol behaviors! Next: Enemy on Platform