/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Collectible = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'coin';
var collectibleGraphics = self.attachAsset(self.type, {
anchorX: 0.5,
anchorY: 0.5
});
self.lane = Math.floor(Math.random() * 3); // 0, 1, 2 for left, center, right
self.speed = 4 + Math.random() * 2;
self.update = function () {
self.y += self.speed;
// Rotate coins for visual effect
if (self.type === 'coin') {
collectibleGraphics.rotation += 0.05;
}
};
return self;
});
var GrassTile = Container.expand(function () {
var self = Container.call(this);
// Main grass tile
var grassBase = self.attachAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5
});
// Add random grass dots for texture
var dotCount = Math.floor(1 + Math.random() * 3);
for (var i = 0; i < dotCount; i++) {
var dot = LK.getAsset('grassDot', {
anchorX: 0.5,
anchorY: 0.5,
x: -30 + Math.random() * 60,
y: -30 + Math.random() * 60
});
// Slightly vary the color of dots for more texture
dot.tint = Math.random() > 0.5 ? 0x7CFC00 : 0x98FB98;
self.addChild(dot);
}
// Grass tiles move with the road but slower
self.speed = roadSpeed * 0.2;
self.update = function () {
self.y += self.speed;
};
return self;
});
var House = Container.expand(function () {
var self = Container.call(this);
// Randomize house properties
var width = 100 + Math.random() * 100;
var height = 150 + Math.random() * 250;
var color = Math.random() > 0.5 ? 0x555555 : 0x777777; // Different gray shades
// Main building
var building = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0,
// Bottom anchor for proper ground alignment
width: width,
height: height,
tint: color
});
// Add windows (simplified 8-bit style)
var windowCount = Math.floor(2 + Math.random() * 4);
var floors = Math.floor(2 + Math.random() * 5);
for (var f = 0; f < floors; f++) {
for (var w = 0; w < windowCount; w++) {
var windowObj = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 15,
height: 20,
tint: Math.random() > 0.2 ? 0xFFFF99 : 0x333333,
// Some windows lit, some dark
x: (w - (windowCount - 1) / 2) * (width / windowCount),
y: -height + 30 + f * (height / (floors + 1))
});
self.addChild(windowObj);
}
}
// Add a roof (optional, for some variation)
if (Math.random() > 0.3) {
var roof = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0,
width: width + 20,
height: 25,
tint: 0x883333,
// Reddish roof color
y: -height
});
self.addChild(roof);
}
// Lane is used for positioning (off the main road)
self.lane = Math.random() > 0.5 ? -1 : 2; // Either far left or far right
self.speed = roadSpeed * 0.4; // Houses move slower than the road to create parallax
self.update = function () {
self.y += self.speed;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Create main mud puddle
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x553300 // Dark brown color for mud base
});
// Add mud pixel texture details
var pixelCount = 12 + Math.floor(Math.random() * 8);
for (var i = 0; i < pixelCount; i++) {
var mudPixel = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 8 + Math.random() * 12,
height: 8 + Math.random() * 12,
x: -30 + Math.random() * 60,
y: -30 + Math.random() * 60,
tint: Math.random() > 0.7 ? 0x664411 : 0x442200 // Variation in mud colors
});
self.addChild(mudPixel);
}
// Add mud "splash" pixels around edge
var splashCount = 6 + Math.floor(Math.random() * 5);
for (var i = 0; i < splashCount; i++) {
var angle = Math.random() * Math.PI * 2;
var distance = 35 + Math.random() * 15;
var splashPixel = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 5 + Math.random() * 8,
height: 5 + Math.random() * 8,
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance,
tint: 0x664411 // Lighter mud color for splashes
});
self.addChild(splashPixel);
}
self.lane = Math.floor(Math.random() * 3); // 0, 1, 2 for left, center, right
self.speed = 4 + Math.random() * 2;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
// Create an 8-bit style car with multiple sprites
self.carContainer = new Container();
self.addChild(self.carContainer);
// Car body - main part
var carBody = self.carContainer.attachAsset('playerCarBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Car window - top part of the car
var carWindow = LK.getAsset('playerCarWindow', {
anchorX: 0.5,
anchorY: 0.5,
y: -35
});
self.carContainer.addChild(carWindow);
// Left front wheel
var leftFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -35,
y: -40
});
self.carContainer.addChild(leftFrontWheel);
// Right front wheel
var rightFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: -40
});
self.carContainer.addChild(rightFrontWheel);
// Left rear wheel
var leftRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -35,
y: 40
});
self.carContainer.addChild(leftRearWheel);
// Right rear wheel
var rightRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: 40
});
self.carContainer.addChild(rightRearWheel);
// Left headlight
var leftHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: -65
});
self.carContainer.addChild(leftHeadlight);
// Right headlight
var rightHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: -65
});
self.carContainer.addChild(rightHeadlight);
// Front grill
var grill = LK.getAsset('playerCarGrill', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -50
});
self.carContainer.addChild(grill);
self.lane = 1; // 0, 1, 2 for left, center, right
self.speed = 5;
self.isInvincible = false;
self.invincibleTime = 0;
self.setInvincible = function (duration) {
self.isInvincible = true;
self.invincibleTime = duration;
// Flash effect for invincibility
var flashInterval = LK.setInterval(function () {
self.carContainer.alpha = self.carContainer.alpha === 1 ? 0.5 : 1;
}, 100);
LK.setTimeout(function () {
self.isInvincible = false;
self.carContainer.alpha = 1;
LK.clearInterval(flashInterval);
}, duration);
};
return self;
});
var RoadLine = Container.expand(function () {
var self = Container.call(this);
var lineGraphics = self.attachAsset('roadLine', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
var TrafficCar = Container.expand(function () {
var self = Container.call(this);
// Create a container for the truck parts
self.truckContainer = new Container();
self.addChild(self.truckContainer);
// Randomize between police vehicle and regular truck
var isPolice = Math.random() < 0.2;
var mainColor = isPolice ? 'policeCar' : 'trafficCar';
// Main truck body (larger than regular cars)
var truckBody = self.truckContainer.attachAsset(mainColor, {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 1.1
});
// Truck cabin (upper part)
var truckCabin = LK.getAsset(mainColor === 'policeCar' ? 'policeCar' : 'trafficCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.5,
y: -65
});
self.truckContainer.addChild(truckCabin);
// Add truck wheels (16-bit style as squares)
var leftFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -40,
y: -35,
scaleX: 1.1
});
self.truckContainer.addChild(leftFrontWheel);
var rightFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: -35,
scaleX: 1.1
});
self.truckContainer.addChild(rightFrontWheel);
var leftRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -40,
y: 50,
scaleX: 1.1
});
self.truckContainer.addChild(leftRearWheel);
var rightRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: 50,
scaleX: 1.1
});
self.truckContainer.addChild(rightRearWheel);
// Add truck headlights
if (isPolice) {
// Police light bar
var policeLight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -80,
scaleX: 3,
scaleY: 0.8,
tint: 0xff0000
});
self.truckContainer.addChild(policeLight);
// Flash the police lights
LK.setInterval(function () {
policeLight.tint = policeLight.tint === 0xff0000 ? 0x0000ff : 0xff0000;
}, 500);
} else {
// Regular truck headlights
var leftHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: -80
});
self.truckContainer.addChild(leftHeadlight);
var rightHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: -80
});
self.truckContainer.addChild(rightHeadlight);
}
// Add a cargo section for regular trucks
if (!isPolice) {
var truckCargo = LK.getAsset('trafficCar', {
anchorX: 0.5,
anchorY: 0.5,
y: 30,
scaleY: 0.4,
tint: Math.random() > 0.5 ? 0x884400 : 0x777777
});
self.truckContainer.addChild(truckCargo);
}
self.lane = Math.floor(Math.random() * 3); // 0, 1, 2 for left, center, right
// Traffic cars get faster as the game progresses
self.baseSpeed = 3 + Math.random() * 2;
self.speed = self.baseSpeed;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22 // Forest green for grass base color
});
/****
* Game Code
****/
// Game constants
// Create an 8-bit style race car using multiple shapes instead of a simple box
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var LANE_WIDTH = 250;
var LANE_POSITIONS = [GAME_WIDTH / 2 - LANE_WIDTH, GAME_WIDTH / 2, GAME_WIDTH / 2 + LANE_WIDTH];
// Game variables
var isGameStarted = false;
var score = 0;
var distance = 0;
var difficulty = 1;
var lastTrafficSpawn = 0;
var lastObstacleSpawn = 0;
var lastCollectibleSpawn = 0;
var lastRoadLineSpawn = 0;
var lastGrassTileSpawn = 0; // Track last grass tile spawn time
var roadSpeed = 10;
var trafficCars = [];
var obstacles = [];
var collectibles = [];
var roadLines = [];
var houses = []; // Array to store house instances
var grassTiles = []; // Array to store grass tiles
var lastHouseSpawn = 0; // Track last house spawn time
var lastBackgroundHouseSpawn = 0; // Track last background house spawn time
var touchStartX = 0;
var touchStartY = 0;
var playerCar;
var scoreTxt;
var highScoreTxt;
var readyTxt;
// Create road background
var roadBackground = LK.getAsset('roadBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2,
width: 800,
height: GAME_HEIGHT
});
game.addChild(roadBackground);
// Initialize player car
playerCar = new PlayerCar();
playerCar.x = LANE_POSITIONS[playerCar.lane];
playerCar.y = GAME_HEIGHT - 300;
game.addChild(playerCar);
// Create score display
scoreTxt = new Text2('SCORE: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
// Create speed display
speedTxt = new Text2('SPEED: 10', {
size: 50,
fill: 0x00FFFF
});
speedTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(speedTxt);
speedTxt.x = -speedTxt.width - 20;
speedTxt.y = 170;
// Create high score display
highScoreTxt = new Text2('HIGH SCORE: ' + storage.highScore, {
size: 50,
fill: 0xFFFF00
});
highScoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreTxt);
highScoreTxt.x = -highScoreTxt.width - 20;
highScoreTxt.y = 100;
// Create ready text
readyTxt = new Text2('TAP TO START', {
size: 100,
fill: 0xFFFFFF
});
readyTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(readyTxt);
// Start game music
LK.playMusic('raceMusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
// Handle game touch events
game.down = function (x, y, obj) {
if (!isGameStarted) {
startGame();
return;
}
touchStartX = x;
touchStartY = y;
};
game.up = function (x, y, obj) {
if (!isGameStarted) {
return;
}
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
// If more horizontal than vertical movement
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (deltaX > 50 && playerCar.lane < 2) {
// Swipe right
playerCar.lane++;
tween(playerCar, {
x: LANE_POSITIONS[playerCar.lane]
}, {
duration: 200,
easing: tween.easeOut
});
} else if (deltaX < -50 && playerCar.lane > 0) {
// Swipe left
playerCar.lane--;
tween(playerCar, {
x: LANE_POSITIONS[playerCar.lane]
}, {
duration: 200,
easing: tween.easeOut
});
}
}
};
game.move = function (x, y, obj) {
// Not using move for this game
};
function startGame() {
isGameStarted = true;
score = 0;
distance = 0;
difficulty = 1;
roadSpeed = 10;
// Reset arrays
clearGameObjects();
// Hide ready text
LK.gui.center.removeChild(readyTxt);
// Update score display
updateScoreDisplay();
}
function clearGameObjects() {
// Clear traffic cars
for (var i = trafficCars.length - 1; i >= 0; i--) {
trafficCars[i].destroy();
trafficCars.splice(i, 1);
}
// Clear obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Clear collectibles
for (var i = collectibles.length - 1; i >= 0; i--) {
collectibles[i].destroy();
collectibles.splice(i, 1);
}
// Clear road lines
for (var i = roadLines.length - 1; i >= 0; i--) {
roadLines[i].destroy();
roadLines.splice(i, 1);
}
// Clear houses
for (var i = houses.length - 1; i >= 0; i--) {
houses[i].destroy();
houses.splice(i, 1);
}
// Clear grass tiles
for (var i = grassTiles.length - 1; i >= 0; i--) {
grassTiles[i].destroy();
grassTiles.splice(i, 1);
}
}
function spawnTrafficCar() {
var trafficCar = new TrafficCar();
trafficCar.x = LANE_POSITIONS[trafficCar.lane];
trafficCar.y = -200;
trafficCars.push(trafficCar);
game.addChild(trafficCar);
}
function spawnObstacle() {
var obstacle = new Obstacle();
obstacle.x = LANE_POSITIONS[obstacle.lane];
obstacle.y = -200;
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnCollectible() {
var type = Math.random() < 0.2 ? 'speedBoost' : 'coin';
var collectible = new Collectible(type);
collectible.x = LANE_POSITIONS[collectible.lane];
collectible.y = -200;
collectibles.push(collectible);
game.addChild(collectible);
}
function spawnRoadLine() {
var leftLine = new RoadLine();
leftLine.x = GAME_WIDTH / 2 - LANE_WIDTH / 2 - 125;
leftLine.y = -100;
roadLines.push(leftLine);
game.addChild(leftLine);
var rightLine = new RoadLine();
rightLine.x = GAME_WIDTH / 2 + LANE_WIDTH / 2 + 125;
rightLine.y = -100;
roadLines.push(rightLine);
game.addChild(rightLine);
}
function spawnHouse() {
// Create multiple houses at once for a more populated city scenery
var houseCount = 1 + Math.floor(Math.random() * 3); // Create 1-3 houses at once
for (var i = 0; i < houseCount; i++) {
var house = new House();
// Position houses far to the left or right of the road
if (house.lane === -1) {
// Far left with some variation in x position
house.x = GAME_WIDTH / 2 - LANE_WIDTH * (2.5 + Math.random() * 1.5);
} else {
// Far right with some variation in x position
house.x = GAME_WIDTH / 2 + LANE_WIDTH * (2.5 + Math.random() * 1.5);
}
// Vary the y position slightly to avoid houses appearing in straight lines
house.y = -300 - i * 150 - Math.random() * 100;
houses.push(house);
// Add houses beneath the road to create a layered effect
game.addChildAt(house, game.getChildIndex(roadBackground));
}
}
function spawnBackgroundHouses() {
// Create distant background houses for additional depth
var house = new House();
// Make the house smaller and more faded to give appearance of distance
house.scale.set(0.7);
house.alpha = 0.7;
// Position houses very far to the left or right
if (Math.random() > 0.5) {
// Far left background
house.x = GAME_WIDTH / 2 - LANE_WIDTH * (4 + Math.random() * 2);
} else {
// Far right background
house.x = GAME_WIDTH / 2 + LANE_WIDTH * (4 + Math.random() * 2);
}
house.y = -300;
houses.push(house);
// Add background houses at the very bottom layer for proper depth perception
game.addChildAt(house, 0);
}
function spawnGrassTiles() {
// Create grass tiles for the whole width of the game
var tilesPerRow = Math.ceil(GAME_WIDTH / 100); // 100 is the width of a tile
for (var i = 0; i < tilesPerRow; i++) {
var grassTile = new GrassTile();
grassTile.x = i * 100;
grassTile.y = -100; // Start above the screen
grassTiles.push(grassTile);
// Add grass tiles below everything for proper layering
game.addChildAt(grassTile, 0);
}
}
function updateScoreDisplay() {
scoreTxt.setText('SCORE: ' + Math.floor(score));
scoreTxt.x = -scoreTxt.width - 20;
// Update speed display
speedTxt.setText('SPEED: ' + Math.floor(roadSpeed));
speedTxt.x = -speedTxt.width - 20;
// Color the speed text based on speed value - faster = more red
var r = Math.min(255, Math.floor(roadSpeed * 10));
var g = 255 - Math.min(255, Math.floor(roadSpeed * 5));
var b = 255;
var speedColor = r << 16 | g << 8 | b;
// Update high score if needed
if (score > storage.highScore) {
storage.highScore = Math.floor(score);
// Save high score to storage
storage.highScore = storage.highScore;
highScoreTxt.setText('HIGH SCORE: ' + storage.highScore);
highScoreTxt.x = -highScoreTxt.width - 20;
}
}
function checkCollisions() {
// Check collision with traffic cars
for (var i = trafficCars.length - 1; i >= 0; i--) {
if (playerCar.intersects(trafficCars[i]) && !playerCar.isInvincible) {
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
}
// Check collision with obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
if (playerCar.intersects(obstacles[i]) && !playerCar.isInvincible) {
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
}
// Check collision with collectibles
for (var i = collectibles.length - 1; i >= 0; i--) {
if (playerCar.intersects(collectibles[i])) {
if (collectibles[i].type === 'coin') {
score += 10;
LK.getSound('coinCollect').play();
} else if (collectibles[i].type === 'speedBoost') {
playerCar.setInvincible(3000);
roadSpeed += 5;
LK.setTimeout(function () {
roadSpeed -= 5;
}, 3000);
LK.getSound('powerup').play();
}
collectibles[i].destroy();
collectibles.splice(i, 1);
updateScoreDisplay();
}
}
}
game.update = function () {
if (!isGameStarted) {
return;
}
// Increase distance and score
distance += roadSpeed / 10;
score += 0.1 * difficulty;
// Update difficulty based on distance and time passed - increase more aggressively
difficulty = 1 + Math.floor(distance / 800) * 0.3 + LK.ticks / 2400;
// Update road speed - increase speed more dramatically over time
roadSpeed = 10 + Math.floor(distance / 400) + LK.ticks / 450;
// Spawn game objects
// Spawn traffic cars much more frequently as time progresses
var trafficSpawnRate = Math.max(40, 120 - difficulty * 15 - LK.ticks / 800);
if (LK.ticks - lastTrafficSpawn > trafficSpawnRate) {
spawnTrafficCar();
lastTrafficSpawn = LK.ticks;
}
if (LK.ticks - lastObstacleSpawn > Math.max(150, 300 - difficulty * 20)) {
spawnObstacle();
lastObstacleSpawn = LK.ticks;
}
if (LK.ticks - lastCollectibleSpawn > 90) {
spawnCollectible();
lastCollectibleSpawn = LK.ticks;
}
if (LK.ticks - lastRoadLineSpawn > 30) {
spawnRoadLine();
lastRoadLineSpawn = LK.ticks;
}
// Spawn houses more frequently to create a dense city environment
if (LK.ticks - lastHouseSpawn > 100) {
// Reduced from 180 to 100
spawnHouse();
lastHouseSpawn = LK.ticks;
}
// Spawn grass tiles for 8-bit grass background
if (LK.ticks - lastGrassTileSpawn > 40) {
spawnGrassTiles();
lastGrassTileSpawn = LK.ticks;
}
// Spawn additional background houses for depth
if (LK.ticks - lastBackgroundHouseSpawn > 150) {
spawnBackgroundHouses();
lastBackgroundHouseSpawn = LK.ticks;
}
// Update road lines
for (var i = roadLines.length - 1; i >= 0; i--) {
roadLines[i].speed = roadSpeed;
roadLines[i].update();
if (roadLines[i].y > GAME_HEIGHT + 100) {
roadLines[i].destroy();
roadLines.splice(i, 1);
}
}
// Update traffic cars - speed increases with game time
for (var i = trafficCars.length - 1; i >= 0; i--) {
// Speed factor increases with time more aggressively
var timeFactor = 0.7 + LK.ticks / 15000;
// Add variety to traffic car speeds based on game time
var speedVariation = Math.random() * 0.3 * (1 + LK.ticks / 30000);
trafficCars[i].speed = roadSpeed * Math.min(timeFactor, 1.8) * (1 - speedVariation);
trafficCars[i].update();
if (trafficCars[i].y > GAME_HEIGHT + 200) {
trafficCars[i].destroy();
trafficCars.splice(i, 1);
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].speed = roadSpeed * 0.6;
obstacles[i].update();
if (obstacles[i].y > GAME_HEIGHT + 100) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
}
// Update collectibles
for (var i = collectibles.length - 1; i >= 0; i--) {
collectibles[i].speed = roadSpeed * 0.8;
collectibles[i].update();
if (collectibles[i].y > GAME_HEIGHT + 100) {
collectibles[i].destroy();
collectibles.splice(i, 1);
}
}
// Update houses
for (var i = houses.length - 1; i >= 0; i--) {
houses[i].speed = roadSpeed * 0.4; // Slower movement for parallax effect
houses[i].update();
if (houses[i].y > GAME_HEIGHT + 300) {
houses[i].destroy();
houses.splice(i, 1);
}
}
// Update grass tiles
for (var i = grassTiles.length - 1; i >= 0; i--) {
grassTiles[i].speed = roadSpeed * 0.2; // Very slow movement for distant background effect
grassTiles[i].update();
if (grassTiles[i].y > GAME_HEIGHT + 100) {
grassTiles[i].destroy();
grassTiles.splice(i, 1);
}
}
// Check collisions
checkCollisions();
// Decrease invincible time
if (playerCar.isInvincible && playerCar.invincibleTime > 0) {
playerCar.invincibleTime -= 16.67; // ~60fps
}
// Update score display every second
if (LK.ticks % 60 === 0) {
updateScoreDisplay();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Collectible = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'coin';
var collectibleGraphics = self.attachAsset(self.type, {
anchorX: 0.5,
anchorY: 0.5
});
self.lane = Math.floor(Math.random() * 3); // 0, 1, 2 for left, center, right
self.speed = 4 + Math.random() * 2;
self.update = function () {
self.y += self.speed;
// Rotate coins for visual effect
if (self.type === 'coin') {
collectibleGraphics.rotation += 0.05;
}
};
return self;
});
var GrassTile = Container.expand(function () {
var self = Container.call(this);
// Main grass tile
var grassBase = self.attachAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5
});
// Add random grass dots for texture
var dotCount = Math.floor(1 + Math.random() * 3);
for (var i = 0; i < dotCount; i++) {
var dot = LK.getAsset('grassDot', {
anchorX: 0.5,
anchorY: 0.5,
x: -30 + Math.random() * 60,
y: -30 + Math.random() * 60
});
// Slightly vary the color of dots for more texture
dot.tint = Math.random() > 0.5 ? 0x7CFC00 : 0x98FB98;
self.addChild(dot);
}
// Grass tiles move with the road but slower
self.speed = roadSpeed * 0.2;
self.update = function () {
self.y += self.speed;
};
return self;
});
var House = Container.expand(function () {
var self = Container.call(this);
// Randomize house properties
var width = 100 + Math.random() * 100;
var height = 150 + Math.random() * 250;
var color = Math.random() > 0.5 ? 0x555555 : 0x777777; // Different gray shades
// Main building
var building = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0,
// Bottom anchor for proper ground alignment
width: width,
height: height,
tint: color
});
// Add windows (simplified 8-bit style)
var windowCount = Math.floor(2 + Math.random() * 4);
var floors = Math.floor(2 + Math.random() * 5);
for (var f = 0; f < floors; f++) {
for (var w = 0; w < windowCount; w++) {
var windowObj = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 15,
height: 20,
tint: Math.random() > 0.2 ? 0xFFFF99 : 0x333333,
// Some windows lit, some dark
x: (w - (windowCount - 1) / 2) * (width / windowCount),
y: -height + 30 + f * (height / (floors + 1))
});
self.addChild(windowObj);
}
}
// Add a roof (optional, for some variation)
if (Math.random() > 0.3) {
var roof = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0,
width: width + 20,
height: 25,
tint: 0x883333,
// Reddish roof color
y: -height
});
self.addChild(roof);
}
// Lane is used for positioning (off the main road)
self.lane = Math.random() > 0.5 ? -1 : 2; // Either far left or far right
self.speed = roadSpeed * 0.4; // Houses move slower than the road to create parallax
self.update = function () {
self.y += self.speed;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Create main mud puddle
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x553300 // Dark brown color for mud base
});
// Add mud pixel texture details
var pixelCount = 12 + Math.floor(Math.random() * 8);
for (var i = 0; i < pixelCount; i++) {
var mudPixel = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 8 + Math.random() * 12,
height: 8 + Math.random() * 12,
x: -30 + Math.random() * 60,
y: -30 + Math.random() * 60,
tint: Math.random() > 0.7 ? 0x664411 : 0x442200 // Variation in mud colors
});
self.addChild(mudPixel);
}
// Add mud "splash" pixels around edge
var splashCount = 6 + Math.floor(Math.random() * 5);
for (var i = 0; i < splashCount; i++) {
var angle = Math.random() * Math.PI * 2;
var distance = 35 + Math.random() * 15;
var splashPixel = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 5 + Math.random() * 8,
height: 5 + Math.random() * 8,
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance,
tint: 0x664411 // Lighter mud color for splashes
});
self.addChild(splashPixel);
}
self.lane = Math.floor(Math.random() * 3); // 0, 1, 2 for left, center, right
self.speed = 4 + Math.random() * 2;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
// Create an 8-bit style car with multiple sprites
self.carContainer = new Container();
self.addChild(self.carContainer);
// Car body - main part
var carBody = self.carContainer.attachAsset('playerCarBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Car window - top part of the car
var carWindow = LK.getAsset('playerCarWindow', {
anchorX: 0.5,
anchorY: 0.5,
y: -35
});
self.carContainer.addChild(carWindow);
// Left front wheel
var leftFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -35,
y: -40
});
self.carContainer.addChild(leftFrontWheel);
// Right front wheel
var rightFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: -40
});
self.carContainer.addChild(rightFrontWheel);
// Left rear wheel
var leftRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -35,
y: 40
});
self.carContainer.addChild(leftRearWheel);
// Right rear wheel
var rightRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 35,
y: 40
});
self.carContainer.addChild(rightRearWheel);
// Left headlight
var leftHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: -65
});
self.carContainer.addChild(leftHeadlight);
// Right headlight
var rightHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: -65
});
self.carContainer.addChild(rightHeadlight);
// Front grill
var grill = LK.getAsset('playerCarGrill', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -50
});
self.carContainer.addChild(grill);
self.lane = 1; // 0, 1, 2 for left, center, right
self.speed = 5;
self.isInvincible = false;
self.invincibleTime = 0;
self.setInvincible = function (duration) {
self.isInvincible = true;
self.invincibleTime = duration;
// Flash effect for invincibility
var flashInterval = LK.setInterval(function () {
self.carContainer.alpha = self.carContainer.alpha === 1 ? 0.5 : 1;
}, 100);
LK.setTimeout(function () {
self.isInvincible = false;
self.carContainer.alpha = 1;
LK.clearInterval(flashInterval);
}, duration);
};
return self;
});
var RoadLine = Container.expand(function () {
var self = Container.call(this);
var lineGraphics = self.attachAsset('roadLine', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
var TrafficCar = Container.expand(function () {
var self = Container.call(this);
// Create a container for the truck parts
self.truckContainer = new Container();
self.addChild(self.truckContainer);
// Randomize between police vehicle and regular truck
var isPolice = Math.random() < 0.2;
var mainColor = isPolice ? 'policeCar' : 'trafficCar';
// Main truck body (larger than regular cars)
var truckBody = self.truckContainer.attachAsset(mainColor, {
anchorX: 0.5,
anchorY: 0.5,
scaleY: 1.1
});
// Truck cabin (upper part)
var truckCabin = LK.getAsset(mainColor === 'policeCar' ? 'policeCar' : 'trafficCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.5,
y: -65
});
self.truckContainer.addChild(truckCabin);
// Add truck wheels (16-bit style as squares)
var leftFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -40,
y: -35,
scaleX: 1.1
});
self.truckContainer.addChild(leftFrontWheel);
var rightFrontWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: -35,
scaleX: 1.1
});
self.truckContainer.addChild(rightFrontWheel);
var leftRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -40,
y: 50,
scaleX: 1.1
});
self.truckContainer.addChild(leftRearWheel);
var rightRearWheel = LK.getAsset('playerCarWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: 50,
scaleX: 1.1
});
self.truckContainer.addChild(rightRearWheel);
// Add truck headlights
if (isPolice) {
// Police light bar
var policeLight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -80,
scaleX: 3,
scaleY: 0.8,
tint: 0xff0000
});
self.truckContainer.addChild(policeLight);
// Flash the police lights
LK.setInterval(function () {
policeLight.tint = policeLight.tint === 0xff0000 ? 0x0000ff : 0xff0000;
}, 500);
} else {
// Regular truck headlights
var leftHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: -80
});
self.truckContainer.addChild(leftHeadlight);
var rightHeadlight = LK.getAsset('playerCarLight', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: -80
});
self.truckContainer.addChild(rightHeadlight);
}
// Add a cargo section for regular trucks
if (!isPolice) {
var truckCargo = LK.getAsset('trafficCar', {
anchorX: 0.5,
anchorY: 0.5,
y: 30,
scaleY: 0.4,
tint: Math.random() > 0.5 ? 0x884400 : 0x777777
});
self.truckContainer.addChild(truckCargo);
}
self.lane = Math.floor(Math.random() * 3); // 0, 1, 2 for left, center, right
// Traffic cars get faster as the game progresses
self.baseSpeed = 3 + Math.random() * 2;
self.speed = self.baseSpeed;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22 // Forest green for grass base color
});
/****
* Game Code
****/
// Game constants
// Create an 8-bit style race car using multiple shapes instead of a simple box
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var LANE_WIDTH = 250;
var LANE_POSITIONS = [GAME_WIDTH / 2 - LANE_WIDTH, GAME_WIDTH / 2, GAME_WIDTH / 2 + LANE_WIDTH];
// Game variables
var isGameStarted = false;
var score = 0;
var distance = 0;
var difficulty = 1;
var lastTrafficSpawn = 0;
var lastObstacleSpawn = 0;
var lastCollectibleSpawn = 0;
var lastRoadLineSpawn = 0;
var lastGrassTileSpawn = 0; // Track last grass tile spawn time
var roadSpeed = 10;
var trafficCars = [];
var obstacles = [];
var collectibles = [];
var roadLines = [];
var houses = []; // Array to store house instances
var grassTiles = []; // Array to store grass tiles
var lastHouseSpawn = 0; // Track last house spawn time
var lastBackgroundHouseSpawn = 0; // Track last background house spawn time
var touchStartX = 0;
var touchStartY = 0;
var playerCar;
var scoreTxt;
var highScoreTxt;
var readyTxt;
// Create road background
var roadBackground = LK.getAsset('roadBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2,
width: 800,
height: GAME_HEIGHT
});
game.addChild(roadBackground);
// Initialize player car
playerCar = new PlayerCar();
playerCar.x = LANE_POSITIONS[playerCar.lane];
playerCar.y = GAME_HEIGHT - 300;
game.addChild(playerCar);
// Create score display
scoreTxt = new Text2('SCORE: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
// Create speed display
speedTxt = new Text2('SPEED: 10', {
size: 50,
fill: 0x00FFFF
});
speedTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(speedTxt);
speedTxt.x = -speedTxt.width - 20;
speedTxt.y = 170;
// Create high score display
highScoreTxt = new Text2('HIGH SCORE: ' + storage.highScore, {
size: 50,
fill: 0xFFFF00
});
highScoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreTxt);
highScoreTxt.x = -highScoreTxt.width - 20;
highScoreTxt.y = 100;
// Create ready text
readyTxt = new Text2('TAP TO START', {
size: 100,
fill: 0xFFFFFF
});
readyTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(readyTxt);
// Start game music
LK.playMusic('raceMusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
// Handle game touch events
game.down = function (x, y, obj) {
if (!isGameStarted) {
startGame();
return;
}
touchStartX = x;
touchStartY = y;
};
game.up = function (x, y, obj) {
if (!isGameStarted) {
return;
}
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
// If more horizontal than vertical movement
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (deltaX > 50 && playerCar.lane < 2) {
// Swipe right
playerCar.lane++;
tween(playerCar, {
x: LANE_POSITIONS[playerCar.lane]
}, {
duration: 200,
easing: tween.easeOut
});
} else if (deltaX < -50 && playerCar.lane > 0) {
// Swipe left
playerCar.lane--;
tween(playerCar, {
x: LANE_POSITIONS[playerCar.lane]
}, {
duration: 200,
easing: tween.easeOut
});
}
}
};
game.move = function (x, y, obj) {
// Not using move for this game
};
function startGame() {
isGameStarted = true;
score = 0;
distance = 0;
difficulty = 1;
roadSpeed = 10;
// Reset arrays
clearGameObjects();
// Hide ready text
LK.gui.center.removeChild(readyTxt);
// Update score display
updateScoreDisplay();
}
function clearGameObjects() {
// Clear traffic cars
for (var i = trafficCars.length - 1; i >= 0; i--) {
trafficCars[i].destroy();
trafficCars.splice(i, 1);
}
// Clear obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Clear collectibles
for (var i = collectibles.length - 1; i >= 0; i--) {
collectibles[i].destroy();
collectibles.splice(i, 1);
}
// Clear road lines
for (var i = roadLines.length - 1; i >= 0; i--) {
roadLines[i].destroy();
roadLines.splice(i, 1);
}
// Clear houses
for (var i = houses.length - 1; i >= 0; i--) {
houses[i].destroy();
houses.splice(i, 1);
}
// Clear grass tiles
for (var i = grassTiles.length - 1; i >= 0; i--) {
grassTiles[i].destroy();
grassTiles.splice(i, 1);
}
}
function spawnTrafficCar() {
var trafficCar = new TrafficCar();
trafficCar.x = LANE_POSITIONS[trafficCar.lane];
trafficCar.y = -200;
trafficCars.push(trafficCar);
game.addChild(trafficCar);
}
function spawnObstacle() {
var obstacle = new Obstacle();
obstacle.x = LANE_POSITIONS[obstacle.lane];
obstacle.y = -200;
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnCollectible() {
var type = Math.random() < 0.2 ? 'speedBoost' : 'coin';
var collectible = new Collectible(type);
collectible.x = LANE_POSITIONS[collectible.lane];
collectible.y = -200;
collectibles.push(collectible);
game.addChild(collectible);
}
function spawnRoadLine() {
var leftLine = new RoadLine();
leftLine.x = GAME_WIDTH / 2 - LANE_WIDTH / 2 - 125;
leftLine.y = -100;
roadLines.push(leftLine);
game.addChild(leftLine);
var rightLine = new RoadLine();
rightLine.x = GAME_WIDTH / 2 + LANE_WIDTH / 2 + 125;
rightLine.y = -100;
roadLines.push(rightLine);
game.addChild(rightLine);
}
function spawnHouse() {
// Create multiple houses at once for a more populated city scenery
var houseCount = 1 + Math.floor(Math.random() * 3); // Create 1-3 houses at once
for (var i = 0; i < houseCount; i++) {
var house = new House();
// Position houses far to the left or right of the road
if (house.lane === -1) {
// Far left with some variation in x position
house.x = GAME_WIDTH / 2 - LANE_WIDTH * (2.5 + Math.random() * 1.5);
} else {
// Far right with some variation in x position
house.x = GAME_WIDTH / 2 + LANE_WIDTH * (2.5 + Math.random() * 1.5);
}
// Vary the y position slightly to avoid houses appearing in straight lines
house.y = -300 - i * 150 - Math.random() * 100;
houses.push(house);
// Add houses beneath the road to create a layered effect
game.addChildAt(house, game.getChildIndex(roadBackground));
}
}
function spawnBackgroundHouses() {
// Create distant background houses for additional depth
var house = new House();
// Make the house smaller and more faded to give appearance of distance
house.scale.set(0.7);
house.alpha = 0.7;
// Position houses very far to the left or right
if (Math.random() > 0.5) {
// Far left background
house.x = GAME_WIDTH / 2 - LANE_WIDTH * (4 + Math.random() * 2);
} else {
// Far right background
house.x = GAME_WIDTH / 2 + LANE_WIDTH * (4 + Math.random() * 2);
}
house.y = -300;
houses.push(house);
// Add background houses at the very bottom layer for proper depth perception
game.addChildAt(house, 0);
}
function spawnGrassTiles() {
// Create grass tiles for the whole width of the game
var tilesPerRow = Math.ceil(GAME_WIDTH / 100); // 100 is the width of a tile
for (var i = 0; i < tilesPerRow; i++) {
var grassTile = new GrassTile();
grassTile.x = i * 100;
grassTile.y = -100; // Start above the screen
grassTiles.push(grassTile);
// Add grass tiles below everything for proper layering
game.addChildAt(grassTile, 0);
}
}
function updateScoreDisplay() {
scoreTxt.setText('SCORE: ' + Math.floor(score));
scoreTxt.x = -scoreTxt.width - 20;
// Update speed display
speedTxt.setText('SPEED: ' + Math.floor(roadSpeed));
speedTxt.x = -speedTxt.width - 20;
// Color the speed text based on speed value - faster = more red
var r = Math.min(255, Math.floor(roadSpeed * 10));
var g = 255 - Math.min(255, Math.floor(roadSpeed * 5));
var b = 255;
var speedColor = r << 16 | g << 8 | b;
// Update high score if needed
if (score > storage.highScore) {
storage.highScore = Math.floor(score);
// Save high score to storage
storage.highScore = storage.highScore;
highScoreTxt.setText('HIGH SCORE: ' + storage.highScore);
highScoreTxt.x = -highScoreTxt.width - 20;
}
}
function checkCollisions() {
// Check collision with traffic cars
for (var i = trafficCars.length - 1; i >= 0; i--) {
if (playerCar.intersects(trafficCars[i]) && !playerCar.isInvincible) {
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
}
// Check collision with obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
if (playerCar.intersects(obstacles[i]) && !playerCar.isInvincible) {
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
}
// Check collision with collectibles
for (var i = collectibles.length - 1; i >= 0; i--) {
if (playerCar.intersects(collectibles[i])) {
if (collectibles[i].type === 'coin') {
score += 10;
LK.getSound('coinCollect').play();
} else if (collectibles[i].type === 'speedBoost') {
playerCar.setInvincible(3000);
roadSpeed += 5;
LK.setTimeout(function () {
roadSpeed -= 5;
}, 3000);
LK.getSound('powerup').play();
}
collectibles[i].destroy();
collectibles.splice(i, 1);
updateScoreDisplay();
}
}
}
game.update = function () {
if (!isGameStarted) {
return;
}
// Increase distance and score
distance += roadSpeed / 10;
score += 0.1 * difficulty;
// Update difficulty based on distance and time passed - increase more aggressively
difficulty = 1 + Math.floor(distance / 800) * 0.3 + LK.ticks / 2400;
// Update road speed - increase speed more dramatically over time
roadSpeed = 10 + Math.floor(distance / 400) + LK.ticks / 450;
// Spawn game objects
// Spawn traffic cars much more frequently as time progresses
var trafficSpawnRate = Math.max(40, 120 - difficulty * 15 - LK.ticks / 800);
if (LK.ticks - lastTrafficSpawn > trafficSpawnRate) {
spawnTrafficCar();
lastTrafficSpawn = LK.ticks;
}
if (LK.ticks - lastObstacleSpawn > Math.max(150, 300 - difficulty * 20)) {
spawnObstacle();
lastObstacleSpawn = LK.ticks;
}
if (LK.ticks - lastCollectibleSpawn > 90) {
spawnCollectible();
lastCollectibleSpawn = LK.ticks;
}
if (LK.ticks - lastRoadLineSpawn > 30) {
spawnRoadLine();
lastRoadLineSpawn = LK.ticks;
}
// Spawn houses more frequently to create a dense city environment
if (LK.ticks - lastHouseSpawn > 100) {
// Reduced from 180 to 100
spawnHouse();
lastHouseSpawn = LK.ticks;
}
// Spawn grass tiles for 8-bit grass background
if (LK.ticks - lastGrassTileSpawn > 40) {
spawnGrassTiles();
lastGrassTileSpawn = LK.ticks;
}
// Spawn additional background houses for depth
if (LK.ticks - lastBackgroundHouseSpawn > 150) {
spawnBackgroundHouses();
lastBackgroundHouseSpawn = LK.ticks;
}
// Update road lines
for (var i = roadLines.length - 1; i >= 0; i--) {
roadLines[i].speed = roadSpeed;
roadLines[i].update();
if (roadLines[i].y > GAME_HEIGHT + 100) {
roadLines[i].destroy();
roadLines.splice(i, 1);
}
}
// Update traffic cars - speed increases with game time
for (var i = trafficCars.length - 1; i >= 0; i--) {
// Speed factor increases with time more aggressively
var timeFactor = 0.7 + LK.ticks / 15000;
// Add variety to traffic car speeds based on game time
var speedVariation = Math.random() * 0.3 * (1 + LK.ticks / 30000);
trafficCars[i].speed = roadSpeed * Math.min(timeFactor, 1.8) * (1 - speedVariation);
trafficCars[i].update();
if (trafficCars[i].y > GAME_HEIGHT + 200) {
trafficCars[i].destroy();
trafficCars.splice(i, 1);
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].speed = roadSpeed * 0.6;
obstacles[i].update();
if (obstacles[i].y > GAME_HEIGHT + 100) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
}
// Update collectibles
for (var i = collectibles.length - 1; i >= 0; i--) {
collectibles[i].speed = roadSpeed * 0.8;
collectibles[i].update();
if (collectibles[i].y > GAME_HEIGHT + 100) {
collectibles[i].destroy();
collectibles.splice(i, 1);
}
}
// Update houses
for (var i = houses.length - 1; i >= 0; i--) {
houses[i].speed = roadSpeed * 0.4; // Slower movement for parallax effect
houses[i].update();
if (houses[i].y > GAME_HEIGHT + 300) {
houses[i].destroy();
houses.splice(i, 1);
}
}
// Update grass tiles
for (var i = grassTiles.length - 1; i >= 0; i--) {
grassTiles[i].speed = roadSpeed * 0.2; // Very slow movement for distant background effect
grassTiles[i].update();
if (grassTiles[i].y > GAME_HEIGHT + 100) {
grassTiles[i].destroy();
grassTiles.splice(i, 1);
}
}
// Check collisions
checkCollisions();
// Decrease invincible time
if (playerCar.isInvincible && playerCar.invincibleTime > 0) {
playerCar.invincibleTime -= 16.67; // ~60fps
}
// Update score display every second
if (LK.ticks % 60 === 0) {
updateScoreDisplay();
}
};