/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Goalkeeper = Container.expand(function () { var self = Container.call(this); var keeperGraphics = self.attachAsset('goalkeeper', { anchorX: 0.5, anchorY: 1.0 }); self.minX = 0; self.maxX = 0; self.isDiving = false; self.dive = function (direction) { if (!self.isDiving) { self.isDiving = true; var targetX = direction > 0 ? self.maxX : self.minX; tween(self, { x: targetX, scaleX: direction > 0 ? 1.2 : -1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1 }, { duration: 200 }); self.isDiving = false; } }); } }; return self; }); var SoccerBall = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('soccerBall', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = 0; self.speedY = 0; self.isActive = true; self.update = function () { if (self.isActive) { self.x += self.speedX; self.y += self.speedY; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x4CAF50 }); /**** * Game Code ****/ // Game variables var goalkeeper; var soccerBalls = []; var goalsAllowed = 0; var maxGoals = 5; var consecutiveSaves = 0; var ballSpawnCounter = 0; var ballSpawnRate = 120; // frames between balls var gameSpeed = 1; var isDragging = false; // Create goal structure var goalBackground = game.addChild(LK.getAsset('goal', { anchorX: 0.5, anchorY: 1.0, alpha: 0.3 })); goalBackground.x = 2048 / 2; goalBackground.y = 2732 - 200; // Goal posts var leftPost = game.addChild(LK.getAsset('goalPost', { anchorX: 0.5, anchorY: 1.0 })); leftPost.x = 2048 / 2 - 400; leftPost.y = 2732 - 200; var rightPost = game.addChild(LK.getAsset('goalPost', { anchorX: 0.5, anchorY: 1.0 })); rightPost.x = 2048 / 2 + 400; rightPost.y = 2732 - 200; // Goal line var goalLine = game.addChild(LK.getAsset('goalLine', { anchorX: 0.5, anchorY: 0.5 })); goalLine.x = 2048 / 2; goalLine.y = 2732 - 200; // Create goalkeeper goalkeeper = game.addChild(new Goalkeeper()); goalkeeper.x = 2048 / 2; goalkeeper.y = 2732 - 200; goalkeeper.minX = 2048 / 2 - 350; goalkeeper.maxX = 2048 / 2 + 350; // UI Elements var scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var goalsTxt = new Text2('Goals: 0/5', { size: 60, fill: 0xFFFFFF }); goalsTxt.anchor.set(1, 0); LK.gui.topRight.addChild(goalsTxt); var streakTxt = new Text2('Saves: 0', { size: 50, fill: 0xFFFF00 }); streakTxt.anchor.set(0, 0); LK.gui.left.addChild(streakTxt); // Touch/drag controls game.down = function (x, y, obj) { isDragging = true; var clampedX = Math.max(goalkeeper.minX, Math.min(goalkeeper.maxX, x)); goalkeeper.x = clampedX; }; game.move = function (x, y, obj) { if (isDragging) { var clampedX = Math.max(goalkeeper.minX, Math.min(goalkeeper.maxX, x)); goalkeeper.x = clampedX; } }; game.up = function (x, y, obj) { isDragging = false; }; // Spawn soccer ball function spawnSoccerBall() { var ball = new SoccerBall(); // Random spawn position along top of screen ball.x = Math.random() * 1848 + 100; ball.y = 100; // Calculate trajectory toward goal var targetX = 2048 / 2 + (Math.random() - 0.5) * 600; var targetY = 2732 - 250; var distance = Math.sqrt(Math.pow(targetX - ball.x, 2) + Math.pow(targetY - ball.y, 2)); var speed = 8 + Math.random() * 4 * gameSpeed; ball.speedX = (targetX - ball.x) / distance * speed; ball.speedY = (targetY - ball.y) / distance * speed; ball.lastY = ball.y; ball.lastActive = true; soccerBalls.push(ball); game.addChild(ball); } // Main game update loop game.update = function () { // Update ball spawn counter ballSpawnCounter++; if (ballSpawnCounter >= ballSpawnRate) { spawnSoccerBall(); ballSpawnCounter = 0; // Increase difficulty over time if (ballSpawnRate > 60) { ballSpawnRate = Math.max(60, ballSpawnRate - 1); } gameSpeed += 0.01; } // Update soccer balls for (var i = soccerBalls.length - 1; i >= 0; i--) { var ball = soccerBalls[i]; if (!ball.isActive) continue; // Check if ball went off screen (missed goal) if (ball.lastY < 2732 - 180 && ball.y >= 2732 - 180 && ball.isActive) { // Check if ball is within goal area if (ball.x > 2048 / 2 - 400 && ball.x < 2048 / 2 + 400) { // Check if goalkeeper saved it if (ball.intersects(goalkeeper)) { // SAVE! ball.isActive = false; LK.setScore(LK.getScore() + 10); consecutiveSaves++; // Bonus points for consecutive saves if (consecutiveSaves > 1) { LK.setScore(LK.getScore() + consecutiveSaves * 5); } // Diving save bonus if (goalkeeper.isDiving) { LK.setScore(LK.getScore() + 20); LK.effects.flashObject(goalkeeper, 0xffff00, 500); } LK.getSound('save').play(); LK.effects.flashObject(ball, 0x00ff00, 300); // Trigger goalkeeper dive animation var direction = ball.x > goalkeeper.x ? 1 : -1; goalkeeper.dive(direction); } else { // GOAL! goalsAllowed++; consecutiveSaves = 0; LK.getSound('goal').play(); LK.effects.flashScreen(0xff0000, 500); if (goalsAllowed >= maxGoals) { LK.getSound('whistle').play(); LK.showGameOver(); return; } } // Remove ball ball.destroy(); soccerBalls.splice(i, 1); continue; } } // Remove balls that go completely off screen if (ball.y > 2732 + 100 || ball.x < -100 || ball.x > 2148) { ball.destroy(); soccerBalls.splice(i, 1); continue; } // Update last position ball.lastY = ball.y; } // Update UI scoreTxt.setText('Score: ' + LK.getScore()); goalsTxt.setText('Goals: ' + goalsAllowed + '/' + maxGoals); streakTxt.setText('Saves: ' + consecutiveSaves); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Goalkeeper = Container.expand(function () {
var self = Container.call(this);
var keeperGraphics = self.attachAsset('goalkeeper', {
anchorX: 0.5,
anchorY: 1.0
});
self.minX = 0;
self.maxX = 0;
self.isDiving = false;
self.dive = function (direction) {
if (!self.isDiving) {
self.isDiving = true;
var targetX = direction > 0 ? self.maxX : self.minX;
tween(self, {
x: targetX,
scaleX: direction > 0 ? 1.2 : -1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1
}, {
duration: 200
});
self.isDiving = false;
}
});
}
};
return self;
});
var SoccerBall = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('soccerBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.isActive = true;
self.update = function () {
if (self.isActive) {
self.x += self.speedX;
self.y += self.speedY;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4CAF50
});
/****
* Game Code
****/
// Game variables
var goalkeeper;
var soccerBalls = [];
var goalsAllowed = 0;
var maxGoals = 5;
var consecutiveSaves = 0;
var ballSpawnCounter = 0;
var ballSpawnRate = 120; // frames between balls
var gameSpeed = 1;
var isDragging = false;
// Create goal structure
var goalBackground = game.addChild(LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0.3
}));
goalBackground.x = 2048 / 2;
goalBackground.y = 2732 - 200;
// Goal posts
var leftPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1.0
}));
leftPost.x = 2048 / 2 - 400;
leftPost.y = 2732 - 200;
var rightPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1.0
}));
rightPost.x = 2048 / 2 + 400;
rightPost.y = 2732 - 200;
// Goal line
var goalLine = game.addChild(LK.getAsset('goalLine', {
anchorX: 0.5,
anchorY: 0.5
}));
goalLine.x = 2048 / 2;
goalLine.y = 2732 - 200;
// Create goalkeeper
goalkeeper = game.addChild(new Goalkeeper());
goalkeeper.x = 2048 / 2;
goalkeeper.y = 2732 - 200;
goalkeeper.minX = 2048 / 2 - 350;
goalkeeper.maxX = 2048 / 2 + 350;
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var goalsTxt = new Text2('Goals: 0/5', {
size: 60,
fill: 0xFFFFFF
});
goalsTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(goalsTxt);
var streakTxt = new Text2('Saves: 0', {
size: 50,
fill: 0xFFFF00
});
streakTxt.anchor.set(0, 0);
LK.gui.left.addChild(streakTxt);
// Touch/drag controls
game.down = function (x, y, obj) {
isDragging = true;
var clampedX = Math.max(goalkeeper.minX, Math.min(goalkeeper.maxX, x));
goalkeeper.x = clampedX;
};
game.move = function (x, y, obj) {
if (isDragging) {
var clampedX = Math.max(goalkeeper.minX, Math.min(goalkeeper.maxX, x));
goalkeeper.x = clampedX;
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Spawn soccer ball
function spawnSoccerBall() {
var ball = new SoccerBall();
// Random spawn position along top of screen
ball.x = Math.random() * 1848 + 100;
ball.y = 100;
// Calculate trajectory toward goal
var targetX = 2048 / 2 + (Math.random() - 0.5) * 600;
var targetY = 2732 - 250;
var distance = Math.sqrt(Math.pow(targetX - ball.x, 2) + Math.pow(targetY - ball.y, 2));
var speed = 8 + Math.random() * 4 * gameSpeed;
ball.speedX = (targetX - ball.x) / distance * speed;
ball.speedY = (targetY - ball.y) / distance * speed;
ball.lastY = ball.y;
ball.lastActive = true;
soccerBalls.push(ball);
game.addChild(ball);
}
// Main game update loop
game.update = function () {
// Update ball spawn counter
ballSpawnCounter++;
if (ballSpawnCounter >= ballSpawnRate) {
spawnSoccerBall();
ballSpawnCounter = 0;
// Increase difficulty over time
if (ballSpawnRate > 60) {
ballSpawnRate = Math.max(60, ballSpawnRate - 1);
}
gameSpeed += 0.01;
}
// Update soccer balls
for (var i = soccerBalls.length - 1; i >= 0; i--) {
var ball = soccerBalls[i];
if (!ball.isActive) continue;
// Check if ball went off screen (missed goal)
if (ball.lastY < 2732 - 180 && ball.y >= 2732 - 180 && ball.isActive) {
// Check if ball is within goal area
if (ball.x > 2048 / 2 - 400 && ball.x < 2048 / 2 + 400) {
// Check if goalkeeper saved it
if (ball.intersects(goalkeeper)) {
// SAVE!
ball.isActive = false;
LK.setScore(LK.getScore() + 10);
consecutiveSaves++;
// Bonus points for consecutive saves
if (consecutiveSaves > 1) {
LK.setScore(LK.getScore() + consecutiveSaves * 5);
}
// Diving save bonus
if (goalkeeper.isDiving) {
LK.setScore(LK.getScore() + 20);
LK.effects.flashObject(goalkeeper, 0xffff00, 500);
}
LK.getSound('save').play();
LK.effects.flashObject(ball, 0x00ff00, 300);
// Trigger goalkeeper dive animation
var direction = ball.x > goalkeeper.x ? 1 : -1;
goalkeeper.dive(direction);
} else {
// GOAL!
goalsAllowed++;
consecutiveSaves = 0;
LK.getSound('goal').play();
LK.effects.flashScreen(0xff0000, 500);
if (goalsAllowed >= maxGoals) {
LK.getSound('whistle').play();
LK.showGameOver();
return;
}
}
// Remove ball
ball.destroy();
soccerBalls.splice(i, 1);
continue;
}
}
// Remove balls that go completely off screen
if (ball.y > 2732 + 100 || ball.x < -100 || ball.x > 2148) {
ball.destroy();
soccerBalls.splice(i, 1);
continue;
}
// Update last position
ball.lastY = ball.y;
}
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
goalsTxt.setText('Goals: ' + goalsAllowed + '/' + maxGoals);
streakTxt.setText('Saves: ' + consecutiveSaves);
};