Made more changes to make the game more efficient

master
IsReal8a 2025-07-04 17:25:53 +02:00
parent d3aa3abeae
commit d602bc16c5
4 changed files with 137 additions and 156 deletions

View File

@ -1,3 +1,4 @@
0.01: It works somehow, early version for testers and feedback :) 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.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. 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.

View File

@ -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. 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 ## Creator
Israel Ochoa <isuraeru at gmail.com> Israel Ochoa <isuraeru at gmail.com>

View File

@ -1,31 +1,31 @@
(function () { (function () {
const BALL_RADIUS = 3; var BALL_RADIUS = 3;
const PADDLE_WIDTH = 26; var PADDLE_WIDTH = 26;
const PADDLE_HEIGHT = 6; var PADDLE_HEIGHT = 6;
const BRICK_ROWS = 2; var BRICK_ROWS = 2;
const BRICK_HEIGHT = 8; var BRICK_HEIGHT = 8;
const BRICK_PADDING = 4; var BRICK_PADDING = 4;
const BRICK_OFFSET_TOP = 40; var BRICK_OFFSET_TOP = 60;
const BRICK_OFFSET_LEFT = 2; var BRICK_OFFSET_LEFT = 2;
const SPEED_MULTIPLIER = 1.1; var SPEED_MULTIPLIER = 1.1;
const PADDLE_SPEED = 12; var PADDLE_SPEED = 12;
let ball, paddle, interval; var ball, paddle, interval;
let bricks = []; var bricks = [];
let BRICK_WIDTH, BRICK_COLS; var BRICK_WIDTH, BRICK_COLS;
let paddleIntervalLeft, paddleIntervalRight; var score = 0;
let score = 0; var level = 1;
let level = 1; var highScore = 0;
let highScore = 0; var paused = false;
let paused = false; var gameOver = false;
let gameOver = false; var lives = 3;
let lives = 3; var paddleMove = 0;
const storage = require("Storage"); var storage = require("Storage");
//const BEEP = () => Bangle.buzz(100);
function loadHighScore() { 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; highScore = saved && saved.highScore ? saved.highScore : 0;
} }
@ -37,46 +37,32 @@
} }
function initBricks() { function initBricks() {
bricks = []; // Reset the array completely bricks = [];
let brickCount = 0; for (var i = 0; i < BRICK_ROWS * BRICK_COLS; i++) {
for (let r = 0; r < BRICK_ROWS; r++) { bricks.push(1);
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);
if (brickCount++ > 20) return;
bricks.push({ x: brickX, y: brickY, status: 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() { 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_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); BRICK_WIDTH = Math.floor((screenWidth - BRICK_OFFSET_LEFT - (BRICK_COLS - 1) * BRICK_PADDING) / BRICK_COLS);
ball = { ball = {
x: screenWidth / 2, x: screenWidth / 2,
y: g.getHeight() - 20, y: g.getHeight() - 40,
dx: 3, dx: (Math.random() > 0.5 ? 1 : -1) * 3,
dy: -3, dy: -3,
radius: BALL_RADIUS radius: BALL_RADIUS,
prevPos: null
}; };
paddle = { paddle = {
x: screenWidth / 2 - PADDLE_WIDTH / 2, x: screenWidth / 2 - PADDLE_WIDTH / 2,
y: g.getHeight() - 10, y: g.getHeight() - 20,
width: PADDLE_WIDTH, width: PADDLE_WIDTH,
height: PADDLE_HEIGHT height: PADDLE_HEIGHT,
prevPos: null
}; };
lives = 3; lives = 3;
score = 0; score = 0;
@ -88,30 +74,26 @@ if (brickCount++ > 20) return;
} }
function drawLives() { function drawLives() {
const heartSize = 6; var heartSize = 6;
const spacing = 2; var spacing = 2;
const startX = g.getWidth() - (lives * (heartSize + spacing)) - 2; var startX = g.getWidth() - (lives * (heartSize + spacing)) - 2;
const y = 12; var y = 32;
g.setColor(1, 0, 0);
g.setColor(1, 0, 0); // red for (var i = 0; i < lives; i++) {
var x = startX + i * (heartSize + spacing);
for (let i = 0; i < lives; i++) { g.fillPoly([x + 3, y, x + 6, y + 3, x + 3, y + 6, x, y + 3], true);
const x = startX + i * (heartSize + spacing);
g.fillPoly([
x + 3, y,
x + 6, y + 3,
x + 3, y + 6,
x, y + 3
], true);
} }
} }
function drawBricks() { function drawBricks() {
g.setColor(0, 1, 0); g.setColor(0, 1, 0);
for (let i = 0; i < bricks.length; i++) { for (var i = 0; i < bricks.length; i++) {
let b = bricks[i]; if (bricks[i]) {
if (b.status) { var c = i % BRICK_COLS;
g.fillRect(b.x, b.y, b.x + BRICK_WIDTH, b.y + BRICK_HEIGHT); 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);
} }
} }
} }
@ -124,19 +106,20 @@ if (brickCount++ > 20) return;
} }
function drawPaddle() { function drawPaddle() {
g.setColor(0,1, 1); g.setColor(0, 1, 1);
g.fillRect(paddle.x, paddle.y, paddle.x + paddle.width, paddle.y + paddle.height); g.fillRect(paddle.x, paddle.y, paddle.x + paddle.width - 1, paddle.y + paddle.height - 1);
} }
function drawHUD() { function drawHUD() {
g.setColor(0, 0, 0).fillRect(0, 0, g.getWidth(), BRICK_OFFSET_TOP - 1);
g.setColor(1, 1, 1); g.setColor(1, 1, 1);
g.setFont("6x8", 1); g.setFont("6x8", 1);
g.setFontAlign(-1, -1); g.setFontAlign(-1, -1);
g.drawString("Score: " + score, 2, 2); g.drawString("Score: " + score, 2, 22);
g.setFontAlign(0, -1); 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.setFontAlign(1, -1);
g.drawString("Lvl: " + level, g.getWidth() - 2, 2); g.drawString("Lvl: " + level, g.getWidth() - 2, 22);
drawLives(); drawLives();
if (paused) { if (paused) {
g.setFontAlign(0, 0); g.setFontAlign(0, 0);
@ -145,12 +128,18 @@ if (brickCount++ > 20) return;
} }
function draw() { function draw() {
g.clear(); if (paddle.prevPos) {
drawBricks(); 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(); drawBall();
drawPaddle(); drawPaddle();
drawHUD();
g.flip(); 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() { function showGameOver() {
@ -168,148 +157,137 @@ if (brickCount++ > 20) return;
} }
function collisionDetection() { function collisionDetection() {
for (let i = 0; i < bricks.length; i++) { for (var i = 0; i < bricks.length; i++) {
let b = bricks[i]; if (bricks[i]) {
if (b.status) { var c = i % BRICK_COLS;
if ( var r = Math.floor(i / BRICK_COLS);
ball.x + ball.radius > b.x && var brickX = BRICK_OFFSET_LEFT + c * (BRICK_WIDTH + BRICK_PADDING);
ball.x - ball.radius < b.x + BRICK_WIDTH && var brickY = BRICK_OFFSET_TOP + r * (BRICK_HEIGHT + BRICK_PADDING);
ball.y + ball.radius > b.y && 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.y - ball.radius < b.y + BRICK_HEIGHT
) {
ball.dy = -ball.dy; ball.dy = -ball.dy;
b.status = 0; bricks[i] = 0;
score += 10; score += 10;
g.setColor(0, 0, 0).fillRect(brickX, brickY, brickX + BRICK_WIDTH - 1, brickY + BRICK_HEIGHT - 1);
break; break;
} }
} }
} }
} }
function allBricksCleared() { function allBricksCleared() {
for (let i = 0; i < bricks.length; i++) { for (var i = 0; i < bricks.length; i++) {
if (bricks[i].status !== 0) return false; if (bricks[i]) return false;
} }
return true; 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() { function update() {
if (paused || gameOver) return; 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.x += ball.dx;
ball.y += ball.dy; ball.y += ball.dy;
if (ball.x + ball.radius > g.getWidth() || ball.x - ball.radius < 0) { if (ball.x + ball.radius > g.getWidth() || ball.x - ball.radius < 0) {
ball.dx = -ball.dx; ball.dx = -ball.dx;
} }
if (ball.y - ball.radius < 0) { if (ball.y - ball.radius < 0) {
ball.dy = -ball.dy; 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.dy = -ball.dy;
ball.y = paddle.y - ball.radius; ball.y = paddle.y - ball.radius;
} }
if (ball.y + ball.radius > g.getHeight()) { if (ball.y + ball.radius > g.getHeight()) {
lives--; lives--;
if (lives > 0) { if (lives > 0) {
// Reset ball and paddle only resetAfterLifeLost();
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();
return; return;
} else { } else {
clearInterval(interval); clearInterval(interval);
interval = undefined; interval = undefined;
if (paddleIntervalLeft) {
clearInterval(paddleIntervalLeft);
paddleIntervalLeft = null;
}
if (paddleIntervalRight) {
clearInterval(paddleIntervalRight);
paddleIntervalRight = null;
}
saveHighScore(); saveHighScore();
gameOver = true; gameOver = true;
draw();
setTimeout(showGameOver, 50); setTimeout(showGameOver, 50);
return; return;
} }
} }
collisionDetection(); collisionDetection();
if (allBricksCleared()) { if (allBricksCleared()) {
ball.dx *= SPEED_MULTIPLIER;
ball.dy *= SPEED_MULTIPLIER;
level++; level++;
ball.dx = (Math.random() > 0.5 ? 1 : -1) * Math.abs(ball.dx) * SPEED_MULTIPLIER;
ball.dy *= SPEED_MULTIPLIER;
initBricks(); initBricks();
ball.prevPos = null;
paddle.prevPos = null;
g.clear();
drawBricks();
} }
draw(); draw();
} }
function movePaddle(x) { function showGetReady(callback) {
if (gameOver || paused) return; // prevent paddle movement when not needed g.clear();
paddle.x += x; g.setFont("6x8", 2);
if (paddle.x < 0) paddle.x = 0; g.setFontAlign(0, 0);
if (paddle.x + paddle.width > g.getWidth()) { g.setColor(1, 1, 0);
paddle.x = g.getWidth() - paddle.width; g.drawString("GET READY!", g.getWidth() / 2, g.getHeight() / 2);
} g.flip();
setTimeout(() => {
g.clear();
drawBricks();
draw();
callback();
}, 1500);
} }
function startGame() { function startGame() {
initGame(); initGame();
draw();
showGetReady(() => { showGetReady(() => {
interval = setInterval(update, 80); interval = setInterval(update, 50);
}); });
} }
setWatch(() => { setWatch(() => {
if (gameOver) { if (gameOver) {
startGame(); startGame();
} else { return;
}
paused = !paused; paused = !paused;
if (paused) {
drawHUD();
g.flip();
} else {
g.clear();
drawBricks();
draw(); draw();
} }
}, BTN2, { repeat: true, edge: "rising" }); }, BTN2, { repeat: true, edge: "rising" });
setWatch(() => { setWatch(() => { paddleMove = -1; }, BTN1, { repeat: true, edge: "rising" });
if (!paddleIntervalLeft) { setWatch(() => { paddleMove = 0; }, BTN1, { repeat: true, edge: "falling" });
paddleIntervalLeft = setInterval(() => movePaddle(-PADDLE_SPEED), 50); setWatch(() => { paddleMove = 1; }, BTN3, { repeat: true, edge: "rising" });
} setWatch(() => { paddleMove = 0; }, BTN3, { repeat: true, edge: "falling" });
}, 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" });
startGame(); startGame();
})(); })();

View File

@ -2,7 +2,7 @@
"name": "BrickBreaker", "name": "BrickBreaker",
"shortName":"BrickBreaker", "shortName":"BrickBreaker",
"icon": "bbreaker.png", "icon": "bbreaker.png",
"version":"0.03", "version":"0.04",
"description": "A simple BreakOut clone for the Banglejs", "description": "A simple BreakOut clone for the Banglejs",
"tags": "game", "tags": "game",
"readme": "README.md", "readme": "README.md",