User prompt
Add a 1vs1 mode where you face a bot, the first one to score or stop 5 goals wins ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add another mode where you have to stop goals that are increasingly difficult, unpredictable, and fast. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
After that, you have a menu with three difficulties: Easy, you only have to stop 25 slow goals, Normal, you have to stop 50 fast goals, and Hard, you have to stop 100 very fast goals.
Remix started
Copy Goalkeeper Hero
/****
* 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.targetX = 1024; // Center of goal
self.moveSpeed = 15;
self.update = function () {
// Smooth movement toward target position
var dx = self.targetX - self.x;
if (Math.abs(dx) > 5) {
self.x += dx * 0.2;
}
};
self.setTarget = function (x) {
// Keep goalkeeper within goal bounds
var goalLeft = 424;
var goalRight = 1624;
self.targetX = Math.max(goalLeft, Math.min(goalRight, x));
};
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.active = true;
self.update = function () {
if (!self.active) return;
self.x += self.speedX;
self.y += self.speedY;
// Add slight gravity effect
self.speedY += 0.2;
// Add curve effect if it exists
if (self.curveX) {
self.speedX += self.curveX;
}
// Add spin effect if it exists
if (self.spinSpeed) {
self.speedX += Math.sin(LK.ticks * 0.1) * self.spinSpeed;
}
// Add rotation if it exists
if (self.rotationSpeed) {
self.rotation += self.rotationSpeed;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var goalkeeper;
var balls = [];
var ballSpawnTimer = 0;
var ballSpawnInterval = 120; // 2 seconds at 60fps
var difficulty = 1;
var saves = 0;
var gameSpeed = 1;
var gameStarted = false;
var selectedDifficulty = null;
var survivalLevel = 1;
var survivalSavesForNextLevel = 10;
var difficultySettings = {
easy: {
goalTarget: 25,
ballSpeed: 1,
spawnRate: 150,
name: "Easy"
},
normal: {
goalTarget: 50,
ballSpeed: 1.5,
spawnRate: 120,
name: "Normal"
},
hard: {
goalTarget: 100,
ballSpeed: 2.5,
spawnRate: 90,
name: "Hard"
},
survival: {
goalTarget: Infinity,
ballSpeed: 1,
spawnRate: 180,
name: "Survival"
}
};
// Create field
var field = game.addChild(LK.getAsset('field', {
anchorX: 0,
anchorY: 1,
x: 0,
y: 2732
}));
// Create goal structure
var goalNet = game.addChild(LK.getAsset('goalNet', {
width: 1200,
anchorX: 0.5,
anchorY: 1,
x: 1024,
y: 2732
}));
goalNet.alpha = 0.3;
var leftPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1,
x: 424,
y: 2732
}));
var rightPost = game.addChild(LK.getAsset('goalPost', {
anchorX: 0.5,
anchorY: 1,
x: 1624,
y: 2732
}));
// Create goalkeeper
goalkeeper = game.addChild(new Goalkeeper());
goalkeeper.x = 1024;
goalkeeper.y = 2732;
// Create UI
var scoreTxt = new Text2('Saves: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create difficulty selection menu
var menuContainer = new Container();
game.addChild(menuContainer);
var titleText = new Text2('Select Difficulty', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
menuContainer.addChild(titleText);
// Easy button
var easyButton = new Container();
var easyBg = LK.getAsset('goalPost', {
width: 400,
height: 120,
anchorX: 0.5,
anchorY: 0.5,
color: 0x00ff00
});
var easyText = new Text2('Easy - 25 Goals', {
size: 60,
fill: 0x000000
});
easyText.anchor.set(0.5, 0.5);
easyButton.addChild(easyBg);
easyButton.addChild(easyText);
easyButton.x = 1024;
easyButton.y = 1200;
menuContainer.addChild(easyButton);
// Normal button
var normalButton = new Container();
var normalBg = LK.getAsset('goalPost', {
width: 400,
height: 120,
anchorX: 0.5,
anchorY: 0.5,
color: 0xffff00
});
var normalText = new Text2('Normal - 50 Goals', {
size: 60,
fill: 0x000000
});
normalText.anchor.set(0.5, 0.5);
normalButton.addChild(normalBg);
normalButton.addChild(normalText);
normalButton.x = 1024;
normalButton.y = 1400;
menuContainer.addChild(normalButton);
// Hard button
var hardButton = new Container();
var hardBg = LK.getAsset('goalPost', {
width: 400,
height: 120,
anchorX: 0.5,
anchorY: 0.5,
color: 0xff0000
});
var hardText = new Text2('Hard - 100 Goals', {
size: 60,
fill: 0x000000
});
hardText.anchor.set(0.5, 0.5);
hardButton.addChild(hardBg);
hardButton.addChild(hardText);
hardButton.x = 1024;
hardButton.y = 1600;
menuContainer.addChild(hardButton);
// Survival button
var survivalButton = new Container();
var survivalBg = LK.getAsset('goalPost', {
width: 400,
height: 120,
anchorX: 0.5,
anchorY: 0.5,
color: 0x800080
});
var survivalText = new Text2('Survival Mode', {
size: 60,
fill: 0xFFFFFF
});
survivalText.anchor.set(0.5, 0.5);
survivalButton.addChild(survivalBg);
survivalButton.addChild(survivalText);
survivalButton.x = 1024;
survivalButton.y = 1800;
menuContainer.addChild(survivalButton);
// Button click handlers
easyButton.down = function (x, y, obj) {
startGame('easy');
};
normalButton.down = function (x, y, obj) {
startGame('normal');
};
hardButton.down = function (x, y, obj) {
startGame('hard');
};
survivalButton.down = function (x, y, obj) {
startGame('survival');
};
// Function to start game with selected difficulty
function startGame(difficultyLevel) {
selectedDifficulty = difficultyLevel;
gameStarted = true;
menuContainer.visible = false;
var settings = difficultySettings[difficultyLevel];
gameSpeed = settings.ballSpeed;
ballSpawnInterval = settings.spawnRate;
if (difficultyLevel === 'survival') {
survivalLevel = 1;
survivalSavesForNextLevel = 10;
scoreTxt.setText('Saves: 0 - Level 1 (Survival)');
} else {
scoreTxt.setText('Saves: 0/' + settings.goalTarget + ' (' + settings.name + ')');
}
}
// Ball spawning function
function spawnBall() {
var ball = new SoccerBall();
// Random spawn position across the field width
ball.x = Math.random() * 1600 + 224;
ball.y = 100;
if (selectedDifficulty === 'survival') {
// Survival mode: increasingly unpredictable and difficult shots
var currentSpeed = 3 + survivalLevel * 0.8;
var targetX = Math.random() * 1200 + 424;
var targetY = 2732 - 100;
var distance = Math.sqrt(Math.pow(targetX - ball.x, 2) + Math.pow(targetY - ball.y, 2));
ball.speedX = (targetX - ball.x) / distance * currentSpeed;
ball.speedY = (targetY - ball.y) / distance * currentSpeed;
// Add curve effect for higher levels
if (survivalLevel > 3) {
ball.curveX = (Math.random() - 0.5) * 0.5;
}
// Add spin effect for even higher levels
if (survivalLevel > 6) {
ball.spinSpeed = (Math.random() - 0.5) * 0.2;
ball.rotationSpeed = Math.random() * 0.3;
}
} else {
// Normal mode behavior
var targetX = Math.random() * 1200 + 424; // Random point in goal area
var targetY = 2732 - 100; // Goal height
var distance = Math.sqrt(Math.pow(targetX - ball.x, 2) + Math.pow(targetY - ball.y, 2));
var speed = 3 * gameSpeed;
ball.speedX = (targetX - ball.x) / distance * speed;
ball.speedY = (targetY - ball.y) / distance * speed;
}
balls.push(ball);
game.addChild(ball);
LK.getSound('kick').play();
}
// Touch controls
var isDragging = false;
game.down = function (x, y, obj) {
isDragging = true;
goalkeeper.setTarget(x);
};
game.move = function (x, y, obj) {
if (isDragging) {
goalkeeper.setTarget(x);
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Main game loop
game.update = function () {
// Don't run game logic until difficulty is selected
if (!gameStarted) return;
// Spawn balls
ballSpawnTimer++;
if (ballSpawnTimer >= ballSpawnInterval) {
spawnBall();
ballSpawnTimer = 0;
}
// Update and check balls
for (var i = balls.length - 1; i >= 0; i--) {
var ball = balls[i];
if (!ball.active) continue;
// Check if ball went past goal line (goal scored)
if (ball.y > 2732) {
LK.getSound('goal').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
// Check for save (collision with goalkeeper)
if (ball.intersects(goalkeeper) && ball.active) {
ball.active = false;
saves++;
LK.setScore(saves);
if (selectedDifficulty === 'survival') {
// Check for level progression in survival mode
if (saves >= survivalSavesForNextLevel) {
survivalLevel++;
survivalSavesForNextLevel += 10 + survivalLevel * 2;
// Increase difficulty - faster spawn rate and more unpredictable shots
ballSpawnInterval = Math.max(45, 180 - survivalLevel * 15);
// Visual feedback for level up
LK.effects.flashScreen(0x00ff00, 500);
tween(scoreTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(scoreTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
scoreTxt.setText('Saves: ' + saves + ' - Level ' + survivalLevel + ' (Survival)');
} else {
var settings = difficultySettings[selectedDifficulty];
scoreTxt.setText('Saves: ' + saves + '/' + settings.goalTarget + ' (' + settings.name + ')');
}
// Visual feedback for save
LK.effects.flashObject(goalkeeper, 0x00ff00, 500);
tween(goalkeeper, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(goalkeeper, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
LK.getSound('save').play();
// Remove ball
ball.destroy();
balls.splice(i, 1);
continue;
}
// Remove balls that went off screen (missed)
if (ball.y < -100 || ball.x < -100 || ball.x > 2148) {
ball.destroy();
balls.splice(i, 1);
continue;
}
// Check if ball is out of bounds horizontally (safe)
if ((ball.x < 424 || ball.x > 1624) && ball.y > 2332) {
ball.active = false;
ball.destroy();
balls.splice(i, 1);
continue;
}
}
// Win condition - based on selected difficulty (survival mode never wins, only tracks progress)
if (selectedDifficulty !== 'survival') {
var settings = difficultySettings[selectedDifficulty];
if (saves >= settings.goalTarget) {
LK.showYouWin();
}
}
};
// LK engine handles pause/play functionality automatically through built-in systems ===================================================================
--- original.js
+++ change.js
@@ -43,8 +43,20 @@
self.x += self.speedX;
self.y += self.speedY;
// Add slight gravity effect
self.speedY += 0.2;
+ // Add curve effect if it exists
+ if (self.curveX) {
+ self.speedX += self.curveX;
+ }
+ // Add spin effect if it exists
+ if (self.spinSpeed) {
+ self.speedX += Math.sin(LK.ticks * 0.1) * self.spinSpeed;
+ }
+ // Add rotation if it exists
+ if (self.rotationSpeed) {
+ self.rotation += self.rotationSpeed;
+ }
};
return self;
});
@@ -67,8 +79,10 @@
var saves = 0;
var gameSpeed = 1;
var gameStarted = false;
var selectedDifficulty = null;
+var survivalLevel = 1;
+var survivalSavesForNextLevel = 10;
var difficultySettings = {
easy: {
goalTarget: 25,
ballSpeed: 1,
@@ -85,8 +99,14 @@
goalTarget: 100,
ballSpeed: 2.5,
spawnRate: 90,
name: "Hard"
+ },
+ survival: {
+ goalTarget: Infinity,
+ ballSpeed: 1,
+ spawnRate: 180,
+ name: "Survival"
}
};
// Create field
var field = game.addChild(LK.getAsset('field', {
@@ -194,8 +214,27 @@
hardButton.addChild(hardText);
hardButton.x = 1024;
hardButton.y = 1600;
menuContainer.addChild(hardButton);
+// Survival button
+var survivalButton = new Container();
+var survivalBg = LK.getAsset('goalPost', {
+ width: 400,
+ height: 120,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ color: 0x800080
+});
+var survivalText = new Text2('Survival Mode', {
+ size: 60,
+ fill: 0xFFFFFF
+});
+survivalText.anchor.set(0.5, 0.5);
+survivalButton.addChild(survivalBg);
+survivalButton.addChild(survivalText);
+survivalButton.x = 1024;
+survivalButton.y = 1800;
+menuContainer.addChild(survivalButton);
// Button click handlers
easyButton.down = function (x, y, obj) {
startGame('easy');
};
@@ -204,31 +243,59 @@
};
hardButton.down = function (x, y, obj) {
startGame('hard');
};
+survivalButton.down = function (x, y, obj) {
+ startGame('survival');
+};
// Function to start game with selected difficulty
function startGame(difficultyLevel) {
selectedDifficulty = difficultyLevel;
gameStarted = true;
menuContainer.visible = false;
var settings = difficultySettings[difficultyLevel];
gameSpeed = settings.ballSpeed;
ballSpawnInterval = settings.spawnRate;
- scoreTxt.setText('Saves: 0/' + settings.goalTarget + ' (' + settings.name + ')');
+ if (difficultyLevel === 'survival') {
+ survivalLevel = 1;
+ survivalSavesForNextLevel = 10;
+ scoreTxt.setText('Saves: 0 - Level 1 (Survival)');
+ } else {
+ scoreTxt.setText('Saves: 0/' + settings.goalTarget + ' (' + settings.name + ')');
+ }
}
// Ball spawning function
function spawnBall() {
var ball = new SoccerBall();
// Random spawn position across the field width
ball.x = Math.random() * 1600 + 224;
ball.y = 100;
- // Calculate trajectory toward goal
- var targetX = Math.random() * 1200 + 424; // Random point in goal area
- var targetY = 2732 - 100; // Goal height
- var distance = Math.sqrt(Math.pow(targetX - ball.x, 2) + Math.pow(targetY - ball.y, 2));
- var speed = 3 * gameSpeed;
- ball.speedX = (targetX - ball.x) / distance * speed;
- ball.speedY = (targetY - ball.y) / distance * speed;
+ if (selectedDifficulty === 'survival') {
+ // Survival mode: increasingly unpredictable and difficult shots
+ var currentSpeed = 3 + survivalLevel * 0.8;
+ var targetX = Math.random() * 1200 + 424;
+ var targetY = 2732 - 100;
+ var distance = Math.sqrt(Math.pow(targetX - ball.x, 2) + Math.pow(targetY - ball.y, 2));
+ ball.speedX = (targetX - ball.x) / distance * currentSpeed;
+ ball.speedY = (targetY - ball.y) / distance * currentSpeed;
+ // Add curve effect for higher levels
+ if (survivalLevel > 3) {
+ ball.curveX = (Math.random() - 0.5) * 0.5;
+ }
+ // Add spin effect for even higher levels
+ if (survivalLevel > 6) {
+ ball.spinSpeed = (Math.random() - 0.5) * 0.2;
+ ball.rotationSpeed = Math.random() * 0.3;
+ }
+ } else {
+ // Normal mode behavior
+ var targetX = Math.random() * 1200 + 424; // Random point in goal area
+ var targetY = 2732 - 100; // Goal height
+ var distance = Math.sqrt(Math.pow(targetX - ball.x, 2) + Math.pow(targetY - ball.y, 2));
+ var speed = 3 * gameSpeed;
+ ball.speedX = (targetX - ball.x) / distance * speed;
+ ball.speedY = (targetY - ball.y) / distance * speed;
+ }
balls.push(ball);
game.addChild(ball);
LK.getSound('kick').play();
}
@@ -271,10 +338,39 @@
if (ball.intersects(goalkeeper) && ball.active) {
ball.active = false;
saves++;
LK.setScore(saves);
- var settings = difficultySettings[selectedDifficulty];
- scoreTxt.setText('Saves: ' + saves + '/' + settings.goalTarget + ' (' + settings.name + ')');
+ if (selectedDifficulty === 'survival') {
+ // Check for level progression in survival mode
+ if (saves >= survivalSavesForNextLevel) {
+ survivalLevel++;
+ survivalSavesForNextLevel += 10 + survivalLevel * 2;
+ // Increase difficulty - faster spawn rate and more unpredictable shots
+ ballSpawnInterval = Math.max(45, 180 - survivalLevel * 15);
+ // Visual feedback for level up
+ LK.effects.flashScreen(0x00ff00, 500);
+ tween(scoreTxt, {
+ scaleX: 1.5,
+ scaleY: 1.5
+ }, {
+ duration: 300,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(scoreTxt, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 300,
+ easing: tween.easeIn
+ });
+ }
+ });
+ }
+ scoreTxt.setText('Saves: ' + saves + ' - Level ' + survivalLevel + ' (Survival)');
+ } else {
+ var settings = difficultySettings[selectedDifficulty];
+ scoreTxt.setText('Saves: ' + saves + '/' + settings.goalTarget + ' (' + settings.name + ')');
+ }
// Visual feedback for save
LK.effects.flashObject(goalkeeper, 0x00ff00, 500);
tween(goalkeeper, {
scaleX: 1.3,
@@ -311,11 +407,13 @@
balls.splice(i, 1);
continue;
}
}
- // Win condition - based on selected difficulty
- var settings = difficultySettings[selectedDifficulty];
- if (saves >= settings.goalTarget) {
- LK.showYouWin();
+ // Win condition - based on selected difficulty (survival mode never wins, only tracks progress)
+ if (selectedDifficulty !== 'survival') {
+ var settings = difficultySettings[selectedDifficulty];
+ if (saves >= settings.goalTarget) {
+ LK.showYouWin();
+ }
}
};
// LK engine handles pause/play functionality automatically through built-in systems
\ No newline at end of file
A common soccer ball made of leather. In-Game asset. 2d. High contrast. No shadows. Cartoon
5-year-old boy with his back turned wearing a worn t-shirt and work gloves. In-Game asset. 2d. High contrast. No shadows. Cartoon
A religious street area. In-Game asset. 2d. High contrast. No shadows
Goal net. In-Game asset. 2d. High contrast. No shadows