/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highscore: 0
});
/****
* Classes
****/
// Player Car
var Car = Container.expand(function () {
var self = Container.call(this);
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = carAsset.width;
self.height = carAsset.height;
// For collision box
// Removed custom getBounds to avoid recursion bug
return self;
});
// Coin
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinAsset = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coinAsset.width;
self.height = coinAsset.height;
self.speed = 10; // Will be increased by game
self.update = function () {
self.y += self.speed;
};
// Removed custom getBounds to avoid recursion bug
return self;
});
// Obstacle
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
// Assign a random color tint to the obstacle asset
var obstacleColors = [0xff4444,
// red
0x44aaff,
// blue
0x44ff44,
// green
0xffcc44,
// yellow
0xff44cc,
// pink
0xffffff,
// white
0xff8800,
// orange
0x888888 // gray
];
var colorIdx = Math.floor(Math.random() * obstacleColors.length);
obsAsset.tint = obstacleColors[colorIdx];
self.width = obsAsset.width;
self.height = obsAsset.height;
self.speed = 10; // Will be increased by game
self.update = function () {
// Store lastX for event triggers if needed
if (self.lastX === undefined) self.lastX = self.x;
// Move down
self.y += self.speed;
// Smoothly tween X position for swerving
if (!self._swerveTween || self._swerveTween.finished) {
// Pick a new target X within lane bounds
var laneIdx = Math.floor(self.x / (2048 / 3));
var laneLeft = laneIdx * (2048 / 3);
var laneRight = (laneIdx + 1) * (2048 / 3);
var minX = laneLeft + self.width / 2;
var maxX = laneRight - self.width / 2;
var swerveAmount = 120 + Math.random() * 120; // up to 240px swerve
var direction = Math.random() < 0.5 ? -1 : 1;
var targetX = self.x + direction * swerveAmount;
targetX = Math.max(minX, Math.min(maxX, targetX));
// Tween to new X over 0.3-0.6s
var duration = 300 + Math.random() * 300;
self._swerveTween = {
finished: false
};
tween(self, {
x: targetX
}, {
duration: duration,
easing: tween.easeInOut,
onFinish: function onFinish() {
self._swerveTween.finished = true;
}
});
}
self.lastX = self.x;
};
// Removed custom getBounds to avoid recursion bug
return self;
});
// RoadLine: vertical dashed line for road center/lanes
var RoadLine = Container.expand(function () {
var self = Container.call(this);
// Parameters for dashed line
var dashHeight = 120;
var gap = 80;
var lineWidth = 24;
var color = 0xffffff;
var y = 0;
// Fill vertical line with dashes
while (y < 2732 + dashHeight) {
var dash = LK.getAsset('roadline_dash', {
width: lineWidth,
height: dashHeight,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0
});
dash.x = 0;
dash.y = y;
self.addChild(dash);
y += dashHeight + gap;
}
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game area
// Car (player)
// Obstacle
// Coin
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
// Lane system (3 lanes)
var LANE_COUNT = 3;
var LANE_WIDTH = GAME_WIDTH / LANE_COUNT;
var LANE_X = [Math.floor(LANE_WIDTH / 2), Math.floor(GAME_WIDTH / 2), Math.floor(GAME_WIDTH - LANE_WIDTH / 2)];
// Draw road lines (between lanes)
var roadLines = [];
for (var i = 1; i < LANE_COUNT; i++) {
var line = new RoadLine();
line.x = i * LANE_WIDTH;
line.y = 0;
game.addChild(line);
roadLines.push(line);
}
// Add roadside bars (left and right)
var barWidth = 60;
var barColor = 0x888888;
var barHeight = GAME_HEIGHT + 200; // a bit longer for scrolling
var leftBar = LK.getAsset('roadline_dash', {
width: barWidth,
height: barHeight,
color: barColor,
shape: 'box',
anchorX: 0.5,
anchorY: 0
});
leftBar.x = barWidth / 2;
leftBar.y = -100;
game.addChild(leftBar);
var rightBar = LK.getAsset('roadline_dash', {
width: barWidth,
height: barHeight,
color: barColor,
shape: 'box',
anchorX: 0.5,
anchorY: 0
});
rightBar.x = GAME_WIDTH - barWidth / 2;
rightBar.y = -100;
game.addChild(rightBar);
// For rolling stripes
var roadLineSpeed = 14; // Stripes move faster than obstacles
// Player car
var car = new Car();
game.addChild(car);
car.x = GAME_WIDTH / 2;
car.y = GAME_HEIGHT - 400;
// Dragging
var dragCar = false;
var dragOffsetX = 0;
// Mouse control toggle
var mouseEnabled = true;
function toggleMouse() {
mouseEnabled = !mouseEnabled;
}
// Obstacles and coins
var obstacles = [];
var coins = [];
// Difficulty
var baseSpeed = 10; // Slower starting speed
var speed = baseSpeed;
var speedIncreaseInterval = 600; // every 10 seconds (60*10)
var minObstacleInterval = 36; // minimum ticks between obstacles
var obstacleInterval = 60; // ticks between obstacles, will decrease
var minCoinInterval = 30;
var coinInterval = 60;
// Score
var score = 0;
var highscore = storage.highscore || 0;
// Game state: wait for click to start
var gameStarted = false;
// Lives for extra life mechanic
var lives = 0; // Start with zero life
// Heart asset for lives display (show as hearts in lower left)
var heartAssets = [];
var maxHearts = 10; // reasonable max for display
for (var h = 0; h < maxHearts; h++) {
var heart = LK.getAsset('coin', {
// Use coin as heart for now
anchorX: 0.5,
anchorY: 0.5
});
heart.width = 80;
heart.height = 80;
// Place hearts in lower right, spaced leftward, with 60px margin from right
heart.x = LK.gui.bottom.width - 60 - h * 90;
heart.y = LK.gui.bottom.height - 80;
heart.visible = false;
LK.gui.bottom.addChild(heart);
heartAssets.push(heart);
}
// Helper to update hearts display
function updateHearts() {
for (var i = 0; i < heartAssets.length; i++) {
heartAssets[i].visible = i < lives;
}
}
updateHearts();
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var highscoreTxt = new Text2('High Score: ' + highscore, {
size: 60,
fill: 0xFFFF88
});
highscoreTxt.anchor.set(0.5, 0);
highscoreTxt.x = LK.gui.top.width / 2;
highscoreTxt.y = 150;
LK.gui.top.addChild(highscoreTxt);
// Start text overlay
var startTxt = new Text2('Tap to Start', {
size: 120,
fill: "#fff"
});
startTxt.anchor.set(0.5, 0.5);
startTxt.x = LK.gui.top.width / 2;
startTxt.y = 400;
LK.gui.top.addChild(startTxt);
// Helper: collision
function rectsIntersect(a, b) {
return !(a.x + a.width < b.x || a.x > b.x + b.width || a.y + a.height < b.y || a.y > b.y + b.height);
}
// Touch controls
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
game.down = function (x, y, obj) {
if (!gameStarted) {
// Start game on first tap
gameStarted = true;
if (startTxt && startTxt.parent) {
startTxt.parent.removeChild(startTxt);
}
return;
}
// Only start drag if touch is on car and mouse is enabled
if (!mouseEnabled) return;
var local = car.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x >= -car.width / 2 && local.x <= car.width / 2 && local.y >= -car.height / 2 && local.y <= car.height / 2) {
dragCar = true;
dragOffsetX = car.x - x;
}
};
game.move = function (x, y, obj) {
if (!gameStarted) return;
if (!mouseEnabled) return;
if (dragCar) {
// Only move horizontally, clamp to game area
var newX = clamp(x + dragOffsetX, car.width / 2, GAME_WIDTH - car.width / 2);
car.x = newX;
}
};
game.up = function (x, y, obj) {
if (!gameStarted) return;
if (!mouseEnabled) return;
dragCar = false;
};
// Spawning
var lastObstacleTick = 0;
var lastCoinTick = 0;
// Main update loop
game.update = function () {
if (!gameStarted) return;
// Increase speed and difficulty over time
if (LK.ticks % speedIncreaseInterval === 0 && LK.ticks > 0) {
speed += 1; // Slower speed increase
obstacleInterval = Math.max(minObstacleInterval, obstacleInterval - 4);
coinInterval = Math.max(minCoinInterval, coinInterval - 2);
}
// Spawn obstacles
if (LK.ticks - lastObstacleTick >= obstacleInterval) {
// Randomly choose which obstacle type to spawn
var obsType = Math.random() < 0.5 ? 'obstacle' : 'obstacle2';
var obs;
if (obsType === 'obstacle') {
obs = new Obstacle();
} else {
// Use obstacle2 asset for variety
obs = new Obstacle();
var obsAsset = obs.children[0];
obsAsset.texture = LK.getAsset('obstacle2', {
anchorX: 0.5,
anchorY: 0.5
}).texture;
obs.width = obsAsset.width;
obs.height = obsAsset.height;
}
obs.speed = speed;
// Random lane
var lane = Math.floor(Math.random() * LANE_COUNT);
// Allow obstacle to be anywhere inside the lane, not just center
var laneLeft = lane * LANE_WIDTH;
var laneRight = (lane + 1) * LANE_WIDTH;
var minX = laneLeft + obs.width / 2;
var maxX = laneRight - obs.width / 2;
obs.x = minX + Math.random() * (maxX - minX);
obs.y = -obs.height / 2;
obstacles.push(obs);
game.addChild(obs);
lastObstacleTick = LK.ticks;
}
// Spawn coins
if (LK.ticks - lastCoinTick >= coinInterval) {
var coin = new Coin();
coin.speed = speed;
// Random lane, but not same as last obstacle
var lane = Math.floor(Math.random() * LANE_COUNT);
coin.x = LANE_X[lane];
coin.y = -coin.height / 2 - 200;
coins.push(coin);
game.addChild(coin);
lastCoinTick = LK.ticks;
}
// Move road lines down and wrap
for (var rl = 0; rl < roadLines.length; rl++) {
var line = roadLines[rl];
line.y += roadLineSpeed;
// The height of the dashed line pattern is dash+gap repeated, so wrap when y > dash+gap
// We know from RoadLine: dashHeight=120, gap=80, so pattern=200
var patternHeight = 120 + 80;
if (line.y >= patternHeight) {
line.y -= patternHeight;
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.y - obs.height / 2 > GAME_HEIGHT + 100) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with car
if (rectsIntersect(obs.getBounds(), car.getBounds())) {
// Flash
LK.effects.flashScreen(0xff0000, 800);
if (lives > 0) {
lives -= 1;
updateHearts();
// Remove the obstacle and continue
obs.destroy();
obstacles.splice(i, 1);
continue;
} else {
LK.showGameOver();
return;
}
}
}
// Update coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
coin.update();
// Remove if off screen
if (coin.y - coin.height / 2 > GAME_HEIGHT + 100) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Collect coin
if (rectsIntersect(coin.getBounds(), car.getBounds())) {
score += 1;
LK.setScore(score);
scoreTxt.setText(score);
// Grant extra life every 50 points
if (score > 0 && score % 50 === 0) {
lives += 1;
updateHearts();
}
// Update highscore if beaten
if (score > highscore) {
highscore = score;
storage.highscore = highscore;
highscoreTxt.setText('High Score: ' + highscore);
}
// Speed up game by 10% every 10 points
if (score % 10 === 0 && score > 0) {
speed = Math.round(speed * 1.1);
// Also update all current obstacles and coins to new speed
for (var oi = 0; oi < obstacles.length; oi++) {
obstacles[oi].speed = speed;
}
for (var ci = 0; ci < coins.length; ci++) {
coins[ci].speed = speed;
}
// Also speed up road lines by 10%
roadLineSpeed = Math.round(roadLineSpeed * 1.1);
}
// Animate coin
tween(coin, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(j, 1);
}
}
};
// Center score text at top, avoid top left 100x100
scoreTxt.x = LK.gui.top.width / 2;
scoreTxt.y = 20; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highscore: 0
});
/****
* Classes
****/
// Player Car
var Car = Container.expand(function () {
var self = Container.call(this);
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = carAsset.width;
self.height = carAsset.height;
// For collision box
// Removed custom getBounds to avoid recursion bug
return self;
});
// Coin
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinAsset = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coinAsset.width;
self.height = coinAsset.height;
self.speed = 10; // Will be increased by game
self.update = function () {
self.y += self.speed;
};
// Removed custom getBounds to avoid recursion bug
return self;
});
// Obstacle
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
// Assign a random color tint to the obstacle asset
var obstacleColors = [0xff4444,
// red
0x44aaff,
// blue
0x44ff44,
// green
0xffcc44,
// yellow
0xff44cc,
// pink
0xffffff,
// white
0xff8800,
// orange
0x888888 // gray
];
var colorIdx = Math.floor(Math.random() * obstacleColors.length);
obsAsset.tint = obstacleColors[colorIdx];
self.width = obsAsset.width;
self.height = obsAsset.height;
self.speed = 10; // Will be increased by game
self.update = function () {
// Store lastX for event triggers if needed
if (self.lastX === undefined) self.lastX = self.x;
// Move down
self.y += self.speed;
// Smoothly tween X position for swerving
if (!self._swerveTween || self._swerveTween.finished) {
// Pick a new target X within lane bounds
var laneIdx = Math.floor(self.x / (2048 / 3));
var laneLeft = laneIdx * (2048 / 3);
var laneRight = (laneIdx + 1) * (2048 / 3);
var minX = laneLeft + self.width / 2;
var maxX = laneRight - self.width / 2;
var swerveAmount = 120 + Math.random() * 120; // up to 240px swerve
var direction = Math.random() < 0.5 ? -1 : 1;
var targetX = self.x + direction * swerveAmount;
targetX = Math.max(minX, Math.min(maxX, targetX));
// Tween to new X over 0.3-0.6s
var duration = 300 + Math.random() * 300;
self._swerveTween = {
finished: false
};
tween(self, {
x: targetX
}, {
duration: duration,
easing: tween.easeInOut,
onFinish: function onFinish() {
self._swerveTween.finished = true;
}
});
}
self.lastX = self.x;
};
// Removed custom getBounds to avoid recursion bug
return self;
});
// RoadLine: vertical dashed line for road center/lanes
var RoadLine = Container.expand(function () {
var self = Container.call(this);
// Parameters for dashed line
var dashHeight = 120;
var gap = 80;
var lineWidth = 24;
var color = 0xffffff;
var y = 0;
// Fill vertical line with dashes
while (y < 2732 + dashHeight) {
var dash = LK.getAsset('roadline_dash', {
width: lineWidth,
height: dashHeight,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0
});
dash.x = 0;
dash.y = y;
self.addChild(dash);
y += dashHeight + gap;
}
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game area
// Car (player)
// Obstacle
// Coin
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
// Lane system (3 lanes)
var LANE_COUNT = 3;
var LANE_WIDTH = GAME_WIDTH / LANE_COUNT;
var LANE_X = [Math.floor(LANE_WIDTH / 2), Math.floor(GAME_WIDTH / 2), Math.floor(GAME_WIDTH - LANE_WIDTH / 2)];
// Draw road lines (between lanes)
var roadLines = [];
for (var i = 1; i < LANE_COUNT; i++) {
var line = new RoadLine();
line.x = i * LANE_WIDTH;
line.y = 0;
game.addChild(line);
roadLines.push(line);
}
// Add roadside bars (left and right)
var barWidth = 60;
var barColor = 0x888888;
var barHeight = GAME_HEIGHT + 200; // a bit longer for scrolling
var leftBar = LK.getAsset('roadline_dash', {
width: barWidth,
height: barHeight,
color: barColor,
shape: 'box',
anchorX: 0.5,
anchorY: 0
});
leftBar.x = barWidth / 2;
leftBar.y = -100;
game.addChild(leftBar);
var rightBar = LK.getAsset('roadline_dash', {
width: barWidth,
height: barHeight,
color: barColor,
shape: 'box',
anchorX: 0.5,
anchorY: 0
});
rightBar.x = GAME_WIDTH - barWidth / 2;
rightBar.y = -100;
game.addChild(rightBar);
// For rolling stripes
var roadLineSpeed = 14; // Stripes move faster than obstacles
// Player car
var car = new Car();
game.addChild(car);
car.x = GAME_WIDTH / 2;
car.y = GAME_HEIGHT - 400;
// Dragging
var dragCar = false;
var dragOffsetX = 0;
// Mouse control toggle
var mouseEnabled = true;
function toggleMouse() {
mouseEnabled = !mouseEnabled;
}
// Obstacles and coins
var obstacles = [];
var coins = [];
// Difficulty
var baseSpeed = 10; // Slower starting speed
var speed = baseSpeed;
var speedIncreaseInterval = 600; // every 10 seconds (60*10)
var minObstacleInterval = 36; // minimum ticks between obstacles
var obstacleInterval = 60; // ticks between obstacles, will decrease
var minCoinInterval = 30;
var coinInterval = 60;
// Score
var score = 0;
var highscore = storage.highscore || 0;
// Game state: wait for click to start
var gameStarted = false;
// Lives for extra life mechanic
var lives = 0; // Start with zero life
// Heart asset for lives display (show as hearts in lower left)
var heartAssets = [];
var maxHearts = 10; // reasonable max for display
for (var h = 0; h < maxHearts; h++) {
var heart = LK.getAsset('coin', {
// Use coin as heart for now
anchorX: 0.5,
anchorY: 0.5
});
heart.width = 80;
heart.height = 80;
// Place hearts in lower right, spaced leftward, with 60px margin from right
heart.x = LK.gui.bottom.width - 60 - h * 90;
heart.y = LK.gui.bottom.height - 80;
heart.visible = false;
LK.gui.bottom.addChild(heart);
heartAssets.push(heart);
}
// Helper to update hearts display
function updateHearts() {
for (var i = 0; i < heartAssets.length; i++) {
heartAssets[i].visible = i < lives;
}
}
updateHearts();
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var highscoreTxt = new Text2('High Score: ' + highscore, {
size: 60,
fill: 0xFFFF88
});
highscoreTxt.anchor.set(0.5, 0);
highscoreTxt.x = LK.gui.top.width / 2;
highscoreTxt.y = 150;
LK.gui.top.addChild(highscoreTxt);
// Start text overlay
var startTxt = new Text2('Tap to Start', {
size: 120,
fill: "#fff"
});
startTxt.anchor.set(0.5, 0.5);
startTxt.x = LK.gui.top.width / 2;
startTxt.y = 400;
LK.gui.top.addChild(startTxt);
// Helper: collision
function rectsIntersect(a, b) {
return !(a.x + a.width < b.x || a.x > b.x + b.width || a.y + a.height < b.y || a.y > b.y + b.height);
}
// Touch controls
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
game.down = function (x, y, obj) {
if (!gameStarted) {
// Start game on first tap
gameStarted = true;
if (startTxt && startTxt.parent) {
startTxt.parent.removeChild(startTxt);
}
return;
}
// Only start drag if touch is on car and mouse is enabled
if (!mouseEnabled) return;
var local = car.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x >= -car.width / 2 && local.x <= car.width / 2 && local.y >= -car.height / 2 && local.y <= car.height / 2) {
dragCar = true;
dragOffsetX = car.x - x;
}
};
game.move = function (x, y, obj) {
if (!gameStarted) return;
if (!mouseEnabled) return;
if (dragCar) {
// Only move horizontally, clamp to game area
var newX = clamp(x + dragOffsetX, car.width / 2, GAME_WIDTH - car.width / 2);
car.x = newX;
}
};
game.up = function (x, y, obj) {
if (!gameStarted) return;
if (!mouseEnabled) return;
dragCar = false;
};
// Spawning
var lastObstacleTick = 0;
var lastCoinTick = 0;
// Main update loop
game.update = function () {
if (!gameStarted) return;
// Increase speed and difficulty over time
if (LK.ticks % speedIncreaseInterval === 0 && LK.ticks > 0) {
speed += 1; // Slower speed increase
obstacleInterval = Math.max(minObstacleInterval, obstacleInterval - 4);
coinInterval = Math.max(minCoinInterval, coinInterval - 2);
}
// Spawn obstacles
if (LK.ticks - lastObstacleTick >= obstacleInterval) {
// Randomly choose which obstacle type to spawn
var obsType = Math.random() < 0.5 ? 'obstacle' : 'obstacle2';
var obs;
if (obsType === 'obstacle') {
obs = new Obstacle();
} else {
// Use obstacle2 asset for variety
obs = new Obstacle();
var obsAsset = obs.children[0];
obsAsset.texture = LK.getAsset('obstacle2', {
anchorX: 0.5,
anchorY: 0.5
}).texture;
obs.width = obsAsset.width;
obs.height = obsAsset.height;
}
obs.speed = speed;
// Random lane
var lane = Math.floor(Math.random() * LANE_COUNT);
// Allow obstacle to be anywhere inside the lane, not just center
var laneLeft = lane * LANE_WIDTH;
var laneRight = (lane + 1) * LANE_WIDTH;
var minX = laneLeft + obs.width / 2;
var maxX = laneRight - obs.width / 2;
obs.x = minX + Math.random() * (maxX - minX);
obs.y = -obs.height / 2;
obstacles.push(obs);
game.addChild(obs);
lastObstacleTick = LK.ticks;
}
// Spawn coins
if (LK.ticks - lastCoinTick >= coinInterval) {
var coin = new Coin();
coin.speed = speed;
// Random lane, but not same as last obstacle
var lane = Math.floor(Math.random() * LANE_COUNT);
coin.x = LANE_X[lane];
coin.y = -coin.height / 2 - 200;
coins.push(coin);
game.addChild(coin);
lastCoinTick = LK.ticks;
}
// Move road lines down and wrap
for (var rl = 0; rl < roadLines.length; rl++) {
var line = roadLines[rl];
line.y += roadLineSpeed;
// The height of the dashed line pattern is dash+gap repeated, so wrap when y > dash+gap
// We know from RoadLine: dashHeight=120, gap=80, so pattern=200
var patternHeight = 120 + 80;
if (line.y >= patternHeight) {
line.y -= patternHeight;
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.y - obs.height / 2 > GAME_HEIGHT + 100) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with car
if (rectsIntersect(obs.getBounds(), car.getBounds())) {
// Flash
LK.effects.flashScreen(0xff0000, 800);
if (lives > 0) {
lives -= 1;
updateHearts();
// Remove the obstacle and continue
obs.destroy();
obstacles.splice(i, 1);
continue;
} else {
LK.showGameOver();
return;
}
}
}
// Update coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
coin.update();
// Remove if off screen
if (coin.y - coin.height / 2 > GAME_HEIGHT + 100) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Collect coin
if (rectsIntersect(coin.getBounds(), car.getBounds())) {
score += 1;
LK.setScore(score);
scoreTxt.setText(score);
// Grant extra life every 50 points
if (score > 0 && score % 50 === 0) {
lives += 1;
updateHearts();
}
// Update highscore if beaten
if (score > highscore) {
highscore = score;
storage.highscore = highscore;
highscoreTxt.setText('High Score: ' + highscore);
}
// Speed up game by 10% every 10 points
if (score % 10 === 0 && score > 0) {
speed = Math.round(speed * 1.1);
// Also update all current obstacles and coins to new speed
for (var oi = 0; oi < obstacles.length; oi++) {
obstacles[oi].speed = speed;
}
for (var ci = 0; ci < coins.length; ci++) {
coins[ci].speed = speed;
}
// Also speed up road lines by 10%
roadLineSpeed = Math.round(roadLineSpeed * 1.1);
}
// Animate coin
tween(coin, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(j, 1);
}
}
};
// Center score text at top, avoid top left 100x100
scoreTxt.x = LK.gui.top.width / 2;
scoreTxt.y = 20;