Brick breaker · qwen3.5-35b-a3b
Output generated Cost / run $0.0102 Runtime 164.6s
Prompt for this run
Build a playable brick-breaker game using the provided Phaser library. Use HTML, CSS, and JavaScript as needed. Write it to `index.html` in the current working directory using your file tools. Inline your own CSS and JavaScript. Do not use external assets, CDNs, or network requests. Use the provided `phaser.min.js` (global `Phaser`) through a plain local script tag; the runner will inline it into the final artifact. Required behavior: - Show a paddle, a ball, and a wall of breakable bricks. Move the paddle with Left/Right or the mouse, keeping it inside the playfield. - Initially rest the ball on the paddle; Space or a visible Launch control starts it. Bounce off the paddle and playfield walls. Hitting a brick removes it and increases the score once. - Start with three lives. A ball falling below the paddle costs one life and returns to the launch state if lives remain. - Clearing all bricks shows a win state; losing all lives shows Game Over. Restart restores all bricks, score, and lives. - Show the controls and generate all graphics using Phaser Graphics or generated textures. Do not load image or audio assets. Verify: Launch the ball, move the paddle, and inspect the opening bounces. Check that contact changes the ball direction and that a brick hit removes the brick and increases the score. Confirm the paddle stays inside the playfield.
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.
RUN
METRICS
Transcript · 13 messages
Build a playable brick-breaker game using the provided Phaser library. Use HTML, CSS, and JavaScript as needed. Write it to `index.html` in the current working directory using your file tools. Inline your own CSS and JavaScript. Do not use external assets, CDNs, or network requests. Use the provided `phaser.min.js` (global `Phaser`) through a plain local script tag; the runner will inline it into the final artifact. Required behavior: - Show a paddle, a ball, and a wall of breakable bricks. Move the paddle with Left/Right or the mouse, keeping it inside the playfield. - Initially rest the ball on the paddle; Space or a visible Launch control starts it. Bounce off the paddle and playfield walls. Hitting a brick removes it and increases the score once. - Start with three lives. A ball falling below the paddle costs one life and returns to the launch state if lives remain. - Clearing all bricks shows a win state; losing all lives shows Game Over. Restart restores all bricks, score, and lives. - Show the controls and generate all graphics using Phaser Graphics or generated textures. Do not load image or audio assets. Verify: Launch the ball, move the paddle, and inspect the opening bounces. Check that contact changes the ball direction and that a brick hit removes the brick and increases the score. Confirm the paddle stays inside the playfield.
thinking
The user wants me to build a brick-breaker game using Phaser.js. Let me first check what files are in the current directory to see the Phaser library file, then create the game. Let me start by listing the files in the current directory.
{"command":"ls -la /tmp/job-bsIV4i/"}ls: cannot access '/tmp/job-bsIV4i/': No such file or directory Command exited with code 2
thinking
Let me check the current working directory first.
{"command":"pwd && ls -la"}/tmp/job-bSIV4i total 1168 drwx------ 3 node node 4096 Oct 16 01:20 . drwxrwxrwt 6 root root 4096 Oct 16 01:20 .. drwxr-xr-x 3 node node 4096 Oct 16 01:20 .home -rw-r--r-- 1 node node 1181917 Oct 16 01:20 phaser.min.js
thinking
I can see the phaser.min.js file is present. Now I'll create the brick-breaker game with all the required features: 1. Paddle, ball, and wall of breakable bricks 2. Paddle controls with Left/Right or mouse 3. Ball starts on paddle, Space or Launch button to start 4. Bounce off paddle and walls 5. Hitting a brick removes it and increases score 6. Three lives, ball falling below costs one life 7. Win state when all bricks cleared, Game Over when all lives lost 8. Restart restores all bricks, score, and lives 9. Show controls and generate all graphics with Phaser Graphics Let me write the complete game in index.html.
/tmp/job-bSIV4i/index.html (14173 bytes)Successfully wrote to /tmp/job-bSIV4i/index.html
thinking
Now let me open the game in the browser to verify the initial output and check that the game works properly.
{"command":"cd /tmp/job-bSIV4i && ls -la"}total 1184 drwx------ 3 node node 4096 Oct 16 01:21 . drwxrwxrwt 6 root root 4096 Oct 16 01:20 .. drwxr-xr-x 3 node node 4096 Oct 16 01:20 .home -rw-r--r-- 1 node node 14177 Oct 16 01:21 index.html -rw-r--r-- 1 node node 1181917 Oct 16 01:20 phaser.min.js
thinking
Good, the file is created. Now let me open the browser to see the game.
{"action":"screenshot"}Frame 1, +0ms
[Browser image supplied to model]
{"console":["warning: An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing.","log: %c %c %c %c %c Phaser v3.80.1 (WebGL | Web Audio) %c https://phaser.io background: #ff0000 background: #ffff00 background: #00ff00 background: #00ffff color: #ffffff; background: #000000 background: transparent","error: Phaser.Display.GenerateCanvas is not a function","warning: [.WebGL-0x1e9c00170200]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels"],"dom":"←/→ or Mouse to move paddle Space to launch/restart","images":"attached"}
thinking
I need to fix the Phaser error about GenerateCanvas - I'll rewrite the HTML file with a corrected implementation that doesn't use outdated API calls.
There's an error with `GenerateCanvas` - this is an older API. I'll fix the code to use proper Phaser 3 texture generation. Let me rewrite the file: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Brick Breaker</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #1a1a2e; font-family: Arial, sans-serif; } #game-container { position: relative; } </style> </head> <body> <div id="game-container"></div> <script src="phaser.min.js"></script> <script> // Brick Breaker Game class GameScene extends Phaser.Scene { constructor() { super('GameScene'); } preload() { // Generate textures programmatically this.createPaddleTexture(); this.createBallTexture(); this.createBrickTexture(); } createPaddleTexture() { const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 15; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#00d4ff'; ctx.fillRect(0, 0, 100, 15); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 2; ctx.strokeRect(0, 0, 100, 15); const texture = this.textures.addCanvas('paddleTexture', canvas); } createBallTexture() { const canvas = document.createElement('canvas'); canvas.width = 20; canvas.height = 20; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#ff0066'; ctx.beginPath(); ctx.arc(10, 10, 10, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 2; ctx.stroke(); const texture = this.textures.addCanvas('ballTexture', canvas); } createBrickTexture() { const canvas = document.createElement('canvas'); canvas.width = 60; canvas.height = 25; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#00ff88'; ctx.fillRect(0, 0, 60, 25); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 1; ctx.strokeRect(0, 0, 60, 25); ctx.fillStyle = '#ffffff'; ctx.fillRect(5, 12, 50, 1); const texture = this.textures.addCanvas('brickTexture', canvas); } create() { const width = 800; const height = 600; // Create graphics for background this.add.rectangle(width / 2, height / 2, width, height, 0x1a1a2e); // Setup game variables this.paddleWidth = 100; this.paddleHeight = 15; this.ballRadius = 10; this.brickRows = 5; this.brickCols = 10; this.brickWidth = 60; this.brickHeight = 25; this.brickPadding = 8; this.brickOffsetX = 50; this.brickOffsetY = 60; // Create paddle - centered at bottom this.paddle = this.add.sprite(width / 2, height - 30, 'paddleTexture'); this.paddle.setOrigin(0.5, 1); // Create ball - resting on paddle this.ball = this.add.sprite(width / 2, height - 45, 'ballTexture'); this.ball.setOrigin(0.5, 0.5); this.ball.body.allowGravity = false; // Game state variables this.paddleSpeed = 400; this.ballSpeed = 300; this.currentBallSpeed = 300; this.score = 0; this.lives = 3; this.isLaunched = false; this.gameOver = false; this.win = false; // Create walls (invisible bodies) this.physics.world.setBounds(0, 0, width, height); this.physics.world.setBoundsCollision = true; // Create walls as invisible rectangles this.wallThickness = 20; this.topWall = this.add.rectangle(width / 2, 0, width, this.wallThickness, 0x000000, 0); this.bottomWall = this.add.rectangle(width / 2, height, width, this.wallThickness, 0x000000, 0); this.leftWall = this.add.rectangle(0, height / 2, this.wallThickness, height, 0x000000, 0); this.rightWall = this.add.rectangle(width, height / 2, this.wallThickness, height, 0x000000, 0); this.physics.add.existing(this.topWall); this.physics.add.existing(this.bottomWall); this.physics.add.existing(this.leftWall); this.physics.add.existing(this.rightWall); this.topWall.body immovable = true; this.bottomWall.body immovable = true; this.leftWall.body immovable = true; this.rightWall.body immovable = true; // Physics for paddle and ball this.physics.add.existing(this.paddle); this.physics.add.existing(this.ball); this.paddle.body.immovable = true; this.paddle.body.checkCollision.up = false; this.paddle.body.checkCollision.down = true; this.paddle.body.checkCollision.left = true; this.paddle.body.checkCollision.right = true; this.ball.body.setCollideWorldBounds(false); // Create bricks this.bricks = []; this.createBricks(); // Add colliders this.physics.add.collider(this.ball, this.topWall, this.handleWallCollision, null, this); this.physics.add.collider(this.ball, this.leftWall, this.handleWallCollision, null, this); this.physics.add.collider(this.ball, this.rightWall, this.handleWallCollision, null, this); this.physics.add.collider(this.ball, this.paddle, this.handlePaddleCollision, null, this); this.physics.add.collider(this.ball, this.bricks, this.handleBrickCollision, null, this); // Create score and lives displays this.scoreText = this.add.text(10, 10, 'Score: 0', { fontSize: '20px', fill: '#ffffff', fontFamily: 'Arial' }); this.livesText = this.add.text(10, 35, 'Lives: 3', { fontSize: '20px', fill: '#ffffff', fontFamily: 'Arial' }); // Create instructions text this.instructionsText = this.add.text(width / 2, 550, '<←/→ or Mouse to move paddle> <Space to launch/restart>', { fontSize: '16px', fill: '#888888', fontFamily: 'Arial', align: 'center' }); // Create controls button for launch this.launchBtn = this.add.text(width - 100, 30, 'Launch', { fontSize: '18px', fill: '#ffffff', backgroundColor: '#00d4ff', padding: { x: 15, y: 8 } }); this.launchBtn.setInteractive(); this.launchBtn.on('pointerdown', () => { if (!this.isLaunched && !this.gameOver && !this.win) { this.launchBall(); } }); // Create instructions for controls display this.controlsText = this.add.text(width / 2, height - 30, '', { fontSize: '14px', fill: '#666666', fontFamily: 'Arial', align: 'center' }); // Keyboard controls this.cursors = this.input.keyboard.createCursorKeys(); this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE); this.mouseControl = false; // Mouse controls for paddle this.input.mouse.setCursor(); this.input.on('pointermove', (pointer) => { if (!this.isLaunched && !this.gameOver && !this.win) { const newX = Phaser.Math.Clamp(pointer.x, this.paddleWidth / 2, width - this.paddleWidth / 2); this.paddle.x = newX; if (!this.isLaunched) { this.ball.x = this.paddle.x; } } }); // Game over text this.messageText = this.add.text(width / 2, height / 2, '', { fontSize: '48px', fill: '#ffffff', fontFamily: 'Arial', stroke: '#000000', strokeThickness: 6 }); this.messageText.setOrigin(0.5, 0.5); this.messageText.setVisible(false); } createBricks() { for (let row = 0; row < this.brickRows; row++) { for (let col = 0; col < this.brickCols; col++) { const x = this.brickOffsetX + col * (this.brickWidth + this.brickPadding); const y = this.brickOffsetY + row * (this.brickHeight + this.brickPadding); const brick = this.add.sprite(x, y, 'brickTexture'); brick.body.allowGravity = false; brick.setCollideWorldBounds(false); // Create brick body for physics const brickBody = new Phaser.Physics.Matter.Body( this.matter.world, brick, { width: this.brickWidth, height: this.brickHeight, density: 0.001, restitution: 0.5, friction: 0.5 } ); brick.body = brickBody; brickBody.isTrigger = false; // Store brick data brick.brickData = { row: row, col: col, active: true, color: this.getBrickColor(row, col) }; this.bricks.push(brick); } } } getBrickColor(row, col) { const colors = [0xff4444, 0xff6600, 0xffaa00, 0x00d4ff, 0xaa00ff]; return colors[row % colors.length]; } handlePaddleCollision(ball, paddle) { if (this.isLaunched) { const diff = ball.x - paddle.x; ball.setVelocity(diff * 4, -ball.velocity.y); const clampedDiff = Phaser.Math.Clamp(diff, -100, 100); const angle = Phaser.Math.Clamp(clampedDiff / 100, -0.7, 0.7); ball.body.velocity.y = -Math.cos(angle) * this.currentBallSpeed; } } handleWallCollision(ball, wall) { if (!this.isLaunched) return; const prevX = ball.x - ball.body.velocity.x * 0.016; const prevY = ball.y - ball.body.velocity.y * 0.016; if (prevX < this.wallThickness || prevX > 800 - this.wallThickness) { ball.setVelocityX(-ball.velocity.x); } if (prevY < this.wallThickness || prevY > 600 - this.wallThickness) { ball.setVelocityY(-ball.velocity.y); } } handleBrickCollision(ball, brick) { if (!this.isLaunched || !brick.brickData.active) return; brick.brickData.active = false; this.score += 10; this.scoreText.setText('Score: ' + this.score); brick.setAlpha(0); brick.body.disable(true); this.currentBallSpeed = Math.min(this.currentBallSpeed + 10, 500); const totalBricks = this.brickRows * this.brickCols; const activeBricks = this.bricks.filter(b => b.brickData.active).length; if (activeBricks === 0) { this.handleWin(); } } launchBall() { if (this.isLaunched || this.gameOver || this.win) return; this.isLaunched = true; this.currentBallSpeed = 300; const angle = -Math.PI / 2 + (Math.random() - 0.5) * 0.5; this.ball.body.velocity.y = -Math.cos(angle) * this.currentBallSpeed; this.ball.body.velocity.x = Math.sin(angle) * this.currentBallSpeed; this.launchBtn.setVisible(false); this.instructionsText.setText('←/→ or Mouse to move paddle'); } loseLife() { this.lives--; this.livesText.setText('Lives: ' + this.lives); if (this.lives <= 0) { this.handleGameOver(); } else { this.resetBall(); } } resetBall() { this.isLaunched = false; this.currentBallSpeed = 300; this.ball.x = this.paddle.x; this.ball.y = this.paddle.y - 15; this.ball.body.velocity.x = 0; this.ball.body.velocity.y = 0; this.launchBtn.setVisible(true); this.instructionsText.setText('Space to launch'); this.input.mousePointer.visible = false; } handleGameOver() { this.gameOver = true; this.messageText.setText('GAME OVER\nClick to Restart'); this.messageText.setVisible(true); this.messageText.setInteractive(); this.messageText.on('pointerdown', () => { this.restartGame(); }); } handleWin() { this.win = true; this.messageText.setText('YOU WIN!\nClick to Restart'); this.messageText.setVisible(true); this.messageText.setInteractive(); this.messageText.on('pointerdown', () => { this.restartGame(); }); } restartGame() { this.score = 0; this.lives = 3; this.scoreText.setText('Score: 0'); this.livesText.setText('Lives: 3'); this.gameOver = false; this.win = false; this.isLaunched = false; this.messageText.setVisible(false); this.bricks.forEach((brick, index) => { brick.brickData.active = true; brick.setAlpha(1); brick.body.enable(true); brick.brickData.color = this.getBrickColor(brick.brickData.row, brick.brickData.col); }); this.resetBall(); this.input.mousePointer.visible = false; } update(time, delta) { if (this.gameOver || this.win) return; const width = 800; const height = 600; if (!this.isLaunched) { if (Phaser.Input.Keyboard.JustDown(this.spaceKey)) { this.launchBall(); } if (Phaser.Input.Keyboard.IsDown(this.cursors.left)) { this.paddle.x = Phaser.Math.Clamp(this.paddle.x - this.paddleSpeed * delta / 1000, this.paddleWidth / 2, width - this.paddleWidth / 2); } else if (Phaser.Input.Keyboard.IsDown(this.cursors.right)) { this.paddle.x = Phaser.Math.Clamp(this.paddle.x + this.paddleSpeed * delta / 1000, this.paddleWidth / 2, width - this.paddleWidth / 2); } this.ball.x = this.paddle.x; this.ball.y = this.paddle.y - 15; } else { this.ball.body.update(time, delta); if (this.ball.y > height + 20) { this.physics.world.remove(this.ball); this.ball.destroy(); this.ball = this.add.sprite(this.paddle.x, this.paddle.y - 15, 'ballTexture'); this.ball.setOrigin(0.5, 0.5); this.physics.add.existing(this.ball); this.handleLoseLife(); } } } handleLoseLife() { this.lives--; this.livesText.setText('Lives: ' + this.lives); if (this.lives <= 0) { this.gameOver = true; this.messageText.setText('GAME OVER\nClick to Restart'); this.messageText.setVisible(true); this.messageText.setInteractive(); this.messageText.on('pointerdown', () => { this.restartGame(); }); } else { this.isLaunched = false; this.ball.x = this.paddle.x; this.ball.y = this.paddle.y - 15; this.ball.body.velocity.x = 0; this.ball.body.velocity.y = 0; this.currentBallSpeed = 300; this.ball.setAlpha(1); this.ball.body.collisionType = 3; this.ball.body.stop(); this.ball.x = this.paddle.x; this.ball.y = this.paddle.y - 15; this.input.mousePointer.visible = false; } } } const config = { type: Phaser.AUTO, width: 800, height: 600, backgroundColor: '#1a1a2e', parent: 'game-container', physics: { default: 'matter', matter: { gravity: { y: 0.5 }, debug: false } }, scene: GameScene }; const game = new Phaser.Game(config); </script> </body> </html> ``` I had syntax errors in the physics setup. Let me fix it properly: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Brick Breaker</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #1a1a2e; font-family: Arial, sans-serif; } #game-container { position: relative; } </style> </head> <body> <div id="game-container"></div> <script src="phaser.min.js"></script> <script> class GameScene extends Phaser.Scene { constructor() { super('GameScene'); } preload() { this.createPaddleTexture(); this.createBallTexture(); this.createBrickTexture(); } createPaddleTexture() { const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 15; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#00d4ff'; ctx.fillRect(0, 0, 100, 15); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 2; ctx.strokeRect(0, 0, 100, 15); this.textures.addCanvas('paddleTexture', canvas); } createBallTexture() { const canvas = document.createElement('canvas'); canvas.width = 20; canvas.height = 20; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#ff0066'; ctx.beginPath(); ctx.arc(10, 10, 10, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 2; ctx.stroke(); this.textures.addCanvas('ballTexture', canvas); } createBrickTexture() { const canvas = document.createElement('canvas'); canvas.width = 60; canvas.height = 25; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#00ff88'; ctx.fillRect(2, 2, 56, 21); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 1; ctx.strokeRect(0, 0, 60, 25); this.textures.addCanvas('brickTexture', canvas); } create() { const width = 800; const height = 600; // Background this.add.rectangle(width / 2, height / 2, width, height, 0x1a1a2e); // Variables this.paddleWidth = 100; this.paddleHeight = 15; this.ballRadius = 10; this.brickRows = 5; this.brickCols = 10; this.brickWidth = 60; this.brickHeight = 25; this.brickPadding = 8; this.brickOffsetX = 50; this.brickOffsetY = 60; // Physics bounds this.physics.world.setBounds(0, 0, width, height); // Walls - using invisible bodies const wallThickness = 20; const topWall = this.physics.add.staticRectangle(width / 2, 0, width, wallThickness); const bottomWall = this.physics.add.staticRectangle(width / 2, height, width, wallThickness); const leftWall = this.physics.add.staticRectangle(0, height / 2, wallThickness, height); const rightWall = this.physics.add.staticRectangle(width, height / 2, wallThickness, height); // Paddle this.paddle = this.physics.add.sprite(width / 2, height - 30, 'paddleTexture'); this.paddle.setImmovable(true); this.paddle.setCollideWorldBounds(true); this.paddle.setCollision({ up: false, down: true, left: true, right: true }); // Ball this.ball = this.physics.add.sprite(width / 2, height - 45, 'ballTexture'); this.ball.setCollideWorldBounds(false); this.ball.body.allowGravity = false; // Score and lives this.score = 0; this.lives = 3; this.isLaunched = false; this.gameOver = false; this.win = false; this.scoreText = this.add.text(10, 10, 'Score: 0', { fontSize: '20px', fill: '#ffffff', fontFamily: 'Arial' }); this.livesText = this.add.text(10, 35, 'Lives: 3', { fontSize: '20px', fill: '#ffffff', fontFamily: 'Arial' }); // Instructions this.instructionsText = this.add.text(width / 2, 550, '←/→ or Mouse to move paddle | Space to launch', { fontSize: '16px', fill: '#888888', fontFamily: 'Arial', align: 'center' }); // Launch button this.launchBtn = this.add.text(width - 100, 30, 'LAUNCH', { fontSize: '18px', fill: '#000', backgroundColor: '#00d4ff', padding: { x: 15, y: 8 } }); this.launchBtn.setInteractive(); this.launchBtn.on('pointerdown', () => { if (!this.isLaunched && !this.gameOver && !this.win) { this.launchBall(); } }); // Message text for game over / win this.messageText = this.add.text(width / 2, height / 2, '', { fontSize: '48px', fill: '#ffffff', fontFamily: 'Arial', stroke: '#000', strokeThickness: 6 }); this.messageText.setOrigin(0.5, 0.5); this.messageText.setVisible(false); // Create bricks this.bricks = []; this.createBricks(); // Collisions this.physics.add.collider(this.ball, this.paddle, this.onPaddleCollision, null, this); this.physics.add.collider(this.ball, this.bricks, this.onBrickCollision, null, this); // Controls this.cursors = this.input.keyboard.createCursorKeys(); this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE); // Mouse control for paddle this.input.on('pointermove', (pointer) => { if (!this.isLaunched && !this.gameOver && !this.win) { const newX = Phaser.Math.Clamp(pointer.x, 50, width - 50); this.paddle.x = newX; if (!this.isLaunched) { this.ball.x = this.paddle.x; } } }); // Initial state - ball on paddle this.resetBallPosition(); } createBricks() { for (let row = 0; row < this.brickRows; row++) { for (let col = 0; col < this.brickCols; col++) { const x = this.brickOffsetX + col * (this.brickWidth + this.brickPadding); const y = this.brickOffsetY + row * (this.brickHeight + this.brickPadding); const brick = this.physics.add.sprite(x, y, 'brickTexture'); brick.body.allowGravity = false; brick.body.setImmovable(true); brick.body.setSize(this.brickWidth, this.brickHeight); brick.brickData = { row: row, col: col, active: true }; brick.body.onWorldBounds = false; this.bricks.push(brick); } } // Wall collision for bricks this.bricks.forEach(brick => { brick.body.checkCollision.up = true; brick.body.checkCollision.down = true; brick.body.checkCollision.left = true; brick.body.checkCollision.right = true; }); } resetBallPosition() { this.ball.x = this.paddle.x; this.ball.y = this.paddle.y - 15; this.ball.body.velocity.x = 0; this.ball.body.velocity.y = 0; } launchBall() { if (this.isLaunched) return; this.isLaunched = true; this.currentBallSpeed = 250; const angle = -Math.PI / 2 + (Math.random() - 0.5) * 0.3; this.ball.body.velocity.x = Math.sin(angle) * this.currentBallSpeed; this.ball.body.velocity.y = Math.cos(angle) * -this.currentBallSpeed; this.launchBtn.destroy(); this.instructionsText.setText('←/→ to move paddle'); } onPaddleCollision(ball, paddle) { if (!this.isLaunched) return; const diff = ball.x - paddle.x; const clampedDiff = Phaser.Math.Clamp(diff, -80, 80); const angle = clampedDiff / 80; this.ball.body.velocity.x = angle * 350; this.ball.body.velocity.y = -Math.abs(this.ball.body.velocity.y) * 0.9 - 100; } onBrickCollision(ball, brick) { if (!this.isLaunched || !brick.brickData.active) return; brick.brickData.active = false; this.score += 10; this.scoreText.setText('Score: ' + this.score); brick.setAlpha(0.3); brick.body.setCollision(false, false, false, false); // Check win const activeBricks = this.bricks.filter(b => b.brickData.active); if (activeBricks.length === 0) { this.handleWin(); } } handleWin() { this.win = true; this.messageText.setText('YOU WIN!\nClick to Restart'); this.messageText.setVisible(true); this.messageText.setInteractive(); this.messageText.on('pointerdown', () => { this.restartGame(); }); } handleLoseLife() { this.lives--; this.livesText.setText('Lives: ' + this.lives); if (this.lives <= 0) { this.gameOver = true; this.messageText.setText('GAME OVER\nClick to Restart'); this.messageText.setVisible(true); this.messageText.setInteractive(); this.messageText.on('pointerdown', () => { this.restartGame(); }); } else { this.resetBallPosition(); this.isLaunched = false; this.launchBtn = this.add.text(700, 30, 'LAUNCH', { fontSize: '18px', fill: '#000', backgroundColor: '#00d4ff', padding: { x: 15, y: 8 } }); this.launchBtn.setInteractive(); this.launchBtn.on('pointerdown', () => { if (!this.isLaunched && !this.gameOver && !this.win) { this.launchBall(); } }); this.instructionsText.setText('←/→ or Mouse to move paddle | Space to launch'); } } restartGame() { this.score = 0; this.lives = 3; this.scoreText.setText('Score: 0'); this.livesText.setText('Lives: 3'); this.gameOver = false; this.win = false; this.isLaunched = false; this.messageText.destroy(); this.messageText = this.add.text(400, 300, '', { fontSize: '48px', fill: '#ffffff', fontFamily: 'Arial', stroke: '#000', strokeThickness: 6 }); this.messageText.setOrigin(0.5, 0.5); // Reset bricks this.bricks.forEach(brick => { brick.brickData.active = true; brick.setAlpha(1); brick.body.setCollision(true, true, true, true); brick.body.checkCollision.up = true; brick.body.checkCollision.down = true; brick.body.checkCollision.left = true; brick.body.checkCollision.right = true; }); // Reset paddle and ball this.paddle.x = 400; this.resetBallPosition(); // Restart controls this.instructionsText.setText('←/→ or Mouse to move paddle | Space to launch'); this.launchBtn = this.add.text(700, 30, 'LAUNCH', { fontSize: '18px', fill: '#000', backgroundColor: '#00d4ff', padding: { x: 15, y: 8 } }); this.launchBtn.setInteractive(); this.launchBtn.on('pointerdown', () => { if (!this.isLaunched && !this.gameOver && !this.win) { this.launchBall(); } }); } update(time, delta) { if (this.gameOver || this.win) return; // Paddle movement with keyboard if (Phaser.Input.Keyboard.IsDown(this.cursors.left)) { this.paddle.x = Phaser.Math.Clamp(this.paddle.x - 400 * delta, 50, 750); } else if (Phaser.Input.Keyboard.IsDown(this.cursors.right)) { this.paddle.x = Phaser.Math.Clamp(this.paddle.x + 400 * delta, 50, 750); } if (!this.isLaunched) { this.ball.x = this.paddle.x; this.ball.y = this.paddle.y - 15; if (Phaser.Input.Keyboard.JustDown(this.spaceKey)) { this.launchBall(); } } else { // Ball falling below paddle if (this.ball.y > 620) { this.physics.world.remove(this.ball); this.ball.destroy(); // Create new ball this.ball = this.physics.add.sprite(this.paddle.x, this.paddle.y - 15, 'ballTexture'); this.ball.setCollideWorldBounds(false); this.ball.body.allowGravity = false; this.handleLoseLife(); } } } } const config = { type: Phaser.AUTO, width: 800, height: 600, backgroundColor: '#1a1a2e', parent: 'game-container', physics: { default: 'arcade', arcade: { gravity: { y: 0 }, debug: false } }, scene: GameScene }; const game = new Phaser.Game(config); </script> </body> </html> ``` Now let me check the file was written correctly: ```bash cat /tmp/job-bSIV4i/index.html | head -50 ```