/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); self.ballSprite = self.attachAsset('football', { anchorX: 0.5, anchorY: 0.5 }); self.width = self.ballSprite.width; self.height = self.ballSprite.height; self.vx = 0; self.vy = 0; self.friction = 0.98; self.maxSpeed = 38; self.update = function () { // Move ball self.x += self.vx; self.y += self.vy; // Friction self.vx *= self.friction; self.vy *= self.friction; // Clamp speed var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy); if (speed > self.maxSpeed) { self.vx *= self.maxSpeed / speed; self.vy *= self.maxSpeed / speed; } // Bounce off walls if (self.x < self.width / 2) { self.x = self.width / 2; self.vx = -self.vx * 0.7; } if (self.x > 2048 - self.width / 2) { self.x = 2048 - self.width / 2; self.vx = -self.vx * 0.7; } if (self.y < self.height / 2) { self.y = self.height / 2; self.vy = -self.vy * 0.7; } if (self.y > 2732 - self.height / 2) { self.y = 2732 - self.height / 2; self.vy = -self.vy * 0.7; } }; return self; }); // Car class (for both player and AI) var Car = Container.expand(function () { var self = Container.call(this); // Attach car asset (default to player car) self.carSprite = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); self.width = self.carSprite.width; self.height = self.carSprite.height; self.speed = 0; self.maxSpeed = 32; self.acceleration = 2.2; self.friction = 0.95; self.angle = 0; // radians self.turnSpeed = 0.09; self.boosting = false; self.boostPower = 48; self.boostCooldown = 0; self.isAI = false; // For AI self.targetX = 0; self.targetY = 0; self.update = function () { // AI logic if (self.isAI) { // Always try to push the ball into the player's (bottom/green) goal var dx = ball.x - self.x; var dy = ball.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); // Target: aim for the ball, but bias target slightly toward the player's goal var goalBias = 180; var targetGoalX = goalPlayer.x; var targetGoalY = goalPlayer.y; // Calculate a point between the ball and the center of the player's goal self.targetX = ball.x + (targetGoalX - ball.x) * 0.18; self.targetY = ball.y + (targetGoalY - ball.y) * 0.18; // Turn towards target var desiredAngle = Math.atan2(self.targetY - self.y, self.targetX - self.x); var angleDiff = desiredAngle - self.angle; while (angleDiff > Math.PI) angleDiff -= Math.PI * 2; while (angleDiff < -Math.PI) angleDiff += Math.PI * 2; if (angleDiff > 0.1) self.angle += self.turnSpeed;else if (angleDiff < -0.1) self.angle -= self.turnSpeed; // Accelerate if facing target if (Math.abs(angleDiff) < 0.5) { self.speed += self.acceleration * 0.7; } // AI boost if close to ball and facing it if (dist < 400 && Math.abs(angleDiff) < 0.3 && self.boostCooldown <= 0) { self.boosting = true; } } // Boost logic if (self.boosting && self.boostCooldown <= 0) { self.speed = self.boostPower; self.boostCooldown = 60; // 1 second cooldown self.boosting = false; } if (self.boostCooldown > 0) self.boostCooldown--; // Clamp speed if (self.speed > self.maxSpeed) self.speed = self.maxSpeed; if (self.speed < -self.maxSpeed / 2) self.speed = -self.maxSpeed / 2; // Move car self.x += Math.cos(self.angle) * self.speed; self.y += Math.sin(self.angle) * self.speed; // Friction self.speed *= self.friction; // Keep in bounds if (self.x < self.width / 2) self.x = self.width / 2; if (self.x > 2048 - self.width / 2) self.x = 2048 - self.width / 2; if (self.y < self.height / 2) self.y = self.height / 2; if (self.y > 2732 - self.height / 2) self.y = 2732 - self.height / 2; // Rotate sprite self.carSprite.rotation = self.angle; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Game state // Car asset (player) // Car asset (AI) // Ball asset // Goal asset (player) // Goal asset (AI) var playerScore = 0; var aiScore = 0; var matchTime = 60; // seconds var timeLeft = matchTime; var gameActive = true; // Add background image var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 1, scaleY: 1 }); game.addChild(background); // Create goals var goalPlayer = LK.getAsset('goal_player', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2732 - 80 }); var goalAI = LK.getAsset('goal_ai', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 80 }); game.addChild(goalPlayer); game.addChild(goalAI); // Create ball var ball = new Ball(); game.addChild(ball); // Create player car var playerCar = new Car(); playerCar.x = 1024; playerCar.y = 2732 - 400; playerCar.angle = -Math.PI / 2; game.addChild(playerCar); // Create AI car var aiCar = new Car(); aiCar.carSprite.destroy(); aiCar.carSprite = aiCar.attachAsset('car_ai', { anchorX: 0.5, anchorY: 0.5 }); aiCar.isAI = true; aiCar.x = 1024; aiCar.y = 400; aiCar.angle = Math.PI / 2; game.addChild(aiCar); // Score display var scoreTxt = new Text2('0 : 0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Timer display var timerTxt = new Text2('60', { size: 90, fill: 0xFFFF00 }); timerTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timerTxt); timerTxt.y = 120; // Helper: Reset ball and cars to center function resetPositions(scoredBy) { ball.x = 1024; ball.y = 1366; ball.vx = 0; ball.vy = 0; playerCar.x = 1024; playerCar.y = 2732 - 400; playerCar.angle = -Math.PI / 2; playerCar.speed = 0; aiCar.x = 1024; aiCar.y = 400; aiCar.angle = Math.PI / 2; aiCar.speed = 0; // Small pause after goal gameActive = false; tween(ball, { alpha: 0.2 }, { duration: 200, onFinish: function onFinish() { tween(ball, { alpha: 1 }, { duration: 200, onFinish: function onFinish() { gameActive = true; } }); } }); } // Touch controls var touchActive = false; var touchStartX = 0, touchStartY = 0; var touchCurX = 0, touchCurY = 0; var boostActive = false; // Touch: steer by dragging, boost by double tap or long press game.down = function (x, y, obj) { // Ignore if not active if (!gameActive) return; // Don't allow touch in top left 100x100 if (x < 100 && y < 100) return; touchActive = true; touchStartX = x; touchStartY = y; touchCurX = x; touchCurY = y; boostActive = false; // Detect long press for boost playerCar.boosting = false; playerCar._boostTimeout = LK.setTimeout(function () { boostActive = true; playerCar.boosting = true; }, 400); }; game.move = function (x, y, obj) { if (!gameActive) return; if (!touchActive) return; touchCurX = x; touchCurY = y; // Steer: angle towards drag direction var dx = touchCurX - playerCar.x; var dy = touchCurY - playerCar.y; var desiredAngle = Math.atan2(dy, dx); var angleDiff = desiredAngle - playerCar.angle; while (angleDiff > Math.PI) angleDiff -= Math.PI * 2; while (angleDiff < -Math.PI) angleDiff += Math.PI * 2; if (angleDiff > 0.1) playerCar.angle += playerCar.turnSpeed;else if (angleDiff < -0.1) playerCar.angle -= playerCar.turnSpeed; // Accelerate if facing drag direction if (Math.abs(angleDiff) < 0.5) { playerCar.speed += playerCar.acceleration; } // Clamp if (playerCar.speed > playerCar.maxSpeed) playerCar.speed = playerCar.maxSpeed; }; game.up = function (x, y, obj) { touchActive = false; if (playerCar._boostTimeout) { LK.clearTimeout(playerCar._boostTimeout); playerCar._boostTimeout = null; } if (boostActive) { boostActive = false; } }; // Double tap for boost var lastTap = 0; game.down = function (origDown) { return function (x, y, obj) { var now = Date.now(); if (now - lastTap < 350) { // Double tap: boost playerCar.boosting = true; } lastTap = now; origDown.call(game, x, y, obj); }; }(game.down); // Physics: Car-ball collision function carBallCollision(car, ball) { var dx = ball.x - car.x; var dy = ball.y - car.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = (car.width / 2 + ball.width / 2) * 0.8; if (dist < minDist) { // Push ball away var nx = dx / (dist || 1); var ny = dy / (dist || 1); var overlap = minDist - dist; ball.x += nx * overlap / 2; ball.y += ny * overlap / 2; // Ball velocity: add car's speed var impact = Math.max(8, car.speed * 1.2); ball.vx += nx * impact; ball.vy += ny * impact; // Car bounces a bit car.speed *= 0.7; } } // Physics: Car-car collision function carCarCollision(car1, car2) { var dx = car2.x - car1.x; var dy = car2.y - car1.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = (car1.width / 2 + car2.width / 2) * 0.9; if (dist < minDist) { var nx = dx / (dist || 1); var ny = dy / (dist || 1); var overlap = minDist - dist; car1.x -= nx * overlap / 2; car1.y -= ny * overlap / 2; car2.x += nx * overlap / 2; car2.y += ny * overlap / 2; // Exchange some speed var temp = car1.speed; car1.speed = car2.speed * 0.7; car2.speed = temp * 0.7; } } // Goal detection function checkGoal() { // Player goal (top) if (ball.y - ball.height / 2 < goalAI.y + goalAI.height / 2 && ball.x > goalAI.x - goalAI.width / 2 && ball.x < goalAI.x + goalAI.width / 2) { playerScore++; scoreTxt.setText(playerScore + " : " + aiScore); LK.effects.flashScreen(0x00ff00, 500); resetPositions('player'); } // AI goal (bottom) if (ball.y + ball.height / 2 > goalPlayer.y - goalPlayer.height / 2 && ball.x > goalPlayer.x - goalPlayer.width / 2 && ball.x < goalPlayer.x + goalPlayer.width / 2) { aiScore++; scoreTxt.setText(playerScore + " : " + aiScore); LK.effects.flashScreen(0xff0000, 500); resetPositions('ai'); } } // Timer var timerInterval = LK.setInterval(function () { if (!gameActive) return; if (timeLeft > 0) { timeLeft--; timerTxt.setText(timeLeft + ""); if (timeLeft <= 10) timerTxt.setText(timeLeft + "!"); } if (timeLeft <= 0) { // End game gameActive = false; if (playerScore > aiScore) { LK.showYouWin(); } else if (aiScore > playerScore) { LK.showGameOver(); } else { // Draw: treat as loss for now LK.showGameOver(); } } }, 1000); // Main update loop game.update = function () { if (!gameActive) return; // Update cars and ball playerCar.update(); aiCar.update(); ball.update(); // Collisions carBallCollision(playerCar, ball); carBallCollision(aiCar, ball); carCarCollision(playerCar, aiCar); // Goal check checkGoal(); }; // Initial positions resetPositions(); /* End of game code */
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
self.ballSprite = self.attachAsset('football', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.ballSprite.width;
self.height = self.ballSprite.height;
self.vx = 0;
self.vy = 0;
self.friction = 0.98;
self.maxSpeed = 38;
self.update = function () {
// Move ball
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= self.friction;
self.vy *= self.friction;
// Clamp speed
var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy);
if (speed > self.maxSpeed) {
self.vx *= self.maxSpeed / speed;
self.vy *= self.maxSpeed / speed;
}
// Bounce off walls
if (self.x < self.width / 2) {
self.x = self.width / 2;
self.vx = -self.vx * 0.7;
}
if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
self.vx = -self.vx * 0.7;
}
if (self.y < self.height / 2) {
self.y = self.height / 2;
self.vy = -self.vy * 0.7;
}
if (self.y > 2732 - self.height / 2) {
self.y = 2732 - self.height / 2;
self.vy = -self.vy * 0.7;
}
};
return self;
});
// Car class (for both player and AI)
var Car = Container.expand(function () {
var self = Container.call(this);
// Attach car asset (default to player car)
self.carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.carSprite.width;
self.height = self.carSprite.height;
self.speed = 0;
self.maxSpeed = 32;
self.acceleration = 2.2;
self.friction = 0.95;
self.angle = 0; // radians
self.turnSpeed = 0.09;
self.boosting = false;
self.boostPower = 48;
self.boostCooldown = 0;
self.isAI = false;
// For AI
self.targetX = 0;
self.targetY = 0;
self.update = function () {
// AI logic
if (self.isAI) {
// Always try to push the ball into the player's (bottom/green) goal
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Target: aim for the ball, but bias target slightly toward the player's goal
var goalBias = 180;
var targetGoalX = goalPlayer.x;
var targetGoalY = goalPlayer.y;
// Calculate a point between the ball and the center of the player's goal
self.targetX = ball.x + (targetGoalX - ball.x) * 0.18;
self.targetY = ball.y + (targetGoalY - ball.y) * 0.18;
// Turn towards target
var desiredAngle = Math.atan2(self.targetY - self.y, self.targetX - self.x);
var angleDiff = desiredAngle - self.angle;
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
if (angleDiff > 0.1) self.angle += self.turnSpeed;else if (angleDiff < -0.1) self.angle -= self.turnSpeed;
// Accelerate if facing target
if (Math.abs(angleDiff) < 0.5) {
self.speed += self.acceleration * 0.7;
}
// AI boost if close to ball and facing it
if (dist < 400 && Math.abs(angleDiff) < 0.3 && self.boostCooldown <= 0) {
self.boosting = true;
}
}
// Boost logic
if (self.boosting && self.boostCooldown <= 0) {
self.speed = self.boostPower;
self.boostCooldown = 60; // 1 second cooldown
self.boosting = false;
}
if (self.boostCooldown > 0) self.boostCooldown--;
// Clamp speed
if (self.speed > self.maxSpeed) self.speed = self.maxSpeed;
if (self.speed < -self.maxSpeed / 2) self.speed = -self.maxSpeed / 2;
// Move car
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
// Friction
self.speed *= self.friction;
// Keep in bounds
if (self.x < self.width / 2) self.x = self.width / 2;
if (self.x > 2048 - self.width / 2) self.x = 2048 - self.width / 2;
if (self.y < self.height / 2) self.y = self.height / 2;
if (self.y > 2732 - self.height / 2) self.y = 2732 - self.height / 2;
// Rotate sprite
self.carSprite.rotation = self.angle;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state
// Car asset (player)
// Car asset (AI)
// Ball asset
// Goal asset (player)
// Goal asset (AI)
var playerScore = 0;
var aiScore = 0;
var matchTime = 60; // seconds
var timeLeft = matchTime;
var gameActive = true;
// Add background image
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1,
scaleY: 1
});
game.addChild(background);
// Create goals
var goalPlayer = LK.getAsset('goal_player', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2732 - 80
});
var goalAI = LK.getAsset('goal_ai', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 80
});
game.addChild(goalPlayer);
game.addChild(goalAI);
// Create ball
var ball = new Ball();
game.addChild(ball);
// Create player car
var playerCar = new Car();
playerCar.x = 1024;
playerCar.y = 2732 - 400;
playerCar.angle = -Math.PI / 2;
game.addChild(playerCar);
// Create AI car
var aiCar = new Car();
aiCar.carSprite.destroy();
aiCar.carSprite = aiCar.attachAsset('car_ai', {
anchorX: 0.5,
anchorY: 0.5
});
aiCar.isAI = true;
aiCar.x = 1024;
aiCar.y = 400;
aiCar.angle = Math.PI / 2;
game.addChild(aiCar);
// Score display
var scoreTxt = new Text2('0 : 0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Timer display
var timerTxt = new Text2('60', {
size: 90,
fill: 0xFFFF00
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
timerTxt.y = 120;
// Helper: Reset ball and cars to center
function resetPositions(scoredBy) {
ball.x = 1024;
ball.y = 1366;
ball.vx = 0;
ball.vy = 0;
playerCar.x = 1024;
playerCar.y = 2732 - 400;
playerCar.angle = -Math.PI / 2;
playerCar.speed = 0;
aiCar.x = 1024;
aiCar.y = 400;
aiCar.angle = Math.PI / 2;
aiCar.speed = 0;
// Small pause after goal
gameActive = false;
tween(ball, {
alpha: 0.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
alpha: 1
}, {
duration: 200,
onFinish: function onFinish() {
gameActive = true;
}
});
}
});
}
// Touch controls
var touchActive = false;
var touchStartX = 0,
touchStartY = 0;
var touchCurX = 0,
touchCurY = 0;
var boostActive = false;
// Touch: steer by dragging, boost by double tap or long press
game.down = function (x, y, obj) {
// Ignore if not active
if (!gameActive) return;
// Don't allow touch in top left 100x100
if (x < 100 && y < 100) return;
touchActive = true;
touchStartX = x;
touchStartY = y;
touchCurX = x;
touchCurY = y;
boostActive = false;
// Detect long press for boost
playerCar.boosting = false;
playerCar._boostTimeout = LK.setTimeout(function () {
boostActive = true;
playerCar.boosting = true;
}, 400);
};
game.move = function (x, y, obj) {
if (!gameActive) return;
if (!touchActive) return;
touchCurX = x;
touchCurY = y;
// Steer: angle towards drag direction
var dx = touchCurX - playerCar.x;
var dy = touchCurY - playerCar.y;
var desiredAngle = Math.atan2(dy, dx);
var angleDiff = desiredAngle - playerCar.angle;
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
if (angleDiff > 0.1) playerCar.angle += playerCar.turnSpeed;else if (angleDiff < -0.1) playerCar.angle -= playerCar.turnSpeed;
// Accelerate if facing drag direction
if (Math.abs(angleDiff) < 0.5) {
playerCar.speed += playerCar.acceleration;
}
// Clamp
if (playerCar.speed > playerCar.maxSpeed) playerCar.speed = playerCar.maxSpeed;
};
game.up = function (x, y, obj) {
touchActive = false;
if (playerCar._boostTimeout) {
LK.clearTimeout(playerCar._boostTimeout);
playerCar._boostTimeout = null;
}
if (boostActive) {
boostActive = false;
}
};
// Double tap for boost
var lastTap = 0;
game.down = function (origDown) {
return function (x, y, obj) {
var now = Date.now();
if (now - lastTap < 350) {
// Double tap: boost
playerCar.boosting = true;
}
lastTap = now;
origDown.call(game, x, y, obj);
};
}(game.down);
// Physics: Car-ball collision
function carBallCollision(car, ball) {
var dx = ball.x - car.x;
var dy = ball.y - car.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = (car.width / 2 + ball.width / 2) * 0.8;
if (dist < minDist) {
// Push ball away
var nx = dx / (dist || 1);
var ny = dy / (dist || 1);
var overlap = minDist - dist;
ball.x += nx * overlap / 2;
ball.y += ny * overlap / 2;
// Ball velocity: add car's speed
var impact = Math.max(8, car.speed * 1.2);
ball.vx += nx * impact;
ball.vy += ny * impact;
// Car bounces a bit
car.speed *= 0.7;
}
}
// Physics: Car-car collision
function carCarCollision(car1, car2) {
var dx = car2.x - car1.x;
var dy = car2.y - car1.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = (car1.width / 2 + car2.width / 2) * 0.9;
if (dist < minDist) {
var nx = dx / (dist || 1);
var ny = dy / (dist || 1);
var overlap = minDist - dist;
car1.x -= nx * overlap / 2;
car1.y -= ny * overlap / 2;
car2.x += nx * overlap / 2;
car2.y += ny * overlap / 2;
// Exchange some speed
var temp = car1.speed;
car1.speed = car2.speed * 0.7;
car2.speed = temp * 0.7;
}
}
// Goal detection
function checkGoal() {
// Player goal (top)
if (ball.y - ball.height / 2 < goalAI.y + goalAI.height / 2 && ball.x > goalAI.x - goalAI.width / 2 && ball.x < goalAI.x + goalAI.width / 2) {
playerScore++;
scoreTxt.setText(playerScore + " : " + aiScore);
LK.effects.flashScreen(0x00ff00, 500);
resetPositions('player');
}
// AI goal (bottom)
if (ball.y + ball.height / 2 > goalPlayer.y - goalPlayer.height / 2 && ball.x > goalPlayer.x - goalPlayer.width / 2 && ball.x < goalPlayer.x + goalPlayer.width / 2) {
aiScore++;
scoreTxt.setText(playerScore + " : " + aiScore);
LK.effects.flashScreen(0xff0000, 500);
resetPositions('ai');
}
}
// Timer
var timerInterval = LK.setInterval(function () {
if (!gameActive) return;
if (timeLeft > 0) {
timeLeft--;
timerTxt.setText(timeLeft + "");
if (timeLeft <= 10) timerTxt.setText(timeLeft + "!");
}
if (timeLeft <= 0) {
// End game
gameActive = false;
if (playerScore > aiScore) {
LK.showYouWin();
} else if (aiScore > playerScore) {
LK.showGameOver();
} else {
// Draw: treat as loss for now
LK.showGameOver();
}
}
}, 1000);
// Main update loop
game.update = function () {
if (!gameActive) return;
// Update cars and ball
playerCar.update();
aiCar.update();
ball.update();
// Collisions
carBallCollision(playerCar, ball);
carBallCollision(aiCar, ball);
carCarCollision(playerCar, aiCar);
// Goal check
checkGoal();
};
// Initial positions
resetPositions();
/* End of game code */