/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // BearObstacle class (spawns bear image) var BearObstacle = Container.expand(function () { var self = Container.call(this); var bear = self.attachAsset('bear', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bear.width * 0.45; self.speed = 0; // Set by game // Walking animation: scaleX alternates to simulate walking function startBearWalkAnim() { // Reset scale bear.scaleX = 1; // Animate scaleX to 0.85 and back to 1.15, then repeat tween(bear, { scaleX: 0.85 }, { duration: 120, easing: tween.linear, onFinish: function onFinish() { tween(bear, { scaleX: 1.15 }, { duration: 120, easing: tween.linear, onFinish: startBearWalkAnim }); } }); } startBearWalkAnim(); return self; }); // Obstacle class (tree or rock) var Obstacle = Container.expand(function () { var self = Container.call(this); self.type = Math.random() < 0.6 ? 'tree' : 'rock'; var assetId = self.type === 'tree' ? 'tree' : 'rock'; var obs = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.radius = obs.width * 0.45; self.speed = 0; // Set by game return self; }); // Powerup class (shield or boost) var Powerup = Container.expand(function () { var self = Container.call(this); self.kind = Math.random() < 0.5 ? 'shield' : 'boost'; var assetId = self.kind === 'shield' ? 'shield' : 'boost'; var pu = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.radius = pu.width * 0.45; self.speed = 0; // Set by game return self; }); // Rock2Obstacle class (spawns rock2 image) var Rock2Obstacle = Container.expand(function () { var self = Container.call(this); var obs = self.attachAsset('rock2', { anchorX: 0.5, anchorY: 0.5 }); self.radius = obs.width * 0.45; self.speed = 0; // Set by game return self; }); // Snowboarder (player) class var Snowboarder = Container.expand(function () { var self = Container.call(this); var sb = self.attachAsset('snowboarder', { anchorX: 0.5, anchorY: 0.5 }); self.radius = sb.width * 0.45; // For collision self.hasShield = false; self.shieldNode = null; // Show shield visual if active self.setShield = function (active) { self.hasShield = active; if (active && !self.shieldNode) { self.shieldNode = self.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3, alpha: 0.5 }); } else if (!active && self.shieldNode) { self.shieldNode.destroy(); self.shieldNode = null; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xffffff // White for snow }); /**** * Game Code ****/ // Add background image (snowy background) var background = LK.getAsset('boost', { anchorX: 0.5, anchorY: 0, scaleX: GAME_WIDTH / 200, // 'boost' asset is 200px wide scaleY: GAME_HEIGHT / 200, // 'boost' asset is 200px tall x: GAME_WIDTH / 2, y: 0, alpha: 0.18 // subtle, not to overpower gameplay }); game.addChild(background); // Speed boost powerup // Shield powerup // Rock obstacle // Tree obstacle // Snowboarder (player) // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var PLAY_AREA_MARGIN = 60; // px, left/right margin for obstacles (reduced for denser spread) var OBSTACLE_MIN_Y_DIST = 320; // Minimum vertical distance between obstacles (denser) var OBSTACLE_MAX_Y_DIST = 540; // Maximum vertical distance between obstacles (denser) var OBSTACLE_MIN_X = PLAY_AREA_MARGIN; var OBSTACLE_MAX_X = GAME_WIDTH - PLAY_AREA_MARGIN; var POWERUP_CHANCE = 0.12; // Chance to spawn a powerup instead of obstacle // Intensity increase after 1000 points var INTENSITY_SCORE_THRESHOLD = 1000; var INTENSE_OBSTACLE_MIN_Y_DIST = 160; // Denser in intense mode var INTENSE_OBSTACLE_MAX_Y_DIST = 320; var INTENSE_POWERUP_CHANCE = 0.16; // Game state var snowboarder; var obstacles = []; var powerups = []; var dragNode = null; var dragOffsetX = 0; var dragStartX = 0; var dragStartSnowboarderX = 0; var lastObstacleY = 0; var speed = 18; // Initial speed (pixels per frame) var maxSpeed = 48; var speedIncreaseEvery = 600; // Increase speed every N ticks var ticksSinceStart = 0; var distance = 0; // Distance traveled (score) var scoreTxt; var shieldTimer = 0; var boostTimer = 0; var isBoosting = false; // Add 6 vertical wires (lines) from top to bottom of the play area var wireCount = 6; var wireColor = 0x888888; var wireWidth = 12; var wireSpacing = (GAME_WIDTH - 2 * PLAY_AREA_MARGIN) / (wireCount - 1); var wires = []; for (var i = 0; i < wireCount; i++) { var wireX = PLAY_AREA_MARGIN + i * wireSpacing; var wire = LK.getAsset('wire', { width: wireWidth, height: GAME_HEIGHT, color: wireColor, shape: 'box', anchorX: 0.5, anchorY: 0 }); wire.x = wireX; wire.y = 0; game.addChild(wire); wires.push(wire); } // Removed static tree background assets from both sides of the screen var leftTrees = []; var rightTrees = []; var treeSpacingY = 320 * 0.95; // GUI: Score scoreTxt = new Text2('0', { size: 120, fill: "#222" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // GUI: Lives var lives = 3; var livesTxt = new Text2('❤❤❤', { size: 100, fill: "#d22" }); // Anchor to right edge, top (anchorX: 1, anchorY: 0) livesTxt.anchor.set(1, 0); // Add to top right of GUI LK.gui.topRight.addChild(livesTxt); // Initialize player snowboarder = new Snowboarder(); game.addChild(snowboarder); snowboarder.x = GAME_WIDTH / 2; snowboarder.y = GAME_HEIGHT * 0.12; // Helper: Clamp value function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } // Helper: Distance between two objects function dist(a, b) { var dx = a.x - b.x; var dy = a.y - b.y; return Math.sqrt(dx * dx + dy * dy); } // Spawn an obstacle or powerup function spawnObstacleOrPowerup(y) { // Determine if intensity should be increased var scoreVal = Math.floor(distance / 10); var minYDist = OBSTACLE_MIN_Y_DIST; var maxYDist = OBSTACLE_MAX_Y_DIST; var powerupChance = POWERUP_CHANCE; if (scoreVal >= INTENSITY_SCORE_THRESHOLD) { minYDist = INTENSE_OBSTACLE_MIN_Y_DIST; maxYDist = INTENSE_OBSTACLE_MAX_Y_DIST; powerupChance = INTENSE_POWERUP_CHANCE; } if (Math.random() < powerupChance) { // Powerup var pu = new Powerup(); pu.x = Math.random() * (OBSTACLE_MAX_X - OBSTACLE_MIN_X) + OBSTACLE_MIN_X; pu.y = y; pu.speed = speed; powerups.push(pu); game.addChild(pu); } else { // Obstacle var obs; if (scoreVal >= 1000 && Math.random() < 0.10) { // After 1000 points, 10% chance to spawn bear obs = new BearObstacle(); } else if (scoreVal >= 500 && Math.random() < 0.33) { // After 500 points, 33% chance to spawn rock2 obs = new Rock2Obstacle(); } else { obs = new Obstacle(); } obs.x = Math.random() * (OBSTACLE_MAX_X - OBSTACLE_MIN_X) + OBSTACLE_MIN_X; obs.y = y; obs.speed = speed; obstacles.push(obs); game.addChild(obs); } } // Initial obstacles lastObstacleY = snowboarder.y - 600; for (var i = 0; i < 6; i++) { lastObstacleY -= Math.random() * (OBSTACLE_MAX_Y_DIST - OBSTACLE_MIN_Y_DIST) + OBSTACLE_MIN_Y_DIST; spawnObstacleOrPowerup(lastObstacleY); } // Touch/drag controls game.down = function (x, y, obj) { // Only allow drag if touch is on lower 2/3 of screen (to avoid top menu) if (y > GAME_HEIGHT * 0.2) { dragNode = snowboarder; dragStartX = x; dragStartSnowboarderX = snowboarder.x; } }; game.move = function (x, y, obj) { if (dragNode === snowboarder) { var dx = x - dragStartX; var newX = clamp(dragStartSnowboarderX + dx, OBSTACLE_MIN_X, OBSTACLE_MAX_X); snowboarder.x = newX; // Rotate snowboarder to face movement direction if (Math.abs(dx) > 8) { // Limit max angle to 30 degrees (PI/6) var maxAngle = Math.PI / 6; var angle = clamp(dx / 400, -1, 1) * maxAngle; snowboarder.rotation = angle; } else { snowboarder.rotation = 0; } } }; game.up = function (x, y, obj) { dragNode = null; if (snowboarder) snowboarder.rotation = 0; }; // Main game loop game.update = function () { ticksSinceStart++; // Increase speed over time if (ticksSinceStart % speedIncreaseEvery === 0 && speed < maxSpeed) { speed += 2; } // Speed scaling: increase by 20% for each 1000 points after the first 1000 points var scoreVal = Math.floor(distance / 10); if (scoreVal >= 1000) { // Number of 1000-point steps after the first 1000 var speedSteps = Math.floor((scoreVal - 1000) / 1000) + 1; var scaledSpeed = speed * Math.pow(1.2, speedSteps); // Clamp to maxSpeed if needed if (scaledSpeed > maxSpeed) { scaledSpeed = maxSpeed; } speed = scaledSpeed; } // If boosting, increase speed temporarily var currentSpeed = isBoosting ? speed * 1.7 : speed; // Move side trees up (reverse flow) for (var i = 0; i < leftTrees.length; i++) { leftTrees[i].y -= currentSpeed; // If tree goes off top, move it to bottom if (leftTrees[i].y < -200) { leftTrees[i].y += treeSpacingY * leftTrees.length; } } for (var i = 0; i < rightTrees.length; i++) { rightTrees[i].y -= currentSpeed; if (rightTrees[i].y < -200) { rightTrees[i].y += treeSpacingY * rightTrees.length; } } // Move obstacles and powerups up (reverse flow) for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.y -= currentSpeed; obs.speed = currentSpeed; // If this is a BearObstacle, move it left to right if (obs instanceof BearObstacle) { // Initialize direction and lastX if not set if (typeof obs.bearDirection === "undefined") { obs.bearDirection = 1; // 1 = right, -1 = left } if (typeof obs.lastX === "undefined") { obs.lastX = obs.x; } // Move bear horizontally var bearSpeedX = 12; // px per frame, adjust for desired speed obs.x += obs.bearDirection * bearSpeedX; // Bounce at screen edges if (obs.x > OBSTACLE_MAX_X && obs.bearDirection > 0 || obs.x < OBSTACLE_MIN_X && obs.bearDirection < 0) { obs.bearDirection *= -1; // Clamp to edge obs.x = clamp(obs.x, OBSTACLE_MIN_X, OBSTACLE_MAX_X); } obs.lastX = obs.x; } // Remove if off screen (now above screen) if (obs.y + obs.height / 2 < -100) { obs.destroy(); obstacles.splice(i, 1); } } for (var i = powerups.length - 1; i >= 0; i--) { var pu = powerups[i]; pu.y -= currentSpeed; pu.speed = currentSpeed; // Remove if off screen (now above screen) if (pu.y + pu.height / 2 < -100) { pu.destroy(); powerups.splice(i, 1); } } // Continuously spawn obstacles and powerups as the player progresses (now below player) while (lastObstacleY < snowboarder.y + 3000) { // Use more intense spawn distances after threshold var scoreVal = Math.floor(distance / 10); var minYDist = OBSTACLE_MIN_Y_DIST; var maxYDist = OBSTACLE_MAX_Y_DIST; if (scoreVal >= INTENSITY_SCORE_THRESHOLD) { minYDist = INTENSE_OBSTACLE_MIN_Y_DIST; maxYDist = INTENSE_OBSTACLE_MAX_Y_DIST; } lastObstacleY += Math.random() * (maxYDist - minYDist) + minYDist; spawnObstacleOrPowerup(lastObstacleY); } // Obstacles and powerups keep coming endlessly as the player progresses // Always ensure obstacles continue to spawn all game, even if the player is not moving if (obstacles.length < 8) { // If for any reason there are too few obstacles, spawn more to keep the game challenging var scoreVal = Math.floor(distance / 10); var minYDist = OBSTACLE_MIN_Y_DIST; var maxYDist = OBSTACLE_MAX_Y_DIST; if (scoreVal >= INTENSITY_SCORE_THRESHOLD) { minYDist = INTENSE_OBSTACLE_MIN_Y_DIST; maxYDist = INTENSE_OBSTACLE_MAX_Y_DIST; } lastObstacleY += Math.random() * (maxYDist - minYDist) + minYDist; spawnObstacleOrPowerup(lastObstacleY); } // Collision: obstacles var scoreVal = Math.floor(distance / 10); // Track if snowboarder was colliding with any obstacle last frame if (typeof snowboarder.lastWasHit === "undefined") snowboarder.lastWasHit = false; var hitThisFrame = false; for (var i = 0; i < obstacles.length; i++) { var obs = obstacles[i]; if (dist(snowboarder, obs) < snowboarder.radius + obs.radius) { if (scoreVal < 200) { // Protect from collision in first 200 points: ignore collision // Optionally, flash snowboarder to indicate invulnerability LK.effects.flashObject(snowboarder, 0x66ffcc, 300); } else { if (snowboarder.hasShield) { snowboarder.setShield(false); // Flash snowboarder LK.effects.flashObject(snowboarder, 0xffe066, 600); } else { hitThisFrame = true; } } // Only check first collision for life loss break; } } // Only deduct a life if we were not hit last frame and are hit this frame if (!snowboarder.lastWasHit && hitThisFrame) { lives -= 1; // Clamp lives to minimum 0 if (lives < 0) lives = 0; // Update lives display var heartStr = ''; for (var i = 0; i < lives; i++) heartStr += '❤'; livesTxt.setText(heartStr); LK.effects.flashScreen(0xff0000, 900); if (lives <= 0) { LK.showGameOver(); return; } } snowboarder.lastWasHit = hitThisFrame; // Collision: powerups for (var i = powerups.length - 1; i >= 0; i--) { var pu = powerups[i]; if (dist(snowboarder, pu) < snowboarder.radius + pu.radius) { if (pu.kind === 'shield') { snowboarder.setShield(true); shieldTimer = 360; // 6 seconds at 60fps } else if (pu.kind === 'boost') { isBoosting = true; boostTimer = 180; // 3 seconds at 60fps // Flash snowboarder red for boost LK.effects.flashObject(snowboarder, 0xff3b3b, 400); } pu.destroy(); powerups.splice(i, 1); } } // Powerup timers if (snowboarder.hasShield) { shieldTimer--; if (shieldTimer <= 0) { snowboarder.setShield(false); } } if (isBoosting) { boostTimer--; if (boostTimer <= 0) { isBoosting = false; } } // Snowboarder stays at the top; do not move snowboarder down the screen (reverse flow) // snowboarder.y += currentSpeed; // Score: distance traveled distance += currentSpeed; var scoreVal = Math.floor(distance / 10); LK.setScore(scoreVal); scoreTxt.setText(scoreVal); // Endless game: no win condition, continues until hit }; /* End of game code */
===================================================================
--- original.js
+++ change.js
@@ -114,14 +114,27 @@
/****
* Game Code
****/
-// Game constants
-// Snowboarder (player)
-// Tree obstacle
-// Rock obstacle
-// Shield powerup
+// Add background image (snowy background)
+var background = LK.getAsset('boost', {
+ anchorX: 0.5,
+ anchorY: 0,
+ scaleX: GAME_WIDTH / 200,
+ // 'boost' asset is 200px wide
+ scaleY: GAME_HEIGHT / 200,
+ // 'boost' asset is 200px tall
+ x: GAME_WIDTH / 2,
+ y: 0,
+ alpha: 0.18 // subtle, not to overpower gameplay
+});
+game.addChild(background);
// Speed boost powerup
+// Shield powerup
+// Rock obstacle
+// Tree obstacle
+// Snowboarder (player)
+// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAY_AREA_MARGIN = 60; // px, left/right margin for obstacles (reduced for denser spread)
var OBSTACLE_MIN_Y_DIST = 320; // Minimum vertical distance between obstacles (denser)