/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Ball speed and direction
self.vx = 0;
self.vy = 0;
// Ball radius for collision
self.radius = ballAsset.width / 2;
// Ball update
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Paddle class (for both player and AI)
var Paddle = Container.expand(function () {
var self = Container.call(this);
// Default to player paddle
var paddleAsset = self.attachAsset('paddlePlayer', {
anchorX: 0.5,
anchorY: 0.5
});
self.setAI = function () {
// Remove old asset if any
if (self.children.length > 0) {
self.removeChild(self.children[0]);
}
paddleAsset = self.attachAsset('paddleAI', {
anchorX: 0.5,
anchorY: 0.5
});
};
// Paddle width/height for collision
self.getWidth = function () {
return paddleAsset.width;
};
self.getHeight = function () {
return paddleAsset.height;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Flash for rally
// Ball
// Paddle (AI)
// Paddle (player)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PADDLE_Y_OFFSET = 120; // Distance from top/bottom for paddles
var BALL_START_SPEED = 22;
var BALL_SPEED_INCREMENT = 2.5;
var MAX_LEVEL = 10;
// Game state
var playerScore = 0;
var aiScore = 0;
var level = 1;
var rallyCount = 0;
var isGameActive = true;
// Create paddles and ball
var playerPaddle = new Paddle();
var aiPaddle = new Paddle();
aiPaddle.setAI();
var ball = new Ball();
// Add to game
game.addChild(playerPaddle);
game.addChild(aiPaddle);
game.addChild(ball);
// Position paddles and ball
function resetPositions() {
// Center paddles
playerPaddle.x = GAME_WIDTH / 2;
playerPaddle.y = GAME_HEIGHT - PADDLE_Y_OFFSET;
aiPaddle.x = GAME_WIDTH / 2;
aiPaddle.y = PADDLE_Y_OFFSET;
// Center ball
ball.x = GAME_WIDTH / 2;
ball.y = GAME_HEIGHT / 2;
}
// Ball initial direction (randomize angle)
function launchBall() {
var angle = Math.PI / 2 + (Math.random() - 0.5) * Math.PI / 3; // Downwards, randomize a bit
if (Math.random() < 0.5) angle = -angle; // Sometimes start upwards
var speed = BALL_START_SPEED + (level - 1) * BALL_SPEED_INCREMENT;
ball.vx = Math.cos(angle) * speed;
ball.vy = Math.sin(angle) * speed;
// Ensure vy is not too small (avoid boring horizontal)
if (Math.abs(ball.vy) < speed * 0.5) {
ball.vy = (ball.vy < 0 ? -1 : 1) * speed * 0.7;
}
}
// Score text
var scoreTxt = new Text2('0 : 0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Level text
var levelTxt = new Text2('Level 1', {
size: 80,
fill: 0xFFD700
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 130;
// Rally feedback
var rallyTxt = new Text2('', {
size: 90,
fill: 0x00FF00
});
rallyTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(rallyTxt);
// Flash overlay for rally/miss
var rallyFlash = LK.getAsset('rallyFlash', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0
});
game.addChild(rallyFlash);
// Reset game state for new round/level
function startLevel(newLevel) {
level = newLevel;
playerScore = 0;
aiScore = 0;
rallyCount = 0;
isGameActive = true;
updateScoreText();
updateLevelText();
rallyTxt.setText('');
resetPositions();
launchBall();
}
// Update score display
function updateScoreText() {
scoreTxt.setText(playerScore + ' : ' + aiScore);
}
// Update level display
function updateLevelText() {
levelTxt.setText('Level ' + level);
}
// Show rally feedback
function showRally(count) {
if (count > 1) {
rallyTxt.setText('Rally! x' + count);
rallyTxt.alpha = 1;
tween(rallyTxt, {
alpha: 0
}, {
duration: 900,
easing: tween.linear
});
// Flash white
rallyFlash.alpha = 0.18;
tween(rallyFlash, {
alpha: 0
}, {
duration: 400,
easing: tween.linear
});
}
}
// Show miss feedback
function showMiss(who) {
rallyTxt.setText(who + ' Miss!');
rallyTxt.alpha = 1;
tween(rallyTxt, {
alpha: 0
}, {
duration: 900,
easing: tween.linear
});
// Flash red
rallyFlash.tint = 0xff0000;
rallyFlash.alpha = 0.18;
tween(rallyFlash, {
alpha: 0
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
rallyFlash.tint = 0xffffff;
}
});
}
// End game (player lost)
function endGame() {
isGameActive = false;
LK.effects.flashScreen(0xff0000, 900);
LK.showGameOver();
}
// Win game (player beat all levels)
function winGame() {
isGameActive = false;
LK.effects.flashScreen(0x00ff00, 900);
LK.showYouWin();
}
// Paddle movement (player drag)
var dragPaddle = null;
function handleMove(x, y, obj) {
if (!isGameActive) return;
if (dragPaddle) {
// Clamp paddle within game bounds
var halfW = dragPaddle.getWidth() / 2;
var newX = x;
if (newX < halfW) newX = halfW;
if (newX > GAME_WIDTH - halfW) newX = GAME_WIDTH - halfW;
dragPaddle.x = newX;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if touch is on/near player paddle
var paddleTop = playerPaddle.y - playerPaddle.getHeight() / 2;
var paddleBottom = playerPaddle.y + playerPaddle.getHeight() / 2;
var paddleLeft = playerPaddle.x - playerPaddle.getWidth() / 2;
var paddleRight = playerPaddle.x + playerPaddle.getWidth() / 2;
if (y >= paddleTop - 60 && y <= paddleBottom + 60 && x >= paddleLeft - 60 && x <= paddleRight + 60) {
dragPaddle = playerPaddle;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragPaddle = null;
};
// AI paddle logic
function updateAIPaddle() {
// AI difficulty increases with level
var aiSpeed = 16 + (level - 1) * 3.5; // px per frame
var aiReact = 0.18 + (level - 1) * 0.07; // how much of the distance to move per frame
if (aiReact > 0.5) aiReact = 0.5;
// Only move if ball is moving towards AI
if (ball.vy < 0) {
var dx = ball.x - aiPaddle.x;
var move = dx * aiReact;
if (Math.abs(move) > aiSpeed) move = (move > 0 ? 1 : -1) * aiSpeed;
aiPaddle.x += move;
// Clamp
var halfW = aiPaddle.getWidth() / 2;
if (aiPaddle.x < halfW) aiPaddle.x = halfW;
if (aiPaddle.x > GAME_WIDTH - halfW) aiPaddle.x = GAME_WIDTH - halfW;
}
}
// Ball collision with paddle
function ballIntersectsPaddle(paddle) {
var px = paddle.x;
var py = paddle.y;
var pw = paddle.getWidth();
var ph = paddle.getHeight();
// Closest point on paddle to ball
var closestX = ball.x;
if (ball.x < px - pw / 2) closestX = px - pw / 2;else if (ball.x > px + pw / 2) closestX = px + pw / 2;
var closestY = ball.y;
if (ball.y < py - ph / 2) closestY = py - ph / 2;else if (ball.y > py + ph / 2) closestY = py + ph / 2;
// Distance to ball center
var dx = ball.x - closestX;
var dy = ball.y - closestY;
return dx * dx + dy * dy <= ball.radius * ball.radius;
}
// Ball collision with wall
function ballWallBounce() {
// Left/right
if (ball.x - ball.radius < 0) {
ball.x = ball.radius;
ball.vx = -ball.vx;
}
if (ball.x + ball.radius > GAME_WIDTH) {
ball.x = GAME_WIDTH - ball.radius;
ball.vx = -ball.vx;
}
}
// Ball out of bounds (top/bottom)
function checkScore() {
if (ball.y - ball.radius < 0) {
// Player scores
playerScore += 1;
updateScoreText();
showMiss('AI');
rallyCount = 0;
if (playerScore >= 5) {
// Next level or win
if (level >= MAX_LEVEL) {
winGame();
} else {
startLevel(level + 1);
}
} else {
resetPositions();
launchBall();
}
return true;
}
if (ball.y + ball.radius > GAME_HEIGHT) {
// AI scores
aiScore += 1;
updateScoreText();
showMiss('Player');
rallyCount = 0;
if (aiScore >= 3) {
endGame();
} else {
resetPositions();
launchBall();
}
return true;
}
return false;
}
// Ball-paddle bounce logic
function handleBallPaddleBounce() {
// Player paddle
if (ball.vy > 0 && ballIntersectsPaddle(playerPaddle)) {
// Bounce up
ball.y = playerPaddle.y - playerPaddle.getHeight() / 2 - ball.radius;
ball.vy = -Math.abs(ball.vy);
// Add spin based on where hit
var offset = (ball.x - playerPaddle.x) / (playerPaddle.getWidth() / 2);
ball.vx += offset * 7;
// Clamp vx
var maxV = BALL_START_SPEED + (level - 1) * BALL_SPEED_INCREMENT + 7;
if (ball.vx > maxV) ball.vx = maxV;
if (ball.vx < -maxV) ball.vx = -maxV;
rallyCount += 1;
showRally(rallyCount);
}
// AI paddle
if (ball.vy < 0 && ballIntersectsPaddle(aiPaddle)) {
// Bounce down
ball.y = aiPaddle.y + aiPaddle.getHeight() / 2 + ball.radius;
ball.vy = Math.abs(ball.vy);
// Add spin
var offset = (ball.x - aiPaddle.x) / (aiPaddle.getWidth() / 2);
ball.vx += offset * 7;
// Clamp vx
var maxV = BALL_START_SPEED + (level - 1) * BALL_SPEED_INCREMENT + 7;
if (ball.vx > maxV) ball.vx = maxV;
if (ball.vx < -maxV) ball.vx = -maxV;
rallyCount += 1;
showRally(rallyCount);
}
}
// Game update loop
game.update = function () {
if (!isGameActive) return;
// Ball movement
ball.update();
// Wall bounce
ballWallBounce();
// Paddle bounce
handleBallPaddleBounce();
// AI paddle
updateAIPaddle();
// Out of bounds/score
checkScore();
};
// Start first level
startLevel(1); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,382 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Ball class
+var Ball = Container.expand(function () {
+ var self = Container.call(this);
+ var ballAsset = self.attachAsset('ball', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Ball speed and direction
+ self.vx = 0;
+ self.vy = 0;
+ // Ball radius for collision
+ self.radius = ballAsset.width / 2;
+ // Ball update
+ self.update = function () {
+ self.x += self.vx;
+ self.y += self.vy;
+ };
+ return self;
+});
+// Paddle class (for both player and AI)
+var Paddle = Container.expand(function () {
+ var self = Container.call(this);
+ // Default to player paddle
+ var paddleAsset = self.attachAsset('paddlePlayer', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.setAI = function () {
+ // Remove old asset if any
+ if (self.children.length > 0) {
+ self.removeChild(self.children[0]);
+ }
+ paddleAsset = self.attachAsset('paddleAI', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ };
+ // Paddle width/height for collision
+ self.getWidth = function () {
+ return paddleAsset.width;
+ };
+ self.getHeight = function () {
+ return paddleAsset.height;
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x222222
+});
+
+/****
+* Game Code
+****/
+// Flash for rally
+// Ball
+// Paddle (AI)
+// Paddle (player)
+// Game constants
+var GAME_WIDTH = 2048;
+var GAME_HEIGHT = 2732;
+var PADDLE_Y_OFFSET = 120; // Distance from top/bottom for paddles
+var BALL_START_SPEED = 22;
+var BALL_SPEED_INCREMENT = 2.5;
+var MAX_LEVEL = 10;
+// Game state
+var playerScore = 0;
+var aiScore = 0;
+var level = 1;
+var rallyCount = 0;
+var isGameActive = true;
+// Create paddles and ball
+var playerPaddle = new Paddle();
+var aiPaddle = new Paddle();
+aiPaddle.setAI();
+var ball = new Ball();
+// Add to game
+game.addChild(playerPaddle);
+game.addChild(aiPaddle);
+game.addChild(ball);
+// Position paddles and ball
+function resetPositions() {
+ // Center paddles
+ playerPaddle.x = GAME_WIDTH / 2;
+ playerPaddle.y = GAME_HEIGHT - PADDLE_Y_OFFSET;
+ aiPaddle.x = GAME_WIDTH / 2;
+ aiPaddle.y = PADDLE_Y_OFFSET;
+ // Center ball
+ ball.x = GAME_WIDTH / 2;
+ ball.y = GAME_HEIGHT / 2;
+}
+// Ball initial direction (randomize angle)
+function launchBall() {
+ var angle = Math.PI / 2 + (Math.random() - 0.5) * Math.PI / 3; // Downwards, randomize a bit
+ if (Math.random() < 0.5) angle = -angle; // Sometimes start upwards
+ var speed = BALL_START_SPEED + (level - 1) * BALL_SPEED_INCREMENT;
+ ball.vx = Math.cos(angle) * speed;
+ ball.vy = Math.sin(angle) * speed;
+ // Ensure vy is not too small (avoid boring horizontal)
+ if (Math.abs(ball.vy) < speed * 0.5) {
+ ball.vy = (ball.vy < 0 ? -1 : 1) * speed * 0.7;
+ }
+}
+// Score text
+var scoreTxt = new Text2('0 : 0', {
+ size: 120,
+ fill: 0xFFFFFF
+});
+scoreTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreTxt);
+// Level text
+var levelTxt = new Text2('Level 1', {
+ size: 80,
+ fill: 0xFFD700
+});
+levelTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(levelTxt);
+levelTxt.y = 130;
+// Rally feedback
+var rallyTxt = new Text2('', {
+ size: 90,
+ fill: 0x00FF00
+});
+rallyTxt.anchor.set(0.5, 0.5);
+LK.gui.center.addChild(rallyTxt);
+// Flash overlay for rally/miss
+var rallyFlash = LK.getAsset('rallyFlash', {
+ anchorX: 0,
+ anchorY: 0,
+ x: 0,
+ y: 0,
+ alpha: 0
+});
+game.addChild(rallyFlash);
+// Reset game state for new round/level
+function startLevel(newLevel) {
+ level = newLevel;
+ playerScore = 0;
+ aiScore = 0;
+ rallyCount = 0;
+ isGameActive = true;
+ updateScoreText();
+ updateLevelText();
+ rallyTxt.setText('');
+ resetPositions();
+ launchBall();
+}
+// Update score display
+function updateScoreText() {
+ scoreTxt.setText(playerScore + ' : ' + aiScore);
+}
+// Update level display
+function updateLevelText() {
+ levelTxt.setText('Level ' + level);
+}
+// Show rally feedback
+function showRally(count) {
+ if (count > 1) {
+ rallyTxt.setText('Rally! x' + count);
+ rallyTxt.alpha = 1;
+ tween(rallyTxt, {
+ alpha: 0
+ }, {
+ duration: 900,
+ easing: tween.linear
+ });
+ // Flash white
+ rallyFlash.alpha = 0.18;
+ tween(rallyFlash, {
+ alpha: 0
+ }, {
+ duration: 400,
+ easing: tween.linear
+ });
+ }
+}
+// Show miss feedback
+function showMiss(who) {
+ rallyTxt.setText(who + ' Miss!');
+ rallyTxt.alpha = 1;
+ tween(rallyTxt, {
+ alpha: 0
+ }, {
+ duration: 900,
+ easing: tween.linear
+ });
+ // Flash red
+ rallyFlash.tint = 0xff0000;
+ rallyFlash.alpha = 0.18;
+ tween(rallyFlash, {
+ alpha: 0
+ }, {
+ duration: 400,
+ easing: tween.linear,
+ onFinish: function onFinish() {
+ rallyFlash.tint = 0xffffff;
+ }
+ });
+}
+// End game (player lost)
+function endGame() {
+ isGameActive = false;
+ LK.effects.flashScreen(0xff0000, 900);
+ LK.showGameOver();
+}
+// Win game (player beat all levels)
+function winGame() {
+ isGameActive = false;
+ LK.effects.flashScreen(0x00ff00, 900);
+ LK.showYouWin();
+}
+// Paddle movement (player drag)
+var dragPaddle = null;
+function handleMove(x, y, obj) {
+ if (!isGameActive) return;
+ if (dragPaddle) {
+ // Clamp paddle within game bounds
+ var halfW = dragPaddle.getWidth() / 2;
+ var newX = x;
+ if (newX < halfW) newX = halfW;
+ if (newX > GAME_WIDTH - halfW) newX = GAME_WIDTH - halfW;
+ dragPaddle.x = newX;
+ }
+}
+game.move = handleMove;
+game.down = function (x, y, obj) {
+ // Only allow drag if touch is on/near player paddle
+ var paddleTop = playerPaddle.y - playerPaddle.getHeight() / 2;
+ var paddleBottom = playerPaddle.y + playerPaddle.getHeight() / 2;
+ var paddleLeft = playerPaddle.x - playerPaddle.getWidth() / 2;
+ var paddleRight = playerPaddle.x + playerPaddle.getWidth() / 2;
+ if (y >= paddleTop - 60 && y <= paddleBottom + 60 && x >= paddleLeft - 60 && x <= paddleRight + 60) {
+ dragPaddle = playerPaddle;
+ handleMove(x, y, obj);
+ }
+};
+game.up = function (x, y, obj) {
+ dragPaddle = null;
+};
+// AI paddle logic
+function updateAIPaddle() {
+ // AI difficulty increases with level
+ var aiSpeed = 16 + (level - 1) * 3.5; // px per frame
+ var aiReact = 0.18 + (level - 1) * 0.07; // how much of the distance to move per frame
+ if (aiReact > 0.5) aiReact = 0.5;
+ // Only move if ball is moving towards AI
+ if (ball.vy < 0) {
+ var dx = ball.x - aiPaddle.x;
+ var move = dx * aiReact;
+ if (Math.abs(move) > aiSpeed) move = (move > 0 ? 1 : -1) * aiSpeed;
+ aiPaddle.x += move;
+ // Clamp
+ var halfW = aiPaddle.getWidth() / 2;
+ if (aiPaddle.x < halfW) aiPaddle.x = halfW;
+ if (aiPaddle.x > GAME_WIDTH - halfW) aiPaddle.x = GAME_WIDTH - halfW;
+ }
+}
+// Ball collision with paddle
+function ballIntersectsPaddle(paddle) {
+ var px = paddle.x;
+ var py = paddle.y;
+ var pw = paddle.getWidth();
+ var ph = paddle.getHeight();
+ // Closest point on paddle to ball
+ var closestX = ball.x;
+ if (ball.x < px - pw / 2) closestX = px - pw / 2;else if (ball.x > px + pw / 2) closestX = px + pw / 2;
+ var closestY = ball.y;
+ if (ball.y < py - ph / 2) closestY = py - ph / 2;else if (ball.y > py + ph / 2) closestY = py + ph / 2;
+ // Distance to ball center
+ var dx = ball.x - closestX;
+ var dy = ball.y - closestY;
+ return dx * dx + dy * dy <= ball.radius * ball.radius;
+}
+// Ball collision with wall
+function ballWallBounce() {
+ // Left/right
+ if (ball.x - ball.radius < 0) {
+ ball.x = ball.radius;
+ ball.vx = -ball.vx;
+ }
+ if (ball.x + ball.radius > GAME_WIDTH) {
+ ball.x = GAME_WIDTH - ball.radius;
+ ball.vx = -ball.vx;
+ }
+}
+// Ball out of bounds (top/bottom)
+function checkScore() {
+ if (ball.y - ball.radius < 0) {
+ // Player scores
+ playerScore += 1;
+ updateScoreText();
+ showMiss('AI');
+ rallyCount = 0;
+ if (playerScore >= 5) {
+ // Next level or win
+ if (level >= MAX_LEVEL) {
+ winGame();
+ } else {
+ startLevel(level + 1);
+ }
+ } else {
+ resetPositions();
+ launchBall();
+ }
+ return true;
+ }
+ if (ball.y + ball.radius > GAME_HEIGHT) {
+ // AI scores
+ aiScore += 1;
+ updateScoreText();
+ showMiss('Player');
+ rallyCount = 0;
+ if (aiScore >= 3) {
+ endGame();
+ } else {
+ resetPositions();
+ launchBall();
+ }
+ return true;
+ }
+ return false;
+}
+// Ball-paddle bounce logic
+function handleBallPaddleBounce() {
+ // Player paddle
+ if (ball.vy > 0 && ballIntersectsPaddle(playerPaddle)) {
+ // Bounce up
+ ball.y = playerPaddle.y - playerPaddle.getHeight() / 2 - ball.radius;
+ ball.vy = -Math.abs(ball.vy);
+ // Add spin based on where hit
+ var offset = (ball.x - playerPaddle.x) / (playerPaddle.getWidth() / 2);
+ ball.vx += offset * 7;
+ // Clamp vx
+ var maxV = BALL_START_SPEED + (level - 1) * BALL_SPEED_INCREMENT + 7;
+ if (ball.vx > maxV) ball.vx = maxV;
+ if (ball.vx < -maxV) ball.vx = -maxV;
+ rallyCount += 1;
+ showRally(rallyCount);
+ }
+ // AI paddle
+ if (ball.vy < 0 && ballIntersectsPaddle(aiPaddle)) {
+ // Bounce down
+ ball.y = aiPaddle.y + aiPaddle.getHeight() / 2 + ball.radius;
+ ball.vy = Math.abs(ball.vy);
+ // Add spin
+ var offset = (ball.x - aiPaddle.x) / (aiPaddle.getWidth() / 2);
+ ball.vx += offset * 7;
+ // Clamp vx
+ var maxV = BALL_START_SPEED + (level - 1) * BALL_SPEED_INCREMENT + 7;
+ if (ball.vx > maxV) ball.vx = maxV;
+ if (ball.vx < -maxV) ball.vx = -maxV;
+ rallyCount += 1;
+ showRally(rallyCount);
+ }
+}
+// Game update loop
+game.update = function () {
+ if (!isGameActive) return;
+ // Ball movement
+ ball.update();
+ // Wall bounce
+ ballWallBounce();
+ // Paddle bounce
+ handleBallPaddleBounce();
+ // AI paddle
+ updateAIPaddle();
+ // Out of bounds/score
+ checkScore();
+};
+// Start first level
+startLevel(1);
\ No newline at end of file