/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Define a class for enemies
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.update = function () {
// Track last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
self.x -= self.speed;
if (self.x < -50) {
self.destroy();
}
};
});
// Define a class for joystick controls
var Joystick = Container.expand(function () {
var self = Container.call(this);
// Create outer circle
var outerCircle = LK.getAsset('button', {
width: 300,
height: 300,
color: 0x000000,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
// Create inner circle (the stick)
var innerCircle = LK.getAsset('button', {
width: 150,
height: 150,
color: 0x3498db,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(outerCircle);
self.addChild(innerCircle);
self.active = false;
self.startX = 0;
self.startY = 0;
self.deltaX = 0;
self.deltaY = 0;
self.maxDistance = 100; // Max distance the joystick can move
self.down = function (x, y) {
self.active = true;
self.startX = x;
self.startY = y;
innerCircle.x = 0;
innerCircle.y = 0;
};
self.move = function (x, y) {
if (self.active) {
// Calculate delta from start position
self.deltaX = x - self.startX;
self.deltaY = y - self.startY;
// Calculate distance
var distance = Math.sqrt(self.deltaX * self.deltaX + self.deltaY * self.deltaY);
// Normalize if distance exceeds max
if (distance > self.maxDistance) {
self.deltaX = self.deltaX / distance * self.maxDistance;
self.deltaY = self.deltaY / distance * self.maxDistance;
}
// Update inner circle position
innerCircle.x = self.deltaX;
innerCircle.y = self.deltaY;
}
};
self.up = function () {
self.active = false;
self.deltaX = 0;
self.deltaY = 0;
innerCircle.x = 0;
innerCircle.y = 0;
};
return self;
});
// Define a class for the mobile jump button
var MobileButton = Container.expand(function () {
var self = Container.call(this);
// Create the button shape
var button = new Container();
var buttonBg = LK.getAsset('button', {
width: 200,
height: 200,
color: 0x3498db,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
button.addChild(buttonBg);
// Add jump text
var jumpText = new Text2("JUMP", {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
jumpText.anchor.set(0.5, 0.5);
button.addChild(jumpText);
self.addChild(button);
// Handle touch events
self.down = function () {
buttonBg.alpha = 0.7;
if (player && typeof player.jump === 'function') {
player.jump();
}
};
self.up = function () {
buttonBg.alpha = 1;
};
return self;
});
// Define a class for pause menu items
var PauseMenuItem = Container.expand(function () {
var self = Container.call(this);
self.createItem = function (text, y, callback) {
var menuText = new Text2(text, {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
menuText.anchor.set(0.5, 0.5);
menuText.y = y;
self.addChild(menuText);
self.down = function () {
if (callback) {
callback();
}
};
return self;
};
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.jumpHeight = 40;
self.isJumping = false;
self.velocityY = 0;
self.velocityX = 0;
self.maxSpeed = 10;
self.update = function () {
// Track last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
// Apply horizontal movement
self.x += self.velocityX;
// Keep player within screen bounds
if (self.x < 100) {
self.x = 100;
}
if (self.x > 2048 - 100) {
self.x = 2048 - 100;
}
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.7; // Decreased gravity effect by 30%
if (self.y >= 2732 / 2) {
// Ground level
self.y = 2732 / 2;
self.isJumping = false;
self.velocityY = 0;
}
}
// Check for keyboard 's' key press for crouching only when not jumping
if (!self.isJumping) {
if (LK.isKeyDown && LK.isKeyDown('s')) {
self.crouch();
} else if (self.isCrouching) {
self.standUp(); // Stand up when 's' key is released
}
// Removed the else condition that would stand up
}
};
self.jump = function () {
if (!self.isJumping && !self.isCrouching) {
self.isJumping = true;
self.velocityY = -self.jumpHeight;
}
};
self.isCrouching = false;
self.crouch = function () {
if (!self.isCrouching && !self.isJumping) {
self.isCrouching = true;
// Stop any existing tweens
tween.stop(playerGraphics.scale, {
y: true
});
tween.stop(self, {
y: true
});
// Animate player crouching with tween
tween(playerGraphics.scale, {
y: 0.5
}, {
duration: 200,
easing: tween.easeOut
});
// Move player down to compensate for height reduction with animation
tween(self, {
y: self.y + playerGraphics.height * 0.25
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.standUp = function () {
if (self.isCrouching) {
self.isCrouching = false;
// Stop any existing tweens
tween.stop(playerGraphics.scale, {
y: true
});
tween.stop(self, {
y: true
});
// Animate player standing up with tween
tween(playerGraphics.scale, {
y: 1
}, {
duration: 200,
easing: tween.easeOut
});
// Move player back up to original position with animation
tween(self, {
y: self.y - playerGraphics.height * 0.25
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.moveWithJoystick = function (deltaX, deltaY) {
// Normalize the input to proper speed
var normalizedX = deltaX / 100; // Based on maxDistance in Joystick
var normalizedY = deltaY / 100; // Based on maxDistance in Joystick
// Set velocity based on joystick position
self.velocityX = normalizedX * self.maxSpeed;
// Handle crouching based on joystick Y position (down), only when not jumping
if (!self.isJumping) {
if (normalizedY > 0.5) {
self.crouch();
} else if (normalizedY <= 0.5 && self.isCrouching) {
self.standUp(); // Stand up when joystick is not pushed down
}
// Removed the else condition that would stand up
}
// Optional: Make the character face the direction it's moving
if (normalizedX > 0) {
playerGraphics.scale.x = 1; // Face right
} else if (normalizedX < 0) {
playerGraphics.scale.x = -1; // Face left
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Make background fit the screen properly
var background = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
}));
background.x = 2048 / 2;
background.y = 2732 / 2;
// Game configuration
var gameConfig = {
touchControlsEnabled: true
};
// Check if there's a stored preference for touch controls
if (storage && storage.touchControlsEnabled !== undefined) {
gameConfig.touchControlsEnabled = storage.touchControlsEnabled;
}
// Initialize player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2;
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Create a new Text2 object to display the score with better mobile visibility
var scoreText = new Text2('Score: 0', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
// Add the score text to the game GUI at the top center of the screen
// Anchoring properly for responsive display
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Handle game updates
game.update = function () {
player.update();
// Reset player velocity if joystick is not active
if (!joystick.active) {
player.velocityX = 0;
}
// Spawn enemies (only if player is not crouching)
enemySpawnCounter++;
if (enemySpawnCounter >= enemySpawnInterval && !player.isCrouching) {
var enemy = new Enemy();
enemy.x = 2048;
enemy.y = 2732 / 2;
enemies.push(enemy);
game.addChild(enemy);
// Randomize the spawn interval for the next enemy
enemySpawnInterval = Math.floor(Math.random() * 150) + 50;
enemySpawnCounter = 0;
}
// Update enemies with better collision detection
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
// More precise collision detection
if (player.intersects(enemies[j])) {
if (!enemies[j].lastWasIntersecting) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
enemies[j].lastWasIntersecting = true;
} else {
enemies[j].lastWasIntersecting = false;
// Check if player passed the enemy
if (player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
// Visual feedback when scoring
var pointText = new Text2('+1', {
size: 60,
fill: 0xFFFFFF
});
pointText.x = enemies[j].x;
pointText.y = enemies[j].y - 100;
game.addChild(pointText);
// Animate and remove the +1 text
LK.setTimeout(function () {
pointText.destroy();
}, 1000);
}
}
}
};
// Custom pause handler
LK.on('pause', function () {
// Create touch controls toggle menu item
var toggleItem = new PauseMenuItem().createItem(gameConfig.touchControlsEnabled ? "Disable Touch Controls" : "Enable Touch Controls", 200, function () {
// Toggle touch controls setting
gameConfig.touchControlsEnabled = !gameConfig.touchControlsEnabled;
// Save setting in storage
if (storage) {
storage.touchControlsEnabled = gameConfig.touchControlsEnabled;
}
// Update button visibility when setting changes
updateButtonVisibility();
// Resume game after changing setting
LK.resumeGame();
});
// Position the menu item in the center of the screen
toggleItem.x = 2048 / 2;
toggleItem.y = 2732 / 2;
// Add to pause menu
LK.gui.center.addChild(toggleItem);
});
// Create joystick for movement
var joystick = new Joystick();
joystick.x = 300; // Position on bottom left
joystick.y = 2732 - 300;
game.addChild(joystick);
// Create mobile jump button
var mobileButton = new MobileButton();
mobileButton.x = 2048 - 200; // Position on bottom right
mobileButton.y = 2732 - 200;
game.addChild(mobileButton);
// Function to update mobile controls visibility based on settings
function updateButtonVisibility() {
mobileButton.visible = gameConfig.touchControlsEnabled;
joystick.visible = gameConfig.touchControlsEnabled;
}
// Setup keyboard input handling if not already present
if (!LK.isKeyDown) {
LK.isKeyDown = function () {
var keysDown = {};
LK.on('keydown', function (e) {
keysDown[e.key.toLowerCase()] = true;
});
LK.on('keyup', function (e) {
keysDown[e.key.toLowerCase()] = false;
});
return function (key) {
return !!keysDown[key.toLowerCase()];
};
}();
}
// Initial visibility
updateButtonVisibility();
// Handle player jump with improved controls for mobile
game.down = function (x, y, obj) {
// Already handled by the button's down event when touch controls enabled
// For anywhere else on screen: only jump when touch controls are enabled AND not pressing the button
if (gameConfig.touchControlsEnabled && obj.event && obj.event.target !== mobileButton && obj.event.target !== joystick) {
player.jump();
}
};
// Add move handler for joystick controls
game.move = function (x, y, obj) {
if (gameConfig.touchControlsEnabled && joystick.active) {
player.moveWithJoystick(joystick.deltaX, joystick.deltaY);
}
};
// Initialize the score to zero
scoreText.setText('0');
// Make score text more visible on mobile screens
scoreText.size = 120; /****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Define a class for enemies
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.update = function () {
// Track last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
self.lastWasIntersecting = false;
self.x -= self.speed;
if (self.x < -50) {
self.destroy();
}
};
});
// Define a class for joystick controls
var Joystick = Container.expand(function () {
var self = Container.call(this);
// Create outer circle
var outerCircle = LK.getAsset('button', {
width: 300,
height: 300,
color: 0x000000,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
// Create inner circle (the stick)
var innerCircle = LK.getAsset('button', {
width: 150,
height: 150,
color: 0x3498db,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(outerCircle);
self.addChild(innerCircle);
self.active = false;
self.startX = 0;
self.startY = 0;
self.deltaX = 0;
self.deltaY = 0;
self.maxDistance = 100; // Max distance the joystick can move
self.down = function (x, y) {
self.active = true;
self.startX = x;
self.startY = y;
innerCircle.x = 0;
innerCircle.y = 0;
};
self.move = function (x, y) {
if (self.active) {
// Calculate delta from start position
self.deltaX = x - self.startX;
self.deltaY = y - self.startY;
// Calculate distance
var distance = Math.sqrt(self.deltaX * self.deltaX + self.deltaY * self.deltaY);
// Normalize if distance exceeds max
if (distance > self.maxDistance) {
self.deltaX = self.deltaX / distance * self.maxDistance;
self.deltaY = self.deltaY / distance * self.maxDistance;
}
// Update inner circle position
innerCircle.x = self.deltaX;
innerCircle.y = self.deltaY;
}
};
self.up = function () {
self.active = false;
self.deltaX = 0;
self.deltaY = 0;
innerCircle.x = 0;
innerCircle.y = 0;
};
return self;
});
// Define a class for the mobile jump button
var MobileButton = Container.expand(function () {
var self = Container.call(this);
// Create the button shape
var button = new Container();
var buttonBg = LK.getAsset('button', {
width: 200,
height: 200,
color: 0x3498db,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
button.addChild(buttonBg);
// Add jump text
var jumpText = new Text2("JUMP", {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
jumpText.anchor.set(0.5, 0.5);
button.addChild(jumpText);
self.addChild(button);
// Handle touch events
self.down = function () {
buttonBg.alpha = 0.7;
if (player && typeof player.jump === 'function') {
player.jump();
}
};
self.up = function () {
buttonBg.alpha = 1;
};
return self;
});
// Define a class for pause menu items
var PauseMenuItem = Container.expand(function () {
var self = Container.call(this);
self.createItem = function (text, y, callback) {
var menuText = new Text2(text, {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
menuText.anchor.set(0.5, 0.5);
menuText.y = y;
self.addChild(menuText);
self.down = function () {
if (callback) {
callback();
}
};
return self;
};
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.jumpHeight = 40;
self.isJumping = false;
self.velocityY = 0;
self.velocityX = 0;
self.maxSpeed = 10;
self.update = function () {
// Track last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
// Apply horizontal movement
self.x += self.velocityX;
// Keep player within screen bounds
if (self.x < 100) {
self.x = 100;
}
if (self.x > 2048 - 100) {
self.x = 2048 - 100;
}
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.7; // Decreased gravity effect by 30%
if (self.y >= 2732 / 2) {
// Ground level
self.y = 2732 / 2;
self.isJumping = false;
self.velocityY = 0;
}
}
// Check for keyboard 's' key press for crouching only when not jumping
if (!self.isJumping) {
if (LK.isKeyDown && LK.isKeyDown('s')) {
self.crouch();
} else if (self.isCrouching) {
self.standUp(); // Stand up when 's' key is released
}
// Removed the else condition that would stand up
}
};
self.jump = function () {
if (!self.isJumping && !self.isCrouching) {
self.isJumping = true;
self.velocityY = -self.jumpHeight;
}
};
self.isCrouching = false;
self.crouch = function () {
if (!self.isCrouching && !self.isJumping) {
self.isCrouching = true;
// Stop any existing tweens
tween.stop(playerGraphics.scale, {
y: true
});
tween.stop(self, {
y: true
});
// Animate player crouching with tween
tween(playerGraphics.scale, {
y: 0.5
}, {
duration: 200,
easing: tween.easeOut
});
// Move player down to compensate for height reduction with animation
tween(self, {
y: self.y + playerGraphics.height * 0.25
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.standUp = function () {
if (self.isCrouching) {
self.isCrouching = false;
// Stop any existing tweens
tween.stop(playerGraphics.scale, {
y: true
});
tween.stop(self, {
y: true
});
// Animate player standing up with tween
tween(playerGraphics.scale, {
y: 1
}, {
duration: 200,
easing: tween.easeOut
});
// Move player back up to original position with animation
tween(self, {
y: self.y - playerGraphics.height * 0.25
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.moveWithJoystick = function (deltaX, deltaY) {
// Normalize the input to proper speed
var normalizedX = deltaX / 100; // Based on maxDistance in Joystick
var normalizedY = deltaY / 100; // Based on maxDistance in Joystick
// Set velocity based on joystick position
self.velocityX = normalizedX * self.maxSpeed;
// Handle crouching based on joystick Y position (down), only when not jumping
if (!self.isJumping) {
if (normalizedY > 0.5) {
self.crouch();
} else if (normalizedY <= 0.5 && self.isCrouching) {
self.standUp(); // Stand up when joystick is not pushed down
}
// Removed the else condition that would stand up
}
// Optional: Make the character face the direction it's moving
if (normalizedX > 0) {
playerGraphics.scale.x = 1; // Face right
} else if (normalizedX < 0) {
playerGraphics.scale.x = -1; // Face left
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Make background fit the screen properly
var background = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
}));
background.x = 2048 / 2;
background.y = 2732 / 2;
// Game configuration
var gameConfig = {
touchControlsEnabled: true
};
// Check if there's a stored preference for touch controls
if (storage && storage.touchControlsEnabled !== undefined) {
gameConfig.touchControlsEnabled = storage.touchControlsEnabled;
}
// Initialize player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2;
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Create a new Text2 object to display the score with better mobile visibility
var scoreText = new Text2('Score: 0', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
// Add the score text to the game GUI at the top center of the screen
// Anchoring properly for responsive display
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Handle game updates
game.update = function () {
player.update();
// Reset player velocity if joystick is not active
if (!joystick.active) {
player.velocityX = 0;
}
// Spawn enemies (only if player is not crouching)
enemySpawnCounter++;
if (enemySpawnCounter >= enemySpawnInterval && !player.isCrouching) {
var enemy = new Enemy();
enemy.x = 2048;
enemy.y = 2732 / 2;
enemies.push(enemy);
game.addChild(enemy);
// Randomize the spawn interval for the next enemy
enemySpawnInterval = Math.floor(Math.random() * 150) + 50;
enemySpawnCounter = 0;
}
// Update enemies with better collision detection
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
// More precise collision detection
if (player.intersects(enemies[j])) {
if (!enemies[j].lastWasIntersecting) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
enemies[j].lastWasIntersecting = true;
} else {
enemies[j].lastWasIntersecting = false;
// Check if player passed the enemy
if (player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
// Visual feedback when scoring
var pointText = new Text2('+1', {
size: 60,
fill: 0xFFFFFF
});
pointText.x = enemies[j].x;
pointText.y = enemies[j].y - 100;
game.addChild(pointText);
// Animate and remove the +1 text
LK.setTimeout(function () {
pointText.destroy();
}, 1000);
}
}
}
};
// Custom pause handler
LK.on('pause', function () {
// Create touch controls toggle menu item
var toggleItem = new PauseMenuItem().createItem(gameConfig.touchControlsEnabled ? "Disable Touch Controls" : "Enable Touch Controls", 200, function () {
// Toggle touch controls setting
gameConfig.touchControlsEnabled = !gameConfig.touchControlsEnabled;
// Save setting in storage
if (storage) {
storage.touchControlsEnabled = gameConfig.touchControlsEnabled;
}
// Update button visibility when setting changes
updateButtonVisibility();
// Resume game after changing setting
LK.resumeGame();
});
// Position the menu item in the center of the screen
toggleItem.x = 2048 / 2;
toggleItem.y = 2732 / 2;
// Add to pause menu
LK.gui.center.addChild(toggleItem);
});
// Create joystick for movement
var joystick = new Joystick();
joystick.x = 300; // Position on bottom left
joystick.y = 2732 - 300;
game.addChild(joystick);
// Create mobile jump button
var mobileButton = new MobileButton();
mobileButton.x = 2048 - 200; // Position on bottom right
mobileButton.y = 2732 - 200;
game.addChild(mobileButton);
// Function to update mobile controls visibility based on settings
function updateButtonVisibility() {
mobileButton.visible = gameConfig.touchControlsEnabled;
joystick.visible = gameConfig.touchControlsEnabled;
}
// Setup keyboard input handling if not already present
if (!LK.isKeyDown) {
LK.isKeyDown = function () {
var keysDown = {};
LK.on('keydown', function (e) {
keysDown[e.key.toLowerCase()] = true;
});
LK.on('keyup', function (e) {
keysDown[e.key.toLowerCase()] = false;
});
return function (key) {
return !!keysDown[key.toLowerCase()];
};
}();
}
// Initial visibility
updateButtonVisibility();
// Handle player jump with improved controls for mobile
game.down = function (x, y, obj) {
// Already handled by the button's down event when touch controls enabled
// For anywhere else on screen: only jump when touch controls are enabled AND not pressing the button
if (gameConfig.touchControlsEnabled && obj.event && obj.event.target !== mobileButton && obj.event.target !== joystick) {
player.jump();
}
};
// Add move handler for joystick controls
game.move = function (x, y, obj) {
if (gameConfig.touchControlsEnabled && joystick.active) {
player.moveWithJoystick(joystick.deltaX, joystick.deltaY);
}
};
// Initialize the score to zero
scoreText.setText('0');
// Make score text more visible on mobile screens
scoreText.size = 120;