User prompt
The player should have a 0.5 larger hitbox, accommodate the player skin I have provided you
User prompt
The player is a car, the obstacles are spikes and the platforms are asphalt.
User prompt
The player has a car skin, car spike obstacles and street or asphalt platforms.
User prompt
The player must be on top of the platforms just as the obstacles must be on top not in the center
User prompt
Platform textures should be finer, the size of their hitbox.
User prompt
Platforms should have a finer texture or a larger hitbox.
User prompt
The platforms will have a stone texture, the player will be a cart and the obstacles will be small and Mediums will be peaks on the ground and large ones will be birds flying ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Every 0.5 seconds is 1 point
User prompt
Remove the record section and change it to "score" Each point is equivalent to 1 second. 1 second = 1 point
User prompt
Large obstacles must appear at least 3 times every 150 points
User prompt
Obstacles tend to appear on the platform where the player is standing. The big obstacle will be a wall that will cover 2 platforms
User prompt
The indicator where the obstacles will emerge from should appear 0.5 seconds before the obstacle appears. The obstacles will have 3 sizes: small, medium and large, the large one will occupy 2 of the 3 platforms ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make an arrow appear where the obstacles come out . Make obstacles increase their speed by 1 for every 100 points
User prompt
That the character moves between the 3 platforms and can go up or down with an arrow button There will be some obstacles that will pass through the platforms.
Code edit (1 edits merged)
Please save this source code
User prompt
Jump Runner - Infinite Obstacle Course
Initial prompt
Create a game where the character runs infinitely but has to jump over obstacles which will come from 3 directions: top, middle and bottom, those below and middle can be jumped over.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var PlayBall = Container.expand(function (lane, size) {
var self = Container.call(this);
self.lane = lane || 'middle';
self.size = size || 'small';
self.speed = 8;
self.isLarge = size === 'large';
// Array of random colors for playballs
var colors = [0xff5722,
// Orange
0x2196f3,
// Blue
0x4caf50,
// Green
0xffeb3b,
// Yellow
0x9c27b0,
// Purple
0xe91e63,
// Pink
0x00bcd4,
// Cyan
0xff9800,
// Deep Orange
0x673ab7,
// Deep Purple
0x3f51b5 // Indigo
];
// Select random color
var randomColor = colors[Math.floor(Math.random() * colors.length)];
// Create playball
var ballAsset = size === 'large' ? 'playballLarge' : 'playball';
// Create playball
var ball = self.attachAsset(ballAsset, {
anchorX: 0.5,
anchorY: 0.5,
tint: randomColor
});
// Add rotation animation
self.rotation = 0;
self.update = function () {
self.x -= self.speed;
// Rotate ball as it rolls
self.rotation -= 0.1;
ball.rotation = self.rotation;
};
return self;
});
// New Player class with boat design
var Player = Container.expand(function () {
var self = Container.call(this);
// Create boat hull (main body) - scaled to 1x (50% smaller than previous 2x)
var boatHull = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.6,
tint: 0x8B4513 // Brown color for wooden boat
});
// Create boat front (bow) - scaled to 1x (50% smaller) with adjusted position
var boatBow = self.attachAsset('spikePoint', {
anchorX: 0.5,
anchorY: 0.5,
x: 70,
y: 0,
scaleX: 0.8,
scaleY: 0.6,
rotation: -Math.PI / 2,
tint: 0x8B4513 // Brown color
});
// Create boat cabin - scaled to 1x (50% smaller) with adjusted position
var boatCabin = self.attachAsset('platformFine', {
anchorX: 0.5,
anchorY: 0.5,
x: -10,
y: 0,
scaleX: 4,
scaleY: 6,
tint: 0xFFFFFF // White cabin
});
// Create boat mast - scaled to 1x (50% smaller) with adjusted position
var boatMast = self.attachAsset('stickBody', {
anchorX: 0.5,
anchorY: 1,
x: -10,
y: -20,
scaleX: 0.5,
scaleY: 1.5,
tint: 0x654321 // Dark brown
});
// Create boat sail - scaled to 1x (50% smaller) with adjusted position
var boatSail = self.attachAsset('spikePoint', {
anchorX: 0.5,
anchorY: 0.8,
x: -10,
y: -30,
scaleX: 1.2,
scaleY: 0.8,
tint: 0xFFFFFF // White sail
});
// Create boat flag - scaled to 1x (50% smaller) with adjusted position
var boatFlag = self.attachAsset('spawnArrow', {
anchorX: 0,
anchorY: 0.5,
x: -10,
y: -65,
scaleX: 0.6,
scaleY: 0.4,
tint: 0xFF0000 // Red flag
});
// Animation variables for boat movement
self.animationTime = 0;
self.animationSpeed = 0.1;
// Player properties
self.currentLane = 0; // 0=top, 1=bottom (only 2 lanes now)
self.isMoving = false;
self.targetY = 0;
self.moveSpeed = 18; // Faster movement
// Initialize lane positions array for cleaner code
self.lanePositions = [0, 0]; // Will be set when lanes are defined
// Move up to previous lane
self.moveUp = function () {
if (!self.isMoving && self.currentLane > 0) {
self.currentLane--;
self.isMoving = true;
self.updateTargetPosition();
LK.getSound('platformSwitch').play();
}
};
// Move down to next lane
self.moveDown = function () {
if (!self.isMoving && self.currentLane < 1) {
self.currentLane++;
self.isMoving = true;
self.updateTargetPosition();
LK.getSound('platformSwitch').play();
}
};
// Update target position based on current lane
self.updateTargetPosition = function () {
// Use global lane positions if available, otherwise use defaults - adjusted for 1x player (50% smaller)
if (typeof topLaneY !== 'undefined') {
self.lanePositions[0] = topLaneY - 80;
self.lanePositions[1] = bottomLaneY - 80;
}
self.targetY = self.lanePositions[self.currentLane];
};
// Update function called every frame
self.update = function () {
// Handle lane switching movement
if (self.isMoving) {
var diff = self.targetY - self.y;
// Vertical movement
if (Math.abs(diff) < self.moveSpeed) {
self.y = self.targetY;
self.isMoving = false;
} else {
self.y += diff > 0 ? self.moveSpeed : -self.moveSpeed;
}
}
// Animate boat with bobbing motion
self.animationTime += self.animationSpeed;
var bobOffset = Math.sin(self.animationTime) * 3;
var tiltOffset = Math.sin(self.animationTime * 0.5) * 0.05;
// Bob the entire boat up and down slightly
self.y += bobOffset * 0.1;
// Tilt the boat slightly
self.rotation = tiltOffset;
// Animate the sail swaying
boatSail.rotation = Math.sin(self.animationTime * 0.7) * 0.1;
// Animate the flag waving
boatFlag.rotation = Math.sin(self.animationTime * 2) * 0.3;
};
return self;
});
var SpawnArrow = Container.expand(function (lane) {
var self = Container.call(this);
self.lane = lane || 'middle';
var arrowGraphics = self.attachAsset('spawnArrow', {
anchorX: 0.5,
anchorY: 0.5
});
arrowGraphics.rotation = Math.PI; // Point left toward incoming obstacles
self.fadeTimer = 0;
self.maxFadeTime = 60; // 1 second at 60fps
self.update = function () {
self.fadeTimer++;
var alpha = 1 - self.fadeTimer / self.maxFadeTime;
if (alpha <= 0) {
self.destroy();
return;
}
arrowGraphics.alpha = alpha;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x00a8e6
});
/****
* Game Code
****/
// Game dimensions: 2048x2732
var Obstacle = PlayBall;
var gameWidth = 2048;
var gameHeight = 2732;
// Lane positions - now only 2 lanes
var topLaneY = gameHeight * 0.4;
var bottomLaneY = gameHeight * 0.6;
// Game variables
var player;
var obstacles = [];
var spawnArrows = [];
var pendingObstacles = [];
var gameSpeed = 1;
var spawnTimer = 0;
var spawnInterval = 120; // frames between spawns
var gameTime = 0; // tracks game time in frames (60fps = 1 second)
var speedIncreaseInterval = 600; // increase speed every 10 seconds at 60fps
var largeObstaclesSpawned = 0;
var lastScoreCheck = 0;
var gameStarted = false;
var startButton;
// Initialize score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create player
player = game.addChild(new Player());
player.x = gameWidth * 0.35; // Move player closer to center for better camera view
player.y = topLaneY - 80; // Position on top of platform with 1x player height (50% smaller)
player.updateTargetPosition(); // Initialize target positions
player.visible = false; // Hide player until game starts
// Create start menu
var startMenu = new Container();
game.addChild(startMenu);
// Create background boat animation
var backgroundBoat = new Player();
backgroundBoat.x = -200; // Start off-screen to the left
backgroundBoat.y = gameHeight * 0.5;
backgroundBoat.alpha = 0.3; // Make it semi-transparent
backgroundBoat.scaleX = 1.2; // 50% larger than player boat
backgroundBoat.scaleY = 1.2;
startMenu.addChild(backgroundBoat);
// Animate background boat moving across screen
var backgroundBoatTween = tween(backgroundBoat, {
x: gameWidth + 200
}, {
duration: 8000,
easing: tween.linear,
onComplete: function onComplete() {
// Reset position and restart animation
backgroundBoat.x = -200;
backgroundBoatTween.restart();
}
});
// Create second background boat at different position
var backgroundBoat2 = new Player();
backgroundBoat2.x = gameWidth * 0.5; // Start in middle
backgroundBoat2.y = gameHeight * 0.35;
backgroundBoat2.alpha = 0.2; // Even more transparent
backgroundBoat2.scaleX = 0.9; // 50% larger scale
backgroundBoat2.scaleY = 0.9;
startMenu.addChild(backgroundBoat2);
// Animate second boat with different speed
var backgroundBoatTween2 = tween(backgroundBoat2, {
x: gameWidth + 200
}, {
duration: 6000,
easing: tween.linear,
onComplete: function onComplete() {
// Reset position and restart animation
backgroundBoat2.x = -200;
backgroundBoatTween2.restart();
}
});
// Add game title
var titleText = new Text2('ESCAPE FROM THE MINES BY BOAT', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = gameWidth / 2;
titleText.y = gameHeight * 0.3;
startMenu.addChild(titleText);
// Create start button
startButton = new Container();
startMenu.addChild(startButton);
var buttonBg = startButton.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 2,
tint: 0x4CAF50
});
var buttonText = new Text2('START', {
size: 80,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
startButton.addChild(buttonText);
startButton.x = gameWidth / 2;
startButton.y = gameHeight / 2;
// Add instructions
var instructionsText = new Text2('Swipe up/down or use arrows to swim', {
size: 50,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = gameWidth / 2;
instructionsText.y = gameHeight * 0.65;
startMenu.addChild(instructionsText);
// Add additional instruction
var dodgeText = new Text2('Dodge the playballs!', {
size: 50,
fill: 0xFFFFFF
});
dodgeText.anchor.set(0.5, 0.5);
dodgeText.x = gameWidth / 2;
dodgeText.y = gameHeight * 0.72;
startMenu.addChild(dodgeText);
// Add decorative playballs to menu
var menuBall1 = startMenu.attachAsset('playball', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth * 0.2,
y: gameHeight * 0.3,
tint: 0xff5722
});
var menuBall2 = startMenu.attachAsset('playball', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth * 0.8,
y: gameHeight * 0.3,
tint: 0x2196f3
});
// Animate menu balls
tween(menuBall1, {
rotation: Math.PI * 2
}, {
duration: 3000,
loop: true,
easing: tween.linear
});
tween(menuBall2, {
rotation: -Math.PI * 2
}, {
duration: 3000,
loop: true,
easing: tween.linear
});
// Start button click handler
startButton.down = function (x, y, obj) {
gameStarted = true;
player.visible = true;
if (backgroundBoatTween) {
backgroundBoatTween.stop();
}
if (backgroundBoatTween2) {
backgroundBoatTween2.stop();
}
startMenu.destroy();
startMenu = null;
startButton = null;
};
// Create arrow buttons
var arrowUp = LK.getAsset('arrowUp', {
anchorX: 0.5,
anchorY: 0.5
});
arrowUp.x = gameWidth - 150;
arrowUp.y = gameHeight - 300;
LK.gui.addChild(arrowUp);
var arrowDown = LK.getAsset('arrowDown', {
anchorX: 0.5,
anchorY: 0.5
});
arrowDown.x = gameWidth - 150;
arrowDown.y = gameHeight - 150;
LK.gui.addChild(arrowDown);
// Arrow button event handlers
arrowUp.down = function (x, y, obj) {
if (gameStarted) {
player.moveUp();
}
};
arrowDown.down = function (x, y, obj) {
if (gameStarted) {
player.moveDown();
}
};
// Draw stone platform tracks with finer texture matching hitbox
// Create top platform using fine texture tiles
for (var i = 0; i < 41; i++) {
var topTile = LK.getAsset('platformFine', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 7.2
});
topTile.x = i * 50;
topTile.y = topLaneY;
game.addChild(topTile);
}
// Create bottom platform using fine texture tiles
for (var i = 0; i < 41; i++) {
var bottomTile = LK.getAsset('platformFine', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 7.2
});
bottomTile.x = i * 50;
bottomTile.y = bottomLaneY;
game.addChild(bottomTile);
}
function spawnObstacle() {
var lanes = ['top', 'bottom'];
var sizes = ['small', 'medium', 'large'];
var currentScore = LK.getScore();
var scoreSegment = Math.floor(currentScore / 150);
var requiredLargeObstacles = (scoreSegment + 1) * 3;
var forceLarge = false;
// Check if we need to force a large obstacle
if (scoreSegment > Math.floor(lastScoreCheck / 150)) {
// We've entered a new 150-point segment, reset counter
largeObstaclesSpawned = 0;
lastScoreCheck = currentScore;
}
// Force large obstacle if we haven't met the requirement and we're near the end of the segment
var progressInSegment = currentScore % 150;
if (progressInSegment > 100 && largeObstaclesSpawned < 3) {
forceLarge = true;
}
var randomSize;
if (forceLarge) {
randomSize = 'large';
} else {
randomSize = sizes[Math.floor(Math.random() * sizes.length)];
}
// Track large obstacles
if (randomSize === 'large') {
largeObstaclesSpawned++;
}
var randomLane;
// 46% more chance to spawn obstacle on player's current lane
// Base probability for 2 lanes is 50%, so 46% more = 73% for player's lane
if (Math.random() < 0.73) {
randomLane = lanes[player.currentLane];
} else {
// 27% chance for the other lane
randomLane = lanes[1 - player.currentLane];
}
// Create spawn arrow indicator
var spawnArrow = new SpawnArrow(randomLane);
spawnArrow.x = gameWidth - 250; // Position spawn arrow closer to match tighter view
if (randomLane === 'top') {
spawnArrow.y = topLaneY;
} else {
spawnArrow.y = bottomLaneY;
}
// Add pulsing animation to spawn arrow
tween(spawnArrow, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 250,
easing: tween.easeInOut
});
tween(spawnArrow, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 250,
easing: tween.easeInOut
});
spawnArrows.push(spawnArrow);
game.addChild(spawnArrow);
// Schedule obstacle to spawn after 0.5 seconds (30 frames at 60fps)
var pendingObstacle = {
lane: randomLane,
size: randomSize,
spawnTime: LK.ticks + 30
};
pendingObstacles.push(pendingObstacle);
}
function createObstacle(lane, size) {
var obstacle = new PlayBall(lane, size);
obstacle.x = gameWidth - 200; // Spawn obstacles closer to screen for tighter view
if (lane === 'top') {
obstacle.y = topLaneY; // Position at center of platform
} else {
obstacle.y = bottomLaneY; // Position at center of platform
}
var baseSpeed = 8;
var speedBonus = Math.floor(LK.getScore() / 100);
obstacle.speed = (baseSpeed + speedBonus) * gameSpeed;
obstacles.push(obstacle);
game.addChild(obstacle);
}
function checkCollisions() {
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
var collision = false;
// Check collision based on lane
if (obstacle.lane === 'top' && player.currentLane === 0 || obstacle.lane === 'bottom' && player.currentLane === 1) {
collision = player.intersects(obstacle);
}
if (collision) {
LK.getSound('collision').play();
LK.getSound('explosion').play();
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
return;
}
}
}
// Touch controls for platform switching
var touchStartY = 0;
game.down = function (x, y, obj) {
touchStartY = y;
};
game.up = function (x, y, obj) {
if (gameStarted) {
var swipeDistance = y - touchStartY;
if (Math.abs(swipeDistance) > 100) {
if (swipeDistance < 0) {
// Swipe up
player.moveUp();
} else {
// Swipe down
player.moveDown();
}
}
}
};
// Main game loop
game.update = function () {
// Only update game logic if game has started
if (!gameStarted) {
return;
}
// Update game time (1 second = 60 frames at 60fps)
gameTime++;
// Update score: 1 point every 0.5 seconds (30 frames)
var timeScore = Math.floor(gameTime / 30);
LK.setScore(timeScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Increase obstacle speed by 1 for every 100 points
var baseSpeed = 8;
var speedBonus = Math.floor(LK.getScore() / 100);
var currentObstacleSpeed = baseSpeed + speedBonus;
// Increase game speed over time
if (LK.ticks % speedIncreaseInterval === 0) {
gameSpeed += 0.1;
if (spawnInterval > 60) {
spawnInterval -= 5;
}
}
// Spawn obstacles
spawnTimer++;
// Reduce spawn interval by 40% to increase frequency (multiply by 0.6)
var adjustedSpawnInterval = Math.floor(spawnInterval * 0.6);
if (spawnTimer >= adjustedSpawnInterval) {
spawnObstacle();
spawnTimer = 0;
}
// Check for pending obstacles to spawn
for (var i = pendingObstacles.length - 1; i >= 0; i--) {
var pending = pendingObstacles[i];
if (LK.ticks >= pending.spawnTime) {
createObstacle(pending.lane, pending.size);
pendingObstacles.splice(i, 1);
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
obstacle.speed = currentObstacleSpeed * gameSpeed;
// Remove obstacles that are off screen
if (obstacle.x < -100) {
obstacle.destroy();
obstacles.splice(i, 1);
}
}
// Update and clean up spawn arrows
for (var i = spawnArrows.length - 1; i >= 0; i--) {
var arrow = spawnArrows[i];
if (arrow.fadeTimer >= arrow.maxFadeTime) {
spawnArrows.splice(i, 1);
}
}
// Check for collisions
checkCollisions();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var PlayBall = Container.expand(function (lane, size) {
var self = Container.call(this);
self.lane = lane || 'middle';
self.size = size || 'small';
self.speed = 8;
self.isLarge = size === 'large';
// Array of random colors for playballs
var colors = [0xff5722,
// Orange
0x2196f3,
// Blue
0x4caf50,
// Green
0xffeb3b,
// Yellow
0x9c27b0,
// Purple
0xe91e63,
// Pink
0x00bcd4,
// Cyan
0xff9800,
// Deep Orange
0x673ab7,
// Deep Purple
0x3f51b5 // Indigo
];
// Select random color
var randomColor = colors[Math.floor(Math.random() * colors.length)];
// Create playball
var ballAsset = size === 'large' ? 'playballLarge' : 'playball';
// Create playball
var ball = self.attachAsset(ballAsset, {
anchorX: 0.5,
anchorY: 0.5,
tint: randomColor
});
// Add rotation animation
self.rotation = 0;
self.update = function () {
self.x -= self.speed;
// Rotate ball as it rolls
self.rotation -= 0.1;
ball.rotation = self.rotation;
};
return self;
});
// New Player class with boat design
var Player = Container.expand(function () {
var self = Container.call(this);
// Create boat hull (main body) - scaled to 1x (50% smaller than previous 2x)
var boatHull = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.6,
tint: 0x8B4513 // Brown color for wooden boat
});
// Create boat front (bow) - scaled to 1x (50% smaller) with adjusted position
var boatBow = self.attachAsset('spikePoint', {
anchorX: 0.5,
anchorY: 0.5,
x: 70,
y: 0,
scaleX: 0.8,
scaleY: 0.6,
rotation: -Math.PI / 2,
tint: 0x8B4513 // Brown color
});
// Create boat cabin - scaled to 1x (50% smaller) with adjusted position
var boatCabin = self.attachAsset('platformFine', {
anchorX: 0.5,
anchorY: 0.5,
x: -10,
y: 0,
scaleX: 4,
scaleY: 6,
tint: 0xFFFFFF // White cabin
});
// Create boat mast - scaled to 1x (50% smaller) with adjusted position
var boatMast = self.attachAsset('stickBody', {
anchorX: 0.5,
anchorY: 1,
x: -10,
y: -20,
scaleX: 0.5,
scaleY: 1.5,
tint: 0x654321 // Dark brown
});
// Create boat sail - scaled to 1x (50% smaller) with adjusted position
var boatSail = self.attachAsset('spikePoint', {
anchorX: 0.5,
anchorY: 0.8,
x: -10,
y: -30,
scaleX: 1.2,
scaleY: 0.8,
tint: 0xFFFFFF // White sail
});
// Create boat flag - scaled to 1x (50% smaller) with adjusted position
var boatFlag = self.attachAsset('spawnArrow', {
anchorX: 0,
anchorY: 0.5,
x: -10,
y: -65,
scaleX: 0.6,
scaleY: 0.4,
tint: 0xFF0000 // Red flag
});
// Animation variables for boat movement
self.animationTime = 0;
self.animationSpeed = 0.1;
// Player properties
self.currentLane = 0; // 0=top, 1=bottom (only 2 lanes now)
self.isMoving = false;
self.targetY = 0;
self.moveSpeed = 18; // Faster movement
// Initialize lane positions array for cleaner code
self.lanePositions = [0, 0]; // Will be set when lanes are defined
// Move up to previous lane
self.moveUp = function () {
if (!self.isMoving && self.currentLane > 0) {
self.currentLane--;
self.isMoving = true;
self.updateTargetPosition();
LK.getSound('platformSwitch').play();
}
};
// Move down to next lane
self.moveDown = function () {
if (!self.isMoving && self.currentLane < 1) {
self.currentLane++;
self.isMoving = true;
self.updateTargetPosition();
LK.getSound('platformSwitch').play();
}
};
// Update target position based on current lane
self.updateTargetPosition = function () {
// Use global lane positions if available, otherwise use defaults - adjusted for 1x player (50% smaller)
if (typeof topLaneY !== 'undefined') {
self.lanePositions[0] = topLaneY - 80;
self.lanePositions[1] = bottomLaneY - 80;
}
self.targetY = self.lanePositions[self.currentLane];
};
// Update function called every frame
self.update = function () {
// Handle lane switching movement
if (self.isMoving) {
var diff = self.targetY - self.y;
// Vertical movement
if (Math.abs(diff) < self.moveSpeed) {
self.y = self.targetY;
self.isMoving = false;
} else {
self.y += diff > 0 ? self.moveSpeed : -self.moveSpeed;
}
}
// Animate boat with bobbing motion
self.animationTime += self.animationSpeed;
var bobOffset = Math.sin(self.animationTime) * 3;
var tiltOffset = Math.sin(self.animationTime * 0.5) * 0.05;
// Bob the entire boat up and down slightly
self.y += bobOffset * 0.1;
// Tilt the boat slightly
self.rotation = tiltOffset;
// Animate the sail swaying
boatSail.rotation = Math.sin(self.animationTime * 0.7) * 0.1;
// Animate the flag waving
boatFlag.rotation = Math.sin(self.animationTime * 2) * 0.3;
};
return self;
});
var SpawnArrow = Container.expand(function (lane) {
var self = Container.call(this);
self.lane = lane || 'middle';
var arrowGraphics = self.attachAsset('spawnArrow', {
anchorX: 0.5,
anchorY: 0.5
});
arrowGraphics.rotation = Math.PI; // Point left toward incoming obstacles
self.fadeTimer = 0;
self.maxFadeTime = 60; // 1 second at 60fps
self.update = function () {
self.fadeTimer++;
var alpha = 1 - self.fadeTimer / self.maxFadeTime;
if (alpha <= 0) {
self.destroy();
return;
}
arrowGraphics.alpha = alpha;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x00a8e6
});
/****
* Game Code
****/
// Game dimensions: 2048x2732
var Obstacle = PlayBall;
var gameWidth = 2048;
var gameHeight = 2732;
// Lane positions - now only 2 lanes
var topLaneY = gameHeight * 0.4;
var bottomLaneY = gameHeight * 0.6;
// Game variables
var player;
var obstacles = [];
var spawnArrows = [];
var pendingObstacles = [];
var gameSpeed = 1;
var spawnTimer = 0;
var spawnInterval = 120; // frames between spawns
var gameTime = 0; // tracks game time in frames (60fps = 1 second)
var speedIncreaseInterval = 600; // increase speed every 10 seconds at 60fps
var largeObstaclesSpawned = 0;
var lastScoreCheck = 0;
var gameStarted = false;
var startButton;
// Initialize score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create player
player = game.addChild(new Player());
player.x = gameWidth * 0.35; // Move player closer to center for better camera view
player.y = topLaneY - 80; // Position on top of platform with 1x player height (50% smaller)
player.updateTargetPosition(); // Initialize target positions
player.visible = false; // Hide player until game starts
// Create start menu
var startMenu = new Container();
game.addChild(startMenu);
// Create background boat animation
var backgroundBoat = new Player();
backgroundBoat.x = -200; // Start off-screen to the left
backgroundBoat.y = gameHeight * 0.5;
backgroundBoat.alpha = 0.3; // Make it semi-transparent
backgroundBoat.scaleX = 1.2; // 50% larger than player boat
backgroundBoat.scaleY = 1.2;
startMenu.addChild(backgroundBoat);
// Animate background boat moving across screen
var backgroundBoatTween = tween(backgroundBoat, {
x: gameWidth + 200
}, {
duration: 8000,
easing: tween.linear,
onComplete: function onComplete() {
// Reset position and restart animation
backgroundBoat.x = -200;
backgroundBoatTween.restart();
}
});
// Create second background boat at different position
var backgroundBoat2 = new Player();
backgroundBoat2.x = gameWidth * 0.5; // Start in middle
backgroundBoat2.y = gameHeight * 0.35;
backgroundBoat2.alpha = 0.2; // Even more transparent
backgroundBoat2.scaleX = 0.9; // 50% larger scale
backgroundBoat2.scaleY = 0.9;
startMenu.addChild(backgroundBoat2);
// Animate second boat with different speed
var backgroundBoatTween2 = tween(backgroundBoat2, {
x: gameWidth + 200
}, {
duration: 6000,
easing: tween.linear,
onComplete: function onComplete() {
// Reset position and restart animation
backgroundBoat2.x = -200;
backgroundBoatTween2.restart();
}
});
// Add game title
var titleText = new Text2('ESCAPE FROM THE MINES BY BOAT', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = gameWidth / 2;
titleText.y = gameHeight * 0.3;
startMenu.addChild(titleText);
// Create start button
startButton = new Container();
startMenu.addChild(startButton);
var buttonBg = startButton.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 2,
tint: 0x4CAF50
});
var buttonText = new Text2('START', {
size: 80,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
startButton.addChild(buttonText);
startButton.x = gameWidth / 2;
startButton.y = gameHeight / 2;
// Add instructions
var instructionsText = new Text2('Swipe up/down or use arrows to swim', {
size: 50,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = gameWidth / 2;
instructionsText.y = gameHeight * 0.65;
startMenu.addChild(instructionsText);
// Add additional instruction
var dodgeText = new Text2('Dodge the playballs!', {
size: 50,
fill: 0xFFFFFF
});
dodgeText.anchor.set(0.5, 0.5);
dodgeText.x = gameWidth / 2;
dodgeText.y = gameHeight * 0.72;
startMenu.addChild(dodgeText);
// Add decorative playballs to menu
var menuBall1 = startMenu.attachAsset('playball', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth * 0.2,
y: gameHeight * 0.3,
tint: 0xff5722
});
var menuBall2 = startMenu.attachAsset('playball', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth * 0.8,
y: gameHeight * 0.3,
tint: 0x2196f3
});
// Animate menu balls
tween(menuBall1, {
rotation: Math.PI * 2
}, {
duration: 3000,
loop: true,
easing: tween.linear
});
tween(menuBall2, {
rotation: -Math.PI * 2
}, {
duration: 3000,
loop: true,
easing: tween.linear
});
// Start button click handler
startButton.down = function (x, y, obj) {
gameStarted = true;
player.visible = true;
if (backgroundBoatTween) {
backgroundBoatTween.stop();
}
if (backgroundBoatTween2) {
backgroundBoatTween2.stop();
}
startMenu.destroy();
startMenu = null;
startButton = null;
};
// Create arrow buttons
var arrowUp = LK.getAsset('arrowUp', {
anchorX: 0.5,
anchorY: 0.5
});
arrowUp.x = gameWidth - 150;
arrowUp.y = gameHeight - 300;
LK.gui.addChild(arrowUp);
var arrowDown = LK.getAsset('arrowDown', {
anchorX: 0.5,
anchorY: 0.5
});
arrowDown.x = gameWidth - 150;
arrowDown.y = gameHeight - 150;
LK.gui.addChild(arrowDown);
// Arrow button event handlers
arrowUp.down = function (x, y, obj) {
if (gameStarted) {
player.moveUp();
}
};
arrowDown.down = function (x, y, obj) {
if (gameStarted) {
player.moveDown();
}
};
// Draw stone platform tracks with finer texture matching hitbox
// Create top platform using fine texture tiles
for (var i = 0; i < 41; i++) {
var topTile = LK.getAsset('platformFine', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 7.2
});
topTile.x = i * 50;
topTile.y = topLaneY;
game.addChild(topTile);
}
// Create bottom platform using fine texture tiles
for (var i = 0; i < 41; i++) {
var bottomTile = LK.getAsset('platformFine', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 7.2
});
bottomTile.x = i * 50;
bottomTile.y = bottomLaneY;
game.addChild(bottomTile);
}
function spawnObstacle() {
var lanes = ['top', 'bottom'];
var sizes = ['small', 'medium', 'large'];
var currentScore = LK.getScore();
var scoreSegment = Math.floor(currentScore / 150);
var requiredLargeObstacles = (scoreSegment + 1) * 3;
var forceLarge = false;
// Check if we need to force a large obstacle
if (scoreSegment > Math.floor(lastScoreCheck / 150)) {
// We've entered a new 150-point segment, reset counter
largeObstaclesSpawned = 0;
lastScoreCheck = currentScore;
}
// Force large obstacle if we haven't met the requirement and we're near the end of the segment
var progressInSegment = currentScore % 150;
if (progressInSegment > 100 && largeObstaclesSpawned < 3) {
forceLarge = true;
}
var randomSize;
if (forceLarge) {
randomSize = 'large';
} else {
randomSize = sizes[Math.floor(Math.random() * sizes.length)];
}
// Track large obstacles
if (randomSize === 'large') {
largeObstaclesSpawned++;
}
var randomLane;
// 46% more chance to spawn obstacle on player's current lane
// Base probability for 2 lanes is 50%, so 46% more = 73% for player's lane
if (Math.random() < 0.73) {
randomLane = lanes[player.currentLane];
} else {
// 27% chance for the other lane
randomLane = lanes[1 - player.currentLane];
}
// Create spawn arrow indicator
var spawnArrow = new SpawnArrow(randomLane);
spawnArrow.x = gameWidth - 250; // Position spawn arrow closer to match tighter view
if (randomLane === 'top') {
spawnArrow.y = topLaneY;
} else {
spawnArrow.y = bottomLaneY;
}
// Add pulsing animation to spawn arrow
tween(spawnArrow, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 250,
easing: tween.easeInOut
});
tween(spawnArrow, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 250,
easing: tween.easeInOut
});
spawnArrows.push(spawnArrow);
game.addChild(spawnArrow);
// Schedule obstacle to spawn after 0.5 seconds (30 frames at 60fps)
var pendingObstacle = {
lane: randomLane,
size: randomSize,
spawnTime: LK.ticks + 30
};
pendingObstacles.push(pendingObstacle);
}
function createObstacle(lane, size) {
var obstacle = new PlayBall(lane, size);
obstacle.x = gameWidth - 200; // Spawn obstacles closer to screen for tighter view
if (lane === 'top') {
obstacle.y = topLaneY; // Position at center of platform
} else {
obstacle.y = bottomLaneY; // Position at center of platform
}
var baseSpeed = 8;
var speedBonus = Math.floor(LK.getScore() / 100);
obstacle.speed = (baseSpeed + speedBonus) * gameSpeed;
obstacles.push(obstacle);
game.addChild(obstacle);
}
function checkCollisions() {
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
var collision = false;
// Check collision based on lane
if (obstacle.lane === 'top' && player.currentLane === 0 || obstacle.lane === 'bottom' && player.currentLane === 1) {
collision = player.intersects(obstacle);
}
if (collision) {
LK.getSound('collision').play();
LK.getSound('explosion').play();
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
return;
}
}
}
// Touch controls for platform switching
var touchStartY = 0;
game.down = function (x, y, obj) {
touchStartY = y;
};
game.up = function (x, y, obj) {
if (gameStarted) {
var swipeDistance = y - touchStartY;
if (Math.abs(swipeDistance) > 100) {
if (swipeDistance < 0) {
// Swipe up
player.moveUp();
} else {
// Swipe down
player.moveDown();
}
}
}
};
// Main game loop
game.update = function () {
// Only update game logic if game has started
if (!gameStarted) {
return;
}
// Update game time (1 second = 60 frames at 60fps)
gameTime++;
// Update score: 1 point every 0.5 seconds (30 frames)
var timeScore = Math.floor(gameTime / 30);
LK.setScore(timeScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Increase obstacle speed by 1 for every 100 points
var baseSpeed = 8;
var speedBonus = Math.floor(LK.getScore() / 100);
var currentObstacleSpeed = baseSpeed + speedBonus;
// Increase game speed over time
if (LK.ticks % speedIncreaseInterval === 0) {
gameSpeed += 0.1;
if (spawnInterval > 60) {
spawnInterval -= 5;
}
}
// Spawn obstacles
spawnTimer++;
// Reduce spawn interval by 40% to increase frequency (multiply by 0.6)
var adjustedSpawnInterval = Math.floor(spawnInterval * 0.6);
if (spawnTimer >= adjustedSpawnInterval) {
spawnObstacle();
spawnTimer = 0;
}
// Check for pending obstacles to spawn
for (var i = pendingObstacles.length - 1; i >= 0; i--) {
var pending = pendingObstacles[i];
if (LK.ticks >= pending.spawnTime) {
createObstacle(pending.lane, pending.size);
pendingObstacles.splice(i, 1);
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
obstacle.speed = currentObstacleSpeed * gameSpeed;
// Remove obstacles that are off screen
if (obstacle.x < -100) {
obstacle.destroy();
obstacles.splice(i, 1);
}
}
// Update and clean up spawn arrows
for (var i = spawnArrows.length - 1; i >= 0; i--) {
var arrow = spawnArrows[i];
if (arrow.fadeTimer >= arrow.maxFadeTime) {
spawnArrows.splice(i, 1);
}
}
// Check for collisions
checkCollisions();
};