<
<
<
<Procedural Platformer
<
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: 'Courier New', Courier, monospace;
}
canvas {
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
#ui {
position: absolute;
top: 20px;
left: 20px;
color: white;
font-size: 24px;
text-shadow: 2px 2px #000;
pointer-events: none;
z-index: 10;
}
<Coins: 0
<
<
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 1000 },
debug: false
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
let player;
let cursors;
let platforms;
let coins;
let enemies;
let goal;
let score = 0;
let scoreText;
let isGameOver = false;
function preload() {
// Procedural Texture Generation
// 1. Player Texture
const playerCanvas = this.textures.createCanvas('player', 32, 32);
const playerCtx = playerCanvas.getContext();
playerCtx.fillStyle = '#ff0000';
playerCtx.fillRect(0, 0, 32, 32);
playerCtx.fillStyle = '#ffffff';
playerCtx.fillRect(8, 8, 8, 8);
playerCtx.fillRect(16, 8, 8, 8);
playerCtx.refresh();
// 2. Enemy Texture
const enemyCanvas = this.textures.createCanvas('enemy', 32, 32);
const enemyCtx = enemyCanvas.getContext();
enemyCtx.fillStyle = '#800080';
enemyCtx.fillRect(0, 0, 32, 32);
enemyCtx.fillStyle = '#ffff00';
enemyCtx.fillRect(4, 4, 8, 8);
enemyCtx.fillRect(20, 4, 8, 8);
enemyCtx.refresh();
// 3. Coin Texture
const coinCanvas = this.textures.createCanvas('coin', 20, 20);
const coinCtx = coinCanvas.getContext();
coinCtx.fillStyle = '#ffd700';
coinCtx.beginPath();
coinCtx.arc(10, 10, 10, 0, Math.PI * 2);
coinCtx.fill();
coinCtx.strokeStyle = '#b8860b';
coinCtx.lineWidth = 2;
coinCtx.stroke();
coinCtx.refresh();
// 4. Platform Texture
const platCanvas = this.textures.createCanvas('platform', 32, 32);
const platCtx = platCanvas.getContext();
platCtx.fillStyle = '#8b4513';
platCtx.fillRect(0, 0, 32, 32);
platCtx.fillStyle = '#228b22';
platCtx.fillRect(0, 0, 32, 8);
platCtx.strokeStyle = '#5d2e0c';
platCtx.strokeRect(0, 0, 32, 32);
platCtx.refresh();
// 5. Goal Texture
const goalCanvas = this.textures.createCanvas('goal', 32, 64);
const goalCtx = goalCanvas.getContext();
goalCtx.fillStyle = '#808080';
goalCtx.fillRect(12, 0, 8, 64);
goalCtx.fillStyle = '#ff0000';
goalCtx.fillRect(20, 0, 12, 20);
goalCtx.refresh();
}
function create() {
const worldWidth = 3200;
const worldHeight = 600;
this.physics.world.setBounds(0, 0, worldWidth, worldHeight);
// Background color
this.cameras.main.setBackgroundColor('#87ceeb');
// Groups
platforms = this.physics.add.staticGroup();
coins = this.physics.add.group();
enemies = this.physics.add.group();
// Floor
for (let x = 0; x << world worldWidth; x += 32) {
platforms.create(x + 16, 584, 'platform');
}
// Procedural Level Design
const layout = [
{ x: 400, y: 450, w: 200, h: 32 },
{ x: 700, y: 350, w: 200, h: 32 },
{ x: 1000, y: 450, w: 300, h: 32 },
{ x: 1400, y: 300, w: 200, h: 32 },
{ x: 1700, y: 400, w: 200, h: 32 },
{ x: 2000, y: 300, w: 300, h: 32 },
{ x: 2400, y: 450, w: 200, h: 32 },
{ x: 2700, y: 350, w: 200, h: 32 },
];
layout.forEach(p => {
for (let x = p.x; x << p p.x + p.w; x += 32) {
platforms.create(x + 16, p.y, 'platform');
}
});
// Coins
const coinPositions = [
{x: 450, y: 420}, {x: 500, y: 420}, {x: 750, y: 320},
{x: 1100, y: 420}, {x: 1200, y: 420}, {x: 1450, y: 270},
{x: 1750, y: 370}, {x: 2100, y: 270}, {x: 2200, y: 270},
{x: 2450, y: 420}, {x: 2750, y: 320}
];
coinPositions.forEach(pos => {
coins.create(pos.x, pos.y, 'coin');
});
// Enemies
const enemyPositions = [
{x: 600, y: 552, range: 100},
{x: 1100, y: 420, range: 100},
{x: 1800, y: 552, range: 150},
{x: 2200, y: 268, range: 100},
{x: 2600, y: 552, range: 100},
];
enemyPositions.forEach(pos => {
const enemy = enemies.create(pos.x, pos.y, 'enemy');
enemy.setCollideWorldBounds(true);
enemy.setBounce(1);
enemy.setVelocityX(100);
enemy.setData('range', pos.range);
enemy.setData('startX', pos.x);
});
// Goal
goal = this.physics.add.staticSprite(3100, 520, 'goal');
// Player
player = this.physics.add.sprite(100, 500, 'player');
player.setCollideWorldBounds(true);
player.setDragX(1000);
// Camera
this.cameras.main.setBounds(0, 0, worldWidth, worldHeight);
this.cameras.main.startFollow(player);
// UI
scoreText = document.getElementById('ui');
// Collisions
this.physics.add.collider(player, platforms);
this.physics.add.collider(enemies, platforms);
this.physics.add.overlap(player, coins, collectCoin, null, this);
this.physics.add.collider(player, enemies, handleEnemyCollision, null, this);
this.physics.add.overlap(player, goal, reachGoal, null, this);
cursors = this.input.keyboard.createCursorKeys();
}
function update() {
if (isGameOver) return;
if (cursors.left.isDown) {
player.setVelocityX(-200);
} else if (cursors.right.isDown) {
player.setVelocityX(200);
}
if ((cursors.up.isDown || cursors.space.isDown) && player.body.touching.down) {
player.setVelocityY(-500);
}
// Enemy AI
enemies.getChildren().forEach(enemy => {
const startX = enemy.getData('startX');
const range = enemy.getData('range');
if (enemy.x > startX + range || enemy.x << start startX - range) {
enemy.setVelocityX(enemy.body.velocity.x * -1);
}
});
}
function collectCoin(player, coin) {
coin.disableBody(true, true);
score += 10;
scoreText.innerText = `Coins: ${score}`;
}
function handleEnemyCollision(player, enemy) {
if (player.body.velocity.y > 0 && player.y << enemy enemy.y - 10) {
// Stomp enemy
enemy.disableBody(true, true);
player.setVelocityY(-300);
} else {
// Player hit enemy
gameOver(this, "Game Over! You hit an enemy.");
}
}
function reachGoal(player, goal) {
gameOver(this, `You Win! Final Score: ${score}`);
}
function gameOver(scene, message) {
isGameOver = true;
scene.physics.pause();
player.setTint(0xff0000);
const overlay = scene.add.graphics();
overlay.fillStyle(0x000000, 0.7);
overlay.fillRect(0, 0, 800, 600);
overlay.setScrollFactor(0);
const text = scene.add.text(400, 300, message, {
fontSize: '32px',
fill: '#fff',
align: 'center'
}).setOrigin(0.5).setScrollFactor(0);
scene.add.text(400, 350, 'Press F5 to Restart', {
fontSize: '20px',
fill: '#fff'
}).setOrigin(0.5).setScrollFactor(0);
}