/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Coin class: collectible var Coin = Container.expand(function () { var self = Container.call(this); var coinAsset = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.width = coinAsset.width; self.height = coinAsset.height; self.lane = 1; // will be set on spawn self.speed = 16; // will be set by game for difficulty self.update = function () { self.y += self.speed; // Optional: spin effect self.rotation += 0.03; }; return self; }); // Obstacle class: cracks, snow piles, ice blocks, trees, rocks var Obstacle = Container.expand(function () { var self = Container.call(this); // Default to 'block', will be replaced on spawn if needed var assetId = 'block'; var obstacleAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.type = assetId; self.width = obstacleAsset.width; self.height = obstacleAsset.height; self.lane = 1; // will be set on spawn self.speed = 16; // will be set by game for difficulty self.update = function () { self.y += self.speed; }; return self; }); // Player class: the sliding character var Player = Container.expand(function () { var self = Container.call(this); // Attach player asset (blue ellipse for ice runner) var playerAsset = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Set up initial properties self.width = playerAsset.width; self.height = playerAsset.height; self.lane = 1; // 0: left, 1: center, 2: right // For smooth lane transitions self.targetX = 0; // Walking animation state self._walkTween = null; self._walkDir = 1; // Start walking animation function startWalking() { // Stop any previous tweens on playerAsset for scaleX and scaleY tween.stop(playerAsset, { scaleX: true, scaleY: true }); // Animate scaleX to create a "waddle" effect tween(playerAsset, { scaleX: 1.15 }, { duration: 120, easing: tween.linear, yoyo: true, repeat: Infinity, onYoyo: function onYoyo() { // Flip direction for next step self._walkDir *= -1; playerAsset.scaleX = 1 + 0.15 * self._walkDir; } }); // Animate scaleY for a subtle bounce tween(playerAsset, { scaleY: 0.92 }, { duration: 120, easing: tween.linear, yoyo: true, repeat: Infinity }); } startWalking(); // Update method: smoothly move to targetX self.update = function () { // Smoothly move towards targetX self.x += (self.targetX - self.x) * 0.25; }; return self; }); // SpecialPower class: clears all obstacles when collected var SpecialPower = Container.expand(function () { var self = Container.call(this); var asset = self.attachAsset('star', { // Use new star asset for superpower anchorX: 0.5, anchorY: 0.5 }); self.width = asset.width; self.height = asset.height; self.lane = 1; self.speed = 16; self.update = function () { self.y += self.speed; self.rotation += 0.03; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ // No title, no description // Always backgroundColor is black backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- Moving, repeating background setup --- var bg1 = LK.getAsset('bg_ice_landscape', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); var bg2 = LK.getAsset('bg_ice_landscape', { anchorX: 0, anchorY: 0, x: 0, y: -2732, width: 2048, height: 2732 }); game.addChild(bg1); game.addChild(bg2); // --- Lane setup --- // new superpower asset var laneCount = 3; var laneWidth = 400; // Each lane is 400px wide var laneCenters = [2048 / 2 - laneWidth, // left 2048 / 2, // center 2048 / 2 + laneWidth // right ]; // --- Player setup --- var player = new Player(); player.y = 2732 - 400; // Near bottom player.lane = 1; player.x = laneCenters[player.lane]; player.targetX = player.x; game.addChild(player); // --- Score and GUI --- var score = 0; var scoreTxt = new Text2('Score: 0', { size: 70, fill: 0xFFFFFF, font: "Pixel, 'Press Start 2P', 'Courier New', monospace" }); scoreTxt.anchor.set(1, 0); // Anchor to right edge, top scoreTxt.x = -40; // Padding from right edge (avoid top right 100x100 for menu) scoreTxt.y = 20; // Padding from top LK.gui.topRight.addChild(scoreTxt); // --- Coin counter --- var coinCount = 0; var coinTxt = new Text2('Coin: 0', { size: 50, fill: 0xFFE066, font: "Pixel, 'Press Start 2P', 'Courier New', monospace" }); coinTxt.anchor.set(1, 0); // Anchor to right edge, top coinTxt.x = -40; // Padding from right edge (avoid top right 100x100 for menu) coinTxt.y = 150; // Place below score LK.gui.topRight.addChild(coinTxt); // --- Game state --- var obstacles = []; var coins = []; var specialPowers = []; var gameSpeed = 16; // Initial speed var ticksSinceLastObstacle = 0; var ticksSinceLastCoin = 0; var ticksSinceLastPower = 0; var nextPowerInterval = 300 + Math.floor(Math.random() * 300); // Random interval for next power var obstacleInterval = 60; // Frames between obstacles var coinInterval = 90; // Frames between coins var isDragging = false; var dragStartX = 0; var dragStartLane = 1; // --- Asset initialization (shapes) --- // --- Touch/drag controls --- game.down = function (x, y, obj) { isDragging = true; dragStartX = x; dragStartLane = player.lane; }; game.move = function (x, y, obj) { if (!isDragging) return; // Calculate drag offset var dx = x - dragStartX; // Determine which lane the drag is closest to, relative to dragStartX var laneOffset = Math.round(dx / laneWidth); var newLane = dragStartLane + laneOffset; if (newLane < 0) newLane = 0; if (newLane > 2) newLane = 2; if (newLane !== player.lane) { player.lane = newLane; player.targetX = laneCenters[player.lane]; } }; game.up = function (x, y, obj) { isDragging = false; }; // --- Main game update loop --- game.update = function () { // --- Animate and repeat background --- bg1.y += gameSpeed; bg2.y += gameSpeed; // If a background has moved off the bottom, move it above the other if (bg1.y >= 2732) { bg1.y = bg2.y - 2732; } if (bg2.y >= 2732) { bg2.y = bg1.y - 2732; } // Scale speed and intervals with score for dynamic difficulty (0 to 1000 points) var difficulty = score; if (difficulty > 1000) difficulty = 1000; // Speed: from 12 (easy) to 40 (hard) linearly gameSpeed = 12 + difficulty / 1000 * (40 - 12); // Intervals: from 60 (easy) to 30 (hard) for obstacles, 90 to 50 for coins obstacleInterval = 60 - difficulty / 1000 * (60 - 30); if (obstacleInterval < 30) obstacleInterval = 30; coinInterval = 90 - difficulty / 1000 * (90 - 50); if (coinInterval < 50) coinInterval = 50; gameSpeed = Math.round(gameSpeed); obstacleInterval = Math.round(obstacleInterval); coinInterval = Math.round(coinInterval); // Player update (smooth lane movement) player.update(); // --- Spawn obstacles --- ticksSinceLastObstacle++; if (ticksSinceLastObstacle >= obstacleInterval) { ticksSinceLastObstacle = 0; // Prevent same obstacle type from spawning consecutively var lastType = obstacles.length > 0 ? obstacles[obstacles.length - 1].type : null; var possibleTypes = ['crack', 'snow', 'block', 'tree', 'rock']; if (lastType !== null && possibleTypes.length > 1) { var idx = possibleTypes.indexOf(lastType); if (idx !== -1) possibleTypes.splice(idx, 1); } var chosenType = possibleTypes[Math.floor(Math.random() * possibleTypes.length)]; var obs = new Obstacle(); obs.lane = Math.floor(Math.random() * laneCount); obs.x = laneCenters[obs.lane]; obs.y = -120; obs.speed = gameSpeed; // Always use current gameSpeed // Overwrite type and asset if needed obs.type = chosenType; // Remove old asset and attach new one while (obs.children.length > 0) obs.removeChild(obs.children[0]); var obstacleAsset = obs.attachAsset(chosenType, { anchorX: 0.5, anchorY: 0.5 }); obs.width = obstacleAsset.width; obs.height = obstacleAsset.height; obstacles.push(obs); game.addChild(obs); } // --- Spawn coins --- ticksSinceLastCoin++; if (ticksSinceLastCoin >= coinInterval) { ticksSinceLastCoin = 0; // Try to spawn coin in a lane without an obstacle at the spawn Y var availableLanes = [0, 1, 2]; // Remove lanes where an obstacle is at the spawn Y (within 200px above) for (var i = 0; i < obstacles.length; i++) { var obs = obstacles[i]; if (Math.abs(obs.y + 100) < 200) { // Obstacle is close to coin spawn Y var idx = availableLanes.indexOf(obs.lane); if (idx !== -1) availableLanes.splice(idx, 1); } } if (availableLanes.length > 0) { var coin = new Coin(); coin.lane = availableLanes[Math.floor(Math.random() * availableLanes.length)]; coin.x = laneCenters[coin.lane]; coin.y = -100; coin.speed = gameSpeed; // Always use current gameSpeed coin.value = 1 + Math.floor(Math.random() * 5); // Random value 1-5 coins.push(coin); game.addChild(coin); } } // --- Update obstacles --- for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.update(); // Remove if off screen if (obs.y > 2732 + 200) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision with player if (obs.lane === player.lane) { // Use bounding box collision if (obs.intersects(player)) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } } // --- Update coins --- for (var j = coins.length - 1; j >= 0; j--) { var coin = coins[j]; coin.update(); // Remove if off screen if (coin.y > 2732 + 120) { coin.destroy(); coins.splice(j, 1); continue; } // Collect coin if (coin.lane === player.lane) { if (coin.intersects(player)) { coinCount += coin.value || 1; coinTxt.setText('Coin: ' + coinCount); coin.destroy(); coins.splice(j, 1); continue; } } } // --- Special Power spawn/update --- ticksSinceLastPower++; if (ticksSinceLastPower >= nextPowerInterval) { ticksSinceLastPower = 0; nextPowerInterval = 300 + Math.floor(Math.random() * 300); // Next random interval // Try to spawn in a lane without an obstacle at spawn Y var availableLanes = [0, 1, 2]; for (var i = 0; i < obstacles.length; i++) { var obs = obstacles[i]; if (Math.abs(obs.y + 100) < 200) { var idx = availableLanes.indexOf(obs.lane); if (idx !== -1) availableLanes.splice(idx, 1); } } if (availableLanes.length > 0) { var power = new SpecialPower(); power.lane = availableLanes[Math.floor(Math.random() * availableLanes.length)]; power.x = laneCenters[power.lane]; power.y = -100; power.speed = gameSpeed; specialPowers.push(power); game.addChild(power); } } // --- Update special powers --- for (var k = specialPowers.length - 1; k >= 0; k--) { var power = specialPowers[k]; power.update(); if (power.y > 2732 + 120) { power.destroy(); specialPowers.splice(k, 1); continue; } // Collect special power if (power.lane === player.lane) { if (power.intersects(player)) { // Clear all obstacles on screen for (var i = obstacles.length - 1; i >= 0; i--) { obstacles[i].destroy(); obstacles.splice(i, 1); } power.destroy(); specialPowers.splice(k, 1); continue; } } } // --- Score increases with time survived --- if (LK.ticks % 6 === 0) { score += 1; scoreTxt.setText('Score: ' + score); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Coin class: collectible
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinAsset = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coinAsset.width;
self.height = coinAsset.height;
self.lane = 1; // will be set on spawn
self.speed = 16; // will be set by game for difficulty
self.update = function () {
self.y += self.speed;
// Optional: spin effect
self.rotation += 0.03;
};
return self;
});
// Obstacle class: cracks, snow piles, ice blocks, trees, rocks
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Default to 'block', will be replaced on spawn if needed
var assetId = 'block';
var obstacleAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = assetId;
self.width = obstacleAsset.width;
self.height = obstacleAsset.height;
self.lane = 1; // will be set on spawn
self.speed = 16; // will be set by game for difficulty
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player class: the sliding character
var Player = Container.expand(function () {
var self = Container.call(this);
// Attach player asset (blue ellipse for ice runner)
var playerAsset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Set up initial properties
self.width = playerAsset.width;
self.height = playerAsset.height;
self.lane = 1; // 0: left, 1: center, 2: right
// For smooth lane transitions
self.targetX = 0;
// Walking animation state
self._walkTween = null;
self._walkDir = 1;
// Start walking animation
function startWalking() {
// Stop any previous tweens on playerAsset for scaleX and scaleY
tween.stop(playerAsset, {
scaleX: true,
scaleY: true
});
// Animate scaleX to create a "waddle" effect
tween(playerAsset, {
scaleX: 1.15
}, {
duration: 120,
easing: tween.linear,
yoyo: true,
repeat: Infinity,
onYoyo: function onYoyo() {
// Flip direction for next step
self._walkDir *= -1;
playerAsset.scaleX = 1 + 0.15 * self._walkDir;
}
});
// Animate scaleY for a subtle bounce
tween(playerAsset, {
scaleY: 0.92
}, {
duration: 120,
easing: tween.linear,
yoyo: true,
repeat: Infinity
});
}
startWalking();
// Update method: smoothly move to targetX
self.update = function () {
// Smoothly move towards targetX
self.x += (self.targetX - self.x) * 0.25;
};
return self;
});
// SpecialPower class: clears all obstacles when collected
var SpecialPower = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('star', {
// Use new star asset for superpower
anchorX: 0.5,
anchorY: 0.5
});
self.width = asset.width;
self.height = asset.height;
self.lane = 1;
self.speed = 16;
self.update = function () {
self.y += self.speed;
self.rotation += 0.03;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- Moving, repeating background setup ---
var bg1 = LK.getAsset('bg_ice_landscape', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
var bg2 = LK.getAsset('bg_ice_landscape', {
anchorX: 0,
anchorY: 0,
x: 0,
y: -2732,
width: 2048,
height: 2732
});
game.addChild(bg1);
game.addChild(bg2);
// --- Lane setup ---
// new superpower asset
var laneCount = 3;
var laneWidth = 400; // Each lane is 400px wide
var laneCenters = [2048 / 2 - laneWidth,
// left
2048 / 2,
// center
2048 / 2 + laneWidth // right
];
// --- Player setup ---
var player = new Player();
player.y = 2732 - 400; // Near bottom
player.lane = 1;
player.x = laneCenters[player.lane];
player.targetX = player.x;
game.addChild(player);
// --- Score and GUI ---
var score = 0;
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF,
font: "Pixel, 'Press Start 2P', 'Courier New', monospace"
});
scoreTxt.anchor.set(1, 0); // Anchor to right edge, top
scoreTxt.x = -40; // Padding from right edge (avoid top right 100x100 for menu)
scoreTxt.y = 20; // Padding from top
LK.gui.topRight.addChild(scoreTxt);
// --- Coin counter ---
var coinCount = 0;
var coinTxt = new Text2('Coin: 0', {
size: 50,
fill: 0xFFE066,
font: "Pixel, 'Press Start 2P', 'Courier New', monospace"
});
coinTxt.anchor.set(1, 0); // Anchor to right edge, top
coinTxt.x = -40; // Padding from right edge (avoid top right 100x100 for menu)
coinTxt.y = 150; // Place below score
LK.gui.topRight.addChild(coinTxt);
// --- Game state ---
var obstacles = [];
var coins = [];
var specialPowers = [];
var gameSpeed = 16; // Initial speed
var ticksSinceLastObstacle = 0;
var ticksSinceLastCoin = 0;
var ticksSinceLastPower = 0;
var nextPowerInterval = 300 + Math.floor(Math.random() * 300); // Random interval for next power
var obstacleInterval = 60; // Frames between obstacles
var coinInterval = 90; // Frames between coins
var isDragging = false;
var dragStartX = 0;
var dragStartLane = 1;
// --- Asset initialization (shapes) ---
// --- Touch/drag controls ---
game.down = function (x, y, obj) {
isDragging = true;
dragStartX = x;
dragStartLane = player.lane;
};
game.move = function (x, y, obj) {
if (!isDragging) return;
// Calculate drag offset
var dx = x - dragStartX;
// Determine which lane the drag is closest to, relative to dragStartX
var laneOffset = Math.round(dx / laneWidth);
var newLane = dragStartLane + laneOffset;
if (newLane < 0) newLane = 0;
if (newLane > 2) newLane = 2;
if (newLane !== player.lane) {
player.lane = newLane;
player.targetX = laneCenters[player.lane];
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// --- Main game update loop ---
game.update = function () {
// --- Animate and repeat background ---
bg1.y += gameSpeed;
bg2.y += gameSpeed;
// If a background has moved off the bottom, move it above the other
if (bg1.y >= 2732) {
bg1.y = bg2.y - 2732;
}
if (bg2.y >= 2732) {
bg2.y = bg1.y - 2732;
}
// Scale speed and intervals with score for dynamic difficulty (0 to 1000 points)
var difficulty = score;
if (difficulty > 1000) difficulty = 1000;
// Speed: from 12 (easy) to 40 (hard) linearly
gameSpeed = 12 + difficulty / 1000 * (40 - 12);
// Intervals: from 60 (easy) to 30 (hard) for obstacles, 90 to 50 for coins
obstacleInterval = 60 - difficulty / 1000 * (60 - 30);
if (obstacleInterval < 30) obstacleInterval = 30;
coinInterval = 90 - difficulty / 1000 * (90 - 50);
if (coinInterval < 50) coinInterval = 50;
gameSpeed = Math.round(gameSpeed);
obstacleInterval = Math.round(obstacleInterval);
coinInterval = Math.round(coinInterval);
// Player update (smooth lane movement)
player.update();
// --- Spawn obstacles ---
ticksSinceLastObstacle++;
if (ticksSinceLastObstacle >= obstacleInterval) {
ticksSinceLastObstacle = 0;
// Prevent same obstacle type from spawning consecutively
var lastType = obstacles.length > 0 ? obstacles[obstacles.length - 1].type : null;
var possibleTypes = ['crack', 'snow', 'block', 'tree', 'rock'];
if (lastType !== null && possibleTypes.length > 1) {
var idx = possibleTypes.indexOf(lastType);
if (idx !== -1) possibleTypes.splice(idx, 1);
}
var chosenType = possibleTypes[Math.floor(Math.random() * possibleTypes.length)];
var obs = new Obstacle();
obs.lane = Math.floor(Math.random() * laneCount);
obs.x = laneCenters[obs.lane];
obs.y = -120;
obs.speed = gameSpeed; // Always use current gameSpeed
// Overwrite type and asset if needed
obs.type = chosenType;
// Remove old asset and attach new one
while (obs.children.length > 0) obs.removeChild(obs.children[0]);
var obstacleAsset = obs.attachAsset(chosenType, {
anchorX: 0.5,
anchorY: 0.5
});
obs.width = obstacleAsset.width;
obs.height = obstacleAsset.height;
obstacles.push(obs);
game.addChild(obs);
}
// --- Spawn coins ---
ticksSinceLastCoin++;
if (ticksSinceLastCoin >= coinInterval) {
ticksSinceLastCoin = 0;
// Try to spawn coin in a lane without an obstacle at the spawn Y
var availableLanes = [0, 1, 2];
// Remove lanes where an obstacle is at the spawn Y (within 200px above)
for (var i = 0; i < obstacles.length; i++) {
var obs = obstacles[i];
if (Math.abs(obs.y + 100) < 200) {
// Obstacle is close to coin spawn Y
var idx = availableLanes.indexOf(obs.lane);
if (idx !== -1) availableLanes.splice(idx, 1);
}
}
if (availableLanes.length > 0) {
var coin = new Coin();
coin.lane = availableLanes[Math.floor(Math.random() * availableLanes.length)];
coin.x = laneCenters[coin.lane];
coin.y = -100;
coin.speed = gameSpeed; // Always use current gameSpeed
coin.value = 1 + Math.floor(Math.random() * 5); // Random value 1-5
coins.push(coin);
game.addChild(coin);
}
}
// --- Update obstacles ---
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.y > 2732 + 200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with player
if (obs.lane === player.lane) {
// Use bounding box collision
if (obs.intersects(player)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
// --- Update coins ---
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
coin.update();
// Remove if off screen
if (coin.y > 2732 + 120) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Collect coin
if (coin.lane === player.lane) {
if (coin.intersects(player)) {
coinCount += coin.value || 1;
coinTxt.setText('Coin: ' + coinCount);
coin.destroy();
coins.splice(j, 1);
continue;
}
}
}
// --- Special Power spawn/update ---
ticksSinceLastPower++;
if (ticksSinceLastPower >= nextPowerInterval) {
ticksSinceLastPower = 0;
nextPowerInterval = 300 + Math.floor(Math.random() * 300); // Next random interval
// Try to spawn in a lane without an obstacle at spawn Y
var availableLanes = [0, 1, 2];
for (var i = 0; i < obstacles.length; i++) {
var obs = obstacles[i];
if (Math.abs(obs.y + 100) < 200) {
var idx = availableLanes.indexOf(obs.lane);
if (idx !== -1) availableLanes.splice(idx, 1);
}
}
if (availableLanes.length > 0) {
var power = new SpecialPower();
power.lane = availableLanes[Math.floor(Math.random() * availableLanes.length)];
power.x = laneCenters[power.lane];
power.y = -100;
power.speed = gameSpeed;
specialPowers.push(power);
game.addChild(power);
}
}
// --- Update special powers ---
for (var k = specialPowers.length - 1; k >= 0; k--) {
var power = specialPowers[k];
power.update();
if (power.y > 2732 + 120) {
power.destroy();
specialPowers.splice(k, 1);
continue;
}
// Collect special power
if (power.lane === player.lane) {
if (power.intersects(player)) {
// Clear all obstacles on screen
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
power.destroy();
specialPowers.splice(k, 1);
continue;
}
}
}
// --- Score increases with time survived ---
if (LK.ticks % 6 === 0) {
score += 1;
scoreTxt.setText('Score: ' + score);
}
};
Pixel 2d coin. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d pixel snow. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d pixel ice block. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d pixel crack. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d pixel potion. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Let it be turned back
2d pixel dried tree. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d pixel rock. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Let it be in a way that can repeat itself when it moves