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 * 1.3;
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
@@ -564,9 +564,9 @@
var proj = projectiles[pi];
proj.update();
// Project to screen
var pz = projectZ(proj.depth);
- proj.scaleX = proj.scaleY = pz.scale * 0.7;
+ proj.scaleX = proj.scaleY = pz.scale * 1.3;
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