/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Ball movement properties
self.speedX = 20;
self.speedY = 20;
self.maxSpeed = 32;
self.minSpeed = 16;
// Evasion properties
self.evasionDistance = 900; // Distance at which ball starts evading (tripled)
self.evasionStrength = 0.3; // How strongly the ball evades
// Direction change timer
self.directionChangeTimer = 0;
self.directionChangeInterval = 80; // Change direction more frequently
self.update = function () {
// Move the ball
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.x <= 30 || self.x >= 2048 - 30) {
self.speedX = -self.speedX;
self.x = Math.max(30, Math.min(2048 - 30, self.x));
}
if (self.y <= 30 || self.y >= 2732 - 30) {
self.speedY = -self.speedY;
self.y = Math.max(30, Math.min(2732 - 30, self.y));
}
// Bounce off obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (self.intersects(obstacle)) {
// Calculate which side was hit by checking previous position
var ballCenterX = self.x;
var ballCenterY = self.y;
var obstacleCenterX = obstacle.x;
var obstacleCenterY = obstacle.y;
var deltaX = ballCenterX - obstacleCenterX;
var deltaY = ballCenterY - obstacleCenterY;
// Get obstacle dimensions based on its asset
var obstacleHalfWidth = obstacle.width / 2;
var obstacleHalfHeight = obstacle.height / 2;
var ballRadius = 30;
// Determine bounce direction based on collision side
if (Math.abs(deltaX) > Math.abs(deltaY)) {
// Horizontal collision
self.speedX = -self.speedX;
// Push ball away from obstacle
if (deltaX > 0) {
self.x = obstacleCenterX + obstacleHalfWidth + ballRadius;
} else {
self.x = obstacleCenterX - obstacleHalfWidth - ballRadius;
}
} else {
// Vertical collision
self.speedY = -self.speedY;
// Push ball away from obstacle
if (deltaY > 0) {
self.y = obstacleCenterY + obstacleHalfHeight + ballRadius;
} else {
self.y = obstacleCenterY - obstacleHalfHeight - ballRadius;
}
}
break; // Only handle one collision per frame
}
}
// Player repulsion and evasive behavior
var distanceToPlayer = Math.sqrt(Math.pow(self.x - player.x, 2) + Math.pow(self.y - player.y, 2));
// Strong repulsion when very close to player
var repulsionDistance = 200; // Distance at which repulsion starts
var repulsionStrength = 1.5; // Strong repulsion force
if (distanceToPlayer < repulsionDistance && distanceToPlayer > 0) {
// Calculate direction away from player for repulsion
var repelAngle = Math.atan2(self.y - player.y, self.x - player.x);
// Apply strong repulsion force (inversely proportional to distance)
var repelForce = (repulsionDistance - distanceToPlayer) / repulsionDistance * repulsionStrength;
self.speedX += Math.cos(repelAngle) * repelForce;
self.speedY += Math.sin(repelAngle) * repelForce;
}
// Regular evasive behavior at longer distance
if (distanceToPlayer < self.evasionDistance) {
// Calculate direction away from player
var evadeAngle = Math.atan2(self.y - player.y, self.x - player.x);
// Apply evasion force
self.speedX += Math.cos(evadeAngle) * self.evasionStrength;
self.speedY += Math.sin(evadeAngle) * self.evasionStrength;
}
// Clamp speed to max values
var currentSpeed = Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY);
if (currentSpeed > self.maxSpeed) {
self.speedX = self.speedX / currentSpeed * self.maxSpeed;
self.speedY = self.speedY / currentSpeed * self.maxSpeed;
}
// Random direction changes (more frequent when being chased)
self.directionChangeTimer++;
var changeInterval = distanceToPlayer < self.evasionDistance ? self.directionChangeInterval / 2 : self.directionChangeInterval;
if (self.directionChangeTimer >= changeInterval) {
self.directionChangeTimer = 0;
self.directionChangeInterval = 40 + Math.random() * 120; // Faster random interval
// Random direction change
if (Math.random() < 0.7) {
// Higher chance of direction change
var angle = Math.random() * Math.PI * 2;
var speed = self.minSpeed + Math.random() * (self.maxSpeed - self.minSpeed);
self.speedX = Math.cos(angle) * speed;
self.speedY = Math.sin(angle) * speed;
}
}
};
return self;
});
var LongObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('longObstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Track last position for direction calculation
self.lastX = 0;
self.lastY = 0;
self.update = function () {
// Calculate movement direction and rotate player accordingly
var deltaX = self.x - self.lastX;
var deltaY = self.y - self.lastY;
// Only update rotation if there's significant movement
if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) {
// Calculate angle from movement direction
var angle = Math.atan2(deltaY, deltaX);
// Apply rotation to player graphics
playerGraphics.rotation = angle;
}
// Store current position for next frame
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var TallObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('tallObstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2196F3
});
/****
* Game Code
****/
// Game variables
var player;
var ball;
var obstacles = [];
var dragNode = null;
var gameWon = false;
// Initialize player
player = game.addChild(new Player());
player.x = 1024; // Center horizontally
player.y = 1800; // Near bottom of screen
// Initialize tracking variables for direction
player.lastX = player.x;
player.lastY = player.y;
// Initialize ball
ball = game.addChild(new Ball());
ball.x = 1024; // Center horizontally
ball.y = 400; // Upper portion of screen
// Set initial random direction for ball
var initialAngle = Math.random() * Math.PI * 2;
var initialSpeed = 20;
ball.speedX = Math.cos(initialAngle) * initialSpeed;
ball.speedY = Math.sin(initialAngle) * initialSpeed;
// Create obstacles in the arena with randomized positions
var numObstacles = 12; // Increased number of obstacles
for (var i = 0; i < numObstacles; i++) {
var obstacle;
var obstacleType = Math.random();
// Create different types of obstacles
if (obstacleType < 0.5) {
obstacle = game.addChild(new Obstacle()); // Regular square obstacles
} else if (obstacleType < 0.75) {
obstacle = game.addChild(new LongObstacle()); // Long horizontal obstacles
} else {
obstacle = game.addChild(new TallObstacle()); // Tall vertical obstacles
}
var validPosition = false;
var attempts = 0;
// Keep trying to find a valid position that doesn't overlap with player
while (!validPosition && attempts < 50) {
// Generate random position with safe margins from screen edges
obstacle.x = 200 + Math.random() * (2048 - 400); // 200px margin from edges
obstacle.y = 500 + Math.random() * (2732 - 700); // 500px from top, 200px from bottom
// Check distance from player starting position (1024, 1800)
var distanceFromPlayer = Math.sqrt(Math.pow(obstacle.x - 1024, 2) + Math.pow(obstacle.y - 1800, 2));
if (distanceFromPlayer > 250) {
// Must be at least 250px away from player
validPosition = true;
}
attempts++;
}
obstacle.lastIntersecting = false;
obstacles.push(obstacle);
}
// Tracking intersection state
var lastIntersecting = false;
// Store target position for smooth chasing
var targetX = 0;
var targetY = 0;
function handleMove(x, y, obj) {
if (dragNode && !gameWon) {
// Calculate target position with bounds
targetX = Math.max(40, Math.min(2048 - 40, x));
targetY = Math.max(40, Math.min(2732 - 40, y));
}
}
// Game input handlers
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameWon) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game update loop
game.update = function () {
if (gameWon) return;
// Smooth player chasing movement
if (dragNode) {
var followSpeed = 0.08; // Adjust this value to control lag (0.1 = faster, 0.05 = slower)
var deltaX = targetX - dragNode.x;
var deltaY = targetY - dragNode.y;
dragNode.x += deltaX * followSpeed;
dragNode.y += deltaY * followSpeed;
}
// Check for collision with obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var obstacleIntersecting = player.intersects(obstacle);
if (!obstacle.lastIntersecting && obstacleIntersecting) {
// Player hit an obstacle - game over!
gameWon = true;
// Flash effect red for failure
LK.effects.flashScreen(0xff0000, 1000);
// Show game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
break;
}
obstacle.lastIntersecting = obstacleIntersecting;
}
// Check for collision between player and ball
var currentIntersecting = player.intersects(ball);
if (!lastIntersecting && currentIntersecting) {
// Player caught the ball!
gameWon = true;
// Play catch sound
LK.getSound('catch').play();
// Flash effect
LK.effects.flashScreen(0x4CAF50, 1000);
// Show victory after a short delay
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
}
lastIntersecting = currentIntersecting;
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Ball movement properties
self.speedX = 20;
self.speedY = 20;
self.maxSpeed = 32;
self.minSpeed = 16;
// Evasion properties
self.evasionDistance = 900; // Distance at which ball starts evading (tripled)
self.evasionStrength = 0.3; // How strongly the ball evades
// Direction change timer
self.directionChangeTimer = 0;
self.directionChangeInterval = 80; // Change direction more frequently
self.update = function () {
// Move the ball
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.x <= 30 || self.x >= 2048 - 30) {
self.speedX = -self.speedX;
self.x = Math.max(30, Math.min(2048 - 30, self.x));
}
if (self.y <= 30 || self.y >= 2732 - 30) {
self.speedY = -self.speedY;
self.y = Math.max(30, Math.min(2732 - 30, self.y));
}
// Bounce off obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (self.intersects(obstacle)) {
// Calculate which side was hit by checking previous position
var ballCenterX = self.x;
var ballCenterY = self.y;
var obstacleCenterX = obstacle.x;
var obstacleCenterY = obstacle.y;
var deltaX = ballCenterX - obstacleCenterX;
var deltaY = ballCenterY - obstacleCenterY;
// Get obstacle dimensions based on its asset
var obstacleHalfWidth = obstacle.width / 2;
var obstacleHalfHeight = obstacle.height / 2;
var ballRadius = 30;
// Determine bounce direction based on collision side
if (Math.abs(deltaX) > Math.abs(deltaY)) {
// Horizontal collision
self.speedX = -self.speedX;
// Push ball away from obstacle
if (deltaX > 0) {
self.x = obstacleCenterX + obstacleHalfWidth + ballRadius;
} else {
self.x = obstacleCenterX - obstacleHalfWidth - ballRadius;
}
} else {
// Vertical collision
self.speedY = -self.speedY;
// Push ball away from obstacle
if (deltaY > 0) {
self.y = obstacleCenterY + obstacleHalfHeight + ballRadius;
} else {
self.y = obstacleCenterY - obstacleHalfHeight - ballRadius;
}
}
break; // Only handle one collision per frame
}
}
// Player repulsion and evasive behavior
var distanceToPlayer = Math.sqrt(Math.pow(self.x - player.x, 2) + Math.pow(self.y - player.y, 2));
// Strong repulsion when very close to player
var repulsionDistance = 200; // Distance at which repulsion starts
var repulsionStrength = 1.5; // Strong repulsion force
if (distanceToPlayer < repulsionDistance && distanceToPlayer > 0) {
// Calculate direction away from player for repulsion
var repelAngle = Math.atan2(self.y - player.y, self.x - player.x);
// Apply strong repulsion force (inversely proportional to distance)
var repelForce = (repulsionDistance - distanceToPlayer) / repulsionDistance * repulsionStrength;
self.speedX += Math.cos(repelAngle) * repelForce;
self.speedY += Math.sin(repelAngle) * repelForce;
}
// Regular evasive behavior at longer distance
if (distanceToPlayer < self.evasionDistance) {
// Calculate direction away from player
var evadeAngle = Math.atan2(self.y - player.y, self.x - player.x);
// Apply evasion force
self.speedX += Math.cos(evadeAngle) * self.evasionStrength;
self.speedY += Math.sin(evadeAngle) * self.evasionStrength;
}
// Clamp speed to max values
var currentSpeed = Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY);
if (currentSpeed > self.maxSpeed) {
self.speedX = self.speedX / currentSpeed * self.maxSpeed;
self.speedY = self.speedY / currentSpeed * self.maxSpeed;
}
// Random direction changes (more frequent when being chased)
self.directionChangeTimer++;
var changeInterval = distanceToPlayer < self.evasionDistance ? self.directionChangeInterval / 2 : self.directionChangeInterval;
if (self.directionChangeTimer >= changeInterval) {
self.directionChangeTimer = 0;
self.directionChangeInterval = 40 + Math.random() * 120; // Faster random interval
// Random direction change
if (Math.random() < 0.7) {
// Higher chance of direction change
var angle = Math.random() * Math.PI * 2;
var speed = self.minSpeed + Math.random() * (self.maxSpeed - self.minSpeed);
self.speedX = Math.cos(angle) * speed;
self.speedY = Math.sin(angle) * speed;
}
}
};
return self;
});
var LongObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('longObstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Track last position for direction calculation
self.lastX = 0;
self.lastY = 0;
self.update = function () {
// Calculate movement direction and rotate player accordingly
var deltaX = self.x - self.lastX;
var deltaY = self.y - self.lastY;
// Only update rotation if there's significant movement
if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) {
// Calculate angle from movement direction
var angle = Math.atan2(deltaY, deltaX);
// Apply rotation to player graphics
playerGraphics.rotation = angle;
}
// Store current position for next frame
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var TallObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('tallObstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2196F3
});
/****
* Game Code
****/
// Game variables
var player;
var ball;
var obstacles = [];
var dragNode = null;
var gameWon = false;
// Initialize player
player = game.addChild(new Player());
player.x = 1024; // Center horizontally
player.y = 1800; // Near bottom of screen
// Initialize tracking variables for direction
player.lastX = player.x;
player.lastY = player.y;
// Initialize ball
ball = game.addChild(new Ball());
ball.x = 1024; // Center horizontally
ball.y = 400; // Upper portion of screen
// Set initial random direction for ball
var initialAngle = Math.random() * Math.PI * 2;
var initialSpeed = 20;
ball.speedX = Math.cos(initialAngle) * initialSpeed;
ball.speedY = Math.sin(initialAngle) * initialSpeed;
// Create obstacles in the arena with randomized positions
var numObstacles = 12; // Increased number of obstacles
for (var i = 0; i < numObstacles; i++) {
var obstacle;
var obstacleType = Math.random();
// Create different types of obstacles
if (obstacleType < 0.5) {
obstacle = game.addChild(new Obstacle()); // Regular square obstacles
} else if (obstacleType < 0.75) {
obstacle = game.addChild(new LongObstacle()); // Long horizontal obstacles
} else {
obstacle = game.addChild(new TallObstacle()); // Tall vertical obstacles
}
var validPosition = false;
var attempts = 0;
// Keep trying to find a valid position that doesn't overlap with player
while (!validPosition && attempts < 50) {
// Generate random position with safe margins from screen edges
obstacle.x = 200 + Math.random() * (2048 - 400); // 200px margin from edges
obstacle.y = 500 + Math.random() * (2732 - 700); // 500px from top, 200px from bottom
// Check distance from player starting position (1024, 1800)
var distanceFromPlayer = Math.sqrt(Math.pow(obstacle.x - 1024, 2) + Math.pow(obstacle.y - 1800, 2));
if (distanceFromPlayer > 250) {
// Must be at least 250px away from player
validPosition = true;
}
attempts++;
}
obstacle.lastIntersecting = false;
obstacles.push(obstacle);
}
// Tracking intersection state
var lastIntersecting = false;
// Store target position for smooth chasing
var targetX = 0;
var targetY = 0;
function handleMove(x, y, obj) {
if (dragNode && !gameWon) {
// Calculate target position with bounds
targetX = Math.max(40, Math.min(2048 - 40, x));
targetY = Math.max(40, Math.min(2732 - 40, y));
}
}
// Game input handlers
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameWon) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game update loop
game.update = function () {
if (gameWon) return;
// Smooth player chasing movement
if (dragNode) {
var followSpeed = 0.08; // Adjust this value to control lag (0.1 = faster, 0.05 = slower)
var deltaX = targetX - dragNode.x;
var deltaY = targetY - dragNode.y;
dragNode.x += deltaX * followSpeed;
dragNode.y += deltaY * followSpeed;
}
// Check for collision with obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var obstacleIntersecting = player.intersects(obstacle);
if (!obstacle.lastIntersecting && obstacleIntersecting) {
// Player hit an obstacle - game over!
gameWon = true;
// Flash effect red for failure
LK.effects.flashScreen(0xff0000, 1000);
// Show game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
break;
}
obstacle.lastIntersecting = obstacleIntersecting;
}
// Check for collision between player and ball
var currentIntersecting = player.intersects(ball);
if (!lastIntersecting && currentIntersecting) {
// Player caught the ball!
gameWon = true;
// Play catch sound
LK.getSound('catch').play();
// Flash effect
LK.effects.flashScreen(0x4CAF50, 1000);
// Show victory after a short delay
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
}
lastIntersecting = currentIntersecting;
};