/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballSprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballSprite.width / 2; self.velocity = { x: 7, y: -15 }; self.gravity = 0.5; self.bounce = 0.8; self.isAlive = true; self.isPoweredUp = false; self.update = function () { if (!self.isAlive) { return; } // Apply gravity self.velocity.y += self.gravity; // Move based on velocity self.x += self.velocity.x; self.y += self.velocity.y; // Bounce off walls if (self.x < self.radius) { self.x = self.radius; self.velocity.x = Math.abs(self.velocity.x); } else if (self.x > 2048 - self.radius) { self.x = 2048 - self.radius; self.velocity.x = -Math.abs(self.velocity.x); } // Bounce off ground if (self.y > 2732 - 100 - self.radius) { self.y = 2732 - 100 - self.radius; self.velocity.y = -Math.abs(self.velocity.y * self.bounce); LK.getSound('bounce').play(); } // Bounce off ceiling if (self.y < self.radius) { self.y = self.radius; self.velocity.y = Math.abs(self.velocity.y * self.bounce); LK.getSound('bounce').play(); } }; self.changeDirection = function () { if (!self.isAlive) { return; } // Reverse horizontal direction on tap self.velocity.x = -self.velocity.x; }; self.activatePowerUp = function (type) { self.isPoweredUp = true; // Different power-up effects if (type === "slow") { var originalVelocityX = self.velocity.x; var originalVelocityY = self.velocity.y; var originalGravity = self.gravity; // Slow down self.velocity.x *= 0.5; self.velocity.y *= 0.5; self.gravity *= 0.5; // Flash the ball to indicate power-up tween(ballSprite, { tint: 0x00FFFF }, { duration: 300, easing: tween.linear }); // Reset after 5 seconds LK.setTimeout(function () { self.velocity.x = originalVelocityX; self.velocity.y = originalVelocityY; self.gravity = originalGravity; self.isPoweredUp = false; tween(ballSprite, { tint: 0xFFFFFF }, { duration: 300, easing: tween.linear }); }, 5000); } else if (type === "small") { var originalRadius = self.radius; // Shrink the ball tween(ballSprite, { scale: 0.5 }, { duration: 300, easing: tween.easeOut }); self.radius *= 0.5; // Flash the ball to indicate power-up tween(ballSprite, { tint: 0xFF00FF }, { duration: 300, easing: tween.linear }); // Reset after 5 seconds LK.setTimeout(function () { tween(ballSprite, { scale: 1 }, { duration: 300, easing: tween.easeOut }); self.radius = originalRadius; self.isPoweredUp = false; tween(ballSprite, { tint: 0xFFFFFF }, { duration: 300, easing: tween.linear }); }, 5000); } }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); var obstacleSprite = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.width = obstacleSprite.width; self.height = obstacleSprite.height; self.speed = 5; self.update = function () { self.x -= self.speed; // Remove if off screen if (self.x < -self.width) { self.shouldRemove = true; } }; self.setSpeed = function (newSpeed) { self.speed = newSpeed; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var powerupSprite = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.radius = powerupSprite.width / 2; self.speed = 5; self.type = Math.random() < 0.5 ? "slow" : "small"; // Set color based on type if (self.type === "slow") { powerupSprite.tint = 0x00FFFF; // Cyan for slow time } else { powerupSprite.tint = 0xFF00FF; // Magenta for shrink } self.update = function () { self.x -= self.speed; // Spin effect powerupSprite.rotation += 0.05; // Remove if off screen if (self.x < -self.radius * 2) { self.shouldRemove = true; } }; self.setSpeed = function (newSpeed) { self.speed = newSpeed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game variables var obstacles = []; var powerups = []; var obstacleSpawnTimer = null; var powerupSpawnTimer = null; var gameSpeed = 5; var spawnRate = 2000; var score = 0; var difficultyTimer = null; var isGameActive = false; // Create background var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); // Create ground var ground = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - 100 })); // Create ball var ball = game.addChild(new Ball()); ball.x = 300; ball.y = 500; // Create score text var scoreTxt = new Text2('Score: 0', { size: 70, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 50; // Create high score text var highScore = storage.highScore || 0; var highScoreTxt = new Text2('Best: ' + highScore, { size: 50, fill: 0xFFFFFF }); highScoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(highScoreTxt); highScoreTxt.y = 130; // Start message var tapToStartTxt = new Text2('Tap to Start', { size: 100, fill: 0xFFFFFF }); tapToStartTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(tapToStartTxt); // Game functions function startGame() { if (isGameActive) { return; } isGameActive = true; score = 0; gameSpeed = 5; scoreTxt.setText('Score: 0'); // Remove start message LK.gui.center.removeChild(tapToStartTxt); // Reset the ball ball.x = 300; ball.y = 500; ball.velocity = { x: 7, y: -15 }; ball.isAlive = true; // Clear existing obstacles and powerups for (var i = obstacles.length - 1; i >= 0; i--) { game.removeChild(obstacles[i]); } obstacles = []; for (var i = powerups.length - 1; i >= 0; i--) { game.removeChild(powerups[i]); } powerups = []; // Start obstacle spawning spawnRate = 2000; obstacleSpawnTimer = LK.setInterval(spawnObstacle, spawnRate); // Start power-up spawning (less frequent) powerupSpawnTimer = LK.setInterval(spawnPowerUp, 7000); // Increase difficulty over time difficultyTimer = LK.setInterval(increaseDifficulty, 10000); // Play background music LK.playMusic('gameMusic', { fade: { start: 0, end: 0.4, duration: 1000 } }); } function gameOver() { isGameActive = false; ball.isAlive = false; // Play crash sound LK.getSound('crash').play(); // Flash screen red LK.effects.flashScreen(0xFF0000, 500); // Stop spawning LK.clearInterval(obstacleSpawnTimer); LK.clearInterval(powerupSpawnTimer); LK.clearInterval(difficultyTimer); // Update high score if needed if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('Best: ' + highScore); } // Show game over LK.showGameOver(); } function spawnObstacle() { if (!isGameActive) { return; } var obstacle = new Obstacle(); // Random height var height = 200 + Math.random() * 300; obstacle.getChildAt(0).height = height; obstacle.height = height; // Position at right side of screen obstacle.x = 2048 + obstacle.width / 2; // Random vertical position, but ensure gap for player var minY = 150; var maxY = 2732 - 100 - 150 - height; obstacle.y = minY + Math.random() * (maxY - minY); // Set speed obstacle.setSpeed(gameSpeed); // Add to game obstacles.push(obstacle); game.addChild(obstacle); } function spawnPowerUp() { if (!isGameActive) { return; } var powerup = new PowerUp(); // Position at right side of screen powerup.x = 2048 + powerup.radius; // Random vertical position powerup.y = 150 + Math.random() * (2732 - 350); // Set speed powerup.setSpeed(gameSpeed); // Add to game powerups.push(powerup); game.addChild(powerup); } function increaseDifficulty() { if (!isGameActive) { return; } // Increase game speed gameSpeed += 0.5; // Update existing obstacles and powerups for (var i = 0; i < obstacles.length; i++) { obstacles[i].setSpeed(gameSpeed); } for (var i = 0; i < powerups.length; i++) { powerups[i].setSpeed(gameSpeed); } // Decrease spawn rate (but not below 1000ms) spawnRate = Math.max(1000, spawnRate - 100); // Clear and reset obstacle spawn timer with new rate LK.clearInterval(obstacleSpawnTimer); obstacleSpawnTimer = LK.setInterval(spawnObstacle, spawnRate); } function checkCollisions() { if (!ball.isAlive) { return; } // Check obstacle collisions for (var i = obstacles.length - 1; i >= 0; i--) { var obstacle = obstacles[i]; // Simple rectangle collision detection var ballLeft = ball.x - ball.radius; var ballRight = ball.x + ball.radius; var ballTop = ball.y - ball.radius; var ballBottom = ball.y + ball.radius; var obstacleLeft = obstacle.x - obstacle.width / 2; var obstacleRight = obstacle.x + obstacle.width / 2; var obstacleTop = obstacle.y - obstacle.height / 2; var obstacleBottom = obstacle.y + obstacle.height / 2; if (ballRight > obstacleLeft && ballLeft < obstacleRight && ballBottom > obstacleTop && ballTop < obstacleBottom) { // If powered up with "small", do more precise collision detection if (ball.isPoweredUp && ball.radius < 25) { // More precise circle-rectangle collision var closestX = Math.max(obstacleLeft, Math.min(ball.x, obstacleRight)); var closestY = Math.max(obstacleTop, Math.min(ball.y, obstacleBottom)); var distanceX = ball.x - closestX; var distanceY = ball.y - closestY; var distanceSquared = distanceX * distanceX + distanceY * distanceY; if (distanceSquared < ball.radius * ball.radius) { gameOver(); return; } } else { gameOver(); return; } } } // Check powerup collisions for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; // Simple circle collision detection var dx = ball.x - powerup.x; var dy = ball.y - powerup.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < ball.radius + powerup.radius) { // Collect powerup LK.getSound('collect').play(); // Apply powerup effect ball.activatePowerUp(powerup.type); // Remove powerup game.removeChild(powerup); powerups.splice(i, 1); // Add score score += 10; scoreTxt.setText('Score: ' + score); } } } function updateGame() { if (!isGameActive) { return; } // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { if (obstacles[i].shouldRemove) { game.removeChild(obstacles[i]); obstacles.splice(i, 1); // Increment score when passing an obstacle score += 1; scoreTxt.setText('Score: ' + score); continue; } } // Update powerups for (var i = powerups.length - 1; i >= 0; i--) { if (powerups[i].shouldRemove) { game.removeChild(powerups[i]); powerups.splice(i, 1); } } // Check for collisions checkCollisions(); } // Event handlers game.down = function (x, y, obj) { if (!isGameActive) { startGame(); } else { if (obj.event && obj.event.detail && obj.event.detail === 2) { // Check for double tap ball.velocity.y = -Math.abs(ball.velocity.y * ball.bounce); // Bounce in the air LK.getSound('bounce').play(); } else { ball.changeDirection(); } } }; // Main update function game.update = function () { updateGame(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballSprite = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ballSprite.width / 2;
self.velocity = {
x: 7,
y: -15
};
self.gravity = 0.5;
self.bounce = 0.8;
self.isAlive = true;
self.isPoweredUp = false;
self.update = function () {
if (!self.isAlive) {
return;
}
// Apply gravity
self.velocity.y += self.gravity;
// Move based on velocity
self.x += self.velocity.x;
self.y += self.velocity.y;
// Bounce off walls
if (self.x < self.radius) {
self.x = self.radius;
self.velocity.x = Math.abs(self.velocity.x);
} else if (self.x > 2048 - self.radius) {
self.x = 2048 - self.radius;
self.velocity.x = -Math.abs(self.velocity.x);
}
// Bounce off ground
if (self.y > 2732 - 100 - self.radius) {
self.y = 2732 - 100 - self.radius;
self.velocity.y = -Math.abs(self.velocity.y * self.bounce);
LK.getSound('bounce').play();
}
// Bounce off ceiling
if (self.y < self.radius) {
self.y = self.radius;
self.velocity.y = Math.abs(self.velocity.y * self.bounce);
LK.getSound('bounce').play();
}
};
self.changeDirection = function () {
if (!self.isAlive) {
return;
}
// Reverse horizontal direction on tap
self.velocity.x = -self.velocity.x;
};
self.activatePowerUp = function (type) {
self.isPoweredUp = true;
// Different power-up effects
if (type === "slow") {
var originalVelocityX = self.velocity.x;
var originalVelocityY = self.velocity.y;
var originalGravity = self.gravity;
// Slow down
self.velocity.x *= 0.5;
self.velocity.y *= 0.5;
self.gravity *= 0.5;
// Flash the ball to indicate power-up
tween(ballSprite, {
tint: 0x00FFFF
}, {
duration: 300,
easing: tween.linear
});
// Reset after 5 seconds
LK.setTimeout(function () {
self.velocity.x = originalVelocityX;
self.velocity.y = originalVelocityY;
self.gravity = originalGravity;
self.isPoweredUp = false;
tween(ballSprite, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.linear
});
}, 5000);
} else if (type === "small") {
var originalRadius = self.radius;
// Shrink the ball
tween(ballSprite, {
scale: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
self.radius *= 0.5;
// Flash the ball to indicate power-up
tween(ballSprite, {
tint: 0xFF00FF
}, {
duration: 300,
easing: tween.linear
});
// Reset after 5 seconds
LK.setTimeout(function () {
tween(ballSprite, {
scale: 1
}, {
duration: 300,
easing: tween.easeOut
});
self.radius = originalRadius;
self.isPoweredUp = false;
tween(ballSprite, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.linear
});
}, 5000);
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleSprite = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obstacleSprite.width;
self.height = obstacleSprite.height;
self.speed = 5;
self.update = function () {
self.x -= self.speed;
// Remove if off screen
if (self.x < -self.width) {
self.shouldRemove = true;
}
};
self.setSpeed = function (newSpeed) {
self.speed = newSpeed;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupSprite = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = powerupSprite.width / 2;
self.speed = 5;
self.type = Math.random() < 0.5 ? "slow" : "small";
// Set color based on type
if (self.type === "slow") {
powerupSprite.tint = 0x00FFFF; // Cyan for slow time
} else {
powerupSprite.tint = 0xFF00FF; // Magenta for shrink
}
self.update = function () {
self.x -= self.speed;
// Spin effect
powerupSprite.rotation += 0.05;
// Remove if off screen
if (self.x < -self.radius * 2) {
self.shouldRemove = true;
}
};
self.setSpeed = function (newSpeed) {
self.speed = newSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var obstacles = [];
var powerups = [];
var obstacleSpawnTimer = null;
var powerupSpawnTimer = null;
var gameSpeed = 5;
var spawnRate = 2000;
var score = 0;
var difficultyTimer = null;
var isGameActive = false;
// Create background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create ground
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - 100
}));
// Create ball
var ball = game.addChild(new Ball());
ball.x = 300;
ball.y = 500;
// Create score text
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
// Create high score text
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('Best: ' + highScore, {
size: 50,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(highScoreTxt);
highScoreTxt.y = 130;
// Start message
var tapToStartTxt = new Text2('Tap to Start', {
size: 100,
fill: 0xFFFFFF
});
tapToStartTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(tapToStartTxt);
// Game functions
function startGame() {
if (isGameActive) {
return;
}
isGameActive = true;
score = 0;
gameSpeed = 5;
scoreTxt.setText('Score: 0');
// Remove start message
LK.gui.center.removeChild(tapToStartTxt);
// Reset the ball
ball.x = 300;
ball.y = 500;
ball.velocity = {
x: 7,
y: -15
};
ball.isAlive = true;
// Clear existing obstacles and powerups
for (var i = obstacles.length - 1; i >= 0; i--) {
game.removeChild(obstacles[i]);
}
obstacles = [];
for (var i = powerups.length - 1; i >= 0; i--) {
game.removeChild(powerups[i]);
}
powerups = [];
// Start obstacle spawning
spawnRate = 2000;
obstacleSpawnTimer = LK.setInterval(spawnObstacle, spawnRate);
// Start power-up spawning (less frequent)
powerupSpawnTimer = LK.setInterval(spawnPowerUp, 7000);
// Increase difficulty over time
difficultyTimer = LK.setInterval(increaseDifficulty, 10000);
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
function gameOver() {
isGameActive = false;
ball.isAlive = false;
// Play crash sound
LK.getSound('crash').play();
// Flash screen red
LK.effects.flashScreen(0xFF0000, 500);
// Stop spawning
LK.clearInterval(obstacleSpawnTimer);
LK.clearInterval(powerupSpawnTimer);
LK.clearInterval(difficultyTimer);
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
// Show game over
LK.showGameOver();
}
function spawnObstacle() {
if (!isGameActive) {
return;
}
var obstacle = new Obstacle();
// Random height
var height = 200 + Math.random() * 300;
obstacle.getChildAt(0).height = height;
obstacle.height = height;
// Position at right side of screen
obstacle.x = 2048 + obstacle.width / 2;
// Random vertical position, but ensure gap for player
var minY = 150;
var maxY = 2732 - 100 - 150 - height;
obstacle.y = minY + Math.random() * (maxY - minY);
// Set speed
obstacle.setSpeed(gameSpeed);
// Add to game
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnPowerUp() {
if (!isGameActive) {
return;
}
var powerup = new PowerUp();
// Position at right side of screen
powerup.x = 2048 + powerup.radius;
// Random vertical position
powerup.y = 150 + Math.random() * (2732 - 350);
// Set speed
powerup.setSpeed(gameSpeed);
// Add to game
powerups.push(powerup);
game.addChild(powerup);
}
function increaseDifficulty() {
if (!isGameActive) {
return;
}
// Increase game speed
gameSpeed += 0.5;
// Update existing obstacles and powerups
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].setSpeed(gameSpeed);
}
for (var i = 0; i < powerups.length; i++) {
powerups[i].setSpeed(gameSpeed);
}
// Decrease spawn rate (but not below 1000ms)
spawnRate = Math.max(1000, spawnRate - 100);
// Clear and reset obstacle spawn timer with new rate
LK.clearInterval(obstacleSpawnTimer);
obstacleSpawnTimer = LK.setInterval(spawnObstacle, spawnRate);
}
function checkCollisions() {
if (!ball.isAlive) {
return;
}
// Check obstacle collisions
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Simple rectangle collision detection
var ballLeft = ball.x - ball.radius;
var ballRight = ball.x + ball.radius;
var ballTop = ball.y - ball.radius;
var ballBottom = ball.y + ball.radius;
var obstacleLeft = obstacle.x - obstacle.width / 2;
var obstacleRight = obstacle.x + obstacle.width / 2;
var obstacleTop = obstacle.y - obstacle.height / 2;
var obstacleBottom = obstacle.y + obstacle.height / 2;
if (ballRight > obstacleLeft && ballLeft < obstacleRight && ballBottom > obstacleTop && ballTop < obstacleBottom) {
// If powered up with "small", do more precise collision detection
if (ball.isPoweredUp && ball.radius < 25) {
// More precise circle-rectangle collision
var closestX = Math.max(obstacleLeft, Math.min(ball.x, obstacleRight));
var closestY = Math.max(obstacleTop, Math.min(ball.y, obstacleBottom));
var distanceX = ball.x - closestX;
var distanceY = ball.y - closestY;
var distanceSquared = distanceX * distanceX + distanceY * distanceY;
if (distanceSquared < ball.radius * ball.radius) {
gameOver();
return;
}
} else {
gameOver();
return;
}
}
}
// Check powerup collisions
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
// Simple circle collision detection
var dx = ball.x - powerup.x;
var dy = ball.y - powerup.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < ball.radius + powerup.radius) {
// Collect powerup
LK.getSound('collect').play();
// Apply powerup effect
ball.activatePowerUp(powerup.type);
// Remove powerup
game.removeChild(powerup);
powerups.splice(i, 1);
// Add score
score += 10;
scoreTxt.setText('Score: ' + score);
}
}
}
function updateGame() {
if (!isGameActive) {
return;
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i].shouldRemove) {
game.removeChild(obstacles[i]);
obstacles.splice(i, 1);
// Increment score when passing an obstacle
score += 1;
scoreTxt.setText('Score: ' + score);
continue;
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
if (powerups[i].shouldRemove) {
game.removeChild(powerups[i]);
powerups.splice(i, 1);
}
}
// Check for collisions
checkCollisions();
}
// Event handlers
game.down = function (x, y, obj) {
if (!isGameActive) {
startGame();
} else {
if (obj.event && obj.event.detail && obj.event.detail === 2) {
// Check for double tap
ball.velocity.y = -Math.abs(ball.velocity.y * ball.bounce); // Bounce in the air
LK.getSound('bounce').play();
} else {
ball.changeDirection();
}
}
};
// Main update function
game.update = function () {
updateGame();
};