diff --git a/apps/bbreaker/ChangeLog b/apps/bbreaker/ChangeLog index 4e6f77f17d..6556ff80c3 100644 --- a/apps/bbreaker/ChangeLog +++ b/apps/bbreaker/ChangeLog @@ -1,3 +1,4 @@ 0.01: It works somehow, early version for testers and feedback :) 0.02: Changed almost all code with Frederic version of Pong and adjusted to be a BrickBreaker!, still Alpha -0.03: Rewrote the whole thing to have less code and better graphics, now it works. \ No newline at end of file +0.03: Rewrote the whole thing to have less code and better graphics, now it works. +0.04: Rewrote part of the code to coupe with the flickering and added better logic to handle the graphics. \ No newline at end of file diff --git a/apps/bbreaker/README.md b/apps/bbreaker/README.md index 032bb87a81..56175463ac 100644 --- a/apps/bbreaker/README.md +++ b/apps/bbreaker/README.md @@ -21,6 +21,8 @@ https://developer.mozilla.org/en-US/docs/Games/Tutorials/2D_Breakout_game_pure_J Started on 2020 but rewrote all in 2025 and this is the version I got without having issues with Memory Exhaustion. +And yes, for Bangle 1, old school! + ## Creator Israel Ochoa \ No newline at end of file diff --git a/apps/bbreaker/app.js b/apps/bbreaker/app.js index 5a5e65e17f..6d6ef94013 100644 --- a/apps/bbreaker/app.js +++ b/apps/bbreaker/app.js @@ -1,31 +1,31 @@ -(function () { - const BALL_RADIUS = 3; - const PADDLE_WIDTH = 26; - const PADDLE_HEIGHT = 6; - const BRICK_ROWS = 4; - const BRICK_HEIGHT = 8; - const BRICK_PADDING = 4; - const BRICK_OFFSET_TOP = 40; - const BRICK_OFFSET_LEFT = 2; - const SPEED_MULTIPLIER = 1.1; - const PADDLE_SPEED = 12; - - let ball, paddle, interval; - let bricks = []; - let BRICK_WIDTH, BRICK_COLS; - let paddleIntervalLeft, paddleIntervalRight; - let score = 0; - let level = 1; - let highScore = 0; - let paused = false; - let gameOver = false; - let lives = 3; - const storage = require("Storage"); - //const BEEP = () => Bangle.buzz(100); +(function () { + var BALL_RADIUS = 3; + var PADDLE_WIDTH = 26; + var PADDLE_HEIGHT = 6; + var BRICK_ROWS = 2; + var BRICK_HEIGHT = 8; + var BRICK_PADDING = 4; + var BRICK_OFFSET_TOP = 60; + var BRICK_OFFSET_LEFT = 2; + var SPEED_MULTIPLIER = 1.1; + var PADDLE_SPEED = 12; + + var ball, paddle, interval; + var bricks = []; + var BRICK_WIDTH, BRICK_COLS; + var score = 0; + var level = 1; + var highScore = 0; + var paused = false; + var gameOver = false; + var lives = 3; + var paddleMove = 0; + + var storage = require("Storage"); function loadHighScore() { - const saved = storage.readJSON("breakout_highscore.json", 1); + var saved = storage.readJSON("breakout_highscore.json", 1); highScore = saved && saved.highScore ? saved.highScore : 0; } @@ -37,44 +37,32 @@ } function initBricks() { - bricks = []; // Reset the array completely - for (let r = 0; r < BRICK_ROWS; r++) { - for (let c = 0; c < BRICK_COLS; c++) { - let brickX = BRICK_OFFSET_LEFT + c * (BRICK_WIDTH + BRICK_PADDING); - let brickY = BRICK_OFFSET_TOP + r * (BRICK_HEIGHT + BRICK_PADDING); - bricks.push({ x: brickX, y: brickY, status: 1 }); - } + bricks = []; + for (var i = 0; i < BRICK_ROWS * BRICK_COLS; i++) { + bricks.push(1); } } - function showGetReady(callback) { - g.clear(); - g.setFont("6x8", 2); - g.setFontAlign(0, 0); - g.setColor(1, 1, 0); - g.drawString("GET READY!", g.getWidth() / 2, g.getHeight() / 2); - g.flip(); - setTimeout(callback, 1500); // wait 1.5 seconds then start - } - function initGame() { - const screenWidth = g.getWidth(); + var screenWidth = g.getWidth(); BRICK_COLS = Math.min(5, Math.floor((screenWidth - BRICK_OFFSET_LEFT + BRICK_PADDING) / (15 + BRICK_PADDING))); BRICK_WIDTH = Math.floor((screenWidth - BRICK_OFFSET_LEFT - (BRICK_COLS - 1) * BRICK_PADDING) / BRICK_COLS); ball = { x: screenWidth / 2, - y: g.getHeight() - 20, - dx: 3, + y: g.getHeight() - 40, + dx: (Math.random() > 0.5 ? 1 : -1) * 3, dy: -3, - radius: BALL_RADIUS + radius: BALL_RADIUS, + prevPos: null }; paddle = { x: screenWidth / 2 - PADDLE_WIDTH / 2, - y: g.getHeight() - 10, + y: g.getHeight() - 20, width: PADDLE_WIDTH, - height: PADDLE_HEIGHT + height: PADDLE_HEIGHT, + prevPos: null }; lives = 3; score = 0; @@ -86,30 +74,26 @@ } function drawLives() { - const heartSize = 6; - const spacing = 2; - const startX = g.getWidth() - (lives * (heartSize + spacing)) - 2; - const y = 12; - - g.setColor(1, 0, 0); // red - - for (let i = 0; i < lives; i++) { - const x = startX + i * (heartSize + spacing); - g.fillPoly([ - x + 3, y, - x + 6, y + 3, - x + 3, y + 6, - x, y + 3 - ], true); + var heartSize = 6; + var spacing = 2; + var startX = g.getWidth() - (lives * (heartSize + spacing)) - 2; + var y = 32; + g.setColor(1, 0, 0); + for (var i = 0; i < lives; i++) { + var x = startX + i * (heartSize + spacing); + g.fillPoly([x + 3, y, x + 6, y + 3, x + 3, y + 6, x, y + 3], true); } } function drawBricks() { g.setColor(0, 1, 0); - for (let i = 0; i < bricks.length; i++) { - let b = bricks[i]; - if (b.status) { - g.fillRect(b.x, b.y, b.x + BRICK_WIDTH, b.y + BRICK_HEIGHT); + for (var i = 0; i < bricks.length; i++) { + if (bricks[i]) { + var c = i % BRICK_COLS; + var r = Math.floor(i / BRICK_COLS); + var brickX = BRICK_OFFSET_LEFT + c * (BRICK_WIDTH + BRICK_PADDING); + var brickY = BRICK_OFFSET_TOP + r * (BRICK_HEIGHT + BRICK_PADDING); + g.fillRect(brickX, brickY, brickX + BRICK_WIDTH - 1, brickY + BRICK_HEIGHT - 1); } } } @@ -122,19 +106,20 @@ } function drawPaddle() { - g.setColor(0,1, 1); - g.fillRect(paddle.x, paddle.y, paddle.x + paddle.width, paddle.y + paddle.height); + g.setColor(0, 1, 1); + g.fillRect(paddle.x, paddle.y, paddle.x + paddle.width - 1, paddle.y + paddle.height - 1); } function drawHUD() { + g.setColor(0, 0, 0).fillRect(0, 0, g.getWidth(), BRICK_OFFSET_TOP - 1); g.setColor(1, 1, 1); g.setFont("6x8", 1); g.setFontAlign(-1, -1); - g.drawString("Score: " + score, 2, 2); + g.drawString("Score: " + score, 2, 22); g.setFontAlign(0, -1); - g.drawString("High: " + highScore, g.getWidth() / 2, 2); + g.drawString("High: " + highScore, g.getWidth() / 2, 22); g.setFontAlign(1, -1); - g.drawString("Lvl: " + level, g.getWidth() - 2, 2); + g.drawString("Lvl: " + level, g.getWidth() - 2, 22); drawLives(); if (paused) { g.setFontAlign(0, 0); @@ -143,12 +128,18 @@ } function draw() { - g.clear(); - drawBricks(); + if (paddle.prevPos) { + g.setColor(0, 0, 0).fillRect(paddle.prevPos.x - 1, paddle.prevPos.y - 1, paddle.prevPos.x + paddle.width + 1, paddle.prevPos.y + paddle.height + 1); + } + if (ball.prevPos) { + g.setColor(0, 0, 0).fillCircle(ball.prevPos.x, ball.prevPos.y, ball.radius + 1); + } + drawHUD(); drawBall(); drawPaddle(); - drawHUD(); g.flip(); + ball.prevPos = { x: ball.x, y: ball.y }; + paddle.prevPos = { x: paddle.x, y: paddle.y, width: paddle.width, height: paddle.height }; } function showGameOver() { @@ -166,148 +157,137 @@ } function collisionDetection() { - for (let i = 0; i < bricks.length; i++) { - let b = bricks[i]; - if (b.status) { - if ( - ball.x + ball.radius > b.x && - ball.x - ball.radius < b.x + BRICK_WIDTH && - ball.y + ball.radius > b.y && - ball.y - ball.radius < b.y + BRICK_HEIGHT - ) { + for (var i = 0; i < bricks.length; i++) { + if (bricks[i]) { + var c = i % BRICK_COLS; + var r = Math.floor(i / BRICK_COLS); + var brickX = BRICK_OFFSET_LEFT + c * (BRICK_WIDTH + BRICK_PADDING); + var brickY = BRICK_OFFSET_TOP + r * (BRICK_HEIGHT + BRICK_PADDING); + if (ball.x + ball.radius > brickX && ball.x - ball.radius < brickX + BRICK_WIDTH && ball.y + ball.radius > brickY && ball.y - ball.radius < brickY + BRICK_HEIGHT) { ball.dy = -ball.dy; - b.status = 0; + bricks[i] = 0; score += 10; + g.setColor(0, 0, 0).fillRect(brickX, brickY, brickX + BRICK_WIDTH - 1, brickY + BRICK_HEIGHT - 1); break; } } } } - function allBricksCleared() { - for (let i = 0; i < bricks.length; i++) { - if (bricks[i].status !== 0) return false; + for (var i = 0; i < bricks.length; i++) { + if (bricks[i]) return false; } return true; } + function resetAfterLifeLost() { + clearInterval(interval); + interval = undefined; + ball.x = g.getWidth() / 2; + ball.y = g.getHeight() - 40; + ball.dx = (Math.random() > 0.5 ? 1 : -1) * 3; + ball.dy = -3; + paddle.x = g.getWidth() / 2 - PADDLE_WIDTH / 2; + ball.prevPos = null; + paddle.prevPos = null; + g.clear(); + drawBricks(); + draw(); + setTimeout(() => { interval = setInterval(update, 50); }, 1000); + } + function update() { if (paused || gameOver) return; - + if (paddleMove) { + paddle.x += paddleMove * PADDLE_SPEED; + if (paddle.x < 0) paddle.x = 0; + if (paddle.x + paddle.width > g.getWidth()) { + paddle.x = g.getWidth() - paddle.width; + } + } ball.x += ball.dx; ball.y += ball.dy; - if (ball.x + ball.radius > g.getWidth() || ball.x - ball.radius < 0) { ball.dx = -ball.dx; } if (ball.y - ball.radius < 0) { ball.dy = -ball.dy; } - - if ( - ball.y + ball.radius >= paddle.y && - ball.x >= paddle.x && - ball.x <= paddle.x + paddle.width - ) { + if (ball.y + ball.radius >= paddle.y && ball.x >= paddle.x && ball.x <= paddle.x + paddle.width) { ball.dy = -ball.dy; ball.y = paddle.y - ball.radius; } - if (ball.y + ball.radius > g.getHeight()) { lives--; if (lives > 0) { - // Reset ball and paddle only - ball.x = g.getWidth() / 2; - ball.y = g.getHeight() - 30; - ball.dx = 3; - ball.dy = -3; - paddle.x = g.getWidth() / 2 - PADDLE_WIDTH / 2; - paddle.y = g.getHeight() - 20; - draw(); + resetAfterLifeLost(); return; } else { clearInterval(interval); interval = undefined; - if (paddleIntervalLeft) { - clearInterval(paddleIntervalLeft); - paddleIntervalLeft = null; - } - if (paddleIntervalRight) { - clearInterval(paddleIntervalRight); - paddleIntervalRight = null; - } saveHighScore(); gameOver = true; - draw(); setTimeout(showGameOver, 50); return; } } - collisionDetection(); - if (allBricksCleared()) { - ball.dx *= SPEED_MULTIPLIER; - ball.dy *= SPEED_MULTIPLIER; level++; + ball.dx = (Math.random() > 0.5 ? 1 : -1) * Math.abs(ball.dx) * SPEED_MULTIPLIER; + ball.dy *= SPEED_MULTIPLIER; initBricks(); + ball.prevPos = null; + paddle.prevPos = null; + g.clear(); + drawBricks(); } - draw(); } - function movePaddle(x) { - if (gameOver || paused) return; // prevent paddle movement when not needed - paddle.x += x; - if (paddle.x < 0) paddle.x = 0; - if (paddle.x + paddle.width > g.getWidth()) { - paddle.x = g.getWidth() - paddle.width; - } + function showGetReady(callback) { + g.clear(); + g.setFont("6x8", 2); + g.setFontAlign(0, 0); + g.setColor(1, 1, 0); + g.drawString("GET READY!", g.getWidth() / 2, g.getHeight() / 2); + g.flip(); + setTimeout(() => { + g.clear(); + drawBricks(); + draw(); + callback(); + }, 1500); } function startGame() { initGame(); - draw(); showGetReady(() => { - interval = setInterval(update, 80); + interval = setInterval(update, 50); }); } setWatch(() => { if (gameOver) { startGame(); + return; + } + paused = !paused; + if (paused) { + drawHUD(); + g.flip(); } else { - paused = !paused; + g.clear(); + drawBricks(); draw(); } }, BTN2, { repeat: true, edge: "rising" }); - setWatch(() => { - if (!paddleIntervalLeft) { - paddleIntervalLeft = setInterval(() => movePaddle(-PADDLE_SPEED), 50); - } - }, BTN1, { repeat: true, edge: "rising" }); - - setWatch(() => { - if (paddleIntervalLeft) { - clearInterval(paddleIntervalLeft); - paddleIntervalLeft = null; - } - }, BTN1, { repeat: true, edge: "falling" }); - - setWatch(() => { - if (!paddleIntervalRight) { - paddleIntervalRight = setInterval(() => movePaddle(PADDLE_SPEED), 50); - } - }, BTN3, { repeat: true, edge: "rising" }); - - setWatch(() => { - if (paddleIntervalRight) { - clearInterval(paddleIntervalRight); - paddleIntervalRight = null; - } - }, BTN3, { repeat: true, edge: "falling" }); + setWatch(() => { paddleMove = -1; }, BTN1, { repeat: true, edge: "rising" }); + setWatch(() => { paddleMove = 0; }, BTN1, { repeat: true, edge: "falling" }); + setWatch(() => { paddleMove = 1; }, BTN3, { repeat: true, edge: "rising" }); + setWatch(() => { paddleMove = 0; }, BTN3, { repeat: true, edge: "falling" }); startGame(); })(); \ No newline at end of file diff --git a/apps/bbreaker/metadata.json b/apps/bbreaker/metadata.json index 5bfebf4218..41d081489d 100644 --- a/apps/bbreaker/metadata.json +++ b/apps/bbreaker/metadata.json @@ -2,7 +2,7 @@ "name": "BrickBreaker", "shortName":"BrickBreaker", "icon": "bbreaker.png", - "version":"0.03", + "version":"0.04", "description": "A simple BreakOut clone for the Banglejs", "tags": "game", "readme": "README.md",