/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Goalkeeper class
var Goalkeeper = Container.expand(function () {
var self = Container.call(this);
var keeper = self.attachAsset('goalkeeper', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8; // Initial speed
self.direction = 1; // 1: right, -1: left
// Update
self.update = function () {
self.x += self.speed * self.direction;
// Bounce off posts
if (self.x > goalRightX - keeper.width / 2) {
self.x = goalRightX - keeper.width / 2;
self.direction = -1;
}
if (self.x < goalLeftX + keeper.width / 2) {
self.x = goalLeftX + keeper.width / 2;
self.direction = 1;
}
};
// Increase speed
self.increaseSpeed = function () {
self.speed += 1.5;
if (self.speed > 22) self.speed = 22;
};
return self;
});
// Soccer Ball class
var SoccerBall = Container.expand(function () {
var self = Container.call(this);
// Shadow
var shadow = self.attachAsset('ballShadow', {
anchorX: 0.5,
anchorY: 0.5,
y: 80,
alpha: 0.4
});
// Ball
var ball = self.attachAsset('soccerBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.isShot = false;
self.hasScored = false;
self.hasMissed = false;
// Feedback
self.showGoal = function () {
var goalFx = LK.getAsset('goalCircle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(goalFx);
tween(goalFx, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
goalFx.destroy();
}
});
};
self.showMiss = function () {
var missFx = LK.getAsset('missCircle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(missFx);
tween(missFx, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
missFx.destroy();
}
});
};
// Ball update
self.update = function () {
if (self.isShot) {
self.x += self.vx;
self.y += self.vy;
// Simulate friction/air resistance
self.vx *= 0.99;
self.vy *= 0.99;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a472a // Dark green pitch
});
/****
* Game Code
****/
// Goal feedback (yellow)
// Miss feedback (red)
// Net posts (gray)
// Ball shadow (gray)
// Goal area (light green)
// Goalkeeper (blue)
// Soccer ball (white)
// --- Game constants ---
var goalWidth = 900;
var goalHeight = 40;
var goalY = 420; // Y position of goal line
var goalX = (2048 - goalWidth) / 2;
var goalLeftX = goalX;
var goalRightX = goalX + goalWidth;
var goalPostHeight = 180;
var shotsTotal = 10;
// --- Game state ---
var balls = [];
var shotsLeft = shotsTotal;
var score = 0;
var isShooting = false;
var canShoot = true;
var swipeStart = null;
var swipeEnd = null;
var currentBall = null;
var goalkeeper = null;
var lastGoalkeeperSpeedupScore = 0;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFF700
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var shotsTxt = new Text2('Shots: ' + shotsLeft, {
size: 70,
fill: 0xFFFFFF
});
shotsTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(shotsTxt);
shotsTxt.y = 120;
// --- Draw goal area ---
var goalArea = LK.getAsset('goalArea', {
anchorX: 0,
anchorY: 0,
x: goalX,
y: goalY
});
game.addChild(goalArea);
// Goal posts
var leftPost = LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1,
x: goalLeftX,
y: goalY + goalPostHeight
});
var rightPost = LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1,
x: goalRightX,
y: goalY + goalPostHeight
});
game.addChild(leftPost);
game.addChild(rightPost);
// --- Add goalkeeper ---
goalkeeper = new Goalkeeper();
goalkeeper.x = 2048 / 2;
goalkeeper.y = goalY + 40;
game.addChild(goalkeeper);
// --- Ball spawn position ---
var ballStartX = 2048 / 2;
var ballStartY = 2732 - 320;
// --- Functions ---
function spawnBall() {
var ball = new SoccerBall();
ball.x = ballStartX;
ball.y = ballStartY;
balls.push(ball);
game.addChild(ball);
currentBall = ball;
isShooting = false;
canShoot = true;
}
function resetGame() {
// Remove all balls
for (var i = 0; i < balls.length; ++i) {
balls[i].destroy();
}
balls = [];
shotsLeft = shotsTotal;
score = 0;
lastGoalkeeperSpeedupScore = 0;
scoreTxt.setText(score);
shotsTxt.setText('Shots: ' + shotsLeft);
goalkeeper.speed = 8;
goalkeeper.x = 2048 / 2;
spawnBall();
}
function shootBall(vx, vy) {
if (!currentBall || !canShoot) return;
currentBall.vx = vx;
currentBall.vy = vy;
currentBall.isShot = true;
isShooting = true;
canShoot = false;
shotsLeft -= 1;
shotsTxt.setText('Shots: ' + shotsLeft);
}
// --- Input handling (swipe or tap) ---
game.down = function (x, y, obj) {
if (!canShoot) return;
// Only allow starting swipe/tap from lower half of screen
if (y < 1800) return;
swipeStart = {
x: x,
y: y
};
swipeEnd = null;
};
game.move = function (x, y, obj) {
if (!canShoot || !swipeStart) return;
swipeEnd = {
x: x,
y: y
};
};
game.up = function (x, y, obj) {
if (!canShoot || !swipeStart) return;
swipeEnd = {
x: x,
y: y
};
// Calculate direction and power
var dx = swipeEnd.x - swipeStart.x;
var dy = swipeEnd.y - swipeStart.y;
// Only allow upward swipes/taps
if (dy > -40) {
// Not enough upward movement, treat as tap
dx = 0;
dy = -900;
}
// Clamp power
var power = Math.sqrt(dx * dx + dy * dy);
if (power < 200) power = 200;
if (power > 1200) power = 1200;
// Normalize
var norm = Math.sqrt(dx * dx + dy * dy);
var vx = 0,
vy = 0;
if (norm > 0) {
vx = dx / norm * (power / 22);
vy = dy / norm * (power / 18);
} else {
vx = 0;
vy = -power / 18;
}
shootBall(vx, vy);
swipeStart = null;
swipeEnd = null;
};
// --- Main update loop ---
game.update = function () {
// Update goalkeeper
if (goalkeeper) goalkeeper.update();
// Update balls
for (var i = balls.length - 1; i >= 0; --i) {
var ball = balls[i];
ball.update();
// Only check for scoring/miss if ball is shot and not already scored/missed
if (ball.isShot && !ball.hasScored && !ball.hasMissed) {
// Check if ball crosses goal line (y <= goalY+goalHeight)
if (ball.y <= goalY + goalHeight / 2 && ball.y > goalY - 80) {
// Check if within goal posts
if (ball.x > goalLeftX + 60 && ball.x < goalRightX - 60) {
// Check collision with goalkeeper
var keeperRect = {
x: goalkeeper.x - 110,
y: goalkeeper.y - 40,
w: 220,
h: 80
};
var ballRect = {
x: ball.x - 60,
y: ball.y - 60,
w: 120,
h: 120
};
var intersects = !(keeperRect.x + keeperRect.w < ballRect.x || keeperRect.x > ballRect.x + ballRect.w || keeperRect.y + keeperRect.h < ballRect.y || keeperRect.y > ballRect.y + ballRect.h);
if (!intersects) {
// GOAL!
ball.hasScored = true;
score += 1;
scoreTxt.setText(score);
ball.showGoal();
// Speed up goalkeeper every 2 goals
if (score - lastGoalkeeperSpeedupScore >= 2) {
goalkeeper.increaseSpeed();
lastGoalkeeperSpeedupScore = score;
}
} else {
// Saved by keeper
ball.hasMissed = true;
ball.showMiss();
}
} else {
// Missed wide
ball.hasMissed = true;
ball.showMiss();
}
}
// Missed high
if (ball.y < goalY - 100) {
if (!ball.hasScored && !ball.hasMissed) {
ball.hasMissed = true;
ball.showMiss();
}
}
}
// Remove ball if out of bounds
if (ball.y < -200 || ball.x < -200 || ball.x > 2248) {
ball.destroy();
balls.splice(i, 1);
if (currentBall === ball) currentBall = null;
}
}
// After shot, wait for ball to stop or leave screen, then spawn next ball or end game
if (isShooting && !currentBall) {
if (shotsLeft > 0) {
spawnBall();
} else {
// Game over
LK.showGameOver();
}
isShooting = false;
}
// If current ball is shot and has scored/missed, and is slow or out of bounds, remove it
if (currentBall && currentBall.isShot && (currentBall.hasScored || currentBall.hasMissed)) {
var speed = Math.sqrt(currentBall.vx * currentBall.vx + currentBall.vy * currentBall.vy);
if (speed < 2 || currentBall.y < -200 || currentBall.x < -200 || currentBall.x > 2248) {
currentBall.destroy();
for (var j = 0; j < balls.length; ++j) {
if (balls[j] === currentBall) {
balls.splice(j, 1);
break;
}
}
currentBall = null;
}
}
// Win condition: score 10 goals
if (score >= 10) {
LK.showYouWin();
}
};
// --- Start game ---
resetGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Goalkeeper class
var Goalkeeper = Container.expand(function () {
var self = Container.call(this);
var keeper = self.attachAsset('goalkeeper', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8; // Initial speed
self.direction = 1; // 1: right, -1: left
// Update
self.update = function () {
self.x += self.speed * self.direction;
// Bounce off posts
if (self.x > goalRightX - keeper.width / 2) {
self.x = goalRightX - keeper.width / 2;
self.direction = -1;
}
if (self.x < goalLeftX + keeper.width / 2) {
self.x = goalLeftX + keeper.width / 2;
self.direction = 1;
}
};
// Increase speed
self.increaseSpeed = function () {
self.speed += 1.5;
if (self.speed > 22) self.speed = 22;
};
return self;
});
// Soccer Ball class
var SoccerBall = Container.expand(function () {
var self = Container.call(this);
// Shadow
var shadow = self.attachAsset('ballShadow', {
anchorX: 0.5,
anchorY: 0.5,
y: 80,
alpha: 0.4
});
// Ball
var ball = self.attachAsset('soccerBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.isShot = false;
self.hasScored = false;
self.hasMissed = false;
// Feedback
self.showGoal = function () {
var goalFx = LK.getAsset('goalCircle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(goalFx);
tween(goalFx, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
goalFx.destroy();
}
});
};
self.showMiss = function () {
var missFx = LK.getAsset('missCircle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(missFx);
tween(missFx, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
missFx.destroy();
}
});
};
// Ball update
self.update = function () {
if (self.isShot) {
self.x += self.vx;
self.y += self.vy;
// Simulate friction/air resistance
self.vx *= 0.99;
self.vy *= 0.99;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a472a // Dark green pitch
});
/****
* Game Code
****/
// Goal feedback (yellow)
// Miss feedback (red)
// Net posts (gray)
// Ball shadow (gray)
// Goal area (light green)
// Goalkeeper (blue)
// Soccer ball (white)
// --- Game constants ---
var goalWidth = 900;
var goalHeight = 40;
var goalY = 420; // Y position of goal line
var goalX = (2048 - goalWidth) / 2;
var goalLeftX = goalX;
var goalRightX = goalX + goalWidth;
var goalPostHeight = 180;
var shotsTotal = 10;
// --- Game state ---
var balls = [];
var shotsLeft = shotsTotal;
var score = 0;
var isShooting = false;
var canShoot = true;
var swipeStart = null;
var swipeEnd = null;
var currentBall = null;
var goalkeeper = null;
var lastGoalkeeperSpeedupScore = 0;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFF700
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var shotsTxt = new Text2('Shots: ' + shotsLeft, {
size: 70,
fill: 0xFFFFFF
});
shotsTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(shotsTxt);
shotsTxt.y = 120;
// --- Draw goal area ---
var goalArea = LK.getAsset('goalArea', {
anchorX: 0,
anchorY: 0,
x: goalX,
y: goalY
});
game.addChild(goalArea);
// Goal posts
var leftPost = LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1,
x: goalLeftX,
y: goalY + goalPostHeight
});
var rightPost = LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1,
x: goalRightX,
y: goalY + goalPostHeight
});
game.addChild(leftPost);
game.addChild(rightPost);
// --- Add goalkeeper ---
goalkeeper = new Goalkeeper();
goalkeeper.x = 2048 / 2;
goalkeeper.y = goalY + 40;
game.addChild(goalkeeper);
// --- Ball spawn position ---
var ballStartX = 2048 / 2;
var ballStartY = 2732 - 320;
// --- Functions ---
function spawnBall() {
var ball = new SoccerBall();
ball.x = ballStartX;
ball.y = ballStartY;
balls.push(ball);
game.addChild(ball);
currentBall = ball;
isShooting = false;
canShoot = true;
}
function resetGame() {
// Remove all balls
for (var i = 0; i < balls.length; ++i) {
balls[i].destroy();
}
balls = [];
shotsLeft = shotsTotal;
score = 0;
lastGoalkeeperSpeedupScore = 0;
scoreTxt.setText(score);
shotsTxt.setText('Shots: ' + shotsLeft);
goalkeeper.speed = 8;
goalkeeper.x = 2048 / 2;
spawnBall();
}
function shootBall(vx, vy) {
if (!currentBall || !canShoot) return;
currentBall.vx = vx;
currentBall.vy = vy;
currentBall.isShot = true;
isShooting = true;
canShoot = false;
shotsLeft -= 1;
shotsTxt.setText('Shots: ' + shotsLeft);
}
// --- Input handling (swipe or tap) ---
game.down = function (x, y, obj) {
if (!canShoot) return;
// Only allow starting swipe/tap from lower half of screen
if (y < 1800) return;
swipeStart = {
x: x,
y: y
};
swipeEnd = null;
};
game.move = function (x, y, obj) {
if (!canShoot || !swipeStart) return;
swipeEnd = {
x: x,
y: y
};
};
game.up = function (x, y, obj) {
if (!canShoot || !swipeStart) return;
swipeEnd = {
x: x,
y: y
};
// Calculate direction and power
var dx = swipeEnd.x - swipeStart.x;
var dy = swipeEnd.y - swipeStart.y;
// Only allow upward swipes/taps
if (dy > -40) {
// Not enough upward movement, treat as tap
dx = 0;
dy = -900;
}
// Clamp power
var power = Math.sqrt(dx * dx + dy * dy);
if (power < 200) power = 200;
if (power > 1200) power = 1200;
// Normalize
var norm = Math.sqrt(dx * dx + dy * dy);
var vx = 0,
vy = 0;
if (norm > 0) {
vx = dx / norm * (power / 22);
vy = dy / norm * (power / 18);
} else {
vx = 0;
vy = -power / 18;
}
shootBall(vx, vy);
swipeStart = null;
swipeEnd = null;
};
// --- Main update loop ---
game.update = function () {
// Update goalkeeper
if (goalkeeper) goalkeeper.update();
// Update balls
for (var i = balls.length - 1; i >= 0; --i) {
var ball = balls[i];
ball.update();
// Only check for scoring/miss if ball is shot and not already scored/missed
if (ball.isShot && !ball.hasScored && !ball.hasMissed) {
// Check if ball crosses goal line (y <= goalY+goalHeight)
if (ball.y <= goalY + goalHeight / 2 && ball.y > goalY - 80) {
// Check if within goal posts
if (ball.x > goalLeftX + 60 && ball.x < goalRightX - 60) {
// Check collision with goalkeeper
var keeperRect = {
x: goalkeeper.x - 110,
y: goalkeeper.y - 40,
w: 220,
h: 80
};
var ballRect = {
x: ball.x - 60,
y: ball.y - 60,
w: 120,
h: 120
};
var intersects = !(keeperRect.x + keeperRect.w < ballRect.x || keeperRect.x > ballRect.x + ballRect.w || keeperRect.y + keeperRect.h < ballRect.y || keeperRect.y > ballRect.y + ballRect.h);
if (!intersects) {
// GOAL!
ball.hasScored = true;
score += 1;
scoreTxt.setText(score);
ball.showGoal();
// Speed up goalkeeper every 2 goals
if (score - lastGoalkeeperSpeedupScore >= 2) {
goalkeeper.increaseSpeed();
lastGoalkeeperSpeedupScore = score;
}
} else {
// Saved by keeper
ball.hasMissed = true;
ball.showMiss();
}
} else {
// Missed wide
ball.hasMissed = true;
ball.showMiss();
}
}
// Missed high
if (ball.y < goalY - 100) {
if (!ball.hasScored && !ball.hasMissed) {
ball.hasMissed = true;
ball.showMiss();
}
}
}
// Remove ball if out of bounds
if (ball.y < -200 || ball.x < -200 || ball.x > 2248) {
ball.destroy();
balls.splice(i, 1);
if (currentBall === ball) currentBall = null;
}
}
// After shot, wait for ball to stop or leave screen, then spawn next ball or end game
if (isShooting && !currentBall) {
if (shotsLeft > 0) {
spawnBall();
} else {
// Game over
LK.showGameOver();
}
isShooting = false;
}
// If current ball is shot and has scored/missed, and is slow or out of bounds, remove it
if (currentBall && currentBall.isShot && (currentBall.hasScored || currentBall.hasMissed)) {
var speed = Math.sqrt(currentBall.vx * currentBall.vx + currentBall.vy * currentBall.vy);
if (speed < 2 || currentBall.y < -200 || currentBall.x < -200 || currentBall.x > 2248) {
currentBall.destroy();
for (var j = 0; j < balls.length; ++j) {
if (balls[j] === currentBall) {
balls.splice(j, 1);
break;
}
}
currentBall = null;
}
}
// Win condition: score 10 goals
if (score >= 10) {
LK.showYouWin();
}
};
// --- Start game ---
resetGame();