/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, difficulty: 1 }); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.width = ballGraphics.width; self.height = ballGraphics.height; self.speedX = 0; self.speedY = 0; self.maxSpeed = 20; self.active = false; self.serveDirection = 1; // 1 = right, -1 = left self.lastHitBy = null; self.reset = function () { self.x = 2048 / 2; self.y = 2732 / 2; self.active = false; self.speedX = 0; self.speedY = 0; // Alternate serve direction self.serveDirection = -self.serveDirection; }; self.serve = function () { // Serve in the direction determined self.speedX = 10 * self.serveDirection; self.speedY = 5 * (Math.random() > 0.5 ? 1 : -1); self.active = true; }; self.update = function () { if (!self.active) { return; } // Move the ball self.x += self.speedX; self.y += self.speedY; // Handle vertical wall collisions if (self.y < self.height / 2 || self.y > 2732 - self.height / 2) { self.speedY = -self.speedY; self.y = self.y < self.height / 2 ? self.height / 2 : 2732 - self.height / 2; LK.getSound('hit').play(); } }; self.checkPaddleCollision = function (paddle) { if (!self.active) { return false; } if (self.intersects(paddle)) { // Calculate bounce angle based on where the ball hits the paddle var relativeIntersectY = (paddle.y - self.y) / (paddle.height / 2); var bounceAngle = relativeIntersectY * (Math.PI / 4); // Max 45 degrees // Calculate new speeds var direction = self.x < 2048 / 2 ? 1 : -1; var speed = Math.min(Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY) + 0.5, self.maxSpeed); self.speedX = direction * speed * Math.cos(bounceAngle); self.speedY = speed * -Math.sin(bounceAngle); // Move ball away from paddle to prevent multiple collisions if (direction > 0) { self.x = paddle.x + paddle.width / 2 + self.width / 2; } else { self.x = paddle.x - paddle.width / 2 - self.width / 2; } self.lastHitBy = paddle; LK.getSound('hit').play(); return true; } return false; }; self.applyPowerup = function (powerupType) { switch (powerupType) { case 'speed': var currentSpeed = Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY); self.speedX = self.speedX / currentSpeed * (currentSpeed + 3); self.speedY = self.speedY / currentSpeed * (currentSpeed + 3); break; case 'size': tween(ballGraphics, { scale: 1.5 }, { duration: 300 }); self.width = ballGraphics.width * 1.5; self.height = ballGraphics.height * 1.5; LK.setTimeout(function () { tween(ballGraphics, { scale: 1 }, { duration: 300 }); self.width = ballGraphics.width / 1.5; self.height = ballGraphics.height / 1.5; }, 5000); break; case 'curve': self.speedY += Math.random() * 10 - 5; break; } }; return self; }); var Paddle = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer === undefined ? true : isPlayer; self.speed = 12; self.score = 0; var paddleGraphics = self.attachAsset('paddle', { anchorX: 0.5, anchorY: 0.5 }); self.width = paddleGraphics.width; self.height = paddleGraphics.height; self.ai = { difficulty: storage.difficulty || 1, reactionDelay: 0, targetY: 0, thinkTime: 0 }; self.move = function (x, y) { if (y < self.height / 2) { self.y = self.height / 2; } else if (y > 2732 - self.height / 2) { self.y = 2732 - self.height / 2; } else { self.y = y; } }; self.updateAI = function (ballY, ballSpeedY) { // Decrease AI think time if (self.ai.thinkTime > 0) { self.ai.thinkTime--; return; } // Calculate where the ball will be var errorMargin = (4 - self.ai.difficulty) * 60; self.ai.targetY = ballY + Math.random() * errorMargin - errorMargin / 2; // Set a new think time self.ai.thinkTime = Math.floor(Math.random() * (8 - self.ai.difficulty * 2)); }; self.runAI = function () { if (self.y < self.ai.targetY - self.speed) { self.y += self.speed; } else if (self.y > self.ai.targetY + self.speed) { self.y -= self.speed; } // Constrain paddle to field if (self.y < self.height / 2) { self.y = self.height / 2; } else if (self.y > 2732 - self.height / 2) { self.y = 2732 - self.height / 2; } }; 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 = 'speed'; // Default type self.active = true; self.types = ['speed', 'size', 'curve']; self.spawn = function () { // Position randomly in the middle third of the court self.x = Math.random() * (2048 * 0.6) + 2048 * 0.2; self.y = Math.random() * (2732 * 0.6) + 2732 * 0.2; // Set random type self.type = self.types[Math.floor(Math.random() * self.types.length)]; // Set color based on type switch (self.type) { case 'speed': powerupGraphics.tint = 0xFF5500; // Orange break; case 'size': powerupGraphics.tint = 0x00AAFF; // Blue break; case 'curve': powerupGraphics.tint = 0xFFAA00; // Yellow break; } self.active = true; // Create a pulsing animation tween(self, { alpha: 0.6 }, { duration: 500, easing: tween.sinusoidal, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 500, easing: tween.sinusoidal, onFinish: function onFinish() { if (self.active) { self.spawn(); // Restart the pulse animation } } }); } }); // Auto-remove after 10 seconds LK.setTimeout(function () { if (self.active) { self.deactivate(); } }, 10000); }; self.deactivate = function () { self.active = false; tween(self, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state var gameState = 'title'; // title, serving, playing, gameover var gameTime = 0; var touchPaddle = null; var servingTimeout = null; var powerupTimeout = null; var powerupActive = false; // Game elements var playerPaddle; var aiPaddle; var ball; var powerup; var scoreLeft; var scoreRight; var centerLine; var titleText; var subtitleText; var gameOverText; // Initialize game elements function setupGame() { // Create center line centerLine = new Container(); for (var i = 0; i < 34; i++) { var linePart = centerLine.attachAsset('dividerLine', { anchorX: 0.5, anchorY: 0.5, y: i * 80 }); } centerLine.x = 2048 / 2; game.addChild(centerLine); // Create player paddle playerPaddle = new Paddle(true); playerPaddle.x = 100; playerPaddle.y = 2732 / 2; game.addChild(playerPaddle); // Create AI paddle aiPaddle = new Paddle(false); aiPaddle.x = 2048 - 100; aiPaddle.y = 2732 / 2; game.addChild(aiPaddle); // Create ball ball = new Ball(); ball.reset(); game.addChild(ball); // Create score text scoreLeft = new Text2('0', { size: 150, fill: 0xFFFFFF }); scoreLeft.anchor.set(0.5, 0.5); scoreLeft.x = 2048 / 4; scoreLeft.y = 200; game.addChild(scoreLeft); scoreRight = new Text2('0', { size: 150, fill: 0xFFFFFF }); scoreRight.anchor.set(0.5, 0.5); scoreRight.x = 2048 * 3 / 4; scoreRight.y = 200; game.addChild(scoreRight); // Title text titleText = new Text2('PIXEL PONG', { size: 200, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 3; game.addChild(titleText); subtitleText = new Text2('TAP TO START\n\nDRAG TO MOVE PADDLE', { size: 80, fill: 0xFFFFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 2048 / 2; subtitleText.y = 2732 / 2; game.addChild(subtitleText); gameOverText = new Text2('', { size: 150, fill: 0xFFFFFF }); gameOverText.anchor.set(0.5, 0.5); gameOverText.x = 2048 / 2; gameOverText.y = 2732 / 2; gameOverText.visible = false; game.addChild(gameOverText); // Set title screen setGameState('title'); // Start background music LK.playMusic('bgMusic'); } function setGameState(state) { gameState = state; switch (state) { case 'title': ball.reset(); playerPaddle.score = 0; aiPaddle.score = 0; updateScoreDisplay(); titleText.visible = true; subtitleText.visible = true; gameOverText.visible = false; break; case 'serving': titleText.visible = false; subtitleText.visible = false; gameOverText.visible = false; // Set serving timeout servingTimeout = LK.setTimeout(function () { ball.serve(); setGameState('playing'); }, 1500); break; case 'playing': // Schedule powerup schedulePowerup(); break; case 'gameover': gameOverText.visible = true; // Check for new high score if (playerPaddle.score > storage.highScore) { storage.highScore = playerPaddle.score; gameOverText.setText('GAME OVER\nNEW HIGH SCORE: ' + storage.highScore); } else { gameOverText.setText('GAME OVER\nSCORE: ' + playerPaddle.score + '\nHIGH SCORE: ' + storage.highScore); } // Clear timeouts if (servingTimeout) { LK.clearTimeout(servingTimeout); servingTimeout = null; } if (powerupTimeout) { LK.clearTimeout(powerupTimeout); powerupTimeout = null; } // Remove any active powerup if (powerupActive && powerup && powerup.parent) { powerup.deactivate(); powerupActive = false; } break; } } function schedulePowerup() { if (powerupTimeout) { LK.clearTimeout(powerupTimeout); } // Schedule next powerup between 5-15 seconds powerupTimeout = LK.setTimeout(function () { spawnPowerup(); }, 5000 + Math.random() * 10000); } function spawnPowerup() { // Only one powerup at a time if (powerupActive) { return; } powerup = new Powerup(); powerup.spawn(); game.addChild(powerup); powerupActive = true; } function updateScoreDisplay() { scoreLeft.setText(playerPaddle.score.toString()); scoreRight.setText(aiPaddle.score.toString()); } function scorePoint(forLeft) { if (forLeft) { playerPaddle.score++; } else { aiPaddle.score++; } updateScoreDisplay(); LK.getSound('score').play(); // Check for game over if (playerPaddle.score >= 11 || aiPaddle.score >= 11) { LK.setTimeout(function () { setGameState('gameover'); }, 1000); } else { // Reset for next round ball.reset(); setGameState('serving'); } } // Event handlers game.down = function (x, y, obj) { if (gameState === 'title') { setGameState('serving'); } else if (gameState === 'gameover') { setGameState('title'); } else { touchPaddle = playerPaddle; touchPaddle.move(x, y); } }; game.move = function (x, y, obj) { if (touchPaddle) { touchPaddle.move(x, y); } }; game.up = function (x, y, obj) { touchPaddle = null; }; // Main game update loop game.update = function () { if (gameState === 'playing' || gameState === 'serving') { // Update ball ball.update(); // Check for paddle collisions ball.checkPaddleCollision(playerPaddle); ball.checkPaddleCollision(aiPaddle); // Check for scoring if (ball.x < -ball.width) { scorePoint(false); } else if (ball.x > 2048 + ball.width) { scorePoint(true); } // Update AI paddle if (ball.active) { if (ball.speedX > 0) { aiPaddle.updateAI(ball.y, ball.speedY); } aiPaddle.runAI(); } // Check for powerup collision if (powerupActive && powerup && ball.intersects(powerup)) { ball.applyPowerup(powerup.type); powerup.deactivate(); powerupActive = false; LK.getSound('powerup').play(); schedulePowerup(); } } // Animate title on title screen if (gameState === 'title') { gameTime++; titleText.scale.set(1 + Math.sin(gameTime / 30) * 0.05); } }; // Initialize everything setupGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
difficulty: 1
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ballGraphics.width;
self.height = ballGraphics.height;
self.speedX = 0;
self.speedY = 0;
self.maxSpeed = 20;
self.active = false;
self.serveDirection = 1; // 1 = right, -1 = left
self.lastHitBy = null;
self.reset = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.active = false;
self.speedX = 0;
self.speedY = 0;
// Alternate serve direction
self.serveDirection = -self.serveDirection;
};
self.serve = function () {
// Serve in the direction determined
self.speedX = 10 * self.serveDirection;
self.speedY = 5 * (Math.random() > 0.5 ? 1 : -1);
self.active = true;
};
self.update = function () {
if (!self.active) {
return;
}
// Move the ball
self.x += self.speedX;
self.y += self.speedY;
// Handle vertical wall collisions
if (self.y < self.height / 2 || self.y > 2732 - self.height / 2) {
self.speedY = -self.speedY;
self.y = self.y < self.height / 2 ? self.height / 2 : 2732 - self.height / 2;
LK.getSound('hit').play();
}
};
self.checkPaddleCollision = function (paddle) {
if (!self.active) {
return false;
}
if (self.intersects(paddle)) {
// Calculate bounce angle based on where the ball hits the paddle
var relativeIntersectY = (paddle.y - self.y) / (paddle.height / 2);
var bounceAngle = relativeIntersectY * (Math.PI / 4); // Max 45 degrees
// Calculate new speeds
var direction = self.x < 2048 / 2 ? 1 : -1;
var speed = Math.min(Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY) + 0.5, self.maxSpeed);
self.speedX = direction * speed * Math.cos(bounceAngle);
self.speedY = speed * -Math.sin(bounceAngle);
// Move ball away from paddle to prevent multiple collisions
if (direction > 0) {
self.x = paddle.x + paddle.width / 2 + self.width / 2;
} else {
self.x = paddle.x - paddle.width / 2 - self.width / 2;
}
self.lastHitBy = paddle;
LK.getSound('hit').play();
return true;
}
return false;
};
self.applyPowerup = function (powerupType) {
switch (powerupType) {
case 'speed':
var currentSpeed = Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY);
self.speedX = self.speedX / currentSpeed * (currentSpeed + 3);
self.speedY = self.speedY / currentSpeed * (currentSpeed + 3);
break;
case 'size':
tween(ballGraphics, {
scale: 1.5
}, {
duration: 300
});
self.width = ballGraphics.width * 1.5;
self.height = ballGraphics.height * 1.5;
LK.setTimeout(function () {
tween(ballGraphics, {
scale: 1
}, {
duration: 300
});
self.width = ballGraphics.width / 1.5;
self.height = ballGraphics.height / 1.5;
}, 5000);
break;
case 'curve':
self.speedY += Math.random() * 10 - 5;
break;
}
};
return self;
});
var Paddle = Container.expand(function (isPlayer) {
var self = Container.call(this);
self.isPlayer = isPlayer === undefined ? true : isPlayer;
self.speed = 12;
self.score = 0;
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = paddleGraphics.width;
self.height = paddleGraphics.height;
self.ai = {
difficulty: storage.difficulty || 1,
reactionDelay: 0,
targetY: 0,
thinkTime: 0
};
self.move = function (x, y) {
if (y < self.height / 2) {
self.y = self.height / 2;
} else if (y > 2732 - self.height / 2) {
self.y = 2732 - self.height / 2;
} else {
self.y = y;
}
};
self.updateAI = function (ballY, ballSpeedY) {
// Decrease AI think time
if (self.ai.thinkTime > 0) {
self.ai.thinkTime--;
return;
}
// Calculate where the ball will be
var errorMargin = (4 - self.ai.difficulty) * 60;
self.ai.targetY = ballY + Math.random() * errorMargin - errorMargin / 2;
// Set a new think time
self.ai.thinkTime = Math.floor(Math.random() * (8 - self.ai.difficulty * 2));
};
self.runAI = function () {
if (self.y < self.ai.targetY - self.speed) {
self.y += self.speed;
} else if (self.y > self.ai.targetY + self.speed) {
self.y -= self.speed;
}
// Constrain paddle to field
if (self.y < self.height / 2) {
self.y = self.height / 2;
} else if (self.y > 2732 - self.height / 2) {
self.y = 2732 - self.height / 2;
}
};
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 = 'speed'; // Default type
self.active = true;
self.types = ['speed', 'size', 'curve'];
self.spawn = function () {
// Position randomly in the middle third of the court
self.x = Math.random() * (2048 * 0.6) + 2048 * 0.2;
self.y = Math.random() * (2732 * 0.6) + 2732 * 0.2;
// Set random type
self.type = self.types[Math.floor(Math.random() * self.types.length)];
// Set color based on type
switch (self.type) {
case 'speed':
powerupGraphics.tint = 0xFF5500; // Orange
break;
case 'size':
powerupGraphics.tint = 0x00AAFF; // Blue
break;
case 'curve':
powerupGraphics.tint = 0xFFAA00; // Yellow
break;
}
self.active = true;
// Create a pulsing animation
tween(self, {
alpha: 0.6
}, {
duration: 500,
easing: tween.sinusoidal,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 500,
easing: tween.sinusoidal,
onFinish: function onFinish() {
if (self.active) {
self.spawn(); // Restart the pulse animation
}
}
});
}
});
// Auto-remove after 10 seconds
LK.setTimeout(function () {
if (self.active) {
self.deactivate();
}
}, 10000);
};
self.deactivate = function () {
self.active = false;
tween(self, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state
var gameState = 'title'; // title, serving, playing, gameover
var gameTime = 0;
var touchPaddle = null;
var servingTimeout = null;
var powerupTimeout = null;
var powerupActive = false;
// Game elements
var playerPaddle;
var aiPaddle;
var ball;
var powerup;
var scoreLeft;
var scoreRight;
var centerLine;
var titleText;
var subtitleText;
var gameOverText;
// Initialize game elements
function setupGame() {
// Create center line
centerLine = new Container();
for (var i = 0; i < 34; i++) {
var linePart = centerLine.attachAsset('dividerLine', {
anchorX: 0.5,
anchorY: 0.5,
y: i * 80
});
}
centerLine.x = 2048 / 2;
game.addChild(centerLine);
// Create player paddle
playerPaddle = new Paddle(true);
playerPaddle.x = 100;
playerPaddle.y = 2732 / 2;
game.addChild(playerPaddle);
// Create AI paddle
aiPaddle = new Paddle(false);
aiPaddle.x = 2048 - 100;
aiPaddle.y = 2732 / 2;
game.addChild(aiPaddle);
// Create ball
ball = new Ball();
ball.reset();
game.addChild(ball);
// Create score text
scoreLeft = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreLeft.anchor.set(0.5, 0.5);
scoreLeft.x = 2048 / 4;
scoreLeft.y = 200;
game.addChild(scoreLeft);
scoreRight = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreRight.anchor.set(0.5, 0.5);
scoreRight.x = 2048 * 3 / 4;
scoreRight.y = 200;
game.addChild(scoreRight);
// Title text
titleText = new Text2('PIXEL PONG', {
size: 200,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 3;
game.addChild(titleText);
subtitleText = new Text2('TAP TO START\n\nDRAG TO MOVE PADDLE', {
size: 80,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 2048 / 2;
subtitleText.y = 2732 / 2;
game.addChild(subtitleText);
gameOverText = new Text2('', {
size: 150,
fill: 0xFFFFFF
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 2048 / 2;
gameOverText.y = 2732 / 2;
gameOverText.visible = false;
game.addChild(gameOverText);
// Set title screen
setGameState('title');
// Start background music
LK.playMusic('bgMusic');
}
function setGameState(state) {
gameState = state;
switch (state) {
case 'title':
ball.reset();
playerPaddle.score = 0;
aiPaddle.score = 0;
updateScoreDisplay();
titleText.visible = true;
subtitleText.visible = true;
gameOverText.visible = false;
break;
case 'serving':
titleText.visible = false;
subtitleText.visible = false;
gameOverText.visible = false;
// Set serving timeout
servingTimeout = LK.setTimeout(function () {
ball.serve();
setGameState('playing');
}, 1500);
break;
case 'playing':
// Schedule powerup
schedulePowerup();
break;
case 'gameover':
gameOverText.visible = true;
// Check for new high score
if (playerPaddle.score > storage.highScore) {
storage.highScore = playerPaddle.score;
gameOverText.setText('GAME OVER\nNEW HIGH SCORE: ' + storage.highScore);
} else {
gameOverText.setText('GAME OVER\nSCORE: ' + playerPaddle.score + '\nHIGH SCORE: ' + storage.highScore);
}
// Clear timeouts
if (servingTimeout) {
LK.clearTimeout(servingTimeout);
servingTimeout = null;
}
if (powerupTimeout) {
LK.clearTimeout(powerupTimeout);
powerupTimeout = null;
}
// Remove any active powerup
if (powerupActive && powerup && powerup.parent) {
powerup.deactivate();
powerupActive = false;
}
break;
}
}
function schedulePowerup() {
if (powerupTimeout) {
LK.clearTimeout(powerupTimeout);
}
// Schedule next powerup between 5-15 seconds
powerupTimeout = LK.setTimeout(function () {
spawnPowerup();
}, 5000 + Math.random() * 10000);
}
function spawnPowerup() {
// Only one powerup at a time
if (powerupActive) {
return;
}
powerup = new Powerup();
powerup.spawn();
game.addChild(powerup);
powerupActive = true;
}
function updateScoreDisplay() {
scoreLeft.setText(playerPaddle.score.toString());
scoreRight.setText(aiPaddle.score.toString());
}
function scorePoint(forLeft) {
if (forLeft) {
playerPaddle.score++;
} else {
aiPaddle.score++;
}
updateScoreDisplay();
LK.getSound('score').play();
// Check for game over
if (playerPaddle.score >= 11 || aiPaddle.score >= 11) {
LK.setTimeout(function () {
setGameState('gameover');
}, 1000);
} else {
// Reset for next round
ball.reset();
setGameState('serving');
}
}
// Event handlers
game.down = function (x, y, obj) {
if (gameState === 'title') {
setGameState('serving');
} else if (gameState === 'gameover') {
setGameState('title');
} else {
touchPaddle = playerPaddle;
touchPaddle.move(x, y);
}
};
game.move = function (x, y, obj) {
if (touchPaddle) {
touchPaddle.move(x, y);
}
};
game.up = function (x, y, obj) {
touchPaddle = null;
};
// Main game update loop
game.update = function () {
if (gameState === 'playing' || gameState === 'serving') {
// Update ball
ball.update();
// Check for paddle collisions
ball.checkPaddleCollision(playerPaddle);
ball.checkPaddleCollision(aiPaddle);
// Check for scoring
if (ball.x < -ball.width) {
scorePoint(false);
} else if (ball.x > 2048 + ball.width) {
scorePoint(true);
}
// Update AI paddle
if (ball.active) {
if (ball.speedX > 0) {
aiPaddle.updateAI(ball.y, ball.speedY);
}
aiPaddle.runAI();
}
// Check for powerup collision
if (powerupActive && powerup && ball.intersects(powerup)) {
ball.applyPowerup(powerup.type);
powerup.deactivate();
powerupActive = false;
LK.getSound('powerup').play();
schedulePowerup();
}
}
// Animate title on title screen
if (gameState === 'title') {
gameTime++;
titleText.scale.set(1 + Math.sin(gameTime / 30) * 0.05);
}
};
// Initialize everything
setupGame();