Exemples Phaser.js

Exemples du moteur de jeu Phaser.js incluant jeux 2D, animations, physique, sprites et mécaniques de jeu interactives

Key Facts

Category
Game Development
Items
3
Format Families
video, text

Sample Overview

Exemples du moteur de jeu Phaser.js incluant jeux 2D, animations, physique, sprites et mécaniques de jeu interactives This sample set belongs to Game Development and can be used to test related workflows inside Elysia Tools.

💻 Phaser.js Hello World javascript

🟢 simple

Configuration de base Phaser.js et exemples Hello World avec sprites, texte et interactions simples

⏱️ 20 min 🏷️ phaser, game, 2d, sprite, physics
Prerequisites: JavaScript basics, HTML5 Canvas basics
// Phaser.js Hello World Examples

// 1. Basic Phaser Game Setup
import Phaser from 'phaser';

class HelloWorldScene extends Phaser.Scene {
    constructor() {
        super('HelloWorld');
    }

    preload() {
        // Load assets
        this.load.image('sky', 'assets/sky.png');
        this.load.image('ground', 'assets/platform.png');
        this.load.image('star', 'assets/star.png');
        this.load.spritesheet('dude', 'assets/dude.png', {
            frameWidth: 32,
            frameHeight: 48
        });
    }

    create() {
        // Add background
        this.add.image(400, 300, 'sky');

        // Add platforms
        const platforms = this.physics.add.staticGroup();
        platforms.create(400, 568, 'ground').setScale(2).refreshBody();
        platforms.create(600, 400, 'ground');
        platforms.create(50, 250, 'ground');
        platforms.create(750, 220, 'ground');

        // Add player
        const player = this.physics.add.sprite(100, 450, 'dude');
        player.setBounce(0.2);
        player.setCollideWorldBounds(true);
        this.physics.add.collider(player, platforms);

        // Add animations
        this.anims.create({
            key: 'left',
            frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
            frameRate: 10,
            repeat: -1
        });

        this.anims.create({
            key: 'turn',
            frames: [{ key: 'dude', frame: 4 }],
            frameRate: 20
        });

        this.anims.create({
            key: 'right',
            frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }),
            frameRate: 10,
            repeat: -1
        });

        // Add stars
        const stars = this.physics.add.group({
            key: 'star',
            repeat: 11,
            setXY: { x: 12, y: 0, stepX: 70 }
        });

        stars.children.entries.forEach(child => {
            child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
        });
        this.physics.add.collider(stars, platforms);
        this.physics.add.overlap(player, stars, this.collectStar, null, this);

        // Score text
        const scoreText = this.add.text(16, 16, 'Score: 0', {
            fontSize: '32px',
            color: '#000'
        });

        // Store references
        this.player = player;
        this.stars = stars;
        this.scoreText = scoreText;
        this.score = 0;

        // Add Hello World text
        this.add.text(400, 100, 'Hello Phaser World!', {
            fontSize: '48px',
            color: '#fff',
            backgroundColor: '#000',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5);

        // Controls
        this.cursors = this.input.keyboard.createCursorKeys();
    }

    update() {
        const { cursors, player } = this;

        if (cursors.left.isDown) {
            player.setVelocityX(-160);
            player.anims.play('left', true);
        } else if (cursors.right.isDown) {
            player.setVelocityX(160);
            player.anims.play('right', true);
        } else {
            player.setVelocityX(0);
            player.anims.play('turn');
        }

        if (cursors.up.isDown && player.body.touching.down) {
            player.setVelocityY(-330);
        }
    }

    collectStar(player, star) {
        star.disableBody(true, true);
        this.score += 10;
        this.scoreText.setText('Score: ' + this.score);
    }
}

// 2. Game Configuration
const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: HelloWorldScene
};

// 3. Initialize Game
const game = new Phaser.Game(config);

// 4. Simple Text Only Game
class TextGame extends Phaser.Scene {
    constructor() {
        super('TextGame');
    }

    create() {
        // Add text elements
        this.add.text(400, 200, 'Hello Phaser!', {
            fontSize: '64px',
            color: '#ff0000',
            fontFamily: 'Arial'
        }).setOrigin(0.5);

        this.add.text(400, 300, 'Welcome to Game Development', {
            fontSize: '32px',
            color: '#00ff00',
            fontFamily: 'Arial'
        }).setOrigin(0.5);

        // Interactive text
        const clickText = this.add.text(400, 400, 'Click Me!', {
            fontSize: '24px',
            color: '#0000ff',
            backgroundColor: '#ffff00',
            padding: { x: 10, y: 5 }
        }).setOrigin(0.5).setInteractive();

        clickText.on('pointerdown', () => {
            clickText.setColor('#ff00ff');
            clickText.setText('Clicked!');
        });
    }
}

// 5. Simple Animation Example
class AnimationExample extends Phaser.Scene {
    constructor() {
        super('AnimationExample');
    }

    preload() {
        // Create colored rectangles as placeholders for sprites
        this.add.graphics()
            .fillStyle(0xff0000)
            .fillRect(0, 0, 64, 64)
            .generateTexture('redBox', 64, 64);

        this.add.graphics()
            .fillStyle(0x00ff00)
            .fillRect(0, 0, 64, 64)
            .generateTexture('greenBox', 64, 64);

        this.add.graphics()
            .fillStyle(0x0000ff)
            .fillRect(0, 0, 64, 64)
            .generateTexture('blueBox', 64, 64);
    }

    create() {
        // Create sprite with tweens
        const sprite = this.add.sprite(400, 300, 'redBox');

        // Scale animation
        this.tweens.add({
            targets: sprite,
            scaleX: 2,
            scaleY: 2,
            duration: 2000,
            ease: 'Power2',
            yoyo: true,
            repeat: -1
        });

        // Rotation animation
        const rotatingSprite = this.add.sprite(200, 200, 'greenBox');
        this.tweens.add({
            targets: rotatingSprite,
            angle: 360,
            duration: 3000,
            repeat: -1
        });

        // Position animation
        const movingSprite = this.add.sprite(600, 200, 'blueBox');
        this.tweens.add({
            targets: movingSprite,
            x: 200,
            y: 400,
            duration: 2500,
            ease: 'Sine.inOut',
            yoyo: true,
            repeat: -1
        });

        this.add.text(400, 500, 'Animation Examples', {
            fontSize: '24px',
            color: '#ffffff'
        }).setOrigin(0.5);
    }
}

