/****
* 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();
}; /****
* 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();
};
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