Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgt08frtkp7x95267Plain Text
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Football Bros 2-Player Clone</title>
<!-- Importing the open-source Phaser 3 game framework from CDN -->
<script src="https://jsdelivr.net"></script>
<style>
body { margin: 0; background: #1a1a1a; display: flex; justify-content: center; align-items: center; height: 100vh; font-family: 'Arial Black', sans-serif; overflow: hidden;}
canvas { border: 6px solid #fff; box-shadow: 0 10px 30px rgba(0,0,0,0.7); border-radius: 4px; }
</style>
</head>
<body>
<script>
const config = {
type: Phaser.AUTO,
width: 900,
height: 500,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 1100 }, // Heavy arcade gravity match
debug: false
}
},
scene: { preload: preload, create: create, update: update }
};
const game = new Phaser.Game(config);
let player1, player2, football, ground;
let leftGoalPost, rightGoalPost;
let cursors, spaceKey, wasd, fKey;
let p1Score = 0, p2Score = 0, timeRemaining = 60;
let uiText, timerText, gameOverText;
let matchActive = true;
function preload() {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
// Player 1 Graphic (Blue Custom Uniform Block)
canvas.width = 44; canvas.height = 44;
ctx.fillStyle = '#0055ff'; ctx.fillRect(0, 0, 44, 44);
ctx.fillStyle = '#ffffff'; ctx.fillRect(28, 10, 8, 8); // Eyes
ctx.fillStyle = '#000000'; ctx.fillRect(32, 12, 4, 4);
this.textures.addCanvas('p1Tex', canvas);
// Player 2 Graphic (Red Custom Uniform Block)
ctx.clearRect(0,0,44,44);
ctx.fillStyle = '#ff2222'; ctx.fillRect(0, 0, 44, 44);
ctx.fillStyle = '#ffffff'; ctx.fillRect(8, 10, 8, 8); // Eyes
ctx.fillStyle = '#000000'; ctx.fillRect(8, 12, 4, 4);
this.textures.addCanvas('p2Tex', canvas);
// Football Graphic (Pro Leather Oval with Laces)
canvas.width = 30; canvas.height = 20;
ctx.clearRect(0,0,30,20);
ctx.fillStyle = '#8B4513'; ctx.beginPath(); ctx.ellipse(15, 10, 15, 10, 0, 0, 2 * Math.PI); ctx.fill();
ctx.fillStyle = '#ffffff'; ctx.fillRect(10, 9, 10, 2); // White Laces
ctx.fillRect(12, 7, 2, 6); ctx.fillRect(16, 7, 2, 6);
this.textures.addCanvas('ballTex', canvas);
}
function create() {
// 1. Draw Field & Football Yard Lines
this.add.rectangle(450, 250, 900, 500, 0x2e6f40);
for(let i = 100; i < 900; i += 100) {
this.add.rectangle(i, 235, 4, 430, 0xffffff, 0.15); // Yard stripes
}
// 2. Static Ground Floor
ground = this.add.rectangle(450, 475, 900, 50, 0x1c4426);
this.physics.add.existing(ground, true);
// 3. Goal Crossbars (Left & Right Nets)
leftGoalPost = this.add.rectangle(25, 330, 50, 240, 0xffffff, 0.3);
rightGoalPost = this.add.rectangle(875, 330, 50, 240, 0xffffff, 0.3);
this.physics.add.existing(leftGoalPost, true);
this.physics.add.existing(rightGoalPost, true);
// 4. Instantiate Players
player1 = this.physics.add.sprite(200, 350, 'p1Tex').setCollideWorldBounds(true).setDragX(1200);
player2 = this.physics.add.sprite(700, 350, 'p2Tex').setCollideWorldBounds(true).setDragX(1200);
// 5. Instantiate Bouncy Football
football = this.physics.add.sprite(450, 150, 'ballTex').setCollideWorldBounds(true).setBounce(0.75).setDragX(120);
// 6. Physics Rules Configuration
this.physics.add.collider(player1, ground);
this.physics.add.collider(player2, ground);
this.physics.add.collider(football, ground);
this.physics.add.collider(player1, player2); // Players bump into each other
this.physics.add.collider(player1, football, (p, b) => { b.setVelocityX(p.body.velocity.x * 1.3 + 80); });
this.physics.add.collider(player2, football, (p, b) => { b.setVelocityX(p.body.velocity.x * 1.3 - 80); });
// 7. Map Controls Keys
cursors = this.input.keyboard.createCursorKeys();
spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
wasd = this.input.keyboard.addKeys({
up: Phaser.Input.Keyboard.KeyCodes.W,
left: Phaser.Input.Keyboard.KeyCodes.A,
down: Phaser.Input.Keyboard.KeyCodes.S,
right: Phaser.Input.Keyboard.KeyCodes.D
});
fKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F);
// 8. Head-up Display (UI)
uiText = this.add.text(450, 45, 'P1: 0 | P2: 0', { fontSize: '32px', fill: '#fff' }).setOrigin(0.5);
timerText = this.add.text(450, 90, 'TIME: 60', { fontSize: '22px', fill: '#ffeb3b' }).setOrigin(0.5);
gameOverText = this.add.text(450, 220, '', { fontSize: '48px', fill: '#fff', backgroundColor: '#000', padding: 15 }).setOrigin(0.5).setVisible(false);
// Run Clock Timer Loop Tick every 1000ms (1 Second)
this.time.addEvent({ delay: 1000, callback: updateTimer, callbackScope: this, loop: true });
}
function update() {
if (!matchActive) return;
// --- PLAYER 1 CONTROLS (ARROWS) ---
if (cursors.left.isDown) player1.setVelocityX(-280);
else if (cursors.right.isDown) player1.setVelocityX(280);
if (cursors.up.isDown && player1.body.touching.down) player1.setVelocityY(-480);
// Player 1 Ball Strike Kick
if (Phaser.Input.Keyboard.JustDown(spaceKey)) {
if (Phaser.Math.Distance.Between(player1.x, player1.y, football.x, football.y) < 60) {
football.setVelocityX(550);
football.setVelocityY(-350);
}
}
// --- PLAYER 2 CONTROLS (WASD) ---
if (wasd.left.isDown) player2.setVelocityX(-280);
else if (wasd.right.isDown) player2.setVelocityX(280);
if (wasd.up.isDown && player2.body.touching.down) player2.setVelocityY(-480);
// Player 2 Ball Strike Kick
if (Phaser.Input.Keyboard.JustDown(fKey)) {
if (Phaser.Math.Distance.Between(player2.x, player2.y, football.x, football.y) < 60) {
football.setVelocityX(-550);
football.setVelocityY(-350);
}
}
// --- SCORING DETECTION LINES ---
if (football.x >= 850 && football.y > 210) {
p1Score++;
triggerGoal("PLAYER 1 SCORED!");
} else if (football.x <= 50 && football.y > 210) {
p2Score++;
triggerGoal("PLAYER 2 SCORED!");
}
}
function updateTimer() {
if (!matchActive) return;
timeRemaining--;
timerText.setText('TIME: ' + timeRemaining);
if (timeRemaining <= 0) {
matchActive = false;
football.setVisible(false);
let winner = p1Score > p2Score ? "PLAYER 1 WINS!" : p2Score > p1Score ? "PLAYER 2 WINS!" : "IT'S A TIE GAME!";
gameOverText.setText('GAME OVER\n' + winner).setVisible(true);
}
}
function triggerGoal(message) {
uiText.setText(`P1: ${p1Score} | P2: ${p2Score}`);
matchActive = false;
let notice = game.scene.scenes[0].add.text(450, 200, message, { fontSize: '40px', fill: '#ffeb3b', backgroundColor: '#111', padding: 10 }).setOrigin(0.5);
game.scene.scenes[0].time.delayedCall(1500, () => {
notice.destroy();
if (timeRemaining > 0) {
matchActive = true;
resetFormations();
}
});
}
function resetFormations() {
football.setPosition(450, 150).setVelocity(0, 0);
player1.setPosition(200, 350).setVelocity(0,0);
player2.setPosition(700, 350).setVelocity(0,0);
}
</script>
</body>
</html> 7,873 chars · codepastes.com