/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, level: 1 }); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); // Ball properties self.speedX = 10; self.speedY = -10; self.active = false; self.update = function () { if (!self.active) { return; } // Update position self.x += self.speedX; self.y += self.speedY; // Handle wall collisions if (self.x < self.width / 2 || self.x > 2048 - self.width / 2) { self.speedX *= -1; self.x = Math.max(self.width / 2, Math.min(self.x, 2048 - self.width / 2)); LK.getSound('hit').play(); } // Top wall collision if (self.y < self.height / 2) { self.y = self.height / 2; self.speedY = Math.abs(self.speedY); LK.getSound('hit').play(); } // Bottom (ball reset and add row) if (self.y > 2732) { self.active = false; LK.getSound('miss').play(); // Reset ball position self.reset(paddleX); // Move bricks down and add a new row moveBricksDown(); addNewBrickRow(); } // Cache last position self.lastX = self.x; self.lastY = self.y; }; self.reset = function (paddleX) { self.x = paddleX; self.y = paddleY - 30; self.active = false; self.speedX = 10; self.speedY = -10; }; self.launch = function () { if (!self.active) { self.active = true; // Add some randomness to the initial direction var angle = Math.PI * 1.5 + (Math.random() * 0.5 - 0.25); var speed = 10; self.speedX = Math.cos(angle) * speed; self.speedY = Math.sin(angle) * speed; } }; return self; }); var Brick = Container.expand(function () { var self = Container.call(this); var brickGraphics = self.attachAsset('brick', { anchorX: 0.5, anchorY: 0.5 }); self.width = brickGraphics.width; self.height = brickGraphics.height; self.hp = 1; self.points = 10; self.containsPowerup = false; self.setType = function (type) { switch (type) { case 1: // Normal brick brickGraphics.tint = 0xed4264; self.hp = 1; self.points = 10; break; case 4: // Ball brick brickGraphics.tint = 0x00ff00; // Green color for ball bricks self.hp = 1; self.points = 10; break; case 2: // Strong brick brickGraphics.tint = 0x4287f5; self.hp = 2; self.points = 20; break; case 3: // Super strong brick brickGraphics.tint = 0x8c44ad; self.hp = 3; self.points = 30; break; } }; self.hit = function () { self.hp--; // Color change based on remaining HP if (self.hp == 2) { brickGraphics.tint = 0x5dadec; } else if (self.hp == 1) { brickGraphics.tint = 0xed4264; } if (self.hp <= 0) { // Brick broken LK.getSound('break').play(); LK.setScore(LK.getScore() + self.points); if (self.containsPowerup) { spawnPowerup(self.x, self.y); } if (self.isBallBrick) { var newBall = addBall(); newBall.x = self.x; newBall.y = self.y; newBall.active = true; } return true; // Brick should be removed } else { // Brick hit but not broken LK.getSound('hit').play(); return false; } }; return self; }); var Paddle = Container.expand(function () { var self = Container.call(this); var paddleGraphics = self.attachAsset('paddle', { anchorX: 0.5, anchorY: 0.5 }); self.width = paddleGraphics.width; self.height = paddleGraphics.height; return self; }); var Powerup = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.type = 0; self.speed = 5; self.setType = function (type) { self.type = type; switch (type) { case 0: // Extra ball powerupGraphics.tint = 0xffcc00; break; case 1: // Wide paddle powerupGraphics.tint = 0x2ecc71; break; } }; self.update = function () { self.y += self.speed; // Remove if it goes off screen if (self.y > 2732) { var index = powerups.indexOf(self); if (index > -1) { powerups.splice(index, 1); self.destroy(); } } }; self.activate = function () { LK.getSound('powerup').play(); switch (self.type) { case 0: // Extra ball addBall(); break; case 1: // Wide paddle tween(paddle, { width: paddle.width * 1.5 }, { duration: 500, easing: tween.easeOut }); LK.setTimeout(function () { tween(paddle, { width: 200 }, { duration: 500, easing: tween.easeOut }); }, 10000); break; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x191919 }); /**** * Game Code ****/ function moveBricksDown() { for (var i = 0; i < bricks.length; i++) { bricks[i].y += bricks[i].height + 10; // Move each brick down by its height plus spacing // Cache last position bricks[i].lastY = bricks[i].y; } } function addNewBrickRow() { var brickWidth = 150; var brickHeight = 50; var spacing = 10; var startX = (2048 - (bricksPerRow * (brickWidth + spacing) - spacing)) / 2; var newRowY = 200; // Start new row at the top for (var col = 0; col < bricksPerRow; col++) { var brick = new Brick(); brick.setType(1); // Set to normal brick type brick.x = startX + col * (brickWidth + spacing); brick.y = newRowY; bricks.push(brick); game.addChild(brick); } } // Game variables var level = storage.level || 1; var paddle; var balls = []; var bricks = []; var powerups = []; var paddleX = 2048 / 2; var paddleY = 2732 - 100; var gameStarted = false; var bricksPerRow = 10; var brickRows = 5; // Initialize game elements function initGame() { // Create paddle paddle = new Paddle(); paddle.x = paddleX; paddle.y = paddleY; game.addChild(paddle); // Create initial ball addBall(); // Create bricks createBricks(); // Initialize UI initUI(); // Start music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.4, duration: 1000 } }); } function addBall() { var ball = new Ball(); ball.reset(paddleX); balls.push(ball); game.addChild(ball); return ball; } function createBricks() { var brickWidth = 150; var brickHeight = 50; var spacing = 10; var startX = (2048 - (bricksPerRow * (brickWidth + spacing) - spacing)) / 2; var startY = 200; for (var row = 0; row < brickRows + Math.floor(level / 2); row++) { for (var col = 0; col < bricksPerRow; col++) { var brick = new Brick(); // Determine brick type based on level and position var type = 1; if (level > 2 && row < 2) { type = 2; } if (level > 4 && row === 0) { type = 3; } brick.setType(type); // Position brick brick.x = startX + col * (brickWidth + spacing); brick.y = startY + row * (brickHeight + spacing); // Add powerup or ball brick chance (20% for powerup, 10% for ball brick) var randomChance = Math.random(); if (randomChance < 0.2) { brick.containsPowerup = true; } else if (randomChance < 0.3) { brick.isBallBrick = true; } bricks.push(brick); game.addChild(brick); } } } function spawnPowerup(x, y) { var powerup = new Powerup(); powerup.x = x; powerup.y = y; powerup.setType(Math.floor(Math.random() * 3)); powerups.push(powerup); game.addChild(powerup); } function checkCollisions() { // Ball-paddle collision for (var i = 0; i < balls.length; i++) { var ball = balls[i]; if (ball.active && ball.y + ball.height / 2 >= paddle.y - paddle.height / 2 && ball.y - ball.height / 2 <= paddle.y + paddle.height / 2 && ball.x + ball.width / 2 >= paddle.x - paddle.width / 2 && ball.x - ball.width / 2 <= paddle.x + paddle.width / 2) { // Ball hit the paddle LK.getSound('hit').play(); // Calculate bounce angle based on where the ball hit the paddle var hitPos = (ball.x - paddle.x) / (paddle.width / 2); var angle = hitPos * (Math.PI / 3); // Max 60 degree bounce var speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY); ball.speedX = Math.sin(angle) * speed; ball.speedY = -Math.cos(angle) * speed; // Ensure ball is above paddle ball.y = paddle.y - paddle.height / 2 - ball.height / 2; } } // Ball-brick collision for (var i = 0; i < balls.length; i++) { var ball = balls[i]; if (!ball.active) { continue; } for (var j = bricks.length - 1; j >= 0; j--) { var brick = bricks[j]; if (ball.intersects(brick)) { // Determine collision side var dx = ball.x - brick.x; var dy = ball.y - brick.y; var absDX = Math.abs(dx); var absDY = Math.abs(dy); // Horizontal collision (left/right of brick) if (absDX > absDY) { ball.speedX *= -1; } else { // Vertical collision (top/bottom of brick) ball.speedY *= -1; } if (brick.hit()) { // Brick broken, remove it bricks.splice(j, 1); brick.destroy(); // Update score display scoreTxt.setText("Score: " + LK.getScore()); // Check if all bricks are cleared if (bricks.length === 0) { nextLevel(); } } // Only one brick collision per frame break; } } } // Powerup-paddle collision for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; if (powerup.intersects(paddle)) { powerup.activate(); powerups.splice(i, 1); powerup.destroy(); } } } function nextLevel() { level++; storage.level = level; // Save high score if (LK.getScore() > storage.highScore) { storage.highScore = LK.getScore(); } // Reset game elements for (var i = 0; i < balls.length; i++) { balls[i].destroy(); } balls = []; for (var i = 0; i < powerups.length; i++) { powerups[i].destroy(); } powerups = []; // Create new level addBall(); createBricks(); gameStarted = false; // Update level text levelTxt.setText("Level: " + level); // Flash screen LK.effects.flashScreen(0xFFFFFF, 500); } // UI Elements var scoreTxt, levelTxt, highScoreTxt, instructionTxt; function initUI() { // Score text scoreTxt = new Text2("Score: 0", { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 30; scoreTxt.y = 30; LK.gui.addChild(scoreTxt); // Level text levelTxt = new Text2("Level: " + level, { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(1, 0); levelTxt.x = 2048 - 30; levelTxt.y = 30; LK.gui.addChild(levelTxt); // High score text highScoreTxt = new Text2("High Score: " + storage.highScore, { size: 40, fill: 0xAAAAAA }); highScoreTxt.anchor.set(1, 0); highScoreTxt.x = 2048 - 30; highScoreTxt.y = 100; LK.gui.addChild(highScoreTxt); // Instruction text instructionTxt = new Text2("Tap to launch ball", { size: 70, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 0); instructionTxt.x = 2048 / 2; instructionTxt.y = 150; LK.gui.addChild(instructionTxt); } // Event handlers function handleDown(x, y, obj) { if (!gameStarted) { gameStarted = true; for (var i = 0; i < balls.length; i++) { balls[i].launch(); } instructionTxt.visible = false; } else { for (var i = 0; i < balls.length; i++) { if (!balls[i].active) { balls[i].launch(); break; } } } } function handleMove(x, y, obj) { // Move paddle with touch/mouse paddleX = x; paddle.x = paddleX; // Keep paddle within screen bounds if (paddle.x < paddle.width / 2) { paddle.x = paddle.width / 2; } else if (paddle.x > 2048 - paddle.width / 2) { paddle.x = 2048 - paddle.width / 2; } // Move unlaunched balls with paddle for (var i = 0; i < balls.length; i++) { if (!balls[i].active) { balls[i].x = paddle.x; } } } game.down = handleDown; game.move = handleMove; // Game update loop game.update = function () { // Update all balls for (var i = 0; i < balls.length; i++) { balls[i].update(); } // Update all powerups for (var i = 0; i < powerups.length; i++) { powerups[i].update(); } // Check collisions checkCollisions(); }; // Initialize the game initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
level: 1
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Ball properties
self.speedX = 10;
self.speedY = -10;
self.active = false;
self.update = function () {
if (!self.active) {
return;
}
// Update position
self.x += self.speedX;
self.y += self.speedY;
// Handle wall collisions
if (self.x < self.width / 2 || self.x > 2048 - self.width / 2) {
self.speedX *= -1;
self.x = Math.max(self.width / 2, Math.min(self.x, 2048 - self.width / 2));
LK.getSound('hit').play();
}
// Top wall collision
if (self.y < self.height / 2) {
self.y = self.height / 2;
self.speedY = Math.abs(self.speedY);
LK.getSound('hit').play();
}
// Bottom (ball reset and add row)
if (self.y > 2732) {
self.active = false;
LK.getSound('miss').play();
// Reset ball position
self.reset(paddleX);
// Move bricks down and add a new row
moveBricksDown();
addNewBrickRow();
}
// Cache last position
self.lastX = self.x;
self.lastY = self.y;
};
self.reset = function (paddleX) {
self.x = paddleX;
self.y = paddleY - 30;
self.active = false;
self.speedX = 10;
self.speedY = -10;
};
self.launch = function () {
if (!self.active) {
self.active = true;
// Add some randomness to the initial direction
var angle = Math.PI * 1.5 + (Math.random() * 0.5 - 0.25);
var speed = 10;
self.speedX = Math.cos(angle) * speed;
self.speedY = Math.sin(angle) * speed;
}
};
return self;
});
var Brick = Container.expand(function () {
var self = Container.call(this);
var brickGraphics = self.attachAsset('brick', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = brickGraphics.width;
self.height = brickGraphics.height;
self.hp = 1;
self.points = 10;
self.containsPowerup = false;
self.setType = function (type) {
switch (type) {
case 1:
// Normal brick
brickGraphics.tint = 0xed4264;
self.hp = 1;
self.points = 10;
break;
case 4:
// Ball brick
brickGraphics.tint = 0x00ff00; // Green color for ball bricks
self.hp = 1;
self.points = 10;
break;
case 2:
// Strong brick
brickGraphics.tint = 0x4287f5;
self.hp = 2;
self.points = 20;
break;
case 3:
// Super strong brick
brickGraphics.tint = 0x8c44ad;
self.hp = 3;
self.points = 30;
break;
}
};
self.hit = function () {
self.hp--;
// Color change based on remaining HP
if (self.hp == 2) {
brickGraphics.tint = 0x5dadec;
} else if (self.hp == 1) {
brickGraphics.tint = 0xed4264;
}
if (self.hp <= 0) {
// Brick broken
LK.getSound('break').play();
LK.setScore(LK.getScore() + self.points);
if (self.containsPowerup) {
spawnPowerup(self.x, self.y);
}
if (self.isBallBrick) {
var newBall = addBall();
newBall.x = self.x;
newBall.y = self.y;
newBall.active = true;
}
return true; // Brick should be removed
} else {
// Brick hit but not broken
LK.getSound('hit').play();
return false;
}
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = paddleGraphics.width;
self.height = paddleGraphics.height;
return self;
});
var Powerup = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 0;
self.speed = 5;
self.setType = function (type) {
self.type = type;
switch (type) {
case 0:
// Extra ball
powerupGraphics.tint = 0xffcc00;
break;
case 1:
// Wide paddle
powerupGraphics.tint = 0x2ecc71;
break;
}
};
self.update = function () {
self.y += self.speed;
// Remove if it goes off screen
if (self.y > 2732) {
var index = powerups.indexOf(self);
if (index > -1) {
powerups.splice(index, 1);
self.destroy();
}
}
};
self.activate = function () {
LK.getSound('powerup').play();
switch (self.type) {
case 0:
// Extra ball
addBall();
break;
case 1:
// Wide paddle
tween(paddle, {
width: paddle.width * 1.5
}, {
duration: 500,
easing: tween.easeOut
});
LK.setTimeout(function () {
tween(paddle, {
width: 200
}, {
duration: 500,
easing: tween.easeOut
});
}, 10000);
break;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x191919
});
/****
* Game Code
****/
function moveBricksDown() {
for (var i = 0; i < bricks.length; i++) {
bricks[i].y += bricks[i].height + 10; // Move each brick down by its height plus spacing
// Cache last position
bricks[i].lastY = bricks[i].y;
}
}
function addNewBrickRow() {
var brickWidth = 150;
var brickHeight = 50;
var spacing = 10;
var startX = (2048 - (bricksPerRow * (brickWidth + spacing) - spacing)) / 2;
var newRowY = 200; // Start new row at the top
for (var col = 0; col < bricksPerRow; col++) {
var brick = new Brick();
brick.setType(1); // Set to normal brick type
brick.x = startX + col * (brickWidth + spacing);
brick.y = newRowY;
bricks.push(brick);
game.addChild(brick);
}
}
// Game variables
var level = storage.level || 1;
var paddle;
var balls = [];
var bricks = [];
var powerups = [];
var paddleX = 2048 / 2;
var paddleY = 2732 - 100;
var gameStarted = false;
var bricksPerRow = 10;
var brickRows = 5;
// Initialize game elements
function initGame() {
// Create paddle
paddle = new Paddle();
paddle.x = paddleX;
paddle.y = paddleY;
game.addChild(paddle);
// Create initial ball
addBall();
// Create bricks
createBricks();
// Initialize UI
initUI();
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
function addBall() {
var ball = new Ball();
ball.reset(paddleX);
balls.push(ball);
game.addChild(ball);
return ball;
}
function createBricks() {
var brickWidth = 150;
var brickHeight = 50;
var spacing = 10;
var startX = (2048 - (bricksPerRow * (brickWidth + spacing) - spacing)) / 2;
var startY = 200;
for (var row = 0; row < brickRows + Math.floor(level / 2); row++) {
for (var col = 0; col < bricksPerRow; col++) {
var brick = new Brick();
// Determine brick type based on level and position
var type = 1;
if (level > 2 && row < 2) {
type = 2;
}
if (level > 4 && row === 0) {
type = 3;
}
brick.setType(type);
// Position brick
brick.x = startX + col * (brickWidth + spacing);
brick.y = startY + row * (brickHeight + spacing);
// Add powerup or ball brick chance (20% for powerup, 10% for ball brick)
var randomChance = Math.random();
if (randomChance < 0.2) {
brick.containsPowerup = true;
} else if (randomChance < 0.3) {
brick.isBallBrick = true;
}
bricks.push(brick);
game.addChild(brick);
}
}
}
function spawnPowerup(x, y) {
var powerup = new Powerup();
powerup.x = x;
powerup.y = y;
powerup.setType(Math.floor(Math.random() * 3));
powerups.push(powerup);
game.addChild(powerup);
}
function checkCollisions() {
// Ball-paddle collision
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (ball.active && ball.y + ball.height / 2 >= paddle.y - paddle.height / 2 && ball.y - ball.height / 2 <= paddle.y + paddle.height / 2 && ball.x + ball.width / 2 >= paddle.x - paddle.width / 2 && ball.x - ball.width / 2 <= paddle.x + paddle.width / 2) {
// Ball hit the paddle
LK.getSound('hit').play();
// Calculate bounce angle based on where the ball hit the paddle
var hitPos = (ball.x - paddle.x) / (paddle.width / 2);
var angle = hitPos * (Math.PI / 3); // Max 60 degree bounce
var speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY);
ball.speedX = Math.sin(angle) * speed;
ball.speedY = -Math.cos(angle) * speed;
// Ensure ball is above paddle
ball.y = paddle.y - paddle.height / 2 - ball.height / 2;
}
}
// Ball-brick collision
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (!ball.active) {
continue;
}
for (var j = bricks.length - 1; j >= 0; j--) {
var brick = bricks[j];
if (ball.intersects(brick)) {
// Determine collision side
var dx = ball.x - brick.x;
var dy = ball.y - brick.y;
var absDX = Math.abs(dx);
var absDY = Math.abs(dy);
// Horizontal collision (left/right of brick)
if (absDX > absDY) {
ball.speedX *= -1;
} else {
// Vertical collision (top/bottom of brick)
ball.speedY *= -1;
}
if (brick.hit()) {
// Brick broken, remove it
bricks.splice(j, 1);
brick.destroy();
// Update score display
scoreTxt.setText("Score: " + LK.getScore());
// Check if all bricks are cleared
if (bricks.length === 0) {
nextLevel();
}
}
// Only one brick collision per frame
break;
}
}
}
// Powerup-paddle collision
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (powerup.intersects(paddle)) {
powerup.activate();
powerups.splice(i, 1);
powerup.destroy();
}
}
}
function nextLevel() {
level++;
storage.level = level;
// Save high score
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
// Reset game elements
for (var i = 0; i < balls.length; i++) {
balls[i].destroy();
}
balls = [];
for (var i = 0; i < powerups.length; i++) {
powerups[i].destroy();
}
powerups = [];
// Create new level
addBall();
createBricks();
gameStarted = false;
// Update level text
levelTxt.setText("Level: " + level);
// Flash screen
LK.effects.flashScreen(0xFFFFFF, 500);
}
// UI Elements
var scoreTxt, levelTxt, highScoreTxt, instructionTxt;
function initUI() {
// Score text
scoreTxt = new Text2("Score: 0", {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 30;
scoreTxt.y = 30;
LK.gui.addChild(scoreTxt);
// Level text
levelTxt = new Text2("Level: " + level, {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
levelTxt.x = 2048 - 30;
levelTxt.y = 30;
LK.gui.addChild(levelTxt);
// High score text
highScoreTxt = new Text2("High Score: " + storage.highScore, {
size: 40,
fill: 0xAAAAAA
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.x = 2048 - 30;
highScoreTxt.y = 100;
LK.gui.addChild(highScoreTxt);
// Instruction text
instructionTxt = new Text2("Tap to launch ball", {
size: 70,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0);
instructionTxt.x = 2048 / 2;
instructionTxt.y = 150;
LK.gui.addChild(instructionTxt);
}
// Event handlers
function handleDown(x, y, obj) {
if (!gameStarted) {
gameStarted = true;
for (var i = 0; i < balls.length; i++) {
balls[i].launch();
}
instructionTxt.visible = false;
} else {
for (var i = 0; i < balls.length; i++) {
if (!balls[i].active) {
balls[i].launch();
break;
}
}
}
}
function handleMove(x, y, obj) {
// Move paddle with touch/mouse
paddleX = x;
paddle.x = paddleX;
// Keep paddle within screen bounds
if (paddle.x < paddle.width / 2) {
paddle.x = paddle.width / 2;
} else if (paddle.x > 2048 - paddle.width / 2) {
paddle.x = 2048 - paddle.width / 2;
}
// Move unlaunched balls with paddle
for (var i = 0; i < balls.length; i++) {
if (!balls[i].active) {
balls[i].x = paddle.x;
}
}
}
game.down = handleDown;
game.move = handleMove;
// Game update loop
game.update = function () {
// Update all balls
for (var i = 0; i < balls.length; i++) {
balls[i].update();
}
// Update all powerups
for (var i = 0; i < powerups.length; i++) {
powerups[i].update();
}
// Check collisions
checkCollisions();
};
// Initialize the game
initGame();