User prompt
can you make the lasers also detect the player and end the game
User prompt
can you make the colliders more accurate so the it detects the player from all sides
User prompt
can you add more obstacles
User prompt
the player should just jump across the walls. It shouldn't jump upwards
User prompt
the game stops producing obstacles after player gets 8 points. That is a bug. Could you fish it
Code edit (1 edits merged)
Please save this source code
User prompt
Ninja Wall Jump
Initial prompt
I want a ninja tuning game. The game will be vertical. There are 2 walls in each side with obstacles. Every time the player taps the ninja character jumps to the other wall.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ninja class var Ninja = Container.expand(function () { var self = Container.call(this); var ninjaGfx = self.attachAsset('ninja', { anchorX: 0.5, anchorY: 0.5 }); // 0: left wall, 1: right wall self.wallSide = 0; // Current vertical speed (for jump animation) self.vy = 0; // Is currently jumping self.isJumping = false; // Called every tick self.update = function () { // Gravity effect (for jump arc) if (self.isJumping) { self.y += self.vy; self.vy += 2.5; // gravity // If reached or passed targetY, snap to targetY and stop jumping if (self.vy > 0 && self.y >= self.targetY || self.vy < 0 && self.y <= self.targetY) { self.y = self.targetY; self.isJumping = false; self.vy = 0; } } }; // Start jump to other wall, upward by jumpHeight self.jump = function (jumpHeight, wallX) { if (self.isJumping) return; self.isJumping = true; self.wallSide = 1 - self.wallSide; self.targetY = self.y - jumpHeight; self.vy = -40; // initial jump velocity // Tween x to other wall tween(self, { x: wallX }, { duration: 120, easing: tween.cubicOut }); // Play jump sound LK.getSound('jump').play(); }; return self; }); // Obstacle class (spike, barrier, or laser) var Obstacle = Container.expand(function () { var self = Container.call(this); // type: 'spike', 'barrier', or 'laser' self.type = 'spike'; self.wallSide = 0; // 0: left, 1: right self.passed = false; // Has ninja passed this obstacle self.laserDir = 1; // Only used for laser self.laserRange = 0; // Only used for laser self.laserBaseX = 0; // Only used for laser // Set up asset self.setType = function (type) { self.type = type; if (self.asset) { self.removeChild(self.asset); } if (type === 'spike') { self.asset = self.attachAsset('spike', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'barrier') { self.asset = self.attachAsset('barrier', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'laser') { self.asset = self.attachAsset('barrier', { anchorX: 0.5, anchorY: 0.5, width: 40, height: 320, color: 0xff3366 }); self.laserDir = Math.random() < 0.5 ? 1 : -1; self.laserRange = 120; self.laserBaseX = 0; } }; // Called every tick self.update = function () { // Obstacles move downward as the ninja climbs self.y += obstacleSpeed; if (self.type === 'laser') { if (self.laserBaseX === 0) self.laserBaseX = self.x; self.x += self.laserDir * 8; if (Math.abs(self.x - self.laserBaseX) > self.laserRange) { self.laserDir *= -1; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Game constants // Ninja (player) - a small box, red // Wall - tall, thin, gray // Obstacle (spike) - yellow ellipse // Obstacle (barrier) - blue box // Sound for jump var wallGap = 1200; // horizontal distance between walls var wallMargin = 200; // margin from screen edge var ninjaStartY = 2000; var jumpHeight = 320; // vertical jump per tap var obstacleMinGap = 400; // min vertical gap between obstacles var obstacleMaxGap = 700; // max vertical gap between obstacles var obstacleSpeed = 8; // how fast obstacles move down var ninjaXLeft = wallMargin + 50; // center of left wall var ninjaXRight = 2048 - wallMargin - 50; // center of right wall // Walls var leftWall = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0 }); leftWall.x = wallMargin + leftWall.width / 2; leftWall.y = 0; game.addChild(leftWall); var rightWall = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0 }); rightWall.x = 2048 - wallMargin - rightWall.width / 2; rightWall.y = 0; game.addChild(rightWall); // Ninja var ninja = new Ninja(); ninja.x = ninjaXLeft; ninja.y = ninjaStartY; ninja.wallSide = 0; game.addChild(ninja); // Obstacles array var obstacles = []; // Score var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // For touch input var isGameStarted = false; var isDead = false; // For obstacle generation var lastObstacleY = ninjaStartY - 500; // Helper: generate a new obstacle above the last one function spawnObstacle() { var obs = new Obstacle(); // Randomly choose type: 40% spike, 40% barrier, 20% laser var r = Math.random(); if (r < 0.4) { obs.setType('spike'); } else if (r < 0.8) { obs.setType('barrier'); } else { obs.setType('laser'); } // Alternate wall side obs.wallSide = Math.random() < 0.5 ? 0 : 1; // X position obs.x = obs.wallSide === 0 ? ninjaXLeft : ninjaXRight; // Y position: above last obstacle var gap = obstacleMinGap + Math.random() * (obstacleMaxGap - obstacleMinGap); obs.y = lastObstacleY - gap; lastObstacleY = obs.y; // For barrier, offset horizontally so it sticks out from wall if (obs.type === 'barrier') { obs.x += obs.wallSide === 0 ? 90 : -90; } // For laser, center between walls and randomize offset if (obs.type === 'laser') { obs.x = (ninjaXLeft + ninjaXRight) / 2 + (Math.random() - 0.5) * 300; obs.wallSide = -1; // not attached to a wall } obstacles.push(obs); game.addChild(obs); } // Initial obstacles for (var i = 0; i < 8; i++) { spawnObstacle(); } // Move handler: tap anywhere to jump game.down = function (x, y, obj) { if (isDead) return; if (!isGameStarted) { isGameStarted = true; } // Only allow jump if not already jumping if (!ninja.isJumping) { // Jump to other wall var targetX = ninja.wallSide === 0 ? ninjaXRight : ninjaXLeft; ninja.jump(jumpHeight, targetX); } }; // Main update loop game.update = function () { if (isDead) return; // Ninja update ninja.update(); // Move obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.update(); // Remove obstacles that are off screen if (obs.y > 2732 + 100) { obs.destroy(); obstacles.splice(i, 1); continue; } // Check for collision (only if ninja is not jumping, i.e. landed) if (!ninja.isJumping && !obs.passed) { // Only check obstacles on the same wall as ninja if (obs.wallSide === ninja.wallSide) { // For spike: collision if close enough if (obs.type === 'spike') { var dx = Math.abs(ninja.x - obs.x); var dy = Math.abs(ninja.y - obs.y); if (dx < 90 && dy < 90) { // Dead isDead = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } else if (obs.type === 'barrier') { // Barrier is horizontal, check vertical overlap var dx = Math.abs(ninja.x - obs.x); var dy = Math.abs(ninja.y - obs.y); if (dx < 120 && dy < 60) { // Dead isDead = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } else if (obs.type === 'laser') { // Laser is vertical, check horizontal overlap var dx = Math.abs(ninja.x - obs.x); var dy = Math.abs(ninja.y - obs.y); if (dx < 40 && dy < 160) { isDead = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } } } // Score: passed obstacle (only once per obstacle) if (!obs.passed && ninja.y < obs.y) { obs.passed = true; score += 1; LK.setScore(score); scoreTxt.setText(score); } } // Camera: move everything down as ninja climbs if (ninja.y < 1000) { var dy = 1000 - ninja.y; ninja.y += dy; for (var i = 0; i < obstacles.length; i++) { obstacles[i].y += dy; } lastObstacleY += dy; } // Spawn new obstacles if needed while (lastObstacleY > -200) { spawnObstacle(); } }; // Reset game state on game over (handled by LK, but for safety) game.onDestroy = function () { obstacles = []; isGameStarted = false; isDead = false; score = 0; LK.setScore(0); };
===================================================================
--- original.js
+++ change.js
@@ -20,41 +20,49 @@
// Is currently jumping
self.isJumping = false;
// Called every tick
self.update = function () {
- // Only handle horizontal jump, no vertical movement
+ // Gravity effect (for jump arc)
if (self.isJumping) {
- // Wait for horizontal tween to finish (handled by tween)
- // We'll set isJumping = false in the jump tween's onComplete
+ self.y += self.vy;
+ self.vy += 2.5; // gravity
+ // If reached or passed targetY, snap to targetY and stop jumping
+ if (self.vy > 0 && self.y >= self.targetY || self.vy < 0 && self.y <= self.targetY) {
+ self.y = self.targetY;
+ self.isJumping = false;
+ self.vy = 0;
+ }
}
};
// Start jump to other wall, upward by jumpHeight
self.jump = function (jumpHeight, wallX) {
if (self.isJumping) return;
self.isJumping = true;
self.wallSide = 1 - self.wallSide;
- // Only tween x, keep y fixed
+ self.targetY = self.y - jumpHeight;
+ self.vy = -40; // initial jump velocity
+ // Tween x to other wall
tween(self, {
x: wallX
}, {
duration: 120,
- easing: tween.cubicOut,
- onComplete: function onComplete() {
- self.isJumping = false;
- }
+ easing: tween.cubicOut
});
// Play jump sound
LK.getSound('jump').play();
};
return self;
});
-// Obstacle class (spike or barrier)
+// Obstacle class (spike, barrier, or laser)
var Obstacle = Container.expand(function () {
var self = Container.call(this);
- // type: 'spike' or 'barrier'
+ // type: 'spike', 'barrier', or 'laser'
self.type = 'spike';
self.wallSide = 0; // 0: left, 1: right
self.passed = false; // Has ninja passed this obstacle
+ self.laserDir = 1; // Only used for laser
+ self.laserRange = 0; // Only used for laser
+ self.laserBaseX = 0; // Only used for laser
// Set up asset
self.setType = function (type) {
self.type = type;
if (self.asset) {
@@ -64,18 +72,37 @@
self.asset = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
- } else {
+ } else if (type === 'barrier') {
self.asset = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5
});
+ } else if (type === 'laser') {
+ self.asset = self.attachAsset('barrier', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 40,
+ height: 320,
+ color: 0xff3366
+ });
+ self.laserDir = Math.random() < 0.5 ? 1 : -1;
+ self.laserRange = 120;
+ self.laserBaseX = 0;
}
};
// Called every tick
self.update = function () {
- // Obstacles do not move vertically in this mode
+ // Obstacles move downward as the ninja climbs
+ self.y += obstacleSpeed;
+ if (self.type === 'laser') {
+ if (self.laserBaseX === 0) self.laserBaseX = self.x;
+ self.x += self.laserDir * 8;
+ if (Math.abs(self.x - self.laserBaseX) > self.laserRange) {
+ self.laserDir *= -1;
+ }
+ }
};
return self;
});
@@ -88,21 +115,21 @@
/****
* Game Code
****/
-// Sound for jump
-// Obstacle (barrier) - blue box
-// Obstacle (spike) - yellow ellipse
-// Wall - tall, thin, gray
-// Ninja (player) - a small box, red
// Game constants
+// Ninja (player) - a small box, red
+// Wall - tall, thin, gray
+// Obstacle (spike) - yellow ellipse
+// Obstacle (barrier) - blue box
+// Sound for jump
var wallGap = 1200; // horizontal distance between walls
var wallMargin = 200; // margin from screen edge
-var ninjaStartY = 2000; // fixed y position for ninja
-var jumpHeight = 0; // no vertical jump
-var obstacleMinGap = 400; // min vertical gap between obstacles (not used, but kept for reference)
-var obstacleMaxGap = 700; // max vertical gap between obstacles (not used, but kept for reference)
-var obstacleSpeed = 0; // obstacles do not move vertically
+var ninjaStartY = 2000;
+var jumpHeight = 320; // vertical jump per tap
+var obstacleMinGap = 400; // min vertical gap between obstacles
+var obstacleMaxGap = 700; // max vertical gap between obstacles
+var obstacleSpeed = 8; // how fast obstacles move down
var ninjaXLeft = wallMargin + 50; // center of left wall
var ninjaXRight = 2048 - wallMargin - 50; // center of right wall
// Walls
var leftWall = LK.getAsset('wall', {
@@ -142,29 +169,41 @@
var lastObstacleY = ninjaStartY - 500;
// Helper: generate a new obstacle above the last one
function spawnObstacle() {
var obs = new Obstacle();
- // Randomly choose type
- obs.setType(Math.random() < 0.5 ? 'spike' : 'barrier');
+ // Randomly choose type: 40% spike, 40% barrier, 20% laser
+ var r = Math.random();
+ if (r < 0.4) {
+ obs.setType('spike');
+ } else if (r < 0.8) {
+ obs.setType('barrier');
+ } else {
+ obs.setType('laser');
+ }
// Alternate wall side
obs.wallSide = Math.random() < 0.5 ? 0 : 1;
// X position
obs.x = obs.wallSide === 0 ? ninjaXLeft : ninjaXRight;
- // Y position: fixed, aligned with ninja
- obs.y = ninjaStartY;
+ // Y position: above last obstacle
+ var gap = obstacleMinGap + Math.random() * (obstacleMaxGap - obstacleMinGap);
+ obs.y = lastObstacleY - gap;
lastObstacleY = obs.y;
// For barrier, offset horizontally so it sticks out from wall
if (obs.type === 'barrier') {
obs.x += obs.wallSide === 0 ? 90 : -90;
}
+ // For laser, center between walls and randomize offset
+ if (obs.type === 'laser') {
+ obs.x = (ninjaXLeft + ninjaXRight) / 2 + (Math.random() - 0.5) * 300;
+ obs.wallSide = -1; // not attached to a wall
+ }
obstacles.push(obs);
game.addChild(obs);
}
// Initial obstacles
for (var i = 0; i < 8; i++) {
spawnObstacle();
}
-// No limit on obstacle spawning after this; obstacles will continue to spawn as the player progresses.
// Move handler: tap anywhere to jump
game.down = function (x, y, obj) {
if (isDead) return;
if (!isGameStarted) {
@@ -217,8 +256,18 @@
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
+ } else if (obs.type === 'laser') {
+ // Laser is vertical, check horizontal overlap
+ var dx = Math.abs(ninja.x - obs.x);
+ var dy = Math.abs(ninja.y - obs.y);
+ if (dx < 40 && dy < 160) {
+ isDead = true;
+ LK.effects.flashScreen(0xff0000, 800);
+ LK.showGameOver();
+ return;
+ }
}
}
}
// Score: passed obstacle (only once per obstacle)
@@ -228,9 +277,17 @@
LK.setScore(score);
scoreTxt.setText(score);
}
}
- // No camera or vertical movement needed; ninja stays at fixed y
+ // Camera: move everything down as ninja climbs
+ if (ninja.y < 1000) {
+ var dy = 1000 - ninja.y;
+ ninja.y += dy;
+ for (var i = 0; i < obstacles.length; i++) {
+ obstacles[i].y += dy;
+ }
+ lastObstacleY += dy;
+ }
// Spawn new obstacles if needed
while (lastObstacleY > -200) {
spawnObstacle();
}