// 6. Mouse Interaction Example
class MouseInteraction extends Phaser.Scene {
    constructor() {
        super('MouseInteraction');
    }

    create() {
        // Create interactive sprites
        for (let i = 0; i < 5; i++) {
            const x = Phaser.Math.Between(50, 750);
            const y = Phaser.Math.Between(50, 550);
            const color = Phaser.Math.Between(0, 0xffffff);

            const sprite = this.add.circle(x, y, 30, color).setInteractive();

            sprite.on('pointerover', () => {
                sprite.setScale(1.5);
                sprite.setFillStyle(0xff0000);
            });

            sprite.on('pointerout', () => {
                sprite.setScale(1);
                sprite.setFillStyle(color);
            });

            sprite.on('pointerdown', () => {
                sprite.setFillStyle(Phaser.Math.Between(0, 0xffffff));
            });
        }

        this.add.text(400, 30, 'Hover and Click the Circles!', {
            fontSize: '20px',
            color: '#ffffff'
        }).setOrigin(0.5);
    }
}

// 7. Particle System Example
class ParticleExample extends Phaser.Scene {
    constructor() {
        super('ParticleExample');
    }

    create() {
        // Create particle system
        const particles = this.add.particles(0, 0, 'redBox', {
            x: 400,
            y: 300,
            speed: { min: -100, max: 100 },
            angle: { min: 0, max: 360 },
            scale: { start: 1, end: 0 },
            lifespan: 2000,
            emitting: false
        });

        const text = this.add.text(400, 500, 'Click to Emit Particles', {
            fontSize: '24px',
            color: '#ffffff'
        }).setOrigin(0.5).setInteractive();

        text.on('pointerdown', () => {
            particles.explode(50);
        });
    }
}

// 8. Game State Management Example
class GameStateExample extends Phaser.Scene {
    constructor() {
        super('GameStateExample');
        this.gameState = {
            score: 0,
            lives: 3,
            level: 1,
            gameOver: false
        };
    }

