User prompt
Make the blocks rendered in a structure shape so the player can jump on and collect minicoins
User prompt
Add a background asset
User prompt
Slow down the jump of the player too
User prompt
Slow down the speed of the player
Code edit (1 edits merged)
Please save this source code
User prompt
Jump Cube Run
Initial prompt
A platformer game that you jump over obstacles like a spike or a block and you play as a cube
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Block obstacle var Block = Container.expand(function () { var self = Container.call(this); var blockGfx = self.attachAsset('block', { anchorX: 0.5, anchorY: 1 }); self.width = blockGfx.width; self.height = blockGfx.height; self.type = 'block'; self.update = function () { self.x -= obstacleSpeed; }; return self; }); // Player Cube var Cube = Container.expand(function () { var self = Container.call(this); var cubeGfx = self.attachAsset('cube', { anchorX: 0.5, anchorY: 1 }); self.width = cubeGfx.width; self.height = cubeGfx.height; self.velY = 0; self.isOnGround = false; // Jump method self.jump = function () { if (self.isOnGround) { self.velY = -36; // Increased jump velocity for higher jump self.isOnGround = false; } }; // Update method self.update = function () { // Gravity self.velY += 2.1; // Slightly increased gravity to balance higher jump arc if (self.velY > 60) self.velY = 60; self.y += self.velY; // Check for landing on top of a block self.isOnGround = false; for (var i = 0; i < obstacles.length; ++i) { var obs = obstacles[i]; if (obs.type === 'block') { // Check if cube is falling and feet are just above the block, and horizontally overlapping var cubeBottom = self.y; var prevCubeBottom = self.y - self.velY; var blockTop = obs.y - obs.height; var blockLeft = obs.x - obs.width / 2; var blockRight = obs.x + obs.width / 2; var cubeLeft = self.x - self.width / 2; var cubeRight = self.x + self.width / 2; if (self.velY >= 0 && prevCubeBottom <= blockTop && cubeBottom >= blockTop && cubeRight > blockLeft && cubeLeft < blockRight) { self.y = blockTop; self.velY = 0; self.isOnGround = true; break; } } } // Prevent falling below ground if (self.y > groundY) { self.y = groundY; self.velY = 0; self.isOnGround = true; } }; return self; }); // Flying obstacle for flying section var FlyingObstacle = Container.expand(function () { var self = Container.call(this); var obsGfx = self.attachAsset('spike', { anchorX: 0.5, anchorY: 0.5 }); self.width = obsGfx.width; self.height = obsGfx.height; self.type = 'flyingObstacle'; self.passed = false; self.lastX = 0; self.update = function () { self.x -= flyingObstacleSpeed; }; return self; }); // Minicoin collectible var Minicoin = Container.expand(function () { var self = Container.call(this); var coinGfx = self.attachAsset('minicoin', { anchorX: 0.5, anchorY: 1 }); self.width = coinGfx.width; self.height = coinGfx.height; self.type = 'minicoin'; self.collected = false; self.update = function () { self.x -= obstacleSpeed; }; return self; }); // Ship player for flying section var Ship = Container.expand(function () { var self = Container.call(this); var shipGfx = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); self.width = shipGfx.width; self.height = shipGfx.height; self.velY = 0; self.targetY = 0; self.isFlying = true; self.lastY = 0; self.update = function () { // Smoothly move toward targetY (touch/move position) var dy = self.targetY - self.y; self.velY = dy * 0.18; self.y += self.velY; // Clamp to screen if (self.y < 100 + self.height / 2) self.y = 100 + self.height / 2; if (self.y > groundY - 200) self.y = groundY - 200; }; return self; }); // ShopIcon class for shop UI var ShopIcon = Container.expand(function () { var self = Container.call(this); self.iconId = null; self.price = 0; self.purchased = false; self.selected = false; var iconGfx = null; var priceTxt = null; var selectBorder = null; // Initialize the icon display self.initIcon = function (iconId, price, purchased, selected) { self.iconId = iconId; self.price = price; self.purchased = purchased; self.selected = selected; if (iconGfx) { self.removeChild(iconGfx); } iconGfx = self.attachAsset(iconId, { anchorX: 0.5, anchorY: 0.5 }); iconGfx.x = 0; iconGfx.y = 0; if (priceTxt) { self.removeChild(priceTxt); } priceTxt = new Text2(purchased ? selected ? "Selected" : "Owned" : price + " pts", { size: 48, fill: purchased ? selected ? 0x44bb44 : 0x888888 : 0x222222 }); priceTxt.anchor.set(0.5, 0); priceTxt.x = 0; priceTxt.y = 70; self.addChild(priceTxt); if (selectBorder) { self.removeChild(selectBorder); } selectBorder = null; if (selected) { selectBorder = LK.getAsset('block', { anchorX: 0.5, anchorY: 0.5 }); selectBorder.width = 140; selectBorder.height = 140; selectBorder.tint = 0x44bb44; selectBorder.alpha = 0.3; selectBorder.x = 0; selectBorder.y = 0; self.addChild(selectBorder); self.setChildIndex(selectBorder, 0); } }; // Handle icon tap: buy or select self.down = function (x, y, obj) { if (!self.purchased && score >= self.price) { score -= self.price; scoreTxt.setText(score); self.purchased = true; // Save purchase storage['shop_' + self.iconId] = true; } if (self.purchased) { // Deselect all others for (var i = 0; i < shopIcons.length; ++i) { shopIcons[i].selected = false; shopIcons[i].initIcon(shopIcons[i].iconId, shopIcons[i].price, !!storage['shop_' + shopIcons[i].iconId] || shopIcons[i].price === 0, false); } self.selected = true; self.initIcon(self.iconId, self.price, self.purchased, true); // Save selection storage['selected_icon'] = self.iconId; // Change player icon setPlayerIcon(self.iconId); } }; return self; }); // Spike obstacle var Spike = Container.expand(function () { var self = Container.call(this); var spikeGfx = self.attachAsset('spike', { anchorX: 0.5, anchorY: 1 }); self.width = spikeGfx.width; self.height = spikeGfx.height; self.type = 'spike'; self.update = function () { self.x -= obstacleSpeed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf0f0f0 }); /**** * Game Code ****/ // Removed old cube asset and re-added it // Constants // Cube player // Ground // Spike obstacle // Block obstacle // New ship asset // Deleted and re-added player asset var groundHeight = 80; var groundY = 2732 - groundHeight; var startX = 400; var startY = groundY; var obstacleSpeed = 12; var minObstacleGap = 420; var maxObstacleGap = 700; var lastObstacleX = 0; var score = 0; var gameStarted = false; // Arrays var obstacles = []; // --- Flying section variables --- var flyingSectionStarted = false; var flyingSectionTriggered = false; var flyingObstacles = []; var ship = null; var flyingObstacleSpeed = 16; var flyingSectionStartX = 1800; // X position to trigger flying section var flyingSectionEndX = 400; // X position to end flying section (ship lands) var flyingSectionDuration = 1200; // px to fly var flyingSectionScore = 0; var flyingSectionMaxScore = 10; var flyingSectionProgress = 0; var flyingSectionActive = false; var flyingSectionShipStartY = 1000; var flyingSectionShipStartX = 400; var flyingSectionShipEndX = 1800; var flyingSectionShipTargetX = 1800; var flyingSectionShipLanded = false; var flyingSectionObstacleGap = 320; var flyingSectionLastObstacleX = 0; var flyingSectionPassedObstacles = 0; // Add background var background = LK.getAsset('background', { anchorX: 0, anchorY: 0 }); background.x = 0; background.y = 0; game.addChild(background); // Add ground var ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0 }); ground.x = 0; ground.y = groundY; game.addChild(ground); // Add player cube (deleted and re-added) var cube = new Cube(); cube.x = startX; cube.y = startY; game.addChild(cube); // Shop UI variables var shopIcons = []; var shopBg = LK.getAsset('block', { anchorX: 0.5, anchorY: 0.5 }); shopBg.width = 900; shopBg.height = 400; shopBg.x = 2048 / 2; shopBg.y = 2732 / 2; shopBg.tint = 0xffffff; shopBg.alpha = 0.97; shopBg.visible = false; var shopTitle = new Text2('Shop', { size: 100, fill: 0x222222 }); shopTitle.anchor.set(0.5, 0); shopTitle.x = 2048 / 2; shopTitle.y = 2732 / 2 - 180; shopTitle.visible = false; var closeShopBtn = new Text2('Close', { size: 70, fill: 0xcc2222 }); closeShopBtn.anchor.set(0.5, 0.5); closeShopBtn.x = 2048 / 2 + 350; closeShopBtn.y = 2732 / 2 - 170; closeShopBtn.visible = false; closeShopBtn.down = function (x, y, obj) { showShop(false); }; LK.gui.center.addChild(shopBg); LK.gui.center.addChild(shopTitle); LK.gui.center.addChild(closeShopBtn); // Shop icon data var iconShopData = [{ iconId: 'icon1', price: 0 }, // Default, free { iconId: 'icon2', price: 50 }, { iconId: 'icon3', price: 100 }]; // Helper: set player icon function setPlayerIcon(iconId) { if (cube && cube.children && cube.children.length > 0) { // Remove old icon for (var i = cube.children.length - 1; i >= 0; --i) { var ch = cube.children[i]; if (ch.assetId && (ch.assetId === 'cube' || ch.assetId === 'icon1' || ch.assetId === 'icon2' || ch.assetId === 'icon3')) { cube.removeChild(ch); } } // Add new icon var newIcon = cube.attachAsset(iconId, { anchorX: 0.5, anchorY: 1 }); newIcon.x = 0; newIcon.y = 0; cube.width = newIcon.width; cube.height = newIcon.height; } } // Helper: show/hide shop function showShop(show) { shopBg.visible = show; shopTitle.visible = show; closeShopBtn.visible = show; for (var i = 0; i < shopIcons.length; ++i) { shopIcons[i].visible = show; } if (show) { gameStarted = false; } } // Create shop icons for (var i = 0; i < iconShopData.length; ++i) { var iconId = iconShopData[i].iconId; var price = iconShopData[i].price; var purchased = !!storage['shop_' + iconId] || price === 0; var selected = false; var selectedIconId = storage['selected_icon']; if (!selectedIconId && price === 0) selected = true; if (selectedIconId && selectedIconId === iconId) selected = true; var shopIcon = new ShopIcon(); shopIcon.x = 2048 / 2 - 250 + i * 250; shopIcon.y = 2732 / 2 + 40; shopIcon.initIcon(iconId, price, purchased, selected); shopIcon.visible = false; LK.gui.center.addChild(shopIcon); shopIcons.push(shopIcon); if (selected) { setPlayerIcon(iconId); } } // Shop button var shopBtn = new Text2('Shop', { size: 80, fill: 0x2266cc }); shopBtn.anchor.set(0.5, 0.5); shopBtn.x = 2048 / 2; shopBtn.y = 2732 / 2; shopBtn.down = function (x, y, obj) { showShop(true); // Optionally, you could pause the game here if needed, but shop UI already disables gameplay. }; LK.gui.center.addChild(shopBtn); // Score text var scoreTxt = new Text2('0', { size: 120, fill: 0x222222 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Progress bar variables var progressBarBg = LK.getAsset('block', { anchorX: 0, anchorY: 0 }); progressBarBg.width = 800; progressBarBg.height = 40; progressBarBg.x = 624; // center horizontally (2048-800)/2 progressBarBg.y = 160; progressBarBg.tint = 0xcccccc; var progressBarFill = LK.getAsset('block', { anchorX: 0, anchorY: 0 }); progressBarFill.width = 0; progressBarFill.height = 40; progressBarFill.x = 624; progressBarFill.y = 160; progressBarFill.tint = 0x44bb44; LK.gui.top.addChild(progressBarBg); LK.gui.top.addChild(progressBarFill); var progressPercent = 0; var progressMax = 100; // 100% var progressPerScore = 2; // Each point increases progress by 2% var winShown = false; // Win text and replay button var winText = new Text2('You win!', { size: 180, fill: 0x228822 }); winText.anchor.set(0.5, 0.5); winText.x = 2048 / 2; winText.y = 2732 / 2 - 120; winText.visible = false; var replayBtn = new Text2('Replay', { size: 120, fill: 0x2266cc }); replayBtn.anchor.set(0.5, 0.5); replayBtn.x = 2048 / 2; replayBtn.y = 2732 / 2 + 80; replayBtn.visible = false; LK.gui.center.addChild(winText); LK.gui.center.addChild(replayBtn); // Replay handler replayBtn.down = function (x, y, obj) { if (winShown) { winText.visible = false; replayBtn.visible = false; winShown = false; resetGame(); gameStarted = true; LK.playMusic('Mad'); } }; // Helper: spawn obstacle function spawnObstacle() { // Randomly choose spike or block structure var type = Math.random() < 0.6 ? 'spike' : 'block'; if (type === 'spike') { var obs = new Spike(); obs.x = 2048 + 100; obs.y = groundY; obstacles.push(obs); game.addChild(obs); lastObstacleX = obs.x; } else { // Structure: always 1 block high for passable platforms var platformHeight = 1; var platformLength = Math.floor(Math.random() * 3) + 2; // 2 to 4 blocks long var baseX = 2048 + 100; var blockW = 120; var blockH = 120; var minicoinsOnTop = Math.random() < 0.8; // 80% chance to spawn coins for (var i = 0; i < platformLength; ++i) { // Only one block high var block = new Block(); block.x = baseX + i * blockW; block.y = groundY - 0 * blockH; obstacles.push(block); game.addChild(block); lastObstacleX = block.x; // Place minicoins on top of the structure if (minicoinsOnTop) { var coin = new Minicoin(); coin.x = baseX + i * blockW; coin.y = groundY - blockH - 10; // 10px above top block obstacles.push(coin); game.addChild(coin); } } // Place a single spike on a random block in the structure if (platformLength > 1) { var spikeIndex = Math.floor(Math.random() * platformLength); var spike = new Spike(); spike.x = baseX + spikeIndex * blockW; spike.y = groundY; obstacles.push(spike); game.addChild(spike); } } } // Helper: reset game state function resetGame() { // Remove obstacles for (var i = 0; i < obstacles.length; ++i) { obstacles[i].destroy(); } obstacles.length = 0; // Remove flying obstacles for (var i = 0; i < flyingObstacles.length; ++i) { flyingObstacles[i].destroy(); } flyingObstacles.length = 0; // Hide ship if (ship) { ship.visible = false; } cube.x = startX; cube.y = startY; cube.velY = 0; cube.isOnGround = true; lastObstacleX = 1200; score = 0; scoreTxt.setText(score); progressPercent = 0; progressBarFill.width = 0; winText.visible = false; replayBtn.visible = false; winShown = false; gameStarted = false; // Reset flying section state flyingSectionStarted = false; flyingSectionTriggered = false; flyingSectionActive = false; flyingSectionShipLanded = false; // Hide shop UI on reset showShop(false); // Update shop icons (in case of new purchases) for (var i = 0; i < shopIcons.length; ++i) { var iconId = shopIcons[i].iconId; var purchased = !!storage['shop_' + iconId] || iconShopData[i].price === 0; var selectedIconId = storage['selected_icon']; var selected = selectedIconId && selectedIconId === iconId || !selectedIconId && iconShopData[i].price === 0; shopIcons[i].initIcon(iconId, iconShopData[i].price, purchased, selected); } } // Start game on first tap game.down = function (x, y, obj) { if (!gameStarted) { gameStarted = true; return; } // If in flying section, do nothing on tap if (flyingSectionActive) return; cube.jump(); }; // Ship control: move ship to touch/move Y in flying section game.move = function (x, y, obj) { if (flyingSectionActive && ship && ship.visible) { // Convert to local game coordinates if needed ship.targetY = y; } }; // Main update loop game.update = function () { if (!gameStarted || winShown) return; // --- FLYING SECTION TRIGGER --- if (!flyingSectionTriggered && score >= 15) { // Start flying section after 15 points flyingSectionTriggered = true; flyingSectionStarted = true; flyingSectionActive = true; // Hide cube, show ship cube.visible = false; if (!ship) { ship = new Ship(); ship.x = flyingSectionShipStartX; ship.y = flyingSectionShipStartY; game.addChild(ship); } ship.visible = true; ship.x = flyingSectionShipStartX; ship.y = flyingSectionShipStartY; ship.targetY = flyingSectionShipStartY; // Remove all ground obstacles for (var i = obstacles.length - 1; i >= 0; --i) { obstacles[i].destroy(); obstacles.splice(i, 1); } // Reset flying obstacles for (var i = flyingObstacles.length - 1; i >= 0; --i) { flyingObstacles[i].destroy(); flyingObstacles.splice(i, 1); } flyingSectionLastObstacleX = 2048 + 200; flyingSectionPassedObstacles = 0; flyingSectionProgress = 0; flyingSectionScore = 0; flyingSectionShipLanded = false; } // --- FLYING SECTION ACTIVE --- if (flyingSectionActive) { // Update ship if (ship) { ship.update(); } // Move ship forward if (ship && !flyingSectionShipLanded) { ship.x += 10; if (ship.x > flyingSectionShipTargetX) ship.x = flyingSectionShipTargetX; } // Spawn flying obstacles if (flyingSectionLastObstacleX - ship.x < 1200) { var gap = flyingSectionObstacleGap + Math.random() * 180; var obs = new FlyingObstacle(); obs.x = flyingSectionLastObstacleX + gap; obs.y = 400 + Math.random() * (groundY - 600); flyingObstacles.push(obs); game.addChild(obs); flyingSectionLastObstacleX = obs.x; } // Update flying obstacles for (var i = flyingObstacles.length - 1; i >= 0; --i) { var obs = flyingObstacles[i]; obs.update(); // Remove if off screen if (obs.x < -200) { obs.destroy(); flyingObstacles.splice(i, 1); continue; } // Collision with ship if (ship && ship.intersects(obs)) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } // Score: passed obstacle if (!obs.passed && obs.x + obs.width / 2 < ship.x - ship.width / 2) { obs.passed = true; flyingSectionScore += 1; score += 2; scoreTxt.setText(score); // Progress bar progressPercent = Math.min(progressPercent + progressPerScore * 2, progressMax); progressBarFill.width = progressPercent / progressMax * 800; if (progressPercent >= progressMax && !winShown) { winShown = true; gameStarted = false; winText.visible = true; replayBtn.visible = true; } } } // End flying section after enough distance or obstacles if (ship && ship.x >= flyingSectionShipTargetX && !flyingSectionShipLanded) { flyingSectionShipLanded = true; flyingSectionActive = false; // Hide ship, show cube, reset cube position ship.visible = false; cube.visible = true; cube.x = ship.x; cube.y = groundY; cube.velY = 0; cube.isOnGround = true; // Remove all flying obstacles for (var i = flyingObstacles.length - 1; i >= 0; --i) { flyingObstacles[i].destroy(); flyingObstacles.splice(i, 1); } // Continue normal section, spawn obstacles ahead lastObstacleX = cube.x + 600; } return; } // Update player cube.update(); // Update obstacles for (var i = obstacles.length - 1; i >= 0; --i) { var obs = obstacles[i]; obs.update(); // Remove if off screen if (obs.x < -200) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision detection if (obs.type === 'minicoin') { if (!obs.collected && cube.intersects(obs)) { obs.collected = true; score += 5; // Minicoin gives 5 points scoreTxt.setText(score); // Play coin sound LK.getSound('Coin').play(); // Update progress progressPercent = Math.min(progressPercent + progressPerScore * 5, progressMax); progressBarFill.width = progressPercent / progressMax * 800; if (progressPercent >= progressMax && !winShown) { winShown = true; gameStarted = false; winText.visible = true; replayBtn.visible = true; // LK.pauseGame(); //{30} // Removed: not needed, LK handles game state on win } obs.destroy(); obstacles.splice(i, 1); continue; } } else { if (cube.intersects(obs)) { // Flash screen and game over LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } // Score: passed obstacle if (!obs.passed && obs.x + obs.width / 2 < cube.x - cube.width / 2) { obs.passed = true; score += 1; scoreTxt.setText(score); // Update progress progressPercent = Math.min(progressPercent + progressPerScore, progressMax); progressBarFill.width = progressPercent / progressMax * 800; if (progressPercent >= progressMax && !winShown) { winShown = true; gameStarted = false; winText.visible = true; replayBtn.visible = true; // LK.pauseGame(); //{3h} // Removed: not needed, LK handles game state on win } } } } // Spawn new obstacles if (obstacles.length === 0 || 2048 - lastObstacleX > minObstacleGap + Math.random() * (maxObstacleGap - minObstacleGap)) { spawnObstacle(); } }; // Reset game on game over LK.on('gameover', function () { resetGame(); LK.playMusic('Mad'); }); // Initial state resetGame(); // Play music asset when the game starts LK.playMusic('Mad'); // Shop icon assets (add more as needed)
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Block obstacle
var Block = Container.expand(function () {
var self = Container.call(this);
var blockGfx = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 1
});
self.width = blockGfx.width;
self.height = blockGfx.height;
self.type = 'block';
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Player Cube
var Cube = Container.expand(function () {
var self = Container.call(this);
var cubeGfx = self.attachAsset('cube', {
anchorX: 0.5,
anchorY: 1
});
self.width = cubeGfx.width;
self.height = cubeGfx.height;
self.velY = 0;
self.isOnGround = false;
// Jump method
self.jump = function () {
if (self.isOnGround) {
self.velY = -36; // Increased jump velocity for higher jump
self.isOnGround = false;
}
};
// Update method
self.update = function () {
// Gravity
self.velY += 2.1; // Slightly increased gravity to balance higher jump arc
if (self.velY > 60) self.velY = 60;
self.y += self.velY;
// Check for landing on top of a block
self.isOnGround = false;
for (var i = 0; i < obstacles.length; ++i) {
var obs = obstacles[i];
if (obs.type === 'block') {
// Check if cube is falling and feet are just above the block, and horizontally overlapping
var cubeBottom = self.y;
var prevCubeBottom = self.y - self.velY;
var blockTop = obs.y - obs.height;
var blockLeft = obs.x - obs.width / 2;
var blockRight = obs.x + obs.width / 2;
var cubeLeft = self.x - self.width / 2;
var cubeRight = self.x + self.width / 2;
if (self.velY >= 0 && prevCubeBottom <= blockTop && cubeBottom >= blockTop && cubeRight > blockLeft && cubeLeft < blockRight) {
self.y = blockTop;
self.velY = 0;
self.isOnGround = true;
break;
}
}
}
// Prevent falling below ground
if (self.y > groundY) {
self.y = groundY;
self.velY = 0;
self.isOnGround = true;
}
};
return self;
});
// Flying obstacle for flying section
var FlyingObstacle = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.type = 'flyingObstacle';
self.passed = false;
self.lastX = 0;
self.update = function () {
self.x -= flyingObstacleSpeed;
};
return self;
});
// Minicoin collectible
var Minicoin = Container.expand(function () {
var self = Container.call(this);
var coinGfx = self.attachAsset('minicoin', {
anchorX: 0.5,
anchorY: 1
});
self.width = coinGfx.width;
self.height = coinGfx.height;
self.type = 'minicoin';
self.collected = false;
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Ship player for flying section
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGfx = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = shipGfx.width;
self.height = shipGfx.height;
self.velY = 0;
self.targetY = 0;
self.isFlying = true;
self.lastY = 0;
self.update = function () {
// Smoothly move toward targetY (touch/move position)
var dy = self.targetY - self.y;
self.velY = dy * 0.18;
self.y += self.velY;
// Clamp to screen
if (self.y < 100 + self.height / 2) self.y = 100 + self.height / 2;
if (self.y > groundY - 200) self.y = groundY - 200;
};
return self;
});
// ShopIcon class for shop UI
var ShopIcon = Container.expand(function () {
var self = Container.call(this);
self.iconId = null;
self.price = 0;
self.purchased = false;
self.selected = false;
var iconGfx = null;
var priceTxt = null;
var selectBorder = null;
// Initialize the icon display
self.initIcon = function (iconId, price, purchased, selected) {
self.iconId = iconId;
self.price = price;
self.purchased = purchased;
self.selected = selected;
if (iconGfx) {
self.removeChild(iconGfx);
}
iconGfx = self.attachAsset(iconId, {
anchorX: 0.5,
anchorY: 0.5
});
iconGfx.x = 0;
iconGfx.y = 0;
if (priceTxt) {
self.removeChild(priceTxt);
}
priceTxt = new Text2(purchased ? selected ? "Selected" : "Owned" : price + " pts", {
size: 48,
fill: purchased ? selected ? 0x44bb44 : 0x888888 : 0x222222
});
priceTxt.anchor.set(0.5, 0);
priceTxt.x = 0;
priceTxt.y = 70;
self.addChild(priceTxt);
if (selectBorder) {
self.removeChild(selectBorder);
}
selectBorder = null;
if (selected) {
selectBorder = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
selectBorder.width = 140;
selectBorder.height = 140;
selectBorder.tint = 0x44bb44;
selectBorder.alpha = 0.3;
selectBorder.x = 0;
selectBorder.y = 0;
self.addChild(selectBorder);
self.setChildIndex(selectBorder, 0);
}
};
// Handle icon tap: buy or select
self.down = function (x, y, obj) {
if (!self.purchased && score >= self.price) {
score -= self.price;
scoreTxt.setText(score);
self.purchased = true;
// Save purchase
storage['shop_' + self.iconId] = true;
}
if (self.purchased) {
// Deselect all others
for (var i = 0; i < shopIcons.length; ++i) {
shopIcons[i].selected = false;
shopIcons[i].initIcon(shopIcons[i].iconId, shopIcons[i].price, !!storage['shop_' + shopIcons[i].iconId] || shopIcons[i].price === 0, false);
}
self.selected = true;
self.initIcon(self.iconId, self.price, self.purchased, true);
// Save selection
storage['selected_icon'] = self.iconId;
// Change player icon
setPlayerIcon(self.iconId);
}
};
return self;
});
// Spike obstacle
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGfx = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1
});
self.width = spikeGfx.width;
self.height = spikeGfx.height;
self.type = 'spike';
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf0f0f0
});
/****
* Game Code
****/
// Removed old cube asset and re-added it
// Constants
// Cube player
// Ground
// Spike obstacle
// Block obstacle
// New ship asset
// Deleted and re-added player asset
var groundHeight = 80;
var groundY = 2732 - groundHeight;
var startX = 400;
var startY = groundY;
var obstacleSpeed = 12;
var minObstacleGap = 420;
var maxObstacleGap = 700;
var lastObstacleX = 0;
var score = 0;
var gameStarted = false;
// Arrays
var obstacles = [];
// --- Flying section variables ---
var flyingSectionStarted = false;
var flyingSectionTriggered = false;
var flyingObstacles = [];
var ship = null;
var flyingObstacleSpeed = 16;
var flyingSectionStartX = 1800; // X position to trigger flying section
var flyingSectionEndX = 400; // X position to end flying section (ship lands)
var flyingSectionDuration = 1200; // px to fly
var flyingSectionScore = 0;
var flyingSectionMaxScore = 10;
var flyingSectionProgress = 0;
var flyingSectionActive = false;
var flyingSectionShipStartY = 1000;
var flyingSectionShipStartX = 400;
var flyingSectionShipEndX = 1800;
var flyingSectionShipTargetX = 1800;
var flyingSectionShipLanded = false;
var flyingSectionObstacleGap = 320;
var flyingSectionLastObstacleX = 0;
var flyingSectionPassedObstacles = 0;
// Add background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
background.x = 0;
background.y = 0;
game.addChild(background);
// Add ground
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
});
ground.x = 0;
ground.y = groundY;
game.addChild(ground);
// Add player cube (deleted and re-added)
var cube = new Cube();
cube.x = startX;
cube.y = startY;
game.addChild(cube);
// Shop UI variables
var shopIcons = [];
var shopBg = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
shopBg.width = 900;
shopBg.height = 400;
shopBg.x = 2048 / 2;
shopBg.y = 2732 / 2;
shopBg.tint = 0xffffff;
shopBg.alpha = 0.97;
shopBg.visible = false;
var shopTitle = new Text2('Shop', {
size: 100,
fill: 0x222222
});
shopTitle.anchor.set(0.5, 0);
shopTitle.x = 2048 / 2;
shopTitle.y = 2732 / 2 - 180;
shopTitle.visible = false;
var closeShopBtn = new Text2('Close', {
size: 70,
fill: 0xcc2222
});
closeShopBtn.anchor.set(0.5, 0.5);
closeShopBtn.x = 2048 / 2 + 350;
closeShopBtn.y = 2732 / 2 - 170;
closeShopBtn.visible = false;
closeShopBtn.down = function (x, y, obj) {
showShop(false);
};
LK.gui.center.addChild(shopBg);
LK.gui.center.addChild(shopTitle);
LK.gui.center.addChild(closeShopBtn);
// Shop icon data
var iconShopData = [{
iconId: 'icon1',
price: 0
},
// Default, free
{
iconId: 'icon2',
price: 50
}, {
iconId: 'icon3',
price: 100
}];
// Helper: set player icon
function setPlayerIcon(iconId) {
if (cube && cube.children && cube.children.length > 0) {
// Remove old icon
for (var i = cube.children.length - 1; i >= 0; --i) {
var ch = cube.children[i];
if (ch.assetId && (ch.assetId === 'cube' || ch.assetId === 'icon1' || ch.assetId === 'icon2' || ch.assetId === 'icon3')) {
cube.removeChild(ch);
}
}
// Add new icon
var newIcon = cube.attachAsset(iconId, {
anchorX: 0.5,
anchorY: 1
});
newIcon.x = 0;
newIcon.y = 0;
cube.width = newIcon.width;
cube.height = newIcon.height;
}
}
// Helper: show/hide shop
function showShop(show) {
shopBg.visible = show;
shopTitle.visible = show;
closeShopBtn.visible = show;
for (var i = 0; i < shopIcons.length; ++i) {
shopIcons[i].visible = show;
}
if (show) {
gameStarted = false;
}
}
// Create shop icons
for (var i = 0; i < iconShopData.length; ++i) {
var iconId = iconShopData[i].iconId;
var price = iconShopData[i].price;
var purchased = !!storage['shop_' + iconId] || price === 0;
var selected = false;
var selectedIconId = storage['selected_icon'];
if (!selectedIconId && price === 0) selected = true;
if (selectedIconId && selectedIconId === iconId) selected = true;
var shopIcon = new ShopIcon();
shopIcon.x = 2048 / 2 - 250 + i * 250;
shopIcon.y = 2732 / 2 + 40;
shopIcon.initIcon(iconId, price, purchased, selected);
shopIcon.visible = false;
LK.gui.center.addChild(shopIcon);
shopIcons.push(shopIcon);
if (selected) {
setPlayerIcon(iconId);
}
}
// Shop button
var shopBtn = new Text2('Shop', {
size: 80,
fill: 0x2266cc
});
shopBtn.anchor.set(0.5, 0.5);
shopBtn.x = 2048 / 2;
shopBtn.y = 2732 / 2;
shopBtn.down = function (x, y, obj) {
showShop(true);
// Optionally, you could pause the game here if needed, but shop UI already disables gameplay.
};
LK.gui.center.addChild(shopBtn);
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0x222222
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Progress bar variables
var progressBarBg = LK.getAsset('block', {
anchorX: 0,
anchorY: 0
});
progressBarBg.width = 800;
progressBarBg.height = 40;
progressBarBg.x = 624; // center horizontally (2048-800)/2
progressBarBg.y = 160;
progressBarBg.tint = 0xcccccc;
var progressBarFill = LK.getAsset('block', {
anchorX: 0,
anchorY: 0
});
progressBarFill.width = 0;
progressBarFill.height = 40;
progressBarFill.x = 624;
progressBarFill.y = 160;
progressBarFill.tint = 0x44bb44;
LK.gui.top.addChild(progressBarBg);
LK.gui.top.addChild(progressBarFill);
var progressPercent = 0;
var progressMax = 100; // 100%
var progressPerScore = 2; // Each point increases progress by 2%
var winShown = false;
// Win text and replay button
var winText = new Text2('You win!', {
size: 180,
fill: 0x228822
});
winText.anchor.set(0.5, 0.5);
winText.x = 2048 / 2;
winText.y = 2732 / 2 - 120;
winText.visible = false;
var replayBtn = new Text2('Replay', {
size: 120,
fill: 0x2266cc
});
replayBtn.anchor.set(0.5, 0.5);
replayBtn.x = 2048 / 2;
replayBtn.y = 2732 / 2 + 80;
replayBtn.visible = false;
LK.gui.center.addChild(winText);
LK.gui.center.addChild(replayBtn);
// Replay handler
replayBtn.down = function (x, y, obj) {
if (winShown) {
winText.visible = false;
replayBtn.visible = false;
winShown = false;
resetGame();
gameStarted = true;
LK.playMusic('Mad');
}
};
// Helper: spawn obstacle
function spawnObstacle() {
// Randomly choose spike or block structure
var type = Math.random() < 0.6 ? 'spike' : 'block';
if (type === 'spike') {
var obs = new Spike();
obs.x = 2048 + 100;
obs.y = groundY;
obstacles.push(obs);
game.addChild(obs);
lastObstacleX = obs.x;
} else {
// Structure: always 1 block high for passable platforms
var platformHeight = 1;
var platformLength = Math.floor(Math.random() * 3) + 2; // 2 to 4 blocks long
var baseX = 2048 + 100;
var blockW = 120;
var blockH = 120;
var minicoinsOnTop = Math.random() < 0.8; // 80% chance to spawn coins
for (var i = 0; i < platformLength; ++i) {
// Only one block high
var block = new Block();
block.x = baseX + i * blockW;
block.y = groundY - 0 * blockH;
obstacles.push(block);
game.addChild(block);
lastObstacleX = block.x;
// Place minicoins on top of the structure
if (minicoinsOnTop) {
var coin = new Minicoin();
coin.x = baseX + i * blockW;
coin.y = groundY - blockH - 10; // 10px above top block
obstacles.push(coin);
game.addChild(coin);
}
}
// Place a single spike on a random block in the structure
if (platformLength > 1) {
var spikeIndex = Math.floor(Math.random() * platformLength);
var spike = new Spike();
spike.x = baseX + spikeIndex * blockW;
spike.y = groundY;
obstacles.push(spike);
game.addChild(spike);
}
}
}
// Helper: reset game state
function resetGame() {
// Remove obstacles
for (var i = 0; i < obstacles.length; ++i) {
obstacles[i].destroy();
}
obstacles.length = 0;
// Remove flying obstacles
for (var i = 0; i < flyingObstacles.length; ++i) {
flyingObstacles[i].destroy();
}
flyingObstacles.length = 0;
// Hide ship
if (ship) {
ship.visible = false;
}
cube.x = startX;
cube.y = startY;
cube.velY = 0;
cube.isOnGround = true;
lastObstacleX = 1200;
score = 0;
scoreTxt.setText(score);
progressPercent = 0;
progressBarFill.width = 0;
winText.visible = false;
replayBtn.visible = false;
winShown = false;
gameStarted = false;
// Reset flying section state
flyingSectionStarted = false;
flyingSectionTriggered = false;
flyingSectionActive = false;
flyingSectionShipLanded = false;
// Hide shop UI on reset
showShop(false);
// Update shop icons (in case of new purchases)
for (var i = 0; i < shopIcons.length; ++i) {
var iconId = shopIcons[i].iconId;
var purchased = !!storage['shop_' + iconId] || iconShopData[i].price === 0;
var selectedIconId = storage['selected_icon'];
var selected = selectedIconId && selectedIconId === iconId || !selectedIconId && iconShopData[i].price === 0;
shopIcons[i].initIcon(iconId, iconShopData[i].price, purchased, selected);
}
}
// Start game on first tap
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
return;
}
// If in flying section, do nothing on tap
if (flyingSectionActive) return;
cube.jump();
};
// Ship control: move ship to touch/move Y in flying section
game.move = function (x, y, obj) {
if (flyingSectionActive && ship && ship.visible) {
// Convert to local game coordinates if needed
ship.targetY = y;
}
};
// Main update loop
game.update = function () {
if (!gameStarted || winShown) return;
// --- FLYING SECTION TRIGGER ---
if (!flyingSectionTriggered && score >= 15) {
// Start flying section after 15 points
flyingSectionTriggered = true;
flyingSectionStarted = true;
flyingSectionActive = true;
// Hide cube, show ship
cube.visible = false;
if (!ship) {
ship = new Ship();
ship.x = flyingSectionShipStartX;
ship.y = flyingSectionShipStartY;
game.addChild(ship);
}
ship.visible = true;
ship.x = flyingSectionShipStartX;
ship.y = flyingSectionShipStartY;
ship.targetY = flyingSectionShipStartY;
// Remove all ground obstacles
for (var i = obstacles.length - 1; i >= 0; --i) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Reset flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
flyingObstacles[i].destroy();
flyingObstacles.splice(i, 1);
}
flyingSectionLastObstacleX = 2048 + 200;
flyingSectionPassedObstacles = 0;
flyingSectionProgress = 0;
flyingSectionScore = 0;
flyingSectionShipLanded = false;
}
// --- FLYING SECTION ACTIVE ---
if (flyingSectionActive) {
// Update ship
if (ship) {
ship.update();
}
// Move ship forward
if (ship && !flyingSectionShipLanded) {
ship.x += 10;
if (ship.x > flyingSectionShipTargetX) ship.x = flyingSectionShipTargetX;
}
// Spawn flying obstacles
if (flyingSectionLastObstacleX - ship.x < 1200) {
var gap = flyingSectionObstacleGap + Math.random() * 180;
var obs = new FlyingObstacle();
obs.x = flyingSectionLastObstacleX + gap;
obs.y = 400 + Math.random() * (groundY - 600);
flyingObstacles.push(obs);
game.addChild(obs);
flyingSectionLastObstacleX = obs.x;
}
// Update flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
var obs = flyingObstacles[i];
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
flyingObstacles.splice(i, 1);
continue;
}
// Collision with ship
if (ship && ship.intersects(obs)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Score: passed obstacle
if (!obs.passed && obs.x + obs.width / 2 < ship.x - ship.width / 2) {
obs.passed = true;
flyingSectionScore += 1;
score += 2;
scoreTxt.setText(score);
// Progress bar
progressPercent = Math.min(progressPercent + progressPerScore * 2, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
}
}
}
// End flying section after enough distance or obstacles
if (ship && ship.x >= flyingSectionShipTargetX && !flyingSectionShipLanded) {
flyingSectionShipLanded = true;
flyingSectionActive = false;
// Hide ship, show cube, reset cube position
ship.visible = false;
cube.visible = true;
cube.x = ship.x;
cube.y = groundY;
cube.velY = 0;
cube.isOnGround = true;
// Remove all flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
flyingObstacles[i].destroy();
flyingObstacles.splice(i, 1);
}
// Continue normal section, spawn obstacles ahead
lastObstacleX = cube.x + 600;
}
return;
}
// Update player
cube.update();
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; --i) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
if (obs.type === 'minicoin') {
if (!obs.collected && cube.intersects(obs)) {
obs.collected = true;
score += 5; // Minicoin gives 5 points
scoreTxt.setText(score);
// Play coin sound
LK.getSound('Coin').play();
// Update progress
progressPercent = Math.min(progressPercent + progressPerScore * 5, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
// LK.pauseGame(); //{30} // Removed: not needed, LK handles game state on win
}
obs.destroy();
obstacles.splice(i, 1);
continue;
}
} else {
if (cube.intersects(obs)) {
// Flash screen and game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Score: passed obstacle
if (!obs.passed && obs.x + obs.width / 2 < cube.x - cube.width / 2) {
obs.passed = true;
score += 1;
scoreTxt.setText(score);
// Update progress
progressPercent = Math.min(progressPercent + progressPerScore, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
// LK.pauseGame(); //{3h} // Removed: not needed, LK handles game state on win
}
}
}
}
// Spawn new obstacles
if (obstacles.length === 0 || 2048 - lastObstacleX > minObstacleGap + Math.random() * (maxObstacleGap - minObstacleGap)) {
spawnObstacle();
}
};
// Reset game on game over
LK.on('gameover', function () {
resetGame();
LK.playMusic('Mad');
});
// Initial state
resetGame();
// Play music asset when the game starts
LK.playMusic('Mad');
// Shop icon assets (add more as needed)