/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Checkpoint class: save progress var Checkpoint = Container.expand(function () { var self = Container.call(this); var cp = self.attachAsset('checkpoint', { anchorX: 0.5, anchorY: 1 }); self.activated = false; self.activate = function () { if (!self.activated) { self.activated = true; tween(cp, { tint: 0x00ff00 }, { duration: 300 }); } }; return self; }); // Platform class: static or moving platform var Platform = Container.expand(function () { var self = Container.call(this); // Attach platform asset (box shape) var plat = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); // Platform movement config self.isMoving = false; self.moveAxis = 'x'; // 'x' or 'y' self.moveFrom = 0; self.moveTo = 0; self.moveDuration = 0; self.moveDir = 1; // For moving platforms, set up tween self.startMove = function () { if (!self.isMoving) return; var prop = {}; prop[self.moveAxis] = self.moveTo; tween(self, prop, { duration: self.moveDuration, easing: tween.linear, onFinish: function onFinish() { // Swap direction var tmp = self.moveFrom; self.moveFrom = self.moveTo; self.moveTo = tmp; self.startMove(); } }); }; return self; }); // Player class: main character var Player = Container.expand(function () { var self = Container.call(this); var _char = self.attachAsset('player', { anchorX: 0.5, anchorY: 1 }); self.width = _char.width; self.height = _char.height; self.vx = 0; self.vy = 0; self.onGround = false; self.dead = false; self.jumpQueued = false; self.respawn = function (x, y) { self.x = x; self.y = y; self.vx = 0; self.vy = 0; self.dead = false; }; return self; }); // Spike class: deadly obstacle var Spike = Container.expand(function () { var self = Container.call(this); // Attach spike asset (triangle shape, but use box for now) var spike = self.attachAsset('spike', { anchorX: 0.5, anchorY: 1 }); return self; }); /**** * Initialize Game ****/ // PlayerController class: handles input and movement for the player var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // PlayerController class: handles input and movement for the player // --- Asset Initialization --- // Tween plugin for platform/character movement and effects // --- Level Data (MVP: 1 level, hardcoded) --- /* Level is an array of objects: {type: 'platform'|'spike'|'checkpoint', x, y, [w], [h], [moveAxis], [moveFrom], [moveTo], [moveDuration]} All positions are in game coordinates. */ var PlayerController = function PlayerController() { var self = {}; self.dirX = 0; self.dirY = 0; self.jumpQueued = false; self.active = false; // Called on controller down self.onDown = function (x, y) { self.active = true; self.updateInput(x, y); self.jumpQueued = false; }; // Called on controller move self.onMove = function (x, y) { if (self.active) { self.updateInput(x, y); } }; // Called on controller up self.onUp = function () { self.active = false; self.dirX = 0; self.dirY = 0; self.jumpQueued = false; }; // Called on jump tap self.onJump = function () { self.jumpQueued = true; }; // Update input direction self.updateInput = function (x, y) { var input = getControllerInput(x, y); self.dirX = input.dx / controllerRadius; self.dirY = input.dy / controllerRadius; }; // Reset jump self.consumeJump = function () { var wasQueued = self.jumpQueued; self.jumpQueued = false; return wasQueued; }; return self; }; var levelData = [ // Floor { type: 'platform', x: 1024, y: 2600, w: 1200, h: 60 }, // First jump { type: 'platform', x: 600, y: 2200, w: 400, h: 40 }, { type: 'spike', x: 800, y: 2160 }, // Second jump { type: 'platform', x: 1400, y: 2000, w: 400, h: 40 }, { type: 'spike', x: 1200, y: 1960 }, // Moving platform { type: 'platform', x: 1024, y: 1700, w: 300, h: 40, moveAxis: 'x', moveFrom: 724, moveTo: 1324, moveDuration: 1800 }, { type: 'spike', x: 1024, y: 1660 }, // Checkpoint { type: 'checkpoint', x: 1024, y: 1600 }, // Final platform { type: 'platform', x: 1024, y: 1200, w: 400, h: 40 }, // End spike { type: 'spike', x: 1024, y: 1160 }]; // --- Game State --- var platforms = []; var spikes = []; var checkpoints = []; var player = null; var currentCheckpoint = null; var deaths = 0; var levelEndY = 1100; // Y position to reach to win // --- UI --- var deathsTxt = new Text2('Deaths: 0', { size: 90, fill: "#fff" }); deathsTxt.anchor.set(0.5, 0); LK.gui.top.addChild(deathsTxt); // --- Level Construction --- function buildLevel() { // Clear previous for (var i = 0; i < platforms.length; ++i) platforms[i].destroy(); for (var i = 0; i < spikes.length; ++i) spikes[i].destroy(); for (var i = 0; i < checkpoints.length; ++i) checkpoints[i].destroy(); platforms = []; spikes = []; checkpoints = []; currentCheckpoint = null; // Build for (var i = 0; i < levelData.length; ++i) { var obj = levelData[i]; if (obj.type === 'platform') { var plat = new Platform(); plat.x = obj.x; plat.y = obj.y; plat.width = obj.w || 400; plat.height = obj.h || 40; plat.children[0].width = plat.width; plat.children[0].height = plat.height; if (obj.moveAxis) { plat.isMoving = true; plat.moveAxis = obj.moveAxis; plat.moveFrom = obj.moveFrom; plat.moveTo = obj.moveTo; plat.moveDuration = obj.moveDuration; plat[plat.moveAxis] = plat.moveFrom; plat.startMove(); } game.addChild(plat); platforms.push(plat); } else if (obj.type === 'spike') { var spike = new Spike(); spike.x = obj.x; spike.y = obj.y; game.addChild(spike); spikes.push(spike); } else if (obj.type === 'checkpoint') { var cp = new Checkpoint(); cp.x = obj.x; cp.y = obj.y; game.addChild(cp); checkpoints.push(cp); } } } // --- Player Spawn --- function spawnPlayer() { if (player) player.destroy(); player = new Player(); var spawnX = 1024, spawnY = 2500; if (currentCheckpoint) { spawnX = currentCheckpoint.x; spawnY = currentCheckpoint.y - 10; } player.respawn(spawnX, spawnY); game.addChild(player); } // --- Death/Respawn --- function killPlayer() { if (player.dead) return; player.dead = true; deaths += 1; deathsTxt.setText('Deaths: ' + deaths); LK.effects.flashScreen(0xff2222, 400); LK.setTimeout(function () { spawnPlayer(); }, 400); } // --- Win Condition --- function checkWin() { if (player.y < levelEndY) { LK.showYouWin(); } } // --- Collision Helpers --- function rectsIntersect(a, b) { return a.x - a.width / 2 < b.x + b.width / 2 && a.x + a.width / 2 > b.x - b.width / 2 && a.y - a.height < b.y && a.y > b.y - b.height; } // --- Tablet Controller (Virtual Joystick) --- // Controller state var controllerRadius = 180; // px, joystick max distance var controllerBase = null; var controllerStick = null; var controllerCenterX = 0; var controllerCenterY = 0; // Player controller instance var playerController = new PlayerController(); // Place controller in bottom left, safe from menu (not in top left 100x100) controllerCenterX = 220; controllerCenterY = 2732 - 220; // Draw controller base and stick function createControllerGraphics() { if (controllerBase) controllerBase.destroy(); if (controllerStick) controllerStick.destroy(); // Use shapes for base and stick controllerBase = LK.getAsset('platform', { width: controllerRadius * 2, height: controllerRadius * 2, color: 0x444444, anchorX: 0.5, anchorY: 0.5, x: controllerCenterX, y: controllerCenterY, alpha: 0.18 }); controllerStick = LK.getAsset('player', { width: 100, height: 100, color: 0xffffff, anchorX: 0.5, anchorY: 0.5, x: controllerCenterX, y: controllerCenterY, alpha: 0.45 }); LK.gui.bottomLeft.addChild(controllerBase); LK.gui.bottomLeft.addChild(controllerStick); } createControllerGraphics(); // Helper: get distance and angle from center function getControllerInput(x, y) { var dx = x - controllerCenterX; var dy = y - controllerCenterY; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > controllerRadius) { dx = dx * controllerRadius / dist; dy = dy * controllerRadius / dist; dist = controllerRadius; } return { dx: dx, dy: dy, dist: dist }; } // Touch down: activate controller if in left-bottom quarter, else jump if in right-bottom game.down = function (x, y, obj) { // Only activate controller if touch is in left-bottom quarter (not in top left 100x100) if (x < 2048 / 2 && y > 2732 - 500 && y > 100) { playerController.onDown(x, y); var input = getControllerInput(x, y); controllerStick.x = controllerCenterX + input.dx; controllerStick.y = controllerCenterY + input.dy; } else if (x > 2048 / 2 && y > 2732 - 500) { // Right-bottom: jump playerController.onJump(); } }; game.move = function (x, y, obj) { if (playerController.active) { playerController.onMove(x, y); var input = getControllerInput(x, y); controllerStick.x = controllerCenterX + input.dx; controllerStick.y = controllerCenterY + input.dy; } }; game.up = function (x, y, obj) { playerController.onUp(); controllerStick.x = controllerCenterX; controllerStick.y = controllerCenterY; }; // --- END Tablet Controller --- // --- Physics Constants --- var GRAVITY = 2.2; var MOVE_SPEED = 18; var JUMP_VEL = -48; var MAX_FALL = 60; // --- Game Update Loop --- game.update = function () { if (!player || player.dead) return; // Horizontal movement (tablet controller) if (playerController.dirX < -0.2) { player.vx = -MOVE_SPEED * Math.min(1, Math.abs(playerController.dirX)); } else if (playerController.dirX > 0.2) { player.vx = MOVE_SPEED * Math.min(1, Math.abs(playerController.dirX)); } else { player.vx = 0; } // Jump (tablet controller) if ((playerController.consumeJump() || playerController.active && playerController.dirY < -0.5) && player.onGround) { player.vy = JUMP_VEL; player.onGround = false; } // Gravity player.vy += GRAVITY; if (player.vy > MAX_FALL) player.vy = MAX_FALL; // Save old position var oldX = player.x, oldY = player.y; // Move X player.x += player.vx; // Collide with platforms (X axis) for (var i = 0; i < platforms.length; ++i) { var plat = platforms[i]; if (rectsIntersect(player, plat)) { if (player.vx > 0) { player.x = plat.x - plat.width / 2 - player.width / 2; } else if (player.vx < 0) { player.x = plat.x + plat.width / 2 + player.width / 2; } player.vx = 0; } } // Move Y player.y += player.vy; player.onGround = false; // Collide with platforms (Y axis) for (var i = 0; i < platforms.length; ++i) { var plat = platforms[i]; if (rectsIntersect(player, plat)) { if (player.vy > 0) { player.y = plat.y - plat.height / 2; player.onGround = true; } else if (player.vy < 0) { player.y = plat.y + plat.height / 2 + player.height; } player.vy = 0; } } // Collide with spikes for (var i = 0; i < spikes.length; ++i) { var spike = spikes[i]; if (rectsIntersect(player, spike)) { killPlayer(); return; } } // Collide with checkpoints for (var i = 0; i < checkpoints.length; ++i) { var cp = checkpoints[i]; if (!cp.activated && rectsIntersect(player, cp)) { cp.activate(); currentCheckpoint = cp; } } // Out of bounds (fall) if (player.y > 2800) { killPlayer(); return; } // Win checkWin(); }; // --- Build Level and Start --- buildLevel(); spawnPlayer(); deaths = 0; deathsTxt.setText('Deaths: 0');
===================================================================
--- original.js
+++ change.js
@@ -96,23 +96,68 @@
/****
* Initialize Game
****/
+// PlayerController class: handles input and movement for the player
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
+// PlayerController class: handles input and movement for the player
// --- Asset Initialization ---
// Tween plugin for platform/character movement and effects
// --- Level Data (MVP: 1 level, hardcoded) ---
/*
Level is an array of objects:
{type: 'platform'|'spike'|'checkpoint', x, y, [w], [h], [moveAxis], [moveFrom], [moveTo], [moveDuration]}
All positions are in game coordinates.
*/
+var PlayerController = function PlayerController() {
+ var self = {};
+ self.dirX = 0;
+ self.dirY = 0;
+ self.jumpQueued = false;
+ self.active = false;
+ // Called on controller down
+ self.onDown = function (x, y) {
+ self.active = true;
+ self.updateInput(x, y);
+ self.jumpQueued = false;
+ };
+ // Called on controller move
+ self.onMove = function (x, y) {
+ if (self.active) {
+ self.updateInput(x, y);
+ }
+ };
+ // Called on controller up
+ self.onUp = function () {
+ self.active = false;
+ self.dirX = 0;
+ self.dirY = 0;
+ self.jumpQueued = false;
+ };
+ // Called on jump tap
+ self.onJump = function () {
+ self.jumpQueued = true;
+ };
+ // Update input direction
+ self.updateInput = function (x, y) {
+ var input = getControllerInput(x, y);
+ self.dirX = input.dx / controllerRadius;
+ self.dirY = input.dy / controllerRadius;
+ };
+ // Reset jump
+ self.consumeJump = function () {
+ var wasQueued = self.jumpQueued;
+ self.jumpQueued = false;
+ return wasQueued;
+ };
+ return self;
+};
var levelData = [
// Floor
{
type: 'platform',
@@ -278,21 +323,15 @@
return a.x - a.width / 2 < b.x + b.width / 2 && a.x + a.width / 2 > b.x - b.width / 2 && a.y - a.height < b.y && a.y > b.y - b.height;
}
// --- Tablet Controller (Virtual Joystick) ---
// Controller state
-var controllerActive = false;
-var controllerStartX = 0;
-var controllerStartY = 0;
-var controllerCurX = 0;
-var controllerCurY = 0;
var controllerRadius = 180; // px, joystick max distance
var controllerBase = null;
var controllerStick = null;
var controllerCenterX = 0;
var controllerCenterY = 0;
-var controllerDirX = 0;
-var controllerDirY = 0;
-var controllerJumpQueued = false;
+// Player controller instance
+var playerController = new PlayerController();
// Place controller in bottom left, safe from menu (not in top left 100x100)
controllerCenterX = 220;
controllerCenterY = 2732 - 220;
// Draw controller base and stick
@@ -343,42 +382,29 @@
// Touch down: activate controller if in left-bottom quarter, else jump if in right-bottom
game.down = function (x, y, obj) {
// Only activate controller if touch is in left-bottom quarter (not in top left 100x100)
if (x < 2048 / 2 && y > 2732 - 500 && y > 100) {
- controllerActive = true;
- controllerStartX = x;
- controllerStartY = y;
- controllerCurX = x;
- controllerCurY = y;
+ playerController.onDown(x, y);
var input = getControllerInput(x, y);
controllerStick.x = controllerCenterX + input.dx;
controllerStick.y = controllerCenterY + input.dy;
- controllerDirX = input.dx / controllerRadius;
- controllerDirY = input.dy / controllerRadius;
- controllerJumpQueued = false;
} else if (x > 2048 / 2 && y > 2732 - 500) {
// Right-bottom: jump
- controllerJumpQueued = true;
+ playerController.onJump();
}
};
game.move = function (x, y, obj) {
- if (controllerActive) {
- controllerCurX = x;
- controllerCurY = y;
+ if (playerController.active) {
+ playerController.onMove(x, y);
var input = getControllerInput(x, y);
controllerStick.x = controllerCenterX + input.dx;
controllerStick.y = controllerCenterY + input.dy;
- controllerDirX = input.dx / controllerRadius;
- controllerDirY = input.dy / controllerRadius;
}
};
game.up = function (x, y, obj) {
- controllerActive = false;
+ playerController.onUp();
controllerStick.x = controllerCenterX;
controllerStick.y = controllerCenterY;
- controllerDirX = 0;
- controllerDirY = 0;
- controllerJumpQueued = false;
};
// --- END Tablet Controller ---
// --- Physics Constants ---
var GRAVITY = 2.2;
@@ -388,20 +414,19 @@
// --- Game Update Loop ---
game.update = function () {
if (!player || player.dead) return;
// Horizontal movement (tablet controller)
- if (controllerDirX < -0.2) {
- player.vx = -MOVE_SPEED * Math.min(1, Math.abs(controllerDirX));
- } else if (controllerDirX > 0.2) {
- player.vx = MOVE_SPEED * Math.min(1, Math.abs(controllerDirX));
+ if (playerController.dirX < -0.2) {
+ player.vx = -MOVE_SPEED * Math.min(1, Math.abs(playerController.dirX));
+ } else if (playerController.dirX > 0.2) {
+ player.vx = MOVE_SPEED * Math.min(1, Math.abs(playerController.dirX));
} else {
player.vx = 0;
}
// Jump (tablet controller)
- if ((controllerJumpQueued || controllerActive && controllerDirY < -0.5) && player.onGround) {
+ if ((playerController.consumeJump() || playerController.active && playerController.dirY < -0.5) && player.onGround) {
player.vy = JUMP_VEL;
player.onGround = false;
- controllerJumpQueued = false;
}
// Gravity
player.vy += GRAVITY;
if (player.vy > MAX_FALL) player.vy = MAX_FALL;