/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Car class: player car, can move left/right between 4 lanes
var Car = Container.expand(function () {
var self = Container.call(this);
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.lane = 1; // 0,1,2,3 (4 lanes)
self.width = carSprite.width;
self.height = carSprite.height;
self.updatePosition = function () {
// Lanes are centered in the road
var laneWidth = 1200 / 4;
self.x = 2048 / 2 - 600 + laneWidth / 2 + self.lane * laneWidth;
self.y = 2300;
// Set car to look right if on left half, left if on right half
var centerX = 2048 / 2;
if (self.x < centerX) {
carSprite.rotation = 0;
carSprite.scaleX = 1; // look right
} else if (self.x > centerX) {
carSprite.rotation = 0;
carSprite.scaleX = -1; // look left
} else {
carSprite.rotation = 0;
carSprite.scaleX = 1; // look right by default
}
};
self.updatePosition();
return self;
});
// DustCloud class: animates a dust cloud behind the car
var DustCloud = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('Duman', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.7
});
self.vx = (Math.random() - 0.5) * 8; // random horizontal drift
self.vy = 12 + Math.random() * 6; // downward speed
self.life = 0;
self.maxLife = 32 + Math.random() * 16;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.life++;
// Fade out as it ages
sprite.alpha = 0.7 * (1 - self.life / self.maxLife);
// Scale up as it ages
sprite.scaleX = sprite.scaleY = 1.2 + 0.8 * (self.life / self.maxLife);
};
return self;
});
// Obstacle class: obstacles that move down the road
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsSprite = self.attachAsset('offtrack', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.75,
scaleY: 0.75
});
self.lane = 0;
self.width = obsSprite.width;
self.height = obsSprite.height;
self.speed = OBSTACLE_SPEED;
self.updatePosition = function () {
var laneWidth = 1200 / 4;
self.x = 2048 / 2 - 600 + laneWidth / 2 + self.lane * laneWidth;
};
self.update = function () {
self.y += self.speed;
};
return self;
});
// Road class: draws the main road, border lines, and dotted lines
var Road = Container.expand(function () {
var self = Container.call(this);
// Red outer lines (left and right)
var outerLineWidth = 30;
var outerLineHeight = 2732;
// Left outer red line
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 - 600 - borderLineWidth - borderLineGap - outerLineWidth / 2,
y: 0,
width: outerLineWidth,
height: outerLineHeight,
color: 0xff0000
});
// Right outer red line
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 + 600 + borderLineWidth + borderLineGap + outerLineWidth / 2,
y: 0,
width: outerLineWidth,
height: outerLineHeight,
color: 0xff0000
});
// Red and white border lines (left and right)
var borderLineWidth = 40;
var borderLineHeight = 2732;
var borderLineGap = 40;
var borderStripeHeight = 80;
var borderStripeGap = 40;
var numStripes = Math.ceil(borderLineHeight / (borderStripeHeight + borderStripeGap));
// Left border (alternating red/white stripes)
for (var i = 0; i < numStripes; i++) {
var color = i % 2 === 0 ? 0xff0000 : 0xffffff;
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 - 600 - borderLineWidth / 2 - borderLineGap,
y: i * (borderStripeHeight + borderStripeGap),
width: borderLineWidth,
height: borderStripeHeight,
color: color
});
}
// Right border (alternating red/white stripes)
for (var i = 0; i < numStripes; i++) {
var color = i % 2 === 0 ? 0xff0000 : 0xffffff;
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 + 600 + borderLineWidth / 2 + borderLineGap,
y: i * (borderStripeHeight + borderStripeGap),
width: borderLineWidth,
height: borderStripeHeight,
color: color
});
}
// Main road
var roadSprite = self.attachAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
width: 1200,
height: 2732
});
// Dotted lane lines (3 vertical, splitting into 4 lanes) - now animated
var dotHeight = 120;
var dotGap = 120;
self.dots = [];
// Center lane dots only (no outer dotted lines)
for (var i = 1; i < 4; i++) {
var lineX = 2048 / 2 - 600 + i * 1200 / 4;
for (var y = 0; y < 2732; y += dotHeight + dotGap) {
var dot = self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: lineX,
y: y,
width: 20,
height: dotHeight,
color: 0xffffff,
alpha: 0.7
});
dot._laneLine = i;
dot._origY = y;
self.dots.push(dot);
}
}
self._dotAnimOffset = 0;
self.update = function () {
// Move dotted lines down, loop when off screen
self._dotAnimOffset += 18; // match obstacle speed for effect
var totalLen = dotHeight + dotGap;
for (var i = 0; i < self.dots.length; i++) {
var dot = self.dots[i];
var newY = (dot._origY + self._dotAnimOffset) % (2732 + totalLen);
if (newY > 2732) newY -= 2732 + totalLen;
dot.y = newY;
}
};
// Outer solid white lines (left and right edge of road)
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 - 600,
y: 0,
width: 20,
height: 2732,
color: 0xffffff,
alpha: 0.9
});
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 + 600,
y: 0,
width: 20,
height: 2732,
color: 0xffffff,
alpha: 0.9
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game constants
var NUM_LANES = 4;
var ROAD_WIDTH = 1200;
var LANE_WIDTH = ROAD_WIDTH / NUM_LANES;
var OBSTACLE_SPEED = 28;
var OBSTACLE_INTERVAL = 60; // frames
var car, road;
var obstacles = [];
var score = 0;
var gameOver = false;
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: update score text
function updateScoreText() {
scoreTxt.setText(score);
}
// Helper: reset game
function initGame() {
// Remove old objects
if (car) car.destroy();
if (road) road.destroy();
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
obstacles = [];
score = 0;
gameOver = false;
updateScoreText();
// Add road
road = new Road();
game.addChild(road);
// Add car
car = new Car();
car.lane = 1;
car.updatePosition();
game.addChild(car);
}
// Helper: spawn an obstacle in a random lane
function spawnObstacle() {
var obs = new Obstacle();
obs.lane = Math.floor(Math.random() * NUM_LANES);
obs.y = -100;
obs.updatePosition();
game.addChild(obs);
obstacles.push(obs);
}
// Touch controls: left/right
game.down = function (x, y, obj) {
if (gameOver) return;
// If left half of screen, move car left
if (x < 2048 / 2) {
if (car.lane > 0) {
car.lane--;
// Animate car movement to new lane
var laneWidth = 1200 / 4;
var targetX = 2048 / 2 - 600 + laneWidth / 2 + car.lane * laneWidth;
tween.stop(car, {
x: true
});
tween(car, {
x: targetX
}, {
duration: 220,
easing: tween.cubicOut
});
car.y = 2300; // keep y fixed
}
} else {
// Right half, move car right
if (car.lane < NUM_LANES - 1) {
car.lane++;
// Animate car movement to new lane
var laneWidth = 1200 / 4;
var targetX = 2048 / 2 - 600 + laneWidth / 2 + car.lane * laneWidth;
tween.stop(car, {
x: true
});
tween(car, {
x: targetX
}, {
duration: 220,
easing: tween.cubicOut
});
car.y = 2300; // keep y fixed
}
}
};
// No up event needed
game.up = function (x, y, obj) {};
// Main game update loop
var obstacleTimer = 0;
game.update = function () {
if (gameOver) return;
// Animate road dotted lines
if (road && typeof road.update === "function") {
road.update();
}
// --- Dust cloud animation behind car ---
if (!game._dustClouds) game._dustClouds = [];
// Only spawn if car exists and game is not over
if (car && !gameOver && LK.ticks % 2 === 0) {
var dust = new DustCloud();
// Place behind car, slightly randomized
dust.x = car.x + (Math.random() - 0.5) * 40;
dust.y = car.y + car.height * 0.45 + Math.random() * 10;
game.addChild(dust);
game._dustClouds.push(dust);
}
// Animate and remove old dust clouds
for (var i = game._dustClouds.length - 1; i >= 0; i--) {
var d = game._dustClouds[i];
if (typeof d.update === "function") d.update();
if (d.life >= d.maxLife) {
d.destroy();
game._dustClouds.splice(i, 1);
}
}
// Move obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.y > 2732 + 100) {
obs.destroy();
obstacles.splice(i, 1);
score++;
updateScoreText();
}
// Collision with car
if (obs.lane === car.lane && Math.abs(obs.y - car.y) < (obs.height + car.height) / 2 - 10) {
LK.effects.flashScreen(0xff0000, 600);
LK.showGameOver();
gameOver = true;
return;
}
}
// Spawn new obstacles
obstacleTimer++;
if (obstacleTimer >= OBSTACLE_INTERVAL) {
spawnObstacle();
obstacleTimer = 0;
}
};
/****
* Start game
****/
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Car class: player car, can move left/right between 4 lanes
var Car = Container.expand(function () {
var self = Container.call(this);
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.lane = 1; // 0,1,2,3 (4 lanes)
self.width = carSprite.width;
self.height = carSprite.height;
self.updatePosition = function () {
// Lanes are centered in the road
var laneWidth = 1200 / 4;
self.x = 2048 / 2 - 600 + laneWidth / 2 + self.lane * laneWidth;
self.y = 2300;
// Set car to look right if on left half, left if on right half
var centerX = 2048 / 2;
if (self.x < centerX) {
carSprite.rotation = 0;
carSprite.scaleX = 1; // look right
} else if (self.x > centerX) {
carSprite.rotation = 0;
carSprite.scaleX = -1; // look left
} else {
carSprite.rotation = 0;
carSprite.scaleX = 1; // look right by default
}
};
self.updatePosition();
return self;
});
// DustCloud class: animates a dust cloud behind the car
var DustCloud = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('Duman', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.7
});
self.vx = (Math.random() - 0.5) * 8; // random horizontal drift
self.vy = 12 + Math.random() * 6; // downward speed
self.life = 0;
self.maxLife = 32 + Math.random() * 16;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.life++;
// Fade out as it ages
sprite.alpha = 0.7 * (1 - self.life / self.maxLife);
// Scale up as it ages
sprite.scaleX = sprite.scaleY = 1.2 + 0.8 * (self.life / self.maxLife);
};
return self;
});
// Obstacle class: obstacles that move down the road
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsSprite = self.attachAsset('offtrack', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.75,
scaleY: 0.75
});
self.lane = 0;
self.width = obsSprite.width;
self.height = obsSprite.height;
self.speed = OBSTACLE_SPEED;
self.updatePosition = function () {
var laneWidth = 1200 / 4;
self.x = 2048 / 2 - 600 + laneWidth / 2 + self.lane * laneWidth;
};
self.update = function () {
self.y += self.speed;
};
return self;
});
// Road class: draws the main road, border lines, and dotted lines
var Road = Container.expand(function () {
var self = Container.call(this);
// Red outer lines (left and right)
var outerLineWidth = 30;
var outerLineHeight = 2732;
// Left outer red line
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 - 600 - borderLineWidth - borderLineGap - outerLineWidth / 2,
y: 0,
width: outerLineWidth,
height: outerLineHeight,
color: 0xff0000
});
// Right outer red line
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 + 600 + borderLineWidth + borderLineGap + outerLineWidth / 2,
y: 0,
width: outerLineWidth,
height: outerLineHeight,
color: 0xff0000
});
// Red and white border lines (left and right)
var borderLineWidth = 40;
var borderLineHeight = 2732;
var borderLineGap = 40;
var borderStripeHeight = 80;
var borderStripeGap = 40;
var numStripes = Math.ceil(borderLineHeight / (borderStripeHeight + borderStripeGap));
// Left border (alternating red/white stripes)
for (var i = 0; i < numStripes; i++) {
var color = i % 2 === 0 ? 0xff0000 : 0xffffff;
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 - 600 - borderLineWidth / 2 - borderLineGap,
y: i * (borderStripeHeight + borderStripeGap),
width: borderLineWidth,
height: borderStripeHeight,
color: color
});
}
// Right border (alternating red/white stripes)
for (var i = 0; i < numStripes; i++) {
var color = i % 2 === 0 ? 0xff0000 : 0xffffff;
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 + 600 + borderLineWidth / 2 + borderLineGap,
y: i * (borderStripeHeight + borderStripeGap),
width: borderLineWidth,
height: borderStripeHeight,
color: color
});
}
// Main road
var roadSprite = self.attachAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
width: 1200,
height: 2732
});
// Dotted lane lines (3 vertical, splitting into 4 lanes) - now animated
var dotHeight = 120;
var dotGap = 120;
self.dots = [];
// Center lane dots only (no outer dotted lines)
for (var i = 1; i < 4; i++) {
var lineX = 2048 / 2 - 600 + i * 1200 / 4;
for (var y = 0; y < 2732; y += dotHeight + dotGap) {
var dot = self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: lineX,
y: y,
width: 20,
height: dotHeight,
color: 0xffffff,
alpha: 0.7
});
dot._laneLine = i;
dot._origY = y;
self.dots.push(dot);
}
}
self._dotAnimOffset = 0;
self.update = function () {
// Move dotted lines down, loop when off screen
self._dotAnimOffset += 18; // match obstacle speed for effect
var totalLen = dotHeight + dotGap;
for (var i = 0; i < self.dots.length; i++) {
var dot = self.dots[i];
var newY = (dot._origY + self._dotAnimOffset) % (2732 + totalLen);
if (newY > 2732) newY -= 2732 + totalLen;
dot.y = newY;
}
};
// Outer solid white lines (left and right edge of road)
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 - 600,
y: 0,
width: 20,
height: 2732,
color: 0xffffff,
alpha: 0.9
});
self.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 + 600,
y: 0,
width: 20,
height: 2732,
color: 0xffffff,
alpha: 0.9
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game constants
var NUM_LANES = 4;
var ROAD_WIDTH = 1200;
var LANE_WIDTH = ROAD_WIDTH / NUM_LANES;
var OBSTACLE_SPEED = 28;
var OBSTACLE_INTERVAL = 60; // frames
var car, road;
var obstacles = [];
var score = 0;
var gameOver = false;
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: update score text
function updateScoreText() {
scoreTxt.setText(score);
}
// Helper: reset game
function initGame() {
// Remove old objects
if (car) car.destroy();
if (road) road.destroy();
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
obstacles = [];
score = 0;
gameOver = false;
updateScoreText();
// Add road
road = new Road();
game.addChild(road);
// Add car
car = new Car();
car.lane = 1;
car.updatePosition();
game.addChild(car);
}
// Helper: spawn an obstacle in a random lane
function spawnObstacle() {
var obs = new Obstacle();
obs.lane = Math.floor(Math.random() * NUM_LANES);
obs.y = -100;
obs.updatePosition();
game.addChild(obs);
obstacles.push(obs);
}
// Touch controls: left/right
game.down = function (x, y, obj) {
if (gameOver) return;
// If left half of screen, move car left
if (x < 2048 / 2) {
if (car.lane > 0) {
car.lane--;
// Animate car movement to new lane
var laneWidth = 1200 / 4;
var targetX = 2048 / 2 - 600 + laneWidth / 2 + car.lane * laneWidth;
tween.stop(car, {
x: true
});
tween(car, {
x: targetX
}, {
duration: 220,
easing: tween.cubicOut
});
car.y = 2300; // keep y fixed
}
} else {
// Right half, move car right
if (car.lane < NUM_LANES - 1) {
car.lane++;
// Animate car movement to new lane
var laneWidth = 1200 / 4;
var targetX = 2048 / 2 - 600 + laneWidth / 2 + car.lane * laneWidth;
tween.stop(car, {
x: true
});
tween(car, {
x: targetX
}, {
duration: 220,
easing: tween.cubicOut
});
car.y = 2300; // keep y fixed
}
}
};
// No up event needed
game.up = function (x, y, obj) {};
// Main game update loop
var obstacleTimer = 0;
game.update = function () {
if (gameOver) return;
// Animate road dotted lines
if (road && typeof road.update === "function") {
road.update();
}
// --- Dust cloud animation behind car ---
if (!game._dustClouds) game._dustClouds = [];
// Only spawn if car exists and game is not over
if (car && !gameOver && LK.ticks % 2 === 0) {
var dust = new DustCloud();
// Place behind car, slightly randomized
dust.x = car.x + (Math.random() - 0.5) * 40;
dust.y = car.y + car.height * 0.45 + Math.random() * 10;
game.addChild(dust);
game._dustClouds.push(dust);
}
// Animate and remove old dust clouds
for (var i = game._dustClouds.length - 1; i >= 0; i--) {
var d = game._dustClouds[i];
if (typeof d.update === "function") d.update();
if (d.life >= d.maxLife) {
d.destroy();
game._dustClouds.splice(i, 1);
}
}
// Move obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.y > 2732 + 100) {
obs.destroy();
obstacles.splice(i, 1);
score++;
updateScoreText();
}
// Collision with car
if (obs.lane === car.lane && Math.abs(obs.y - car.y) < (obs.height + car.height) / 2 - 10) {
LK.effects.flashScreen(0xff0000, 600);
LK.showGameOver();
gameOver = true;
return;
}
}
// Spawn new obstacles
obstacleTimer++;
if (obstacleTimer >= OBSTACLE_INTERVAL) {
spawnObstacle();
obstacleTimer = 0;
}
};
/****
* Start game
****/
initGame();