    create() {
        // Create game state display
        this.scoreText = this.add.text(16, 16, `Score: ${this.gameState.score}`, {
            fontSize: '20px',
            color: '#ffffff'
        });

        this.livesText = this.add.text(16, 50, `Lives: ${this.gameState.lives}`, {
            fontSize: '20px',
            color: '#ffffff'
        });

        this.levelText = this.add.text(16, 84, `Level: ${this.gameState.level}`, {
            fontSize: '20px',
            color: '#ffffff'
        });

        // Create buttons to modify game state
        const scoreButton = this.add.text(400, 200, 'Add Score', {
            fontSize: '24px',
            backgroundColor: '#00ff00',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5).setInteractive();

        scoreButton.on('pointerdown', () => {
            this.gameState.score += 10;
            this.updateDisplay();
        });

        const loseLifeButton = this.add.text(400, 300, 'Lose Life', {
            fontSize: '24px',
            backgroundColor: '#ff0000',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5).setInteractive();

        loseLifeButton.on('pointerdown', () => {
            this.gameState.lives--;
            this.updateDisplay();

            if (this.gameState.lives <= 0) {
                this.gameOver();
            }
        });

        const levelButton = this.add.text(400, 400, 'Next Level', {
            fontSize: '24px',
            backgroundColor: '#0000ff',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5).setInteractive();

        levelButton.on('pointerdown', () => {
            this.gameState.level++;
            this.updateDisplay();
        });
    }

    updateDisplay() {
        this.scoreText.setText(`Score: ${this.gameState.score}`);
        this.livesText.setText(`Lives: ${this.gameState.lives}`);
        this.levelText.setText(`Level: ${this.gameState.level}`);
    }

    gameOver() {
        this.gameState.gameOver = true;
        this.add.text(400, 300, 'GAME OVER', {
            fontSize: '64px',
            color: '#ff0000',
            backgroundColor: '#000000',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5);
    }
}

export {
    HelloWorldScene,
    TextGame,
    AnimationExample,
    MouseInteraction,
    ParticleExample,
    GameStateExample
};

💻 Animation de Sprites et Tweening javascript

🟡 intermediate ⭐⭐⭐

Animations avancées de sprites, sprite sheets, tweening et mouvement de personnages dans Phaser.js

⏱️ 30 min 🏷️ phaser, animation, sprite, effects
Prerequisites: Phaser.js basics, JavaScript ES6+, Animation concepts
// Phaser Sprite Animation and Tweening Examples

import Phaser from 'phaser';

// 1. Character Animation System
class CharacterAnimation extends Phaser.Scene {
    constructor() {
        super('CharacterAnimation');
        this.character = null;
        this.cursors = null;
        this.wasMoving = false;
    }

    preload() {
        // Create placeholder sprite sheet
        this.createPlayerSpritesheet();
        this.load.spritesheet('player', 'player_spritesheet', {
            frameWidth: 32,
            frameHeight: 48
        });
    }

    createPlayerSpritesheet() {
        // Create simple colored rectangles for each animation frame
        const graphics = this.add.graphics();

        // Idle frames (frames 0-1)
        for (let i = 0; i < 2; i++) {
            graphics.clear();
            graphics.fillStyle(0x00ff00);
            graphics.fillRect(i * 32, 0, 32, 48);
            graphics.fillStyle(0x000000);
            graphics.fillRect(i * 32 + 8, 8, 6, 6); // left eye
            graphics.fillRect(i * 32 + 18, 8, 6, 6); // right eye
        }

        // Walk frames (frames 2-7)
        for (let i = 2; i < 8; i++) {
            graphics.clear();
            graphics.fillStyle(0x0000ff);
            graphics.fillRect(i * 32, 0, 32, 48);

            // Animated legs
            const legOffset = (i % 2) * 4;
            graphics.fillRect(i * 32 + 10, 32 + legOffset, 4, 16);
            graphics.fillRect(i * 32 + 18, 32 - legOffset, 4, 16);
        }

        // Jump frame (frame 8)
        graphics.clear();
        graphics.fillStyle(0xff0000);
        graphics.fillRect(8 * 32, 0, 32, 48);

        graphics.generateTexture('player_spritesheet', 9 * 32, 48);
        graphics.destroy();
    }

    create() {
        // Create character
        this.character = this.physics.add.sprite(400, 300, 'player');
        this.character.setCollideWorldBounds(true);

        // Create animations
        this.createAnimations();

        // Setup controls
        this.cursors = this.input.keyboard.createCursorKeys();
        this.wasd = this.input.keyboard.addKeys('W,S,A,D');

        // Display instructions
        this.add.text(400, 50, 'Use Arrow Keys or WASD to Move', {
            fontSize: '20px',
            color: '#ffffff'
        }).setOrigin(0.5);

        // Character info display
        this.stateText = this.add.text(16, 16, 'State: Idle', {
            fontSize: '16px',
            color: '#ffffff'
        });
    }

    createAnimations() {
        // Idle animation
        this.anims.create({
            key: 'idle',
            frames: [{ key: 'player', frame: 0 }],
            frameRate: 1,
            repeat: -1
        });

        // Walk animation
        this.anims.create({
            key: 'walk',
            frames: this.anims.generateFrameNumbers('player', { start: 2, end: 7 }),
            frameRate: 10,
            repeat: -1
        });

        // Jump animation
        this.anims.create({
            key: 'jump',
            frames: [{ key: 'player', frame: 8 }],
            frameRate: 1
        });
    }

    update() {
        const { cursors, wasd, character } = this;
        const left = cursors.left.isDown || wasd.A.isDown;
        const right = cursors.right.isDown || wasd.D.isDown;
        const up = cursors.up.isDown || wasd.W.isDown;

        let isMoving = false;
        let currentState = 'Idle';

        // Horizontal movement
        if (left) {
            character.setVelocityX(-200);
            character.setFlipX(true);
            isMoving = true;
            currentState = 'Walking';
        } else if (right) {
            character.setVelocityX(200);
            character.setFlipX(false);
            isMoving = true;
            currentState = 'Walking';
        } else {
            character.setVelocityX(0);
        }

        // Jump
        if (up && character.body.touching.down) {
            character.setVelocityY(-400);
            character.anims.play('jump');
            currentState = 'Jumping';
        }

        // Play appropriate animation
        if (!character.body.touching.down) {
            // Keep jump animation when in air
            if (!character.anims.isPlaying || character.anims.currentAnim.key !== 'jump') {
                character.anims.play('jump');
            }
        } else if (isMoving) {
            character.anims.play('walk', true);
        } else {
            character.anims.play('idle');
        }

        // Update state display
        this.stateText.setText(`State: ${currentState}`);
        this.wasMoving = isMoving;
    }
}

// 2. Advanced Tweening System
class TweeningSystem extends Phaser.Scene {
    constructor() {
        super('TweeningSystem');
        this.sprites = [];
    }

    create() {
        this.createTweeningExamples();
        this.createUI();
    }

    createTweeningExamples() {
        // Linear movement
        const sprite1 = this.add.rectangle(100, 100, 50, 50, 0xff0000);
        this.tweens.add({
            targets: sprite1,
            x: 700,
            duration: 2000,
            ease: 'Linear',
            repeat: -1,
            yoyo: true
        });

        // Easing examples
        const easeTypes = ['Power0', 'Power1', 'Power2', 'Power3', 'Sine', 'Back', 'Elastic', 'Bounce'];
        easeTypes.forEach((ease, index) => {
            const x = 100 + (index % 4) * 180;
            const y = 200 + Math.floor(index / 4) * 100;
            const color = Phaser.Math.Between(0x100000, 0xffffff);

            const sprite = this.add.circle(x, y, 20, color);

            this.tweens.add({
                targets: sprite,
                x: x + 140,
                duration: 2000,
                ease: ease,
                repeat: -1,
                yoyo: true,
                delay: index * 100
            });

            // Label
            this.add.text(x + 70, y - 30, ease, {
                fontSize: '12px',
                color: '#ffffff'
            }).setOrigin(0.5);
        });

        // Complex path animation
        const pathSprite = this.add.rectangle(400, 400, 40, 40, 0x00ff00);
        this.tweens.timeline({
            targets: pathSprite,
            loop: -1,
            tweens: [
                {
                    x: 200,
                    y: 300,
                    duration: 500,
                    ease: 'Power2'
                },
                {
                    x: 600,
                    y: 300,
                    duration: 1000,
                    ease: 'Sine.inOut'
                },
                {
                    x: 600,
                    y: 500,
                    duration: 500,
                    ease: 'Power2'
                },
                {
                    x: 200,
                    y: 500,
                    duration: 1000,
                    ease: 'Sine.inOut'
                },
                {
                    x: 200,
                    y: 300,
                    duration: 500,
                    ease: 'Power2'
                },
                {
                    x: 400,
                    y: 400,
                    duration: 500,
                    ease: 'Power2'
                }
            ]
        });
    }

    createUI() {
        this.add.text(400, 30, 'Tweening Examples - Different Easing Functions', {
            fontSize: '24px',
            color: '#ffffff'
        }).setOrigin(0.5);
    }
}

// 3. Particle Effects System
class ParticleEffects extends Phaser.Scene {
    constructor() {
        super('ParticleEffects');
        this.particleManager = null;
    }

    preload() {
        // Create particle textures
        this.createParticleTextures();
    }

    createParticleTextures() {
        // Create star particle
        const starGraphics = this.add.graphics();
        starGraphics.fillStyle(0xffff00);
        starGraphics.beginPath();
        for (let i = 0; i < 5; i++) {
            const angle = (i * 72 - 90) * Math.PI / 180;
            const x = Math.cos(angle) * 15;
            const y = Math.sin(angle) * 15;
            if (i === 0) starGraphics.moveTo(x, y);
            else starGraphics.lineTo(x, y);

            const innerAngle = ((i * 72 + 36) - 90) * Math.PI / 180;
            const innerX = Math.cos(innerAngle) * 7;
            const innerY = Math.sin(innerAngle) * 7;
            starGraphics.lineTo(innerX, innerY);
        }
        starGraphics.closePath();
        starGraphics.fillPath();
        starGraphics.generateTexture('star_particle', 30, 30);
        starGraphics.destroy();
    }

    create() {
        this.add.text(400, 30, 'Click to Create Different Particle Effects', {
            fontSize: '20px',
            color: '#ffffff'
        }).setOrigin(0.5);

        // Create different particle effects
        this.createExplosionEffect();
        this.createTrailEffect();
        this.createFountainEffect();
        this.createRainEffect();
    }

    createExplosionEffect() {
        const explosionZone = this.add.zone(200, 200, 100, 100).setInteractive();
        this.add.graphics().lineStyle(2, 0xff0000).strokeRect(150, 150, 100, 100);
        this.add.text(200, 120, 'Explosion', { fontSize: '16px', color: '#ffffff' }).setOrigin(0.5);

        explosionZone.on('pointerdown', (pointer) => {
            const particles = this.add.particles(pointer.x, pointer.y, 'star_particle', {
                speed: { min: -300, max: 300 },
                angle: { min: 0, max: 360 },
                scale: { start: 0.5, end: 0 },
                lifespan: 1500,
                quantity: 30,
                blendMode: 'ADD'
            });
            particles.explode();
        });
    }

    createTrailEffect() {
        const trailSprite = this.add.circle(400, 300, 20, 0x00ff00);

        const trailParticles = this.add.particles(trailSprite.x, trailSprite.y, 'star_particle', {
            scale: { start: 0.3, end: 0 },
            speed: 50,
            lifespan: 500,
            quantity: 2
        });

        this.tweens.add({
            targets: trailSprite,
            x: 600,
            y: 300,
            duration: 2000,
            ease: 'Sine.inOut',
            yoyo: true,
            repeat: -1,
            onUpdate: () => {
                trailParticles.setPosition(trailSprite.x, trailSprite.y);
            }
        });

        this.add.text(400, 250, 'Trail Effect', { fontSize: '16px', color: '#ffffff' }).setOrigin(0.5);
    }

    createFountainEffect() {
        const fountainParticles = this.add.particles(600, 450, 'star_particle', {
            x: 600,
            y: 450,
            speedY: { min: -400, max: -200 },
            speedX: { min: -50, max: 50 },
            scale: { start: 0.3, end: 0.1 },
            lifespan: 3000,
            gravityY: 300,
            quantity: 3
        });

        this.add.text(600, 500, 'Fountain', { fontSize: '16px', color: '#ffffff' }).setOrigin(0.5);
    }

    createRainEffect() {
        const rainParticles = this.add.particles(0, 0, 'star_particle', {
            x: { min: 0, max: 800 },
            y: -10,
            speedY: 200,
            scale: { start: 0.2, end: 0.2 },
            lifespan: 4000,
            quantity: 2,
            frequency: 50
        });

        this.add.text(100, 500, 'Rain Effect', { fontSize: '16px', color: '#ffffff' }).setOrigin(0.5);
    }
}

// 4. Sprite Manipulation and Effects
class SpriteEffects extends Phaser.Scene {
    constructor() {
        super('SpriteEffects');
        this.sprites = [];
    }

    create() {
        this.createInteractiveSprites();
        this.addInstructions();
    }

    createInteractiveSprites() {
        // Scale effect
        const scaleSprite = this.add.circle(150, 200, 30, 0xff0000).setInteractive();
        scaleSprite.on('pointerover', () => {
            this.tweens.add({
                targets: scaleSprite,
                scaleX: 1.5,
                scaleY: 1.5,
                duration: 200,
                ease: 'Back.out'
            });
        });
        scaleSprite.on('pointerout', () => {
            this.tweens.add({
                targets: scaleSprite,
                scaleX: 1,
                scaleY: 1,
                duration: 200,
                ease: 'Back.out'
            });
        });

        // Color effect
        const colorSprite = this.add.circle(400, 200, 30, 0x00ff00).setInteractive();
        colorSprite.on('pointerdown', () => {
            const newColor = Phaser.Math.Between(0, 0xffffff);
            this.tweens.add({
                targets: colorSprite,
                duration: 500,
                props: {
                    red: { value: (newColor >> 16) & 255, ease: 'Power1' },
                    green: { value: (newColor >> 8) & 255, ease: 'Power1' },
                    blue: { value: newColor & 255, ease: 'Power1' }
                }
            });
        });

        // Rotation effect
        const rotationSprite = this.add.rectangle(650, 200, 60, 30, 0x0000ff).setInteractive();
        rotationSprite.on('pointerdown', () => {
            this.tweens.add({
                targets: rotationSprite,
                angle: 360,
                duration: 1000,
                ease: 'Power2',
                repeat: -1
            });
        });

        // Fade effect
        const fadeSprite = this.add.circle(150, 350, 30, 0xffff00).setInteractive();
        fadeSprite.on('pointerdown', () => {
            this.tweens.add({
                targets: fadeSprite,
                alpha: 0,
                duration: 1000,
                yoyo: true,
                repeat: -1
            });
        });

        // Shake effect
        const shakeSprite = this.add.circle(400, 350, 30, 0xff00ff).setInteractive();
        shakeSprite.on('pointerdown', () => {
            this.cameras.main.shake(500, 0.01);
        });

        // Blink effect
        const blinkSprite = this.add.circle(650, 350, 30, 0x00ffff).setInteractive();
        blinkSprite.on('pointerdown', () => {
            this.tweens.add({
                targets: blinkSprite,
                alpha: { from: 1, to: 0 },
                duration: 200,
                ease: 'Power2',
                yoyo: true,
                repeat: 5
            });
        });
    }

    addInstructions() {
        this.add.text(400, 50, 'Interactive Sprite Effects', {
            fontSize: '24px',
            color: '#ffffff'
        }).setOrigin(0.5);

        const instructions = [
            'Red: Hover to Scale',
            'Green: Click to Change Color',
            'Blue: Click to Rotate',
            'Yellow: Click to Fade',
            'Purple: Click to Shake Camera',
            'Cyan: Click to Blink'
        ];

        instructions.forEach((text, index) => {
            const x = 150 + (index % 3) * 250;
            const y = 450 + Math.floor(index / 3) * 30;
            this.add.text(x, y, text, {
                fontSize: '14px',
                color: '#ffffff'
            }).setOrigin(0.5);
        });
    }
}

// 5. Animation Blending and States
class AnimationBlending extends Phaser.Scene {
    constructor() {
        super('AnimationBlending');
        this.character = null;
        this.animationState = 'idle';
        this.blendValue = 0;
    }

    preload() {
        this.createAnimationSpritesheet();
    }

    createAnimationSpritesheet() {
        const graphics = this.add.graphics();

        // Create blend animation frames
        for (let i = 0; i < 8; i++) {
            graphics.clear();

            // Blend between idle and run based on frame
            const blend = i / 7;
            const width = 32;
            const height = 48;

            // Body
            graphics.fillStyle(
                Phaser.Math.Interpolation.Colors.Interpolation([0x00ff00, 0xff0000], blend)
            );
            graphics.fillRect(i * width, height/2, width, height/2);

            // Head
            graphics.fillRect(i * width + 8, 4, 16, 16);

            // Moving legs
            if (blend > 0.3) {
                const legMove = (i % 2) * 8 * blend;
                graphics.fillRect(i * width + 8, height - 12 + legMove, 4, 12);
                graphics.fillRect(i * width + 20, height - 12 - legMove, 4, 12);
            }
        }

        graphics.generateTexture('character_anim', 256, 48);
        graphics.destroy();
    }

    create() {
        this.character = this.add.sprite(400, 300, 'character_anim');

        // Create blend animation
        this.anims.create({
            key: 'blend',
            frames: this.anims.generateFrameNumbers('character_anim', { start: 0, end: 7 }),
            frameRate: 12,
            repeat: -1
        });

        this.character.anims.play('blend');

        // Create UI
        this.createUI();
    }

    createUI() {
        this.add.text(400, 50, 'Animation Blending Control', {
            fontSize: '24px',
            color: '#ffffff'
        }).setOrigin(0.5);

        this.blendText = this.add.text(400, 500, 'Blend: Idle', {
            fontSize: '18px',
            color: '#ffffff'
        }).setOrigin(0.5);

        // Create clickable zones for different blend states
        const states = [
            { x: 200, y: 200, label: 'Idle', value: 0 },
            { x: 400, y: 200, label: 'Walk', value: 0.5 },
            { x: 600, y: 200, label: 'Run', value: 1 }
        ];

        states.forEach(state => {
            const zone = this.add.zone(state.x, state.y, 100, 60).setInteractive();
            this.add.graphics()
                .lineStyle(2, 0xffffff)
                .strokeRect(state.x - 50, state.y - 30, 100, 60);

            this.add.text(state.x, state.y, state.label, {
                fontSize: '16px',
                color: '#ffffff'
            }).setOrigin(0.5);

            zone.on('pointerdown', () => {
                this.blendValue = state.value;
                this.updateAnimation();
            });
        });
    }

    updateAnimation() {
        let stateLabel = 'Idle';
        if (this.blendValue === 0.5) stateLabel = 'Walk';
        else if (this.blendValue === 1) stateLabel = 'Run';

        this.blendText.setText(`Blend: ${stateLabel}`);

        // Update animation speed based on blend
        const frameRate = 12 + (this.blendValue * 8);
        this.character.anims.currentAnim.msPerFrame = 1000 / frameRate;
    }
}

export {
    CharacterAnimation,
    TweeningSystem,
    ParticleEffects,
    SpriteEffects,
    AnimationBlending
};

💻 Mécaniques de Jeu et Physique javascript

🔴 complex ⭐⭐⭐⭐

Mécaniques complètes de jeu incluant physique, détection de collisions, états de jeu et contrôles joueur

⏱️ 45 min 🏷️ phaser, physics, collision, game mechanics
Prerequisites: Phaser.js basics, Physics concepts, Game design fundamentals
// Phaser Game Mechanics and Physics Examples

import Phaser from 'phaser';

// 1. Platformer Physics System
class PlatformerGame extends Phaser.Scene {
    constructor() {
        super('PlatformerGame');
        this.player = null;
        this.platforms = null;
        this.collectibles = null;
        this.enemies = null;
        this.gameState = {
            score: 0,
            lives: 3,
            level: 1,
            gameOver: false
        };
    }

    preload() {
        // Create game assets
        this.createGameAssets();
    }

    createGameAssets() {
        // Create tile graphics
        const graphics = this.add.graphics();

        // Ground tile
        graphics.fillStyle(0x8B4513).fillRect(0, 0, 64, 64);
        graphics.fillStyle(0x228B22).fillRect(0, 0, 64, 20);
        graphics.generateTexture('ground', 64, 64);

        // Platform tile
        graphics.clear();
        graphics.fillStyle(0x808080).fillRect(0, 0, 64, 16);
        graphics.generateTexture('platform', 64, 16);

        // Collectible (star)
        graphics.clear();
        graphics.fillStyle(0xFFD700);
        graphics.beginPath();
        for (let i = 0; i < 8; i++) {
            const angle = (i * 45 - 90) * Math.PI / 180;
            const x = Math.cos(angle) * 20;
            const y = Math.sin(angle) * 20;
            if (i === 0) graphics.moveTo(x + 25, y + 25);
            else graphics.lineTo(x + 25, y + 25);

            const innerAngle = ((i * 45 + 22.5) - 90) * Math.PI / 180;
            const innerX = Math.cos(innerAngle) * 10;
            const innerY = Math.sin(innerAngle) * 10;
            graphics.lineTo(innerX + 25, innerY + 25);
        }
        graphics.closePath();
        graphics.fillPath();
        graphics.generateTexture('star', 50, 50);

        // Enemy
        graphics.clear();
        graphics.fillStyle(0xFF0000).fillRect(0, 0, 40, 40);
        graphics.fillStyle(0x000000).fillRect(8, 8, 6, 6);
        graphics.fillStyle(0x000000).fillRect(26, 8, 6, 6);
        graphics.generateTexture('enemy', 40, 40);

        // Player
        graphics.clear();
        graphics.fillStyle(0x00FF00).fillRect(0, 0, 32, 48);
        graphics.fillStyle(0x000000).fillRect(6, 8, 5, 5);
        graphics.fillStyle(0x000000).fillRect(21, 8, 5, 5);
        graphics.generateTexture('player', 32, 48);

        graphics.destroy();
    }

    create() {
        // Setup physics world
        this.physics.world.setBounds(0, 0, 1600, 600);
        this.cameras.main.setBounds(0, 0, 1600, 600);

        // Create platforms
        this.platforms = this.physics.add.staticGroup();

        // Ground
        for (let i = 0; i < 25; i++) {
            this.platforms.create(i * 64, 600 - 32, 'ground').refreshBody();
        }

        // Floating platforms
        this.platforms.create(300, 450, 'platform');
        this.platforms.create(500, 350, 'platform');
        this.platforms.create(800, 400, 'platform');
        this.platforms.create(1100, 300, 'platform');
        this.platforms.create(1300, 450, 'platform');

        // Create player
        this.player = this.physics.add.sprite(100, 400, 'player');
        this.player.setBounce(0.2);
        this.player.setCollideWorldBounds(true);
        this.player.body.setGravityY(300);
        this.physics.add.collider(this.player, this.platforms);

        // Create collectibles
        this.collectibles = this.physics.add.group({
            key: 'star',
            repeat: 15,
            setXY: { x: 200, y: 100, stepX: 80 }
        });

        this.collectibles.children.entries.forEach(child => {
            child.setBounce(Phaser.Math.FloatBetween(0.4, 0.8));
            child.setGravityY(100);
        });

        this.physics.add.collider(this.collectibles, this.platforms);
        this.physics.add.overlap(this.player, this.collectibles, this.collectStar, null, this);

        // Create enemies
        this.enemies = this.physics.add.group();
        this.createEnemies();

        this.physics.add.collider(this.enemies, this.platforms);
        this.physics.add.collider(this.player, this.enemies, this.hitEnemy, null, this);

        // Setup controls
        this.cursors = this.input.keyboard.createCursorKeys();
        this.wasd = this.input.keyboard.addKeys('W,S,A,D');

        // Camera follow
        this.cameras.main.startFollow(this.player, true, 0.05, 0.05);

        // Create UI
        this.createUI();

        // Create player animations
        this.createPlayerAnimations();
    }

    createPlayerAnimations() {
        // Create simple animation
        this.anims.create({
            key: 'idle',
            frames: [{ key: 'player', frame: 0 }],
            frameRate: 1
        });
    }

    createEnemies() {
        const enemyPositions = [
            { x: 400, y: 400 },
            { x: 700, y: 250 },
            { x: 1000, y: 300 },
            { x: 1400, y: 400 }
        ];

        enemyPositions.forEach(pos => {
            const enemy = this.enemies.create(pos.x, pos.y, 'enemy');
            enemy.setBounce(1);
            enemy.setCollideWorldBounds(true);
            enemy.setVelocity(Phaser.Math.Between(-50, 50), 0);
            enemy.body.setGravityY(300);
        });
    }

    createUI() {
        this.scoreText = this.add.text(16, 16, `Score: ${this.gameState.score}`, {
            fontSize: '20px',
            color: '#ffffff',
            backgroundColor: '#000000',
            padding: { x: 10, y: 5 }
        }).setScrollFactor(0);

        this.livesText = this.add.text(16, 50, `Lives: ${this.gameState.lives}`, {
            fontSize: '20px',
            color: '#ffffff',
            backgroundColor: '#000000',
            padding: { x: 10, y: 5 }
        }).setScrollFactor(0);
    }

    update() {
        if (this.gameState.gameOver) return;

        // Player movement
        const { cursors, wasd, player } = this;
        const left = cursors.left.isDown || wasd.A.isDown;
        const right = cursors.right.isDown || wasd.D.isDown;
        const up = cursors.up.isDown || wasd.W.isDown;

        if (left) {
            player.setVelocityX(-200);
            player.setFlipX(true);
        } else if (right) {
            player.setVelocityX(200);
            player.setFlipX(false);
        } else {
            player.setVelocityX(0);
        }

        if (up && player.body.touching.down) {
            player.setVelocityY(-500);
        }

        // Check win condition
        if (this.collectibles.countActive() === 0) {
            this.levelComplete();
        }
    }

    collectStar(player, star) {
        star.disableBody(true, true);
        this.gameState.score += 10;
        this.scoreText.setText(`Score: ${this.gameState.score}`);
    }

    hitEnemy(player, enemy) {
        if (player.body.touching.down && enemy.body.touching.up) {
            // Player stomped enemy
            enemy.disableBody(true, true);
            this.gameState.score += 50;
            this.scoreText.setText(`Score: ${this.gameState.score}`);
            player.setVelocityY(-300);
        } else {
            // Player hit by enemy
            this.playerHit();
        }
    }

    playerHit() {
        this.gameState.lives--;
        this.livesText.setText(`Lives: ${this.gameState.lives}`);

        if (this.gameState.lives <= 0) {
            this.gameOver();
        } else {
            // Reset player position
            this.player.setPosition(100, 400);
            this.player.setVelocity(0, 0);

            // Flash player
            this.tweens.add({
                targets: this.player,
                alpha: 0.5,
                duration: 100,
                repeat: 5,
                yoyo: true
            });
        }
    }

    levelComplete() {
        this.gameState.level++;
        this.gameState.score += 100;

        this.add.text(this.cameras.main.midX, this.cameras.main.midY, 'Level Complete!', {
            fontSize: '64px',
            color: '#00ff00',
            backgroundColor: '#000000',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5).setScrollFactor(0);

        this.time.delayedCall(2000, () => {
            this.nextLevel();
        });
    }

    nextLevel() {
        // Reset collectibles
        this.collectibles.clear(true, true);
        this.collectibles = this.physics.add.group({
            key: 'star',
            repeat: 20,
            setXY: { x: 200, y: 100, stepX: 70 }
        });

        this.collectibles.children.entries.forEach(child => {
            child.setBounce(Phaser.Math.FloatBetween(0.4, 0.8));
            child.setGravityY(100);
        });

        this.physics.add.collider(this.collectibles, this.platforms);
        this.physics.add.overlap(this.player, this.collectibles, this.collectStar, null, this);
    }

    gameOver() {
        this.gameState.gameOver = true;
        this.physics.pause();

        this.add.text(this.cameras.main.midX, this.cameras.main.midY, 'GAME OVER', {
            fontSize: '64px',
            color: '#ff0000',
            backgroundColor: '#000000',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5).setScrollFactor(0);
    }
}

// 2. Top-Down Shooter Mechanics
class TopDownShooter extends Phaser.Scene {
    constructor() {
        super('TopDownShooter');
        this.player = null;
        this.bullets = null;
        this.enemies = null;
        this.walls = null;
        this.gameState = {
            score: 0,
            wave: 1,
            gameOver: false
        };
    }

    preload() {
        this.createShooterAssets();
    }

    createShooterAssets() {
        const graphics = this.add.graphics();

        // Player (triangle)
        graphics.fillStyle(0x0000FF);
        graphics.beginPath();
        graphics.moveTo(15, 0);
        graphics.lineTo(0, 25);
        graphics.lineTo(30, 25);
        graphics.closePath();
        graphics.fillPath();
        graphics.generateTexture('player', 30, 30);

        // Bullet
        graphics.clear();
        graphics.fillStyle(0xFFFF00).fillCircle(5, 5, 4);
        graphics.generateTexture('bullet', 10, 10);

        // Enemy
        graphics.clear();
        graphics.fillStyle(0xFF0000).fillCircle(15, 15, 15);
        graphics.generateTexture('enemy', 30, 30);

        // Wall
        graphics.clear();
        graphics.fillStyle(0x808080).fillRect(0, 0, 50, 50);
        graphics.generateTexture('wall', 50, 50);

        graphics.destroy();
    }

    create() {
        // Create world boundaries
        this.physics.world.setBounds(0, 0, 800, 600);

        // Create player
        this.player = this.physics.add.sprite(400, 300, 'player');
        this.player.setCollideWorldBounds(true);

        // Create walls
        this.walls = this.physics.add.staticGroup();
        this.createWalls();

        // Create bullets
        this.bullets = this.physics.add.group({
            defaultKey: 'bullet',
            maxSize: 50
        });

        // Create enemies
        this.enemies = this.physics.add.group();
        this.spawnEnemyWave();

        // Setup physics
        this.physics.add.collider(this.player, this.walls);
        this.physics.add.collider(this.enemies, this.walls);
        this.physics.add.overlap(this.bullets, this.enemies, this.hitEnemy, null, this);
        this.physics.add.overlap(this.bullets, this.walls, this.bulletHitWall, null, this);
        this.physics.add.overlap(this.player, this.enemies, this.playerHitEnemy, null, this);

        // Setup controls
        this.cursors = this.input.keyboard.createCursorKeys();
        this.wasd = this.input.keyboard.addKeys('W,S,A,D');
        this.space = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

        // Follow player with camera
        this.cameras.main.startFollow(this.player);

        // Create UI
        this.createShooterUI();

        // Setup continuous enemy spawning
        this.enemySpawnTimer = this.time.addEvent({
            delay: 2000,
            callback: this.spawnEnemy,
            callbackScope: this,
            loop: true
        });
    }

    createWalls() {
        // Create maze-like walls
        const wallPositions = [
            { x: 200, y: 150 },
            { x: 600, y: 150 },
            { x: 200, y: 450 },
            { x: 600, y: 450 },
            { x: 100, y: 300 },
            { x: 700, y: 300 },
            { x: 400, y: 100 },
            { x: 400, y: 500 }
        ];

        wallPositions.forEach(pos => {
            this.walls.create(pos.x, pos.y, 'wall');
        });
    }

    createShooterUI() {
        this.scoreText = this.add.text(16, 16, `Score: ${this.gameState.score}`, {
            fontSize: '18px',
            color: '#ffffff',
            backgroundColor: '#000000',
            padding: { x: 10, y: 5 }
        }).setScrollFactor(0);

        this.waveText = this.add.text(16, 45, `Wave: ${this.gameState.wave}`, {
            fontSize: '18px',
            color: '#ffffff',
            backgroundColor: '#000000',
            padding: { x: 10, y: 5 }
        }).setScrollFactor(0);

        this.healthText = this.add.text(16, 74, 'Health: 100', {
            fontSize: '18px',
            color: '#ffffff',
            backgroundColor: '#000000',
            padding: { x: 10, y: 5 }
        }).setScrollFactor(0);
    }

    spawnEnemyWave() {
        const enemyCount = 3 + this.gameState.wave;
        for (let i = 0; i < enemyCount; i++) {
            this.spawnEnemy();
        }
    }

    spawnEnemy() {
        const side = Math.floor(Math.random() * 4);
        let x, y;

        switch(side) {
            case 0: // Top
                x = Math.random() * 800;
                y = 0;
                break;
            case 1: // Right
                x = 800;
                y = Math.random() * 600;
                break;
            case 2: // Bottom
                x = Math.random() * 800;
                y = 600;
                break;
            case 3: // Left
                x = 0;
                y = Math.random() * 600;
                break;
        }

        const enemy = this.enemies.create(x, y, 'enemy');
        enemy.health = 2;
    }

    update() {
        if (this.gameState.gameOver) return;

        // Player movement
        const speed = 200;
        let velocityX = 0;
        let velocityY = 0;

        if (this.cursors.left.isDown || this.wasd.A.isDown) velocityX = -speed;
        if (this.cursors.right.isDown || this.wasd.D.isDown) velocityX = speed;
        if (this.cursors.up.isDown || this.wasd.W.isDown) velocityY = -speed;
        if (this.cursors.down.isDown || this.wasd.S.isDown) velocityY = speed;

        this.player.setVelocity(velocityX, velocityY);

        // Point player towards mouse
        const pointer = this.input.activePointer;
        const angle = Phaser.Math.Angle.BetweenPoints(this.player, pointer);
        this.player.rotation = angle + Math.PI / 2;

        // Shooting
        if (this.space.isDown && !this.shootCooldown) {
            this.shoot(angle);
            this.shootCooldown = true;
            this.time.delayedCall(200, () => {
                this.shootCooldown = false;
            });
        }

        // Move enemies towards player
        this.enemies.children.entries.forEach(enemy => {
            if (enemy.active) {
                const enemyAngle = Phaser.Math.Angle.BetweenPoints(enemy, this.player);
                const enemySpeed = 50 + (this.gameState.wave * 10);
                enemy.setVelocity(
                    Math.cos(enemyAngle) * enemySpeed,
                    Math.sin(enemyAngle) * enemySpeed
                );
                enemy.rotation = enemyAngle + Math.PI / 2;
            }
        });

        // Check wave completion
        if (this.enemies.countActive() === 0) {
            this.nextWave();
        }
    }

    shoot(angle) {
        const bullet = this.bullets.get(this.player.x, this.player.y);
        if (bullet) {
            bullet.setActive(true);
            bullet.setVisible(true);
            bullet.body.setVelocity(
                Math.cos(angle) * 600,
                Math.sin(angle) * 600
            );

            // Remove bullet after 1 second
            this.time.delayedCall(1000, () => {
                bullet.setActive(false);
                bullet.setVisible(false);
            });
        }
    }

    hitEnemy(bullet, enemy) {
        bullet.setActive(false);
        bullet.setVisible(false);

        enemy.health--;
        if (enemy.health <= 0) {
            enemy.setActive(false);
            enemy.setVisible(false);
            this.gameState.score += 10;
            this.scoreText.setText(`Score: ${this.gameState.score}`);
        } else {
            // Flash enemy
            this.tweens.add({
                targets: enemy,
                tint: 0xFFFFFF,
                duration: 100,
                yoyo: true
            });
        }
    }

    bulletHitWall(bullet, wall) {
        bullet.setActive(false);
        bullet.setVisible(false);
    }

    playerHitEnemy(player, enemy) {
        this.gameState.health = (this.gameState.health || 100) - 10;
        this.healthText.setText(`Health: ${this.gameState.health}`);

        // Push enemy back
        const pushAngle = Phaser.Math.Angle.BetweenPoints(enemy, player);
        enemy.setVelocity(
            Math.cos(pushAngle) * 200,
            Math.sin(pushAngle) * 200
        );

        // Flash player
        this.tweens.add({
            targets: player,
            tint: 0xFF0000,
            duration: 100,
            yoyo: true
        });

        if (this.gameState.health <= 0) {
            this.gameOverShooter();
        }
    }

    nextWave() {
        this.gameState.wave++;
        this.waveText.setText(`Wave: ${this.gameState.wave}`);
        this.gameState.health = Math.min(100, (this.gameState.health || 50) + 20);
        this.healthText.setText(`Health: ${this.gameState.health}`);

        this.add.text(400, 300, `Wave ${this.gameState.wave}!`, {
            fontSize: '48px',
            color: '#00ff00',
            backgroundColor: '#000000',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5).setScrollFactor(0);

        this.time.delayedCall(2000, () => {
            this.spawnEnemyWave();
        });
    }

    gameOverShooter() {
        this.gameState.gameOver = true;
        this.physics.pause();
        this.enemySpawnTimer.remove();

        this.add.text(400, 300, 'GAME OVER', {
            fontSize: '64px',
            color: '#ff0000',
            backgroundColor: '#000000',
            padding: { x: 20, y: 10 }
        }).setOrigin(0.5).setScrollFactor(0);
    }
}

// 3. Pong Game Physics
class PongGame extends Phaser.Scene {
    constructor() {
        super('PongGame');
        this.leftPaddle = null;
        this.rightPaddle = null;
        this.ball = null;
        this.score = { left: 0, right: 0 };
        this.paddleSpeed = 400;
    }

    preload() {
        this.createPongAssets();
    }

    createPongAssets() {
        const graphics = this.add.graphics();

        // Paddle
        graphics.fillStyle(0xFFFFFF).fillRect(0, 0, 15, 80);
        graphics.generateTexture('paddle', 15, 80);

        // Ball
        graphics.clear();
        graphics.fillStyle(0xFFFFFF).fillCircle(10, 10, 8);
        graphics.generateTexture('ball', 20, 20);

        graphics.destroy();
    }

    create() {
        // Create paddles
        this.leftPaddle = this.physics.add.sprite(50, 300, 'paddle')
            .setImmovable(true)
            .setCollideWorldBounds(true);

        this.rightPaddle = this.physics.add.sprite(750, 300, 'paddle')
            .setImmovable(true)
            .setCollideWorldBounds(true);

        // Create ball
        this.ball = this.physics.add.sprite(400, 300, 'ball')
            .setBounce(1)
            .setCollideWorldBounds(true);

        // Setup collisions
        this.physics.add.collider(this.ball, this.leftPaddle, this.hitPaddle, null, this);
        this.physics.add.collider(this.ball, this.rightPaddle, this.hitPaddle, null, this);

        // Setup world bounds
        this.physics.world.setBoundsCollision(true, true, false, false);

        // Setup controls
        this.cursors = this.input.keyboard.createCursorKeys();
        this.wasd = this.input.keyboard.addKeys('W,S,A,D');

        // Create UI
        this.createPongUI();

        // Start ball
        this.resetBall();
    }

    createPongUI() {
        this.scoreText = this.add.text(400, 50, `${this.score.left} - ${this.score.right}`, {
            fontSize: '48px',
            color: '#ffffff'
        }).setOrigin(0.5);

        this.add.text(400, 100, 'W/S: Left Paddle | Arrow Keys: Right Paddle', {
            fontSize: '16px',
            color: '#ffffff'
        }).setOrigin(0.5);
    }

    update() {
        // Left paddle controls (W/S)
        if (this.wasd.W.isDown) {
            this.leftPaddle.setVelocityY(-this.paddleSpeed);
        } else if (this.wasd.S.isDown) {
            this.leftPaddle.setVelocityY(this.paddleSpeed);
        } else {
            this.leftPaddle.setVelocityY(0);
        }

        // Right paddle controls (Arrow Keys)
        if (this.cursors.up.isDown) {
            this.rightPaddle.setVelocityY(-this.paddleSpeed);
        } else if (this.cursors.down.isDown) {
            this.rightPaddle.setVelocityY(this.paddleSpeed);
        } else {
            this.rightPaddle.setVelocityY(0);
        }

        // Keep paddles on screen
        this.leftPaddle.y = Phaser.Math.Clamp(this.leftPaddle.y, 40, 560);
        this.rightPaddle.y = Phaser.Math.Clamp(this.rightPaddle.y, 40, 560);

        // Check scoring
        if (this.ball.x < 0) {
            this.score.right++;
            this.scoreText.setText(`${this.score.left} - ${this.score.right}`);
            this.resetBall();
        } else if (this.ball.x > 800) {
            this.score.left++;
            this.scoreText.setText(`${this.score.left} - ${this.score.right}`);
            this.resetBall();
        }
    }

    hitPaddle(ball, paddle) {
        // Increase ball speed slightly
        ball.setVelocityX(ball.body.velocity.x * 1.05);

        // Add some spin based on where the ball hits the paddle
        const paddleCenter = paddle.y;
        const ballCenter = ball.y;
        const diff = ballCenter - paddleCenter;
        ball.setVelocityY(diff * 4);
    }

    resetBall() {
        this.ball.setPosition(400, 300);
        this.ball.setVelocity(
            Phaser.Math.Between(-200, 200),
            Phaser.Math.Between(-200, 200)
        );
    }
}

export {
    PlatformerGame,
    TopDownShooter,
    PongGame
};