User prompt
add grass to the sides, make the plan better
User prompt
When we collect 20 coins, small balloons will appear on the screen for 2 seconds but the game will continue.
User prompt
improve quality
User prompt
add green grass to the right and left
User prompt
add green grass to the right and left
User prompt
add green grass to the right and left
Code edit (1 edits merged)
Please save this source code
User prompt
Turbo Touch Racer
Initial prompt
make a car racing game
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Balloon (for celebration)
var Balloon = Container.expand(function () {
var self = Container.call(this);
var balloonColors = [0xff6666, 0x66ccff, 0x66ff66, 0xffcc66, 0xcc66ff];
var color = balloonColors[Math.floor(Math.random() * balloonColors.length)];
var balloonAsset = self.attachAsset('centerCircle', {
width: 80,
height: 110,
color: color,
anchorX: 0.5,
anchorY: 1
});
// Add a string (line) as a small rectangle
var stringAsset = self.attachAsset('lane', {
width: 6,
height: 40,
color: 0x888888,
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 0
});
stringAsset.y = 0;
stringAsset.x = 0;
balloonAsset.y = 0;
self.addChild(stringAsset);
self.addChild(balloonAsset);
self.update = function () {
// Balloons float up a bit
self.y -= 2;
};
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.speed = 0; // Will be set on spawn
self.update = function () {
self.y += self.speed;
};
return self;
});
// Obstacle Car
var ObstacleCar = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0; // Will be set on spawn
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Car
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// For collision, use the container itself
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Road config
// Car (player)
// Obstacle (enemy car)
// Coin
// Road lane
var ROAD_WIDTH = 900;
var ROAD_X = (2048 - ROAD_WIDTH) / 2;
var ROAD_Y = 0;
var ROAD_HEIGHT = 2732;
// Lane config
var LANE_COUNT = 3;
var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT;
// Player config
var PLAYER_START_LANE = 1; // 0 = left, 1 = center, 2 = right
var PLAYER_Y = 2732 - 400;
// Obstacle config
var OBSTACLE_MIN_INTERVAL = 60; // frames
var OBSTACLE_MAX_INTERVAL = 120; // frames
// Coin config
var COIN_MIN_INTERVAL = 80;
var COIN_MAX_INTERVAL = 160;
// Speed config
var BASE_SPEED = 16;
var SPEED_INCREMENT = 0.015; // per tick
var MAX_SPEED = 38;
// Game state
var playerCar;
var obstacles = [];
var coins = [];
var dragNode = null;
var dragOffsetX = 0;
var lastObstacleTick = 0;
var nextObstacleInterval = 90;
var lastCoinTick = 0;
var nextCoinInterval = 120;
var currentSpeed = BASE_SPEED;
var ticksSurvived = 0;
// Improved grass plan: Layered grass with two tones and a border for each side
// Left grass (darker base)
var leftGrassDark = LK.getAsset('lane', {
width: ROAD_X,
height: ROAD_HEIGHT,
color: 0x228B22,
// darker green
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(leftGrassDark);
// Left grass (lighter highlight, inner edge)
var leftGrassLight = LK.getAsset('lane', {
width: Math.max(ROAD_X - 40, 0),
height: ROAD_HEIGHT,
color: 0x2ecc40,
// lighter green
anchorX: 0,
anchorY: 0,
x: 20,
y: 0
});
game.addChild(leftGrassLight);
// Left border (between grass and road)
var leftBorder = LK.getAsset('lane', {
width: 16,
height: ROAD_HEIGHT,
color: 0x333333,
anchorX: 1,
anchorY: 0,
x: ROAD_X,
y: ROAD_Y
});
game.addChild(leftBorder);
// Right grass (darker base)
var rightGrassDark = LK.getAsset('lane', {
width: ROAD_X,
height: ROAD_HEIGHT,
color: 0x228B22,
anchorX: 0,
anchorY: 0,
x: ROAD_X + ROAD_WIDTH,
y: 0
});
game.addChild(rightGrassDark);
// Right grass (lighter highlight, inner edge)
var rightGrassLight = LK.getAsset('lane', {
width: Math.max(ROAD_X - 40, 0),
height: ROAD_HEIGHT,
color: 0x2ecc40,
anchorX: 0,
anchorY: 0,
x: ROAD_X + ROAD_WIDTH + 20,
y: 0
});
game.addChild(rightGrassLight);
// Right border (between grass and road)
var rightBorder = LK.getAsset('lane', {
width: 16,
height: ROAD_HEIGHT,
color: 0x333333,
anchorX: 0,
anchorY: 0,
x: ROAD_X + ROAD_WIDTH,
y: ROAD_Y
});
game.addChild(rightBorder);
// Draw road background (just a gray rectangle)
var roadBg = LK.getAsset('lane', {
width: ROAD_WIDTH,
height: ROAD_HEIGHT,
color: 0x444444,
anchorX: 0,
anchorY: 0,
x: ROAD_X,
y: ROAD_Y
});
game.addChild(roadBg);
// Draw lane markers
var laneMarkers = [];
for (var i = 1; i < LANE_COUNT; i++) {
var marker = LK.getAsset('lane', {
width: 40,
height: ROAD_HEIGHT,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0,
x: ROAD_X + i * LANE_WIDTH,
y: ROAD_Y
});
marker.alpha = 0.18;
game.addChild(marker);
laneMarkers.push(marker);
}
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Place player car
playerCar = new PlayerCar();
playerCar.x = ROAD_X + (PLAYER_START_LANE + 0.5) * LANE_WIDTH;
playerCar.y = PLAYER_Y;
game.addChild(playerCar);
// Helper: Clamp x to road
function clampToRoad(x) {
var minX = ROAD_X + playerCar.width / 2;
var maxX = ROAD_X + ROAD_WIDTH - playerCar.width / 2;
if (x < minX) return minX;
if (x > maxX) return maxX;
return x;
}
// Touch/drag controls
game.down = function (x, y, obj) {
// Only start drag if touch is on car
var local = playerCar.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x >= -playerCar.width / 2 && local.x <= playerCar.width / 2 && local.y >= -playerCar.height / 2 && local.y <= playerCar.height / 2) {
dragNode = playerCar;
dragOffsetX = playerCar.x - x;
}
};
game.move = function (x, y, obj) {
if (dragNode) {
// Only allow horizontal movement, clamp to road
dragNode.x = clampToRoad(x + dragOffsetX);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Spawn obstacle
function spawnObstacle() {
var lane = Math.floor(Math.random() * LANE_COUNT);
var obs = new ObstacleCar();
obs.x = ROAD_X + (lane + 0.5) * LANE_WIDTH;
obs.y = -obs.height / 2;
obs.speed = currentSpeed;
obstacles.push(obs);
game.addChild(obs);
}
// Spawn coin
function spawnCoin() {
var lane = Math.floor(Math.random() * LANE_COUNT);
var coin = new Coin();
coin.x = ROAD_X + (lane + 0.5) * LANE_WIDTH;
coin.y = -coin.height / 2 - 100;
coin.speed = currentSpeed;
coins.push(coin);
game.addChild(coin);
}
// Main game update
game.update = function () {
ticksSurvived++;
// Increase speed over time
if (currentSpeed < MAX_SPEED) {
currentSpeed += SPEED_INCREMENT;
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.speed = currentSpeed;
obs.update();
// Remove if off screen
if (obs.y - obs.height / 2 > 2732) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with player
if (obs.intersects(playerCar)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
// Update coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
coin.speed = currentSpeed;
coin.update();
// Remove if off screen
if (coin.y - coin.height / 2 > 2732) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Collect coin
if (coin.intersects(playerCar)) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
// 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);
// Show balloons if score reaches 20 (only once)
if (typeof balloonsShown === "undefined") {
balloonsShown = false;
}
if (!balloonsShown && LK.getScore() === 20) {
balloonsShown = true;
if (typeof balloons === "undefined") {
balloons = [];
}
// Spawn several balloons at random positions
var balloonCount = 7;
for (var b = 0; b < balloonCount; b++) {
var balloon = new Balloon();
// Spread balloons horizontally across the road area
balloon.x = ROAD_X + 80 + Math.random() * (ROAD_WIDTH - 160);
balloon.y = 900 + Math.random() * 600;
game.addChild(balloon);
balloons.push(balloon);
}
// Remove balloons after 2 seconds
LK.setTimeout(function () {
for (var b = 0; b < balloons.length; b++) {
if (balloons[b] && balloons[b].destroy) {
balloons[b].destroy();
}
}
balloons = [];
}, 2000);
}
continue;
}
}
// Spawn obstacles
if (LK.ticks - lastObstacleTick >= nextObstacleInterval) {
spawnObstacle();
lastObstacleTick = LK.ticks;
nextObstacleInterval = OBSTACLE_MIN_INTERVAL + Math.floor(Math.random() * (OBSTACLE_MAX_INTERVAL - OBSTACLE_MIN_INTERVAL));
}
// Spawn coins
if (LK.ticks - lastCoinTick >= nextCoinInterval) {
spawnCoin();
lastCoinTick = LK.ticks;
nextCoinInterval = COIN_MIN_INTERVAL + Math.floor(Math.random() * (COIN_MAX_INTERVAL - COIN_MIN_INTERVAL));
}
};
// Set initial score
LK.setScore(0);
scoreTxt.setText('0'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Balloon (for celebration)
var Balloon = Container.expand(function () {
var self = Container.call(this);
var balloonColors = [0xff6666, 0x66ccff, 0x66ff66, 0xffcc66, 0xcc66ff];
var color = balloonColors[Math.floor(Math.random() * balloonColors.length)];
var balloonAsset = self.attachAsset('centerCircle', {
width: 80,
height: 110,
color: color,
anchorX: 0.5,
anchorY: 1
});
// Add a string (line) as a small rectangle
var stringAsset = self.attachAsset('lane', {
width: 6,
height: 40,
color: 0x888888,
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 0
});
stringAsset.y = 0;
stringAsset.x = 0;
balloonAsset.y = 0;
self.addChild(stringAsset);
self.addChild(balloonAsset);
self.update = function () {
// Balloons float up a bit
self.y -= 2;
};
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.speed = 0; // Will be set on spawn
self.update = function () {
self.y += self.speed;
};
return self;
});
// Obstacle Car
var ObstacleCar = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0; // Will be set on spawn
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Car
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// For collision, use the container itself
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Road config
// Car (player)
// Obstacle (enemy car)
// Coin
// Road lane
var ROAD_WIDTH = 900;
var ROAD_X = (2048 - ROAD_WIDTH) / 2;
var ROAD_Y = 0;
var ROAD_HEIGHT = 2732;
// Lane config
var LANE_COUNT = 3;
var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT;
// Player config
var PLAYER_START_LANE = 1; // 0 = left, 1 = center, 2 = right
var PLAYER_Y = 2732 - 400;
// Obstacle config
var OBSTACLE_MIN_INTERVAL = 60; // frames
var OBSTACLE_MAX_INTERVAL = 120; // frames
// Coin config
var COIN_MIN_INTERVAL = 80;
var COIN_MAX_INTERVAL = 160;
// Speed config
var BASE_SPEED = 16;
var SPEED_INCREMENT = 0.015; // per tick
var MAX_SPEED = 38;
// Game state
var playerCar;
var obstacles = [];
var coins = [];
var dragNode = null;
var dragOffsetX = 0;
var lastObstacleTick = 0;
var nextObstacleInterval = 90;
var lastCoinTick = 0;
var nextCoinInterval = 120;
var currentSpeed = BASE_SPEED;
var ticksSurvived = 0;
// Improved grass plan: Layered grass with two tones and a border for each side
// Left grass (darker base)
var leftGrassDark = LK.getAsset('lane', {
width: ROAD_X,
height: ROAD_HEIGHT,
color: 0x228B22,
// darker green
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(leftGrassDark);
// Left grass (lighter highlight, inner edge)
var leftGrassLight = LK.getAsset('lane', {
width: Math.max(ROAD_X - 40, 0),
height: ROAD_HEIGHT,
color: 0x2ecc40,
// lighter green
anchorX: 0,
anchorY: 0,
x: 20,
y: 0
});
game.addChild(leftGrassLight);
// Left border (between grass and road)
var leftBorder = LK.getAsset('lane', {
width: 16,
height: ROAD_HEIGHT,
color: 0x333333,
anchorX: 1,
anchorY: 0,
x: ROAD_X,
y: ROAD_Y
});
game.addChild(leftBorder);
// Right grass (darker base)
var rightGrassDark = LK.getAsset('lane', {
width: ROAD_X,
height: ROAD_HEIGHT,
color: 0x228B22,
anchorX: 0,
anchorY: 0,
x: ROAD_X + ROAD_WIDTH,
y: 0
});
game.addChild(rightGrassDark);
// Right grass (lighter highlight, inner edge)
var rightGrassLight = LK.getAsset('lane', {
width: Math.max(ROAD_X - 40, 0),
height: ROAD_HEIGHT,
color: 0x2ecc40,
anchorX: 0,
anchorY: 0,
x: ROAD_X + ROAD_WIDTH + 20,
y: 0
});
game.addChild(rightGrassLight);
// Right border (between grass and road)
var rightBorder = LK.getAsset('lane', {
width: 16,
height: ROAD_HEIGHT,
color: 0x333333,
anchorX: 0,
anchorY: 0,
x: ROAD_X + ROAD_WIDTH,
y: ROAD_Y
});
game.addChild(rightBorder);
// Draw road background (just a gray rectangle)
var roadBg = LK.getAsset('lane', {
width: ROAD_WIDTH,
height: ROAD_HEIGHT,
color: 0x444444,
anchorX: 0,
anchorY: 0,
x: ROAD_X,
y: ROAD_Y
});
game.addChild(roadBg);
// Draw lane markers
var laneMarkers = [];
for (var i = 1; i < LANE_COUNT; i++) {
var marker = LK.getAsset('lane', {
width: 40,
height: ROAD_HEIGHT,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0,
x: ROAD_X + i * LANE_WIDTH,
y: ROAD_Y
});
marker.alpha = 0.18;
game.addChild(marker);
laneMarkers.push(marker);
}
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Place player car
playerCar = new PlayerCar();
playerCar.x = ROAD_X + (PLAYER_START_LANE + 0.5) * LANE_WIDTH;
playerCar.y = PLAYER_Y;
game.addChild(playerCar);
// Helper: Clamp x to road
function clampToRoad(x) {
var minX = ROAD_X + playerCar.width / 2;
var maxX = ROAD_X + ROAD_WIDTH - playerCar.width / 2;
if (x < minX) return minX;
if (x > maxX) return maxX;
return x;
}
// Touch/drag controls
game.down = function (x, y, obj) {
// Only start drag if touch is on car
var local = playerCar.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x >= -playerCar.width / 2 && local.x <= playerCar.width / 2 && local.y >= -playerCar.height / 2 && local.y <= playerCar.height / 2) {
dragNode = playerCar;
dragOffsetX = playerCar.x - x;
}
};
game.move = function (x, y, obj) {
if (dragNode) {
// Only allow horizontal movement, clamp to road
dragNode.x = clampToRoad(x + dragOffsetX);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Spawn obstacle
function spawnObstacle() {
var lane = Math.floor(Math.random() * LANE_COUNT);
var obs = new ObstacleCar();
obs.x = ROAD_X + (lane + 0.5) * LANE_WIDTH;
obs.y = -obs.height / 2;
obs.speed = currentSpeed;
obstacles.push(obs);
game.addChild(obs);
}
// Spawn coin
function spawnCoin() {
var lane = Math.floor(Math.random() * LANE_COUNT);
var coin = new Coin();
coin.x = ROAD_X + (lane + 0.5) * LANE_WIDTH;
coin.y = -coin.height / 2 - 100;
coin.speed = currentSpeed;
coins.push(coin);
game.addChild(coin);
}
// Main game update
game.update = function () {
ticksSurvived++;
// Increase speed over time
if (currentSpeed < MAX_SPEED) {
currentSpeed += SPEED_INCREMENT;
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.speed = currentSpeed;
obs.update();
// Remove if off screen
if (obs.y - obs.height / 2 > 2732) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with player
if (obs.intersects(playerCar)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
// Update coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
coin.speed = currentSpeed;
coin.update();
// Remove if off screen
if (coin.y - coin.height / 2 > 2732) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Collect coin
if (coin.intersects(playerCar)) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
// 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);
// Show balloons if score reaches 20 (only once)
if (typeof balloonsShown === "undefined") {
balloonsShown = false;
}
if (!balloonsShown && LK.getScore() === 20) {
balloonsShown = true;
if (typeof balloons === "undefined") {
balloons = [];
}
// Spawn several balloons at random positions
var balloonCount = 7;
for (var b = 0; b < balloonCount; b++) {
var balloon = new Balloon();
// Spread balloons horizontally across the road area
balloon.x = ROAD_X + 80 + Math.random() * (ROAD_WIDTH - 160);
balloon.y = 900 + Math.random() * 600;
game.addChild(balloon);
balloons.push(balloon);
}
// Remove balloons after 2 seconds
LK.setTimeout(function () {
for (var b = 0; b < balloons.length; b++) {
if (balloons[b] && balloons[b].destroy) {
balloons[b].destroy();
}
}
balloons = [];
}, 2000);
}
continue;
}
}
// Spawn obstacles
if (LK.ticks - lastObstacleTick >= nextObstacleInterval) {
spawnObstacle();
lastObstacleTick = LK.ticks;
nextObstacleInterval = OBSTACLE_MIN_INTERVAL + Math.floor(Math.random() * (OBSTACLE_MAX_INTERVAL - OBSTACLE_MIN_INTERVAL));
}
// Spawn coins
if (LK.ticks - lastCoinTick >= nextCoinInterval) {
spawnCoin();
lastCoinTick = LK.ticks;
nextCoinInterval = COIN_MIN_INTERVAL + Math.floor(Math.random() * (COIN_MAX_INTERVAL - COIN_MIN_INTERVAL));
}
};
// Set initial score
LK.setScore(0);
scoreTxt.setText('0');