User prompt
Make the projectiles red
User prompt
Make the music I added play in game
User prompt
Make the projectile bigger
User prompt
Finish thee projectiels
User prompt
Move the shoot button a little to the right
User prompt
Move the shoot button to the righht
User prompt
Move the button to the right
User prompt
Add a shoot button
User prompt
Make the player hidden behind the buttons
User prompt
Make it so the more collectibles I get, the faster I go
User prompt
Add a scrolling backround to the game
User prompt
Make the buttons show in front of everything else
User prompt
Move the buttons a little to the left
User prompt
Make the buttons much closer together
User prompt
Move the buttons up and to the right
User prompt
Squish the buttons together
User prompt
Move the buttons a little more to the right
User prompt
Move the buttons a tiny bit to the right and a tiny bit up
User prompt
Make the buttons closer together
User prompt
Move the buttons a tiny bit to the right and a tiny bit up
User prompt
Make them even closer
User prompt
Make the buttons closer to each other
User prompt
Make the buttons bigger
User prompt
Move the buttons to the left
User prompt
Make it use the four buttons to move
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Represents a single collectible in the corridor var Collectible = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('collectible', { anchorX: 0.5, anchorY: 1 }); self.lane = 1; self.depth = 0; self.type = 'collectible'; self.active = true; self.getHitbox = function () { return { x: self.x - sprite.width / 2, y: self.y - sprite.height, w: sprite.width, h: sprite.height }; }; return self; }); // Represents a single obstacle in the corridor var Obstacle = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 1 }); // Logical position in corridor space self.lane = 1; // 0=left, 1=center, 2=right self.depth = 0; // Distance from player (z) self.type = 'obstacle'; self.active = true; // For collision detection self.getHitbox = function () { // Returns {x, y, w, h} in screen space return { x: self.x - sprite.width / 2, y: self.y - sprite.height, w: sprite.width, h: sprite.height }; }; return self; }); // Represents a projectile shot by the player var Projectile = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('btnShoot', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'projectile'; self.active = true; self.lane = 1; self.row = 1; self.depth = 0; self.speed = 0.7; // how fast the projectile moves forward (z axis) self.lastDepth = self.depth; self.update = function () { self.lastDepth = self.depth; self.depth += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- Scrolling background setup --- var bg1 = LK.getAsset('ceilingSlice', { anchorX: 0.5, anchorY: 0 }); var bg2 = LK.getAsset('ceilingSlice', { anchorX: 0.5, anchorY: 0 }); bg1.width = 2048; bg1.height = 2732; bg2.width = 2048; bg2.height = 2732; bg1.x = 2048 / 2; bg2.x = 2048 / 2; bg1.y = 0; bg2.y = -2732; bg1.alpha = 0.18; bg2.alpha = 0.18; game.addChild(bg1); game.addChild(bg2); // Collectible (ellipse) // Obstacle (box) // Ceiling slice (horizontal rectangle, will be stretched/scaled for perspective) // Floor slice (horizontal rectangle, will be stretched/scaled for perspective) // Corridor wall slice (vertical rectangle, will be stretched/scaled for perspective) // --- Raycasting & Corridor Parameters --- var corridor = { width: 3, height: 2, // 2 rows: 0=top, 1=bottom // 6 lanes: (0,0)=top-left, (1,0)=top-center, (2,0)=top-right, (0,1)=bottom-left, (1,1)=bottom-center, (2,1)=bottom-right laneWidth: 700, rowHeight: 900, // vertical distance between rows (was 600, now lower lane is much lower) // vertical distance between rows depthSlices: 16, maxDepth: 16, fov: Math.PI / 3, wallColor: 0x4444aa, floorColor: 0x222222, ceilingColor: 0x111133 }; // Player state var player = { lane: 1, // 0=left, 1=center, 2=right row: 1, // 0=top, 1=bottom moving: false, moveTarget: 1, moveRowTarget: 1, moveTween: null, moveRowTween: null, alive: true }; // Input delay state var inputDelayActive = false; var inputDelayTimer = null; var inputDelayMs = 180; // ms, matches tween duration // Game state var speed = 0.18; // units per frame (z axis) var speedIncrease = 0.00004; // per frame var maxSpeed = 0.38; var objects = []; // all obstacles and collectibles var spawnTimer = 0; var spawnInterval = 38; // frames between spawns var score = 0; var distance = 0; // how far player has run (for difficulty) var lastTouchX = null; // --- GUI --- var scoreTxt = new Text2('0', { size: 220, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var distTxt = new Text2('0m', { size: 120, fill: "#aaa" }); distTxt.anchor.set(0.5, 0); LK.gui.top.addChild(distTxt); distTxt.y = 220; // --- Corridor rendering nodes --- var wallNodes = []; var floorNodes = []; var ceilingNodes = []; for (var i = 0; i < corridor.depthSlices; i++) { // Left wall var wl = LK.getAsset('wallSlice', { anchorX: 0.5, anchorY: 1 }); game.addChild(wl); wallNodes.push(wl); // Right wall var wr = LK.getAsset('wallSlice', { anchorX: 0.5, anchorY: 1 }); game.addChild(wr); wallNodes.push(wr); // Floor var fl = LK.getAsset('floorSlice', { anchorX: 0.5, anchorY: 0 }); game.addChild(fl); floorNodes.push(fl); // Ceiling var cl = LK.getAsset('ceilingSlice', { anchorX: 0.5, anchorY: 1 }); game.addChild(cl); ceilingNodes.push(cl); } // --- Helper: Perspective projection for fake-3D --- function projectZ(z) { // Returns scale and y offset for a given depth (z) // Camera is at y = horizonY, looking down corridor var screenH = 2732; var horizonY = screenH * 0.42; var vanishY = horizonY; var baseScale = 1.0; // Perspective: scale = nearPlane / (z + nearPlane) var near = 1.2; var scale = near / (z + near); var y = vanishY + screenH * 0.32 / (z + 0.7); return { scale: scale, y: y }; } // --- Helper: X position for lane at given depth --- function laneX(lane, z) { var centerX = 2048 / 2; var laneOffset = (lane - 1) * corridor.laneWidth; var p = projectZ(z); return centerX + laneOffset * p.scale; } // --- Helper: Y position for row at given depth --- function laneY(row, z) { // row: 0=top, 1=bottom var baseY = 0; var rowOffset = (row - 0.5) * corridor.rowHeight; var p = projectZ(z); return baseY + rowOffset * p.scale; } // --- Spawning obstacles and collectibles --- function spawnObject() { // Randomly choose obstacle or collectible var isObstacle = Math.random() < 0.7; var lane = Math.floor(Math.random() * corridor.width); var row = Math.floor(Math.random() * corridor.height); var depth = corridor.maxDepth + 2; // spawn just beyond farthest slice var obj; if (isObstacle) { obj = new Obstacle(); } else { obj = new Collectible(); } obj.lane = lane; obj.row = row; obj.depth = depth; obj.active = true; objects.push(obj); game.addChild(obj); } // --- On-screen button controls for movement --- var btnSize = 340; var btnMargin = 0; // Make buttons much closer together (no margin at all) var btnAlpha = 0.92; // Create button assets var btnUp = LK.getAsset('btnUp', { anchorX: 0.5, anchorY: 0.5 }); var btnDown = LK.getAsset('btnDown', { anchorX: 0.5, anchorY: 0.5 }); var btnLeft = LK.getAsset('btnLeft', { anchorX: 0.5, anchorY: 0.5 }); var btnRight = LK.getAsset('btnRight', { anchorX: 0.5, anchorY: 0.5 }); // Set button sizes and alpha btnUp.width = btnUp.height = btnSize; btnDown.width = btnDown.height = btnSize; btnLeft.width = btnLeft.height = btnSize; btnRight.width = btnRight.height = btnSize; btnUp.alpha = btnDown.alpha = btnLeft.alpha = btnRight.alpha = btnAlpha; // Position buttons in a diamond at the bottom left of the screen, avoiding the top left 100x100 area // Move the buttons further up and to the right var btnYBase = 2732 - btnSize - btnMargin - 120; // move up by 120px (was 40) var btnXBase = btnMargin + btnSize + 140; // move right by 140px (was 180) btnUp.x = btnXBase; btnUp.y = btnYBase - btnSize - btnMargin; btnDown.x = btnXBase; btnDown.y = btnYBase + btnSize + btnMargin; btnLeft.x = btnXBase - btnSize - btnMargin; btnLeft.y = btnYBase; btnRight.x = btnXBase + btnSize + btnMargin; btnRight.y = btnYBase; // Add buttons to game game.addChild(btnUp); game.addChild(btnDown); game.addChild(btnLeft); game.addChild(btnRight); // --- Shoot button setup --- var btnShoot = LK.getAsset('btnShoot', { anchorX: 0.5, anchorY: 0.5 }); btnShoot.width = btnShoot.height = btnSize; btnShoot.alpha = btnAlpha; // Place shoot button even further to the right of the right button, with a slightly larger margin btnShoot.x = btnRight.x + btnSize + btnMargin + 240; btnShoot.y = btnRight.y; game.addChild(btnShoot); // Button event handlers btnUp.down = function (x, y, obj) { if (!player.moving && player.alive && !inputDelayActive && player.row > 0) { movePlayerTo(player.lane, player.row - 1); inputDelayActive = true; if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer); inputDelayTimer = LK.setTimeout(function () { inputDelayActive = false; inputDelayTimer = null; }, inputDelayMs); } }; btnDown.down = function (x, y, obj) { if (!player.moving && player.alive && !inputDelayActive && player.row < 1) { movePlayerTo(player.lane, player.row + 1); inputDelayActive = true; if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer); inputDelayTimer = LK.setTimeout(function () { inputDelayActive = false; inputDelayTimer = null; }, inputDelayMs); } }; btnLeft.down = function (x, y, obj) { if (!player.moving && player.alive && !inputDelayActive && player.lane > 0) { movePlayerTo(player.lane - 1, player.row); inputDelayActive = true; if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer); inputDelayTimer = LK.setTimeout(function () { inputDelayActive = false; inputDelayTimer = null; }, inputDelayMs); } }; btnRight.down = function (x, y, obj) { if (!player.moving && player.alive && !inputDelayActive && player.lane < 2) { movePlayerTo(player.lane + 1, player.row); inputDelayActive = true; if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer); inputDelayTimer = LK.setTimeout(function () { inputDelayActive = false; inputDelayTimer = null; }, inputDelayMs); } }; // --- Projectiles array --- var projectiles = []; // --- Shoot button event handler --- btnShoot.down = function (x, y, obj) { if (!player.alive) return; // Create a new projectile at the player's current lane/row, just in front of the player var proj = new Projectile(); proj.lane = player.lane; proj.row = player.row; proj.depth = 0.7; // just in front of player proj.lastDepth = proj.depth; projectiles.push(proj); game.addChild(proj); }; // Disable tap-to-move and swipe game.down = function (x, y, obj) {}; game.move = function (x, y, obj) {}; game.up = function (x, y, obj) {}; // --- Move player to lane with tween --- function movePlayerTo(targetLane, targetRow) { if (player.moving || !player.alive) return; if (typeof targetRow === "undefined") targetRow = player.row; player.moveTarget = targetLane; player.moveRowTarget = targetRow; player.moving = true; // Activate input delay immediately on move inputDelayActive = true; if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer); inputDelayTimer = LK.setTimeout(function () { inputDelayActive = false; inputDelayTimer = null; }, inputDelayMs); var startLane = player.lane; var endLane = targetLane; var startRow = player.row; var endRow = targetRow; var t = { v: startLane }; var tr = { v: startRow }; player.moveTween = tween(t, { v: endLane }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { player.lane = endLane; player.moving = false; // If a move was queued during the tween, perform it now if (typeof player.queuedMove !== "undefined" && player.queuedMove !== null && (player.queuedMove !== player.lane || player.queuedRowMove !== player.row)) { var nextLane = player.queuedMove; var nextRow = player.queuedRowMove; player.queuedMove = null; player.queuedRowMove = null; movePlayerTo(nextLane, nextRow); } } }); player.moveRowTween = tween(tr, { v: endRow }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { player.row = endRow; } }); // We'll interpolate player.lane and player.row visually in render player.laneTweenObj = t; player.rowTweenObj = tr; } // --- Main update loop --- game.update = function () { // --- Scroll background --- var bgScrollSpeed = speed * 2732 * 0.18; // scale to match corridor speed visually bg1.y += bgScrollSpeed; bg2.y += bgScrollSpeed; if (bg1.y >= 2732) bg1.y = bg2.y - 2732; if (bg2.y >= 2732) bg2.y = bg1.y - 2732; if (!player.alive) return; // Increase speed over time, and also based on collectibles collected (score) var speedFromCollectibles = 0.018 * score; // Each collectible increases speed by 0.018 speed += speedIncrease; speed = 0.18 + speedFromCollectibles + (speed - 0.18); // base + collectibles + time if (speed > maxSpeed) speed = maxSpeed; distance += speed; distTxt.setText(Math.floor(distance) + "m"); // Spawn new objects spawnTimer--; if (spawnTimer <= 0) { spawnObject(); spawnTimer = spawnInterval - Math.floor(distance / 60); if (spawnTimer < 18) spawnTimer = 18; } // Move objects closer (decrease depth) for (var i = objects.length - 1; i >= 0; i--) { var obj = objects[i]; obj.depth -= speed; if (obj.depth < 0.2) { // Passed player, remove obj.destroy(); objects.splice(i, 1); continue; } // Project to screen var p = projectZ(obj.depth); obj.scaleX = obj.scaleY = p.scale * 1.2; if (typeof obj.row === "undefined") obj.row = 1; obj.x = laneX(obj.lane, obj.depth); obj.y = p.y + 320 * p.scale + laneY(obj.row, obj.depth); obj.visible = obj.depth > 0.2 && obj.depth < corridor.maxDepth + 2; } // Player lane interpolation (for smooth movement) var visLane = player.lane; var visRow = player.row; if (player.moving && player.laneTweenObj) { visLane = player.laneTweenObj.v; } if (player.moving && player.rowTweenObj) { visRow = player.rowTweenObj.v; } // --- Render corridor slices --- for (var i = 0; i < corridor.depthSlices; i++) { // Offset z by distance to make walls/floor/ceiling scroll opposite to objects var z = i + 1.2 - distance % 1; // reverse scroll direction var p = projectZ(z); // Walls var wallW = 80 * p.scale * 2.2; var wallH = 800 * p.scale * 2.2; // Left wall var wl = wallNodes[i * 2]; wl.x = laneX(0, z) - corridor.laneWidth * p.scale / 2 - wallW / 2; wl.y = p.y + wallH; wl.width = wallW; wl.height = wallH; wl.visible = true; // Right wall var wr = wallNodes[i * 2 + 1]; wr.x = laneX(2, z) + corridor.laneWidth * p.scale / 2 + wallW / 2; wr.y = p.y + wallH; wr.width = wallW; wr.height = wallH; wr.visible = true; // Floor var fl = floorNodes[i]; fl.x = 2048 / 2; fl.y = p.y + wallH; fl.width = corridor.laneWidth * corridor.width * p.scale * 1.1; fl.height = 80 * p.scale * 2.2; fl.visible = true; // Ceiling var cl = ceilingNodes[i]; cl.x = 2048 / 2; cl.y = p.y - 80 * p.scale * 1.2; cl.width = corridor.laneWidth * corridor.width * p.scale * 1.1; cl.height = 80 * p.scale * 2.2; cl.visible = true; } // --- Collision detection --- // Player is always at z=0, y=bottom of screen var playerZ = 0.7; var playerP = projectZ(playerZ); var playerX = laneX(visLane, playerZ); var playerY = playerP.y + 320 * playerP.scale + laneY(visRow, playerZ); var playerW = 340 * playerP.scale * 1.2; var playerH = 420 * playerP.scale * 1.2; // --- Render player visual representation --- if (typeof player.visual === "undefined") { // Create the player visual using a new asset, anchor center player.visual = LK.getAsset('player', { anchorX: 0.5, anchorY: 1 }); player.visual.width = 340; player.visual.height = 420; player.visual.alpha = 0.98; game.addChild(player.visual); } player.visual.visible = player.alive; player.visual.x = playerX; player.visual.y = playerY; player.visual.scaleX = playerW / player.visual.width; player.visual.scaleY = playerH / player.visual.height; // Move player visual behind the movement buttons in the display list if (player.visual.parent && player.visual.parent.children) { var parent = player.visual.parent; // Remove player.visual if it exists if (parent) { parent.removeChild(player.visual); // Find the lowest index of the button visuals var btns = [btnUp, btnDown, btnLeft, btnRight]; var minBtnIdx = parent.children.length; for (var i = 0; i < btns.length; i++) { var idx = parent.children.indexOf(btns[i]); if (idx !== -1 && idx < minBtnIdx) minBtnIdx = idx; } // Insert player.visual just before the first button, or at the end if not found if (minBtnIdx < parent.children.length) { parent.addChildAt(player.visual, minBtnIdx); } else { parent.addChild(player.visual); } } } ; // --- Projectiles update/render/collision --- for (var pi = projectiles.length - 1; pi >= 0; pi--) { var proj = projectiles[pi]; proj.update(); // Project to screen var pz = projectZ(proj.depth); proj.scaleX = proj.scaleY = pz.scale * 0.7; proj.x = laneX(proj.lane, proj.depth); proj.y = pz.y + 320 * pz.scale + laneY(proj.row, proj.depth); proj.visible = proj.depth > 0.2 && proj.depth < corridor.maxDepth + 2; // Remove projectile if out of range if (proj.depth > corridor.maxDepth + 2) { proj.destroy(); projectiles.splice(pi, 1); continue; } // Check collision with obstacles for (var oi = objects.length - 1; oi >= 0; oi--) { var obj = objects[oi]; if (!obj.active) continue; if (obj.type !== 'obstacle') continue; // Only check collision if close enough in depth if (Math.abs(obj.depth - proj.depth) < 0.7 && obj.lane === proj.lane && obj.row === proj.row) { // Remove both projectile and obstacle obj.active = false; tween(obj, { scaleX: 2.2, scaleY: 2.2, alpha: 0 }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { obj.destroy(); } }); objects.splice(oi, 1); proj.destroy(); projectiles.splice(pi, 1); break; } } } // For debugging, could render a player shadow here for (var i = objects.length - 1; i >= 0; i--) { var obj = objects[i]; if (!obj.active) continue; // Only check collision if close enough if (obj.depth < 1.2 && obj.depth > 0.2) { var dx = Math.abs(obj.x - playerX); var dy = Math.abs(obj.y - playerY); var hitW = (playerW + obj.width) / 2; var hitH = (playerH + obj.height) / 2; if (dx < hitW * 0.7 && dy < hitH * 0.7) { if (obj.type === 'obstacle') { // Hit obstacle: game over player.alive = false; LK.effects.flashScreen(0xff0000, 900); LK.showGameOver(); return; } else if (obj.type === 'collectible') { // Collect item obj.active = false; score += 1; LK.setScore(score); scoreTxt.setText(score); tween(obj, { scaleX: 2.2, scaleY: 2.2, alpha: 0 }, { duration: 220, easing: tween.cubicOut, onFinish: function onFinish() { obj.destroy(); } }); objects.splice(i, 1); } } } } }; // --- Reset game state on new game --- function resetGame() { // Remove all objects for (var i = 0; i < objects.length; i++) { objects[i].destroy(); } objects = []; player.lane = 1; player.row = 1; player.moving = false; player.moveTarget = 1; player.moveRowTarget = 1; player.moveTween = null; player.moveRowTween = null; player.queuedMove = null; player.queuedRowMove = null; player.alive = true; speed = 0.18; score = 0; distance = 0; spawnTimer = 18; scoreTxt.setText('0'); distTxt.setText('0m'); // Reset input delay state inputDelayActive = false; if (inputDelayTimer !== null) { LK.clearTimeout(inputDelayTimer); inputDelayTimer = null; } } resetGame(); // --- LK handles game over and reset automatically, but listen for new game start --- LK.on('gameStart', function () { resetGame(); });
===================================================================
--- original.js
+++ change.js
@@ -50,8 +50,28 @@
};
};
return self;
});
+// Represents a projectile shot by the player
+var Projectile = Container.expand(function () {
+ var self = Container.call(this);
+ var sprite = self.attachAsset('btnShoot', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.type = 'projectile';
+ self.active = true;
+ self.lane = 1;
+ self.row = 1;
+ self.depth = 0;
+ self.speed = 0.7; // how fast the projectile moves forward (z axis)
+ self.lastDepth = self.depth;
+ self.update = function () {
+ self.lastDepth = self.depth;
+ self.depth += self.speed;
+ };
+ return self;
+});
/****
* Initialize Game
****/
@@ -331,11 +351,21 @@
inputDelayTimer = null;
}, inputDelayMs);
}
};
-// Shoot button event handler (placeholder for now)
+// --- Projectiles array ---
+var projectiles = [];
+// --- Shoot button event handler ---
btnShoot.down = function (x, y, obj) {
- // TODO: Implement shooting logic
+ if (!player.alive) return;
+ // Create a new projectile at the player's current lane/row, just in front of the player
+ var proj = new Projectile();
+ proj.lane = player.lane;
+ proj.row = player.row;
+ proj.depth = 0.7; // just in front of player
+ proj.lastDepth = proj.depth;
+ projectiles.push(proj);
+ game.addChild(proj);
};
// Disable tap-to-move and swipe
game.down = function (x, y, obj) {};
game.move = function (x, y, obj) {};
@@ -527,8 +557,52 @@
parent.addChild(player.visual);
}
}
}
+ ;
+ // --- Projectiles update/render/collision ---
+ for (var pi = projectiles.length - 1; pi >= 0; pi--) {
+ var proj = projectiles[pi];
+ proj.update();
+ // Project to screen
+ var pz = projectZ(proj.depth);
+ proj.scaleX = proj.scaleY = pz.scale * 0.7;
+ proj.x = laneX(proj.lane, proj.depth);
+ proj.y = pz.y + 320 * pz.scale + laneY(proj.row, proj.depth);
+ proj.visible = proj.depth > 0.2 && proj.depth < corridor.maxDepth + 2;
+ // Remove projectile if out of range
+ if (proj.depth > corridor.maxDepth + 2) {
+ proj.destroy();
+ projectiles.splice(pi, 1);
+ continue;
+ }
+ // Check collision with obstacles
+ for (var oi = objects.length - 1; oi >= 0; oi--) {
+ var obj = objects[oi];
+ if (!obj.active) continue;
+ if (obj.type !== 'obstacle') continue;
+ // Only check collision if close enough in depth
+ if (Math.abs(obj.depth - proj.depth) < 0.7 && obj.lane === proj.lane && obj.row === proj.row) {
+ // Remove both projectile and obstacle
+ obj.active = false;
+ tween(obj, {
+ scaleX: 2.2,
+ scaleY: 2.2,
+ alpha: 0
+ }, {
+ duration: 180,
+ easing: tween.cubicOut,
+ onFinish: function onFinish() {
+ obj.destroy();
+ }
+ });
+ objects.splice(oi, 1);
+ proj.destroy();
+ projectiles.splice(pi, 1);
+ break;
+ }
+ }
+ }
// For debugging, could render a player shadow here
for (var i = objects.length - 1; i >= 0; i--) {
var obj = objects[i];
if (!obj.active) continue;