/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
aiDifficulty: 0.7
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ballGraphics.width / 2;
self.velocity = {
x: 0,
y: 0
};
self.baseSpeed = 12;
self.speedMultiplier = 1.0;
self.maxSpeedMultiplier = 2.5;
self.reset = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.speedMultiplier = 1.0;
// Random initial direction
var angle = Math.random() * Math.PI / 2 - Math.PI / 4;
if (Math.random() < 0.5) {
angle += Math.PI;
}
self.velocity.x = Math.cos(angle) * self.baseSpeed;
self.velocity.y = Math.sin(angle) * self.baseSpeed;
// Make sure horizontal speed is significant
if (Math.abs(self.velocity.x) < self.baseSpeed * 0.6) {
self.velocity.x = (self.velocity.x > 0 ? 1 : -1) * self.baseSpeed * 0.6;
}
};
self.update = function () {
// Move ball based on velocity
self.x += self.velocity.x * self.speedMultiplier;
self.y += self.velocity.y * self.speedMultiplier;
// Collision with top and bottom walls
if (self.y < self.radius) {
self.y = self.radius;
self.velocity.y *= -1;
LK.getSound('hit').play();
} else if (self.y > 2732 - self.radius) {
self.y = 2732 - self.radius;
self.velocity.y *= -1;
LK.getSound('hit').play();
}
};
self.hitPaddle = function (paddle) {
// Normalize collision point (-1 to 1) from paddle center
var relativeY = (self.y - paddle.y) / (paddle.height / 2);
// Adjust angle based on where the ball hit the paddle
var angle = relativeY * Math.PI / 3; // max ±60 degrees
// Reverse x direction and apply new angle
self.velocity.x = -Math.sign(self.velocity.x) * Math.cos(angle) * self.baseSpeed;
self.velocity.y = Math.sin(angle) * self.baseSpeed;
// Increase speed with each hit, up to a maximum
self.speedMultiplier = Math.min(self.maxSpeedMultiplier, self.speedMultiplier + 0.05);
// Play hit sound
LK.getSound('hit').play();
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = paddleGraphics.width;
self.height = paddleGraphics.height;
self.speed = 0;
self.target = 0;
self.isAI = false;
self.aiReactionSpeed = 0.7; // Default AI difficulty level (0.1-0.9)
// AI paddle movement logic
self.updateAI = function (ball, dt) {
if (!self.isAI) {
return;
}
// Only update target if ball is moving toward this paddle
if (self.x < 1024 && ball.velocity.x < 0 || self.x > 1024 && ball.velocity.x > 0) {
// Predict where the ball will be
var distanceX = Math.abs(self.x - ball.x);
var timeToReach = distanceX / Math.abs(ball.velocity.x);
var futureY = ball.y + ball.velocity.y * timeToReach;
// Keep the future position in bounds
futureY = Math.max(self.height / 2, Math.min(2732 - self.height / 2, futureY));
// Add some imperfection based on AI difficulty
var errorFactor = (1 - self.aiReactionSpeed) * 300;
futureY += Math.random() * errorFactor * 2 - errorFactor;
// Gradually move toward the predicted position
self.target = futureY;
}
};
self.update = function (dt) {
// Gradually move toward target position (whether controlled by player or AI)
if (self.target !== null) {
self.speed = (self.target - self.y) * 0.2;
// Cap movement speed
var maxSpeed = 40;
self.speed = Math.max(-maxSpeed, Math.min(maxSpeed, self.speed));
self.y += self.speed;
// Keep paddle in bounds
self.y = Math.max(self.height / 2, Math.min(2732 - self.height / 2, self.y));
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state
var gameStarted = false;
var gameMode = "single"; // "single" or "multi"
var scoreLeft = 0;
var scoreRight = 0;
var maxScore = 11;
var lastFrameTime = Date.now();
// Create paddles
var leftPaddle = game.addChild(new Paddle());
leftPaddle.x = 100;
leftPaddle.y = 2732 / 2;
var rightPaddle = game.addChild(new Paddle());
rightPaddle.x = 2048 - 100;
rightPaddle.y = 2732 / 2;
rightPaddle.isAI = true;
rightPaddle.aiReactionSpeed = storage.aiDifficulty;
// Create ball
var ball = game.addChild(new Ball());
ball.reset();
// Create center line
var centerLineContainer = game.addChild(new Container());
centerLineContainer.x = 2048 / 2;
for (var y = 15; y < 2732; y += 60) {
var lineSegment = centerLineContainer.attachAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5,
y: y
});
}
// Score text
var scoreLeftText = new Text2(scoreLeft.toString(), {
size: 150,
fill: 0xFFFFFF
});
scoreLeftText.anchor.set(1, 0);
scoreLeftText.x = 2048 / 2 - 50;
scoreLeftText.y = 50;
LK.gui.addChild(scoreLeftText);
var scoreRightText = new Text2(scoreRight.toString(), {
size: 150,
fill: 0xFFFFFF
});
scoreRightText.anchor.set(0, 0);
scoreRightText.x = 2048 / 2 + 50;
scoreRightText.y = 50;
LK.gui.addChild(scoreRightText);
// Game mode button
var singlePlayerButton = new Text2("1P", {
size: 80,
fill: gameMode === "single" ? "#ffffff" : "#888888"
});
singlePlayerButton.anchor.set(1, 0);
singlePlayerButton.x = 2048 / 2 - 20;
singlePlayerButton.y = 220;
LK.gui.addChild(singlePlayerButton);
var multiPlayerButton = new Text2("2P", {
size: 80,
fill: gameMode === "multi" ? "#ffffff" : "#888888"
});
multiPlayerButton.anchor.set(0, 0);
multiPlayerButton.x = 2048 / 2 + 20;
multiPlayerButton.y = 220;
LK.gui.addChild(multiPlayerButton);
// Start text
var startText = new Text2("TAP TO START", {
size: 100,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 / 2;
LK.gui.addChild(startText);
// Store which paddle the user is currently controlling
var activePaddle = null;
// Update scores
function updateScore() {
scoreLeftText.setText(scoreLeft.toString());
scoreRightText.setText(scoreRight.toString());
}
// Check for win condition
function checkWinCondition() {
if (scoreLeft >= maxScore) {
if (gameMode === "single") {
// Update high score if playing single player
if (scoreLeft > storage.highScore) {
storage.highScore = scoreLeft;
}
}
LK.showYouWin();
} else if (scoreRight >= maxScore) {
if (gameMode === "multi") {
LK.showYouWin(); // In multiplayer, right player winning is still a win
} else {
LK.showGameOver();
}
}
}
// Handle touch events
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
startText.visible = false;
LK.playMusic('gameMusic');
return;
}
// Check if game mode buttons were clicked
if (!gameStarted && y < 300) {
var centerX = 2048 / 2;
if (x < centerX) {
gameMode = "single";
rightPaddle.isAI = true;
singlePlayerButton.style.fill = "#ffffff";
multiPlayerButton.style.fill = "#888888";
} else {
gameMode = "multi";
rightPaddle.isAI = false;
singlePlayerButton.style.fill = "#888888";
multiPlayerButton.style.fill = "#ffffff";
}
return;
}
// Determine which paddle to control based on screen side
if (gameMode === "multi") {
if (x < 2048 / 2) {
activePaddle = leftPaddle;
} else {
activePaddle = rightPaddle;
}
} else {
// In single player, only control left paddle
activePaddle = leftPaddle;
}
if (activePaddle) {
activePaddle.target = y;
}
};
game.move = function (x, y, obj) {
if (activePaddle) {
activePaddle.target = y;
}
};
game.up = function (x, y, obj) {
// Keep tracking the paddle's target position
// This allows for smooth movement even after lifting finger
};
// Main game update loop
game.update = function () {
// Calculate delta time for smoother animations
var currentTime = Date.now();
var dt = (currentTime - lastFrameTime) / 16.67; // Normalize to 60fps
lastFrameTime = currentTime;
// Don't update game logic if not started
if (!gameStarted) {
return;
}
// Update AI
rightPaddle.updateAI(ball, dt);
// Update paddles
leftPaddle.update(dt);
rightPaddle.update(dt);
// Update ball
ball.update();
// Check for collisions with paddles
if (ball.velocity.x < 0 && ball.x - ball.radius <= leftPaddle.x + leftPaddle.width / 2 && ball.x - ball.radius > leftPaddle.x - leftPaddle.width / 2 && ball.y >= leftPaddle.y - leftPaddle.height / 2 && ball.y <= leftPaddle.y + leftPaddle.height / 2) {
// Position ball at paddle edge to prevent sticking
ball.x = leftPaddle.x + leftPaddle.width / 2 + ball.radius;
ball.hitPaddle(leftPaddle);
} else if (ball.velocity.x > 0 && ball.x + ball.radius >= rightPaddle.x - rightPaddle.width / 2 && ball.x + ball.radius < rightPaddle.x + rightPaddle.width / 2 && ball.y >= rightPaddle.y - rightPaddle.height / 2 && ball.y <= rightPaddle.y + rightPaddle.height / 2) {
// Position ball at paddle edge to prevent sticking
ball.x = rightPaddle.x - rightPaddle.width / 2 - ball.radius;
ball.hitPaddle(rightPaddle);
}
// Check for scoring
if (ball.x < -ball.radius) {
// Right scores
scoreRight++;
updateScore();
LK.getSound('score').play();
ball.reset();
checkWinCondition();
} else if (ball.x > 2048 + ball.radius) {
// Left scores
scoreLeft++;
updateScore();
LK.getSound('score').play();
ball.reset();
checkWinCondition();
}
// Flash effect for center line if ball crosses it
if (ball.x < 1024 && ball.velocity.x > 0 && ball.x + ball.velocity.x >= 1024 || ball.x > 1024 && ball.velocity.x < 0 && ball.x + ball.velocity.x <= 1024) {
tween(centerLineContainer, {
alpha: 0.3
}, {
duration: 100,
onFinish: function onFinish() {
tween(centerLineContainer, {
alpha: 1
}, {
duration: 100
});
}
});
}
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,333 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1", {
+ highScore: 0,
+ aiDifficulty: 0.7
+});
+
+/****
+* Classes
+****/
+var Ball = Container.expand(function () {
+ var self = Container.call(this);
+ var ballGraphics = self.attachAsset('ball', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = ballGraphics.width / 2;
+ self.velocity = {
+ x: 0,
+ y: 0
+ };
+ self.baseSpeed = 12;
+ self.speedMultiplier = 1.0;
+ self.maxSpeedMultiplier = 2.5;
+ self.reset = function () {
+ self.x = 2048 / 2;
+ self.y = 2732 / 2;
+ self.speedMultiplier = 1.0;
+ // Random initial direction
+ var angle = Math.random() * Math.PI / 2 - Math.PI / 4;
+ if (Math.random() < 0.5) {
+ angle += Math.PI;
+ }
+ self.velocity.x = Math.cos(angle) * self.baseSpeed;
+ self.velocity.y = Math.sin(angle) * self.baseSpeed;
+ // Make sure horizontal speed is significant
+ if (Math.abs(self.velocity.x) < self.baseSpeed * 0.6) {
+ self.velocity.x = (self.velocity.x > 0 ? 1 : -1) * self.baseSpeed * 0.6;
+ }
+ };
+ self.update = function () {
+ // Move ball based on velocity
+ self.x += self.velocity.x * self.speedMultiplier;
+ self.y += self.velocity.y * self.speedMultiplier;
+ // Collision with top and bottom walls
+ if (self.y < self.radius) {
+ self.y = self.radius;
+ self.velocity.y *= -1;
+ LK.getSound('hit').play();
+ } else if (self.y > 2732 - self.radius) {
+ self.y = 2732 - self.radius;
+ self.velocity.y *= -1;
+ LK.getSound('hit').play();
+ }
+ };
+ self.hitPaddle = function (paddle) {
+ // Normalize collision point (-1 to 1) from paddle center
+ var relativeY = (self.y - paddle.y) / (paddle.height / 2);
+ // Adjust angle based on where the ball hit the paddle
+ var angle = relativeY * Math.PI / 3; // max ±60 degrees
+ // Reverse x direction and apply new angle
+ self.velocity.x = -Math.sign(self.velocity.x) * Math.cos(angle) * self.baseSpeed;
+ self.velocity.y = Math.sin(angle) * self.baseSpeed;
+ // Increase speed with each hit, up to a maximum
+ self.speedMultiplier = Math.min(self.maxSpeedMultiplier, self.speedMultiplier + 0.05);
+ // Play hit sound
+ LK.getSound('hit').play();
+ };
+ return self;
+});
+var Paddle = Container.expand(function () {
+ var self = Container.call(this);
+ var paddleGraphics = self.attachAsset('paddle', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.width = paddleGraphics.width;
+ self.height = paddleGraphics.height;
+ self.speed = 0;
+ self.target = 0;
+ self.isAI = false;
+ self.aiReactionSpeed = 0.7; // Default AI difficulty level (0.1-0.9)
+ // AI paddle movement logic
+ self.updateAI = function (ball, dt) {
+ if (!self.isAI) {
+ return;
+ }
+ // Only update target if ball is moving toward this paddle
+ if (self.x < 1024 && ball.velocity.x < 0 || self.x > 1024 && ball.velocity.x > 0) {
+ // Predict where the ball will be
+ var distanceX = Math.abs(self.x - ball.x);
+ var timeToReach = distanceX / Math.abs(ball.velocity.x);
+ var futureY = ball.y + ball.velocity.y * timeToReach;
+ // Keep the future position in bounds
+ futureY = Math.max(self.height / 2, Math.min(2732 - self.height / 2, futureY));
+ // Add some imperfection based on AI difficulty
+ var errorFactor = (1 - self.aiReactionSpeed) * 300;
+ futureY += Math.random() * errorFactor * 2 - errorFactor;
+ // Gradually move toward the predicted position
+ self.target = futureY;
+ }
+ };
+ self.update = function (dt) {
+ // Gradually move toward target position (whether controlled by player or AI)
+ if (self.target !== null) {
+ self.speed = (self.target - self.y) * 0.2;
+ // Cap movement speed
+ var maxSpeed = 40;
+ self.speed = Math.max(-maxSpeed, Math.min(maxSpeed, self.speed));
+ self.y += self.speed;
+ // Keep paddle in bounds
+ self.y = Math.max(self.height / 2, Math.min(2732 - self.height / 2, self.y));
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
backgroundColor: 0x000000
-});
\ No newline at end of file
+});
+
+/****
+* Game Code
+****/
+// Game state
+var gameStarted = false;
+var gameMode = "single"; // "single" or "multi"
+var scoreLeft = 0;
+var scoreRight = 0;
+var maxScore = 11;
+var lastFrameTime = Date.now();
+// Create paddles
+var leftPaddle = game.addChild(new Paddle());
+leftPaddle.x = 100;
+leftPaddle.y = 2732 / 2;
+var rightPaddle = game.addChild(new Paddle());
+rightPaddle.x = 2048 - 100;
+rightPaddle.y = 2732 / 2;
+rightPaddle.isAI = true;
+rightPaddle.aiReactionSpeed = storage.aiDifficulty;
+// Create ball
+var ball = game.addChild(new Ball());
+ball.reset();
+// Create center line
+var centerLineContainer = game.addChild(new Container());
+centerLineContainer.x = 2048 / 2;
+for (var y = 15; y < 2732; y += 60) {
+ var lineSegment = centerLineContainer.attachAsset('centerLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ y: y
+ });
+}
+// Score text
+var scoreLeftText = new Text2(scoreLeft.toString(), {
+ size: 150,
+ fill: 0xFFFFFF
+});
+scoreLeftText.anchor.set(1, 0);
+scoreLeftText.x = 2048 / 2 - 50;
+scoreLeftText.y = 50;
+LK.gui.addChild(scoreLeftText);
+var scoreRightText = new Text2(scoreRight.toString(), {
+ size: 150,
+ fill: 0xFFFFFF
+});
+scoreRightText.anchor.set(0, 0);
+scoreRightText.x = 2048 / 2 + 50;
+scoreRightText.y = 50;
+LK.gui.addChild(scoreRightText);
+// Game mode button
+var singlePlayerButton = new Text2("1P", {
+ size: 80,
+ fill: gameMode === "single" ? "#ffffff" : "#888888"
+});
+singlePlayerButton.anchor.set(1, 0);
+singlePlayerButton.x = 2048 / 2 - 20;
+singlePlayerButton.y = 220;
+LK.gui.addChild(singlePlayerButton);
+var multiPlayerButton = new Text2("2P", {
+ size: 80,
+ fill: gameMode === "multi" ? "#ffffff" : "#888888"
+});
+multiPlayerButton.anchor.set(0, 0);
+multiPlayerButton.x = 2048 / 2 + 20;
+multiPlayerButton.y = 220;
+LK.gui.addChild(multiPlayerButton);
+// Start text
+var startText = new Text2("TAP TO START", {
+ size: 100,
+ fill: 0xFFFFFF
+});
+startText.anchor.set(0.5, 0.5);
+startText.x = 2048 / 2;
+startText.y = 2732 / 2;
+LK.gui.addChild(startText);
+// Store which paddle the user is currently controlling
+var activePaddle = null;
+// Update scores
+function updateScore() {
+ scoreLeftText.setText(scoreLeft.toString());
+ scoreRightText.setText(scoreRight.toString());
+}
+// Check for win condition
+function checkWinCondition() {
+ if (scoreLeft >= maxScore) {
+ if (gameMode === "single") {
+ // Update high score if playing single player
+ if (scoreLeft > storage.highScore) {
+ storage.highScore = scoreLeft;
+ }
+ }
+ LK.showYouWin();
+ } else if (scoreRight >= maxScore) {
+ if (gameMode === "multi") {
+ LK.showYouWin(); // In multiplayer, right player winning is still a win
+ } else {
+ LK.showGameOver();
+ }
+ }
+}
+// Handle touch events
+game.down = function (x, y, obj) {
+ if (!gameStarted) {
+ gameStarted = true;
+ startText.visible = false;
+ LK.playMusic('gameMusic');
+ return;
+ }
+ // Check if game mode buttons were clicked
+ if (!gameStarted && y < 300) {
+ var centerX = 2048 / 2;
+ if (x < centerX) {
+ gameMode = "single";
+ rightPaddle.isAI = true;
+ singlePlayerButton.style.fill = "#ffffff";
+ multiPlayerButton.style.fill = "#888888";
+ } else {
+ gameMode = "multi";
+ rightPaddle.isAI = false;
+ singlePlayerButton.style.fill = "#888888";
+ multiPlayerButton.style.fill = "#ffffff";
+ }
+ return;
+ }
+ // Determine which paddle to control based on screen side
+ if (gameMode === "multi") {
+ if (x < 2048 / 2) {
+ activePaddle = leftPaddle;
+ } else {
+ activePaddle = rightPaddle;
+ }
+ } else {
+ // In single player, only control left paddle
+ activePaddle = leftPaddle;
+ }
+ if (activePaddle) {
+ activePaddle.target = y;
+ }
+};
+game.move = function (x, y, obj) {
+ if (activePaddle) {
+ activePaddle.target = y;
+ }
+};
+game.up = function (x, y, obj) {
+ // Keep tracking the paddle's target position
+ // This allows for smooth movement even after lifting finger
+};
+// Main game update loop
+game.update = function () {
+ // Calculate delta time for smoother animations
+ var currentTime = Date.now();
+ var dt = (currentTime - lastFrameTime) / 16.67; // Normalize to 60fps
+ lastFrameTime = currentTime;
+ // Don't update game logic if not started
+ if (!gameStarted) {
+ return;
+ }
+ // Update AI
+ rightPaddle.updateAI(ball, dt);
+ // Update paddles
+ leftPaddle.update(dt);
+ rightPaddle.update(dt);
+ // Update ball
+ ball.update();
+ // Check for collisions with paddles
+ if (ball.velocity.x < 0 && ball.x - ball.radius <= leftPaddle.x + leftPaddle.width / 2 && ball.x - ball.radius > leftPaddle.x - leftPaddle.width / 2 && ball.y >= leftPaddle.y - leftPaddle.height / 2 && ball.y <= leftPaddle.y + leftPaddle.height / 2) {
+ // Position ball at paddle edge to prevent sticking
+ ball.x = leftPaddle.x + leftPaddle.width / 2 + ball.radius;
+ ball.hitPaddle(leftPaddle);
+ } else if (ball.velocity.x > 0 && ball.x + ball.radius >= rightPaddle.x - rightPaddle.width / 2 && ball.x + ball.radius < rightPaddle.x + rightPaddle.width / 2 && ball.y >= rightPaddle.y - rightPaddle.height / 2 && ball.y <= rightPaddle.y + rightPaddle.height / 2) {
+ // Position ball at paddle edge to prevent sticking
+ ball.x = rightPaddle.x - rightPaddle.width / 2 - ball.radius;
+ ball.hitPaddle(rightPaddle);
+ }
+ // Check for scoring
+ if (ball.x < -ball.radius) {
+ // Right scores
+ scoreRight++;
+ updateScore();
+ LK.getSound('score').play();
+ ball.reset();
+ checkWinCondition();
+ } else if (ball.x > 2048 + ball.radius) {
+ // Left scores
+ scoreLeft++;
+ updateScore();
+ LK.getSound('score').play();
+ ball.reset();
+ checkWinCondition();
+ }
+ // Flash effect for center line if ball crosses it
+ if (ball.x < 1024 && ball.velocity.x > 0 && ball.x + ball.velocity.x >= 1024 || ball.x > 1024 && ball.velocity.x < 0 && ball.x + ball.velocity.x <= 1024) {
+ tween(centerLineContainer, {
+ alpha: 0.3
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ tween(centerLineContainer, {
+ alpha: 1
+ }, {
+ duration: 100
+ });
+ }
+ });
+ }
+};
\ No newline at end of file