/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballSprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballSprite.width / 2; self.vx = 0; self.vy = 0; self.maxSpeed = 60; self.friction = 0.96; self.owner = null; // If not null, ball is attached to player self.update = function () { if (self.owner) { // Stick to owner's feet var angle = self.owner.facing; self.x = self.owner.x + Math.cos(angle) * (self.owner.radius + self.radius + 10); self.y = self.owner.y + Math.sin(angle) * (self.owner.radius + self.radius + 10); self.vx = 0; self.vy = 0; } else { // Move ball self.x += self.vx; self.y += self.vy; self.vx *= self.friction; self.vy *= self.friction; if (Math.abs(self.vx) < 0.1) self.vx = 0; if (Math.abs(self.vy) < 0.1) self.vy = 0; } }; // Kick the ball in a direction self.kick = function (angle, power) { self.owner = null; self.vx = Math.cos(angle) * power; self.vy = Math.sin(angle) * power; if (Math.sqrt(self.vx * self.vx + self.vy * self.vy) > self.maxSpeed) { var mag = Math.sqrt(self.vx * self.vx + self.vy * self.vy); self.vx = self.vx / mag * self.maxSpeed; self.vy = self.vy / mag * self.maxSpeed; } LK.getSound('kick').play(); }; return self; }); // Defender class var Defender = Container.expand(function () { var self = Container.call(this); var defenderSprite = self.attachAsset('defender', { anchorX: 0.5, anchorY: 0.5 }); self.radius = defenderSprite.width / 2; self.speed = 12 + Math.random() * 5; self.facing = Math.PI / 2; self.target = null; // Ball or player self.cooldown = 0; // For tackle attempts self.update = function () { if (self.cooldown > 0) self.cooldown--; // Simple AI: move toward ball if not owned, else toward player var tx, ty; if (ball.owner) { tx = player.x; ty = player.y; } else { tx = ball.x; ty = ball.y; } var dx = tx - self.x; var dy = ty - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 1) { self.facing = Math.atan2(dy, dx); var moveDist = Math.min(self.speed, dist); self.x += Math.cos(self.facing) * moveDist; self.y += Math.sin(self.facing) * moveDist; } }; // Tackle attempt self.tryTackle = function () { if (self.cooldown > 0) return false; if (ball.owner === player) { var dx = self.x - player.x; var dy = self.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.radius + player.radius + 2) { // Smaller tackle range // Successful tackle ball.owner = null; ball.vx = (Math.random() - 0.5) * 20; ball.vy = (Math.random() - 0.5) * 20; self.cooldown = 60; LK.getSound('tackle').play(); // Flash player red LK.effects.flashObject(player, 0xff0000, 400); return true; } } return false; }; return self; }); // Goalkeeper class var Goalkeeper = Container.expand(function () { var self = Container.call(this); var keeperSprite = self.attachAsset('goalkeeper', { anchorX: 0.5, anchorY: 0.5 }); self.radius = keeperSprite.width / 2; self.speed = 15; self.facing = -Math.PI / 2; self.goalX = 0; self.goalY = 0; self.range = 320; // How far left/right can move self.update = function () { // Track ball's y, but only move left/right var targetX = Math.max(goal.x - self.range, Math.min(goal.x + self.range, ball.x)); var dx = targetX - self.x; if (Math.abs(dx) > 2) { self.x += Math.sign(dx) * Math.min(self.speed, Math.abs(dx)); } // Try to block ball if close var dist = Math.sqrt((self.x - ball.x) * (self.x - ball.x) + (self.y - ball.y) * (self.y - ball.y)); if (dist < self.radius + ball.radius + 10 && !ball.owner) { // Block: stop ball, send it back ball.vx = -ball.vx * 0.7; ball.vy = -Math.abs(ball.vy) * 0.7; LK.effects.flashObject(self, 0x00ff00, 300); } }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.radius = playerSprite.width / 2; self.speed = 40; self.facing = -Math.PI / 2; // Up self.hasBall = false; self.update = function () { // Facing is set by movement direction }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x006600 }); /**** * Game Code ****/ // Pitch // Player (footballer) // Ball // Opponent (defender) // Goalkeeper // Goal area // Pitch outline // Sound effects var pitch = LK.getAsset('pitch', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(pitch); // Goal (top) var goal = LK.getAsset('goal', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 120 }); game.addChild(goal); // Goalkeeper var goalkeeper = new Goalkeeper(); goalkeeper.x = 2048 / 2; goalkeeper.y = goal.y + 80; game.addChild(goalkeeper); // Player var player = new Player(); player.x = 2048 / 2; player.y = 2732 - 400; game.addChild(player); // Ball var ball = new Ball(); ball.x = player.x; ball.y = player.y - player.radius - ball.radius - 10; ball.owner = player; game.addChild(ball); // Defenders var defenders = []; function spawnDefenders(level) { // Remove old defenders for (var i = 0; i < defenders.length; ++i) { defenders[i].destroy(); } defenders = []; // Place defenders in a line, with some random offset var n = 1 + Math.floor(level / 3); // Fewer defenders, slower increase for (var i = 0; i < n; ++i) { var d = new Defender(); d.x = 2048 / 2 + (i - (n - 1) / 2) * 320 + (Math.random() - 0.5) * 60; d.y = 1100 + i * 80 + Math.random() * 60; defenders.push(d); game.addChild(d); } } var level = 1; spawnDefenders(level); // Score and timer var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: 0xFFF700 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var timer = 60; // seconds var timerTxt = new Text2('1:00', { size: 90, fill: 0xFFFFFF }); timerTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timerTxt); timerTxt.y = 120; // Touch controls var dragNode = null; var dragOffsetX = 0; var dragOffsetY = 0; var lastTouchX = 0; var lastTouchY = 0; var isKicking = false; var kickStartX = 0; var kickStartY = 0; var kickStartTime = 0; // Helper: clamp player inside pitch function clampPlayer() { var minX = pitch.x - pitch.width / 2 + player.radius + 20; var maxX = pitch.x + pitch.width / 2 - player.radius - 20; var minY = pitch.y - pitch.height / 2 + player.radius + 20; var maxY = pitch.y + pitch.height / 2 - player.radius - 20; player.x = Math.max(minX, Math.min(maxX, player.x)); player.y = Math.max(minY, Math.min(maxY, player.y)); } // Helper: clamp ball inside pitch function clampBall() { var minX = pitch.x - pitch.width / 2 + ball.radius + 10; var maxX = pitch.x + pitch.width / 2 - ball.radius - 10; var minY = pitch.y - pitch.height / 2 + ball.radius + 10; var maxY = pitch.y + pitch.height / 2 - ball.radius - 10; ball.x = Math.max(minX, Math.min(maxX, ball.x)); ball.y = Math.max(minY, Math.min(maxY, ball.y)); } // Move handler: drag to move player, swipe to kick function handleMove(x, y, obj) { lastTouchX = x; lastTouchY = y; if (dragNode === player) { // Move player toward touch, but limit speed var dx = x - player.x; var dy = y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { var moveDist = Math.min(player.speed, dist); player.x += dx / dist * moveDist; player.y += dy / dist * moveDist; player.facing = Math.atan2(dy, dx); } clampPlayer(); } } game.move = handleMove; game.down = function (x, y, obj) { // If touch is near player, start drag var dx = x - player.x; var dy = y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + 60) { dragNode = player; kickStartX = x; kickStartY = y; kickStartTime = LK.ticks; isKicking = false; } }; game.up = function (x, y, obj) { if (dragNode === player) { // If swipe is fast and long enough, kick var dx = x - kickStartX; var dy = y - kickStartY; var dist = Math.sqrt(dx * dx + dy * dy); var dt = LK.ticks - kickStartTime; if (dist > 80 && dt < 40 && ball.owner === player) { // Kick in swipe direction, power based on swipe length var angle = Math.atan2(dy, dx); var power = Math.min(60, dist / 2 + 18); ball.kick(angle, power); isKicking = true; } } dragNode = null; }; // Timer logic var timerInterval = LK.setInterval(function () { if (timer > 0) { timer--; var min = Math.floor(timer / 60); var sec = timer % 60; timerTxt.setText(min + ":" + (sec < 10 ? "0" : "") + sec); if (timer === 0) { LK.getSound('whistle').play(); LK.showGameOver(); } } }, 1000); // Goal detection function checkGoal() { // Ball inside goal area (top) if (!ball.owner && ball.y - ball.radius < goal.y + goal.height / 2 && ball.y + ball.radius > goal.y - goal.height / 2 && ball.x > goal.x - goal.width / 2 + 40 && ball.x < goal.x + goal.width / 2 - 40) { // Score! score++; LK.setScore(score); scoreTxt.setText(score); LK.getSound('goal').play(); LK.effects.flashScreen(0xffff00, 600); // Reset positions resetPositions(); // Increase difficulty every 2 goals if (score % 2 === 0) { level++; spawnDefenders(level); } } } // Reset player, ball, keeper positions function resetPositions() { player.x = 2048 / 2; player.y = 2732 - 400; player.facing = -Math.PI / 2; ball.x = player.x; ball.y = player.y - player.radius - ball.radius - 10; ball.owner = player; ball.vx = 0; ball.vy = 0; goalkeeper.x = 2048 / 2; goalkeeper.y = goal.y + 80; for (var i = 0; i < defenders.length; ++i) { defenders[i].x = 2048 / 2 + (i - (defenders.length - 1) / 2) * 320 + (Math.random() - 0.5) * 60; defenders[i].y = 1100 + i * 80 + Math.random() * 60; defenders[i].cooldown = 0; } } // Main update loop game.update = function () { // Update player player.update(); // Update ball ball.update(); clampBall(); // Ball pickup if (!ball.owner) { var dx = player.x - ball.x; var dy = player.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + ball.radius + 10) { ball.owner = player; } } // Update defenders for (var i = 0; i < defenders.length; ++i) { defenders[i].update(); defenders[i].tryTackle(); } // Update goalkeeper goalkeeper.update(); // Prevent player/defender overlap for (var i = 0; i < defenders.length; ++i) { var d = defenders[i]; var dx = d.x - player.x; var dy = d.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = d.radius + player.radius + 8; if (dist < minDist) { var overlap = minDist - dist; if (dist > 0) { d.x += dx / dist * overlap / 2; d.y += dy / dist * overlap / 2; player.x -= dx / dist * overlap / 2; player.y -= dy / dist * overlap / 2; clampPlayer(); } } } // Prevent defenders from overlapping each other for (var i = 0; i < defenders.length; ++i) { for (var j = i + 1; j < defenders.length; ++j) { var d1 = defenders[i]; var d2 = defenders[j]; var dx = d2.x - d1.x; var dy = d2.y - d1.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = d1.radius + d2.radius + 4; if (dist < minDist && dist > 0) { var overlap = minDist - dist; d1.x -= dx / dist * overlap / 2; d1.y -= dy / dist * overlap / 2; d2.x += dx / dist * overlap / 2; d2.y += dy / dist * overlap / 2; } } } // Prevent ball from sticking to defenders for (var i = 0; i < defenders.length; ++i) { var d = defenders[i]; var dx = d.x - ball.x; var dy = d.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = d.radius + ball.radius + 2; if (dist < minDist && !ball.owner) { var overlap = minDist - dist; if (dist > 0) { ball.x -= dx / dist * overlap / 2; ball.y -= dy / dist * overlap / 2; d.x += dx / dist * overlap / 2; d.y += dy / dist * overlap / 2; } } } // Prevent player from entering goal area var goalAreaY = goal.y + goal.height / 2 + player.radius + 10; if (player.y - player.radius < goalAreaY) { player.y = goalAreaY + player.radius; } // Check for goal checkGoal(); };
===================================================================
--- original.js
+++ change.js
@@ -58,9 +58,9 @@
anchorX: 0.5,
anchorY: 0.5
});
self.radius = defenderSprite.width / 2;
- self.speed = 18 + Math.random() * 8;
+ self.speed = 12 + Math.random() * 5;
self.facing = Math.PI / 2;
self.target = null; // Ball or player
self.cooldown = 0; // For tackle attempts
self.update = function () {
@@ -90,9 +90,10 @@
if (ball.owner === player) {
var dx = self.x - player.x;
var dy = self.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
- if (dist < self.radius + player.radius + 10) {
+ if (dist < self.radius + player.radius + 2) {
+ // Smaller tackle range
// Successful tackle
ball.owner = null;
ball.vx = (Math.random() - 0.5) * 20;
ball.vy = (Math.random() - 0.5) * 20;
@@ -114,9 +115,9 @@
anchorX: 0.5,
anchorY: 0.5
});
self.radius = keeperSprite.width / 2;
- self.speed = 22;
+ self.speed = 15;
self.facing = -Math.PI / 2;
self.goalX = 0;
self.goalY = 0;
self.range = 320; // How far left/right can move
@@ -145,9 +146,9 @@
anchorX: 0.5,
anchorY: 0.5
});
self.radius = playerSprite.width / 2;
- self.speed = 32;
+ self.speed = 40;
self.facing = -Math.PI / 2; // Up
self.hasBall = false;
self.update = function () {
// Facing is set by movement direction
@@ -164,16 +165,16 @@
/****
* Game Code
****/
-// Sound effects
-// Pitch outline
-// Goal area
-// Goalkeeper
-// Opponent (defender)
-// Ball
-// Player (footballer)
// Pitch
+// Player (footballer)
+// Ball
+// Opponent (defender)
+// Goalkeeper
+// Goal area
+// Pitch outline
+// Sound effects
var pitch = LK.getAsset('pitch', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
@@ -212,9 +213,9 @@
defenders[i].destroy();
}
defenders = [];
// Place defenders in a line, with some random offset
- var n = 2 + Math.floor(level / 2);
+ var n = 1 + Math.floor(level / 3); // Fewer defenders, slower increase
for (var i = 0; i < n; ++i) {
var d = new Defender();
d.x = 2048 / 2 + (i - (n - 1) / 2) * 320 + (Math.random() - 0.5) * 60;
d.y = 1100 + i * 80 + Math.random() * 60;