User prompt
Add a power-up that the player can collect while running. When the player touches it, the character becomes temporarily invincible for a few seconds. During this time, the character can go through obstacles without getting hurt. The power-up should be easy to spot and appear sometimes during the run. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Sonic Lane Runner
Initial prompt
An endless runner game with infinite levels where the player controls a Roblox-style Sonic character. The character runs forward on 3 lanes (left, center, right), dodging obstacles like spikes and cube-shaped enemies, while collecting golden rings. The camera should be in third-person view, positioned behind the character (not top-down), just like in Subway Surfers. The game is fast-paced and gets harder over time. The player can swipe left or right to change lanes, swipe up to jump, and swipe down to slide. The world is colorful and 3D, with cheerful music and simple controls for kids. Inspired by Sonic and Roblox.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Obstacle = Container.expand(function (type) { var self = Container.call(this); self.type = type; var assetName = type === 'spike' ? 'spike' : type === 'cube' ? 'cubeEnemy' : 'barrier'; var obstacleGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.speed = gameSpeed; self.update = function () { self.y += self.speed; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = gameSpeed; self.collected = false; self.update = function () { self.y += self.speed; // Pulsing effect to make it stand out powerupGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.2; powerupGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.2; powerupGraphics.rotation += 0.05; }; return self; }); var Ring = Container.expand(function () { var self = Container.call(this); var ringGraphics = self.attachAsset('ring', { anchorX: 0.5, anchorY: 0.5 }); self.speed = gameSpeed; self.collected = false; self.update = function () { self.y += self.speed; ringGraphics.rotation += 0.1; }; return self; }); var SonicCharacter = Container.expand(function () { var self = Container.call(this); var characterGraphics = self.attachAsset('sonicCharacter', { anchorX: 0.5, anchorY: 0.5 }); self.currentLane = 1; // 0 = left, 1 = center, 2 = right self.isJumping = false; self.isSliding = false; self.jumpStartY = 0; self.baseY = 0; self.isInvincible = false; self.invincibilityTimer = 0; self.activateInvincibility = function () { self.isInvincible = true; self.invincibilityTimer = LK.ticks + 300; // 5 seconds at 60fps // Visual effect - golden tint tween(characterGraphics, { tint: 0xFFD700 }, { duration: 200 }); // Pulsing effect during invincibility self.invincibilityPulse(); }; self.invincibilityPulse = function () { if (self.isInvincible) { tween(characterGraphics, { alpha: 0.5 }, { duration: 250, easing: tween.easeInOut, onFinish: function onFinish() { if (self.isInvincible) { tween(characterGraphics, { alpha: 1 }, { duration: 250, easing: tween.easeInOut, onFinish: function onFinish() { self.invincibilityPulse(); } }); } } }); } }; self.deactivateInvincibility = function () { self.isInvincible = false; // Remove visual effects tween.stop(characterGraphics, { alpha: true }); tween(characterGraphics, { tint: 0xFFFFFF, alpha: 1 }, { duration: 200 }); }; self.update = function () { // Check if invincibility should end if (self.isInvincible && LK.ticks >= self.invincibilityTimer) { self.deactivateInvincibility(); } }; self.moveTo = function (lane) { if (lane < 0 || lane > 2) return; self.currentLane = lane; var targetX = lanePositions[lane]; tween(self, { x: targetX }, { duration: 200, easing: tween.easeOut }); }; self.jump = function () { if (self.isJumping || self.isSliding) return; self.isJumping = true; self.jumpStartY = self.y; LK.getSound('jump').play(); tween(self, { y: self.jumpStartY - 150 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { y: self.jumpStartY }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.isJumping = false; } }); } }); }; self.slide = function () { if (self.isJumping || self.isSliding) return; self.isSliding = true; var originalScale = characterGraphics.scaleY; characterGraphics.scaleY = 0.5; LK.setTimeout(function () { characterGraphics.scaleY = originalScale; self.isSliding = false; }, 500); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game variables var gameSpeed = 8; var speedIncrement = 0.02; var lanePositions = [2048 * 0.25, 2048 * 0.5, 2048 * 0.75]; var obstacles = []; var rings = []; var powerups = []; var lastObstacleSpawn = 0; var lastRingSpawn = 0; var lastPowerupSpawn = 0; var ringCount = 0; var gameDistance = 0; // Create lanes visual for (var i = 0; i < 3; i++) { var lane = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, x: lanePositions[i], y: 2732 * 0.8 }); game.addChild(lane); } // Create character var sonic = game.addChild(new SonicCharacter()); sonic.x = lanePositions[1]; sonic.y = 2732 * 0.7; sonic.baseY = sonic.y; // Create UI var scoreText = new Text2('Rings: 0', { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var distanceText = new Text2('Distance: 0m', { size: 60, fill: 0xFFFFFF }); distanceText.anchor.set(0, 0); distanceText.x = 50; distanceText.y = 50; LK.gui.topLeft.addChild(distanceText); // Touch controls var touchStartX = 0; var touchStartY = 0; var minSwipeDistance = 50; game.down = function (x, y, obj) { touchStartX = x; touchStartY = y; }; game.up = function (x, y, obj) { var deltaX = x - touchStartX; var deltaY = y - touchStartY; if (Math.abs(deltaX) > Math.abs(deltaY)) { // Horizontal swipe if (Math.abs(deltaX) > minSwipeDistance) { if (deltaX > 0) { // Swipe right sonic.moveTo(sonic.currentLane + 1); } else { // Swipe left sonic.moveTo(sonic.currentLane - 1); } } } else { // Vertical swipe if (Math.abs(deltaY) > minSwipeDistance) { if (deltaY < 0) { // Swipe up - jump sonic.jump(); } else { // Swipe down - slide sonic.slide(); } } } }; function spawnObstacle() { var obstacleTypes = ['spike', 'cube', 'barrier']; var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)]; var lane = Math.floor(Math.random() * 3); var obstacle = new Obstacle(type); obstacle.x = lanePositions[lane]; obstacle.y = -100; obstacle.speed = gameSpeed; obstacles.push(obstacle); game.addChild(obstacle); } function spawnRing() { var lane = Math.floor(Math.random() * 3); var ring = new Ring(); ring.x = lanePositions[lane]; ring.y = -100; ring.speed = gameSpeed; rings.push(ring); game.addChild(ring); } function spawnPowerup() { var lane = Math.floor(Math.random() * 3); var powerup = new PowerUp(); powerup.x = lanePositions[lane]; powerup.y = -100; powerup.speed = gameSpeed; powerups.push(powerup); game.addChild(powerup); } function checkCollisions() { // Check ring collection for (var i = rings.length - 1; i >= 0; i--) { var ring = rings[i]; if (!ring.collected && ring.intersects(sonic)) { ring.collected = true; ringCount++; LK.setScore(ringCount); scoreText.setText('Rings: ' + ringCount); LK.getSound('collect').play(); // Visual effect tween(ring, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, onFinish: function onFinish() { ring.destroy(); } }); rings.splice(i, 1); } } // Check power-up collection for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; if (!powerup.collected && powerup.intersects(sonic)) { powerup.collected = true; sonic.activateInvincibility(); LK.getSound('powerup').play(); // Visual effect tween(powerup, { alpha: 0, scaleX: 3, scaleY: 3 }, { duration: 400, onFinish: function onFinish() { powerup.destroy(); } }); powerups.splice(i, 1); } } // Check obstacle collisions for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; if (obstacle.intersects(sonic)) { // Check if player can avoid obstacle var canAvoid = false; if (obstacle.type === 'spike' && sonic.isJumping) { canAvoid = true; } else if (obstacle.type === 'barrier' && sonic.isSliding) { canAvoid = true; } else if (obstacle.type === 'cube' && (sonic.isJumping || sonic.isSliding)) { canAvoid = true; } if (!canAvoid && !sonic.isInvincible) { // Game over LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); return; } } } } function updateGameObjects() { // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obstacle = obstacles[i]; obstacle.speed = gameSpeed; if (obstacle.y > 2732 + 100) { obstacle.destroy(); obstacles.splice(i, 1); } } // Update rings for (var i = rings.length - 1; i >= 0; i--) { var ring = rings[i]; ring.speed = gameSpeed; if (ring.y > 2732 + 100) { ring.destroy(); rings.splice(i, 1); } } // Update power-ups for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; powerup.speed = gameSpeed; if (powerup.y > 2732 + 100) { powerup.destroy(); powerups.splice(i, 1); } } // Increase game speed gradually gameSpeed += speedIncrement; // Update distance gameDistance += gameSpeed * 0.1; distanceText.setText('Distance: ' + Math.floor(gameDistance) + 'm'); } // Start background music LK.playMusic('bgmusic'); game.update = function () { // Spawn obstacles if (LK.ticks - lastObstacleSpawn > 120 - Math.min(gameSpeed * 2, 60)) { spawnObstacle(); lastObstacleSpawn = LK.ticks; } // Spawn rings if (LK.ticks - lastRingSpawn > 80) { if (Math.random() < 0.7) { spawnRing(); } lastRingSpawn = LK.ticks; } // Spawn power-ups (less frequent than rings) if (LK.ticks - lastPowerupSpawn > 600) { // Every 10 seconds if (Math.random() < 0.3) { // 30% chance spawnPowerup(); } lastPowerupSpawn = LK.ticks; } checkCollisions(); updateGameObjects(); };
===================================================================
--- original.js
+++ change.js
@@ -19,8 +19,25 @@
self.y += self.speed;
};
return self;
});
+var PowerUp = Container.expand(function () {
+ var self = Container.call(this);
+ var powerupGraphics = self.attachAsset('powerup', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = gameSpeed;
+ self.collected = false;
+ self.update = function () {
+ self.y += self.speed;
+ // Pulsing effect to make it stand out
+ powerupGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.2;
+ powerupGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.2;
+ powerupGraphics.rotation += 0.05;
+ };
+ return self;
+});
var Ring = Container.expand(function () {
var self = Container.call(this);
var ringGraphics = self.attachAsset('ring', {
anchorX: 0.5,
@@ -44,8 +61,64 @@
self.isJumping = false;
self.isSliding = false;
self.jumpStartY = 0;
self.baseY = 0;
+ self.isInvincible = false;
+ self.invincibilityTimer = 0;
+ self.activateInvincibility = function () {
+ self.isInvincible = true;
+ self.invincibilityTimer = LK.ticks + 300; // 5 seconds at 60fps
+ // Visual effect - golden tint
+ tween(characterGraphics, {
+ tint: 0xFFD700
+ }, {
+ duration: 200
+ });
+ // Pulsing effect during invincibility
+ self.invincibilityPulse();
+ };
+ self.invincibilityPulse = function () {
+ if (self.isInvincible) {
+ tween(characterGraphics, {
+ alpha: 0.5
+ }, {
+ duration: 250,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ if (self.isInvincible) {
+ tween(characterGraphics, {
+ alpha: 1
+ }, {
+ duration: 250,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ self.invincibilityPulse();
+ }
+ });
+ }
+ }
+ });
+ }
+ };
+ self.deactivateInvincibility = function () {
+ self.isInvincible = false;
+ // Remove visual effects
+ tween.stop(characterGraphics, {
+ alpha: true
+ });
+ tween(characterGraphics, {
+ tint: 0xFFFFFF,
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ };
+ self.update = function () {
+ // Check if invincibility should end
+ if (self.isInvincible && LK.ticks >= self.invincibilityTimer) {
+ self.deactivateInvincibility();
+ }
+ };
self.moveTo = function (lane) {
if (lane < 0 || lane > 2) return;
self.currentLane = lane;
var targetX = lanePositions[lane];
@@ -107,10 +180,12 @@
var speedIncrement = 0.02;
var lanePositions = [2048 * 0.25, 2048 * 0.5, 2048 * 0.75];
var obstacles = [];
var rings = [];
+var powerups = [];
var lastObstacleSpawn = 0;
var lastRingSpawn = 0;
+var lastPowerupSpawn = 0;
var ringCount = 0;
var gameDistance = 0;
// Create lanes visual
for (var i = 0; i < 3; i++) {
@@ -197,8 +272,17 @@
ring.speed = gameSpeed;
rings.push(ring);
game.addChild(ring);
}
+function spawnPowerup() {
+ var lane = Math.floor(Math.random() * 3);
+ var powerup = new PowerUp();
+ powerup.x = lanePositions[lane];
+ powerup.y = -100;
+ powerup.speed = gameSpeed;
+ powerups.push(powerup);
+ game.addChild(powerup);
+}
function checkCollisions() {
// Check ring collection
for (var i = rings.length - 1; i >= 0; i--) {
var ring = rings[i];
@@ -221,8 +305,29 @@
});
rings.splice(i, 1);
}
}
+ // Check power-up collection
+ for (var i = powerups.length - 1; i >= 0; i--) {
+ var powerup = powerups[i];
+ if (!powerup.collected && powerup.intersects(sonic)) {
+ powerup.collected = true;
+ sonic.activateInvincibility();
+ LK.getSound('powerup').play();
+ // Visual effect
+ tween(powerup, {
+ alpha: 0,
+ scaleX: 3,
+ scaleY: 3
+ }, {
+ duration: 400,
+ onFinish: function onFinish() {
+ powerup.destroy();
+ }
+ });
+ powerups.splice(i, 1);
+ }
+ }
// Check obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.intersects(sonic)) {
@@ -234,9 +339,9 @@
canAvoid = true;
} else if (obstacle.type === 'cube' && (sonic.isJumping || sonic.isSliding)) {
canAvoid = true;
}
- if (!canAvoid) {
+ if (!canAvoid && !sonic.isInvincible) {
// Game over
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
return;
@@ -262,8 +367,17 @@
ring.destroy();
rings.splice(i, 1);
}
}
+ // Update power-ups
+ for (var i = powerups.length - 1; i >= 0; i--) {
+ var powerup = powerups[i];
+ powerup.speed = gameSpeed;
+ if (powerup.y > 2732 + 100) {
+ powerup.destroy();
+ powerups.splice(i, 1);
+ }
+ }
// Increase game speed gradually
gameSpeed += speedIncrement;
// Update distance
gameDistance += gameSpeed * 0.1;
@@ -283,7 +397,16 @@
spawnRing();
}
lastRingSpawn = LK.ticks;
}
+ // Spawn power-ups (less frequent than rings)
+ if (LK.ticks - lastPowerupSpawn > 600) {
+ // Every 10 seconds
+ if (Math.random() < 0.3) {
+ // 30% chance
+ spawnPowerup();
+ }
+ lastPowerupSpawn = LK.ticks;
+ }
checkCollisions();
updateGameObjects();
};
\ No newline at end of file
Make Sonic run but with the camera above. In-Game asset. High contrast. No shadows
make sonic's enemy doctor eggman. In-Game asset. High contrast. No shadows
beam to sonic's ring. In-Game asset. High contrast. No shadows
barrel. In-Game asset. High contrast. No shadows
hot dog. In-Game asset. High contrast. No shadows
make a cactus. In-Game asset. High contrast. No shadows
make sand. In-Game asset. High contrast. No shadows