/****
* 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
});
self.velocityX = 8;
self.velocityY = 6;
self.baseSpeed = 8;
self.reset = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.velocityX = 0;
self.velocityY = 0;
startCountdown();
};
self.update = function () {
if (gameState === 'playing') {
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with center line segments
for (var i = 0; i < centerLineSegments.length; i++) {
var segment = centerLineSegments[i];
if (self.intersects(segment)) {
// Increase speed by 0.5
if (self.velocityX > 0) {
self.velocityX += 0.5;
} else {
self.velocityX -= 0.5;
}
if (self.velocityY > 0) {
self.velocityY += 0.5;
} else {
self.velocityY -= 0.5;
}
break;
}
}
// Bounce off top and bottom walls
if (self.y <= 10 || self.y >= 2732 - 10) {
self.velocityY = -self.velocityY;
LK.getSound('wallHit').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.speed = 20;
self.targetY = self.y;
self.moveToTarget = function () {
var diff = self.targetY - self.y;
if (Math.abs(diff) > 2) {
self.y += diff * 0.25;
}
};
self.update = function () {
self.moveToTarget();
// Keep paddle within screen bounds
if (self.y < 60) self.y = 60;
if (self.y > 2732 - 60) self.y = 2732 - 60;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var leftPaddle = game.addChild(new Paddle());
var rightPaddle = game.addChild(new Paddle());
var ball = game.addChild(new Ball());
// Position paddles
leftPaddle.x = 50;
leftPaddle.y = 2732 / 2;
rightPaddle.x = 2048 - 50;
rightPaddle.y = 2732 / 2;
// Score tracking
var leftScore = 0;
var rightScore = 0;
var winningScore = 11;
// Game state
var gameState = 'playing'; // 'countdown' or 'playing'
var countdownNumber = 3;
// Score display
var leftScoreText = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
leftScoreText.anchor.set(0.5, 0);
leftScoreText.x = 2048 / 4;
leftScoreText.y = 100;
LK.gui.addChild(leftScoreText);
var rightScoreText = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
rightScoreText.anchor.set(0.5, 0);
rightScoreText.x = 2048 / 4 * 3;
rightScoreText.y = 100;
LK.gui.addChild(rightScoreText);
// Countdown text
var countdownText = new Text2('', {
size: 300,
fill: 0xFFFFFF
});
countdownText.anchor.set(0.5, 0.5);
countdownText.x = 2048 / 2;
countdownText.y = 2732 / 2;
countdownText.visible = false;
LK.gui.addChild(countdownText);
// Initialize ball
ball.reset();
// Center line (dashed effect with multiple small rectangles)
var centerLineSegments = [];
for (var i = 0; i < 20; i++) {
var segment = LK.getAsset('paddle', {
width: 4,
height: 75,
anchorX: 0.5,
anchorY: 0.5
});
segment.x = 2048 / 2;
segment.y = 100 + i * 70;
game.addChild(segment);
centerLineSegments.push(segment);
}
// Touch controls
var isDragging = false;
var dragStartY = 0;
var paddleStartY = 0;
game.down = function (x, y, obj) {
if (x < 2048 / 2) {
isDragging = true;
dragStartY = y;
paddleStartY = leftPaddle.y;
}
};
game.move = function (x, y, obj) {
if (isDragging && x < 2048 / 2) {
var deltaY = y - dragStartY;
leftPaddle.targetY = paddleStartY + deltaY;
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Improved AI for right paddle
function updateAI() {
var targetY = ball.y;
var paddleCenter = rightPaddle.y;
// Predict ball position when it reaches paddle
if (ball.velocityX > 0) {
var timeToReachPaddle = (rightPaddle.x - ball.x) / ball.velocityX;
targetY = ball.y + ball.velocityY * timeToReachPaddle;
// Account for wall bounces in prediction
if (targetY < 60) {
targetY = 120 - targetY;
} else if (targetY > 2732 - 60) {
targetY = 2 * (2732 - 60) - targetY;
}
}
// Professional AI with perfect tracking and faster response
if (Math.abs(targetY - paddleCenter) > 5) {
var moveDistance = rightPaddle.speed * 1.5;
if (targetY > paddleCenter) {
rightPaddle.targetY = paddleCenter + moveDistance;
} else {
rightPaddle.targetY = paddleCenter - moveDistance;
}
} else {
// Fine adjustment for precise positioning
rightPaddle.targetY = targetY;
}
}
// Countdown function
function startCountdown() {
gameState = 'countdown';
countdownNumber = 3;
ball.velocityX = 0;
ball.velocityY = 0;
function showCountdownNumber() {
if (countdownNumber > 0) {
countdownText.setText(countdownNumber.toString());
countdownText.visible = true;
countdownText.alpha = 0;
countdownText.scaleX = 2;
countdownText.scaleY = 2;
tween(countdownText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(countdownText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
countdownNumber--;
if (countdownNumber > 0) {
showCountdownNumber();
} else {
countdownText.visible = false;
gameState = 'playing';
ball.velocityX = (Math.random() > 0.5 ? 1 : -1) * ball.baseSpeed;
ball.velocityY = (Math.random() - 0.5) * 6;
}
}
});
}
});
} else {
gameState = 'playing';
ball.velocityX = (Math.random() > 0.5 ? 1 : -1) * ball.baseSpeed;
ball.velocityY = (Math.random() - 0.5) * 6;
}
}
showCountdownNumber();
}
// Ball collision detection
function checkBallCollisions() {
// Left paddle collision
if (ball.x <= leftPaddle.x + 10 && ball.x >= leftPaddle.x - 10 && ball.y >= leftPaddle.y - 60 && ball.y <= leftPaddle.y + 60 && ball.velocityX < 0) {
ball.velocityX = -ball.velocityX;
// Add some angle based on where ball hits paddle
var hitPosition = (ball.y - leftPaddle.y) / 60;
ball.velocityY += hitPosition * 3;
// Increase speed by 0.5
if (ball.velocityX > 0) {
ball.velocityX += 0.5;
} else {
ball.velocityX -= 0.5;
}
if (ball.velocityY > 0) {
ball.velocityY += 0.5;
} else {
ball.velocityY -= 0.5;
}
LK.getSound('paddleHit').play();
}
// Right paddle collision
if (ball.x >= rightPaddle.x - 10 && ball.x <= rightPaddle.x + 10 && ball.y >= rightPaddle.y - 60 && ball.y <= rightPaddle.y + 60 && ball.velocityX > 0) {
ball.velocityX = -ball.velocityX;
// Add some angle based on where ball hits paddle
var hitPosition = (ball.y - rightPaddle.y) / 60;
ball.velocityY += hitPosition * 3;
// Increase speed by 0.5
if (ball.velocityX > 0) {
ball.velocityX += 0.5;
} else {
ball.velocityX -= 0.5;
}
if (ball.velocityY > 0) {
ball.velocityY += 0.5;
} else {
ball.velocityY -= 0.5;
}
LK.getSound('paddleHit').play();
}
}
// Scoring
function checkScoring() {
if (ball.x < -20) {
// Right player scores
rightScore++;
rightScoreText.setText(rightScore.toString());
LK.getSound('score').play();
if (rightScore >= winningScore) {
LK.showGameOver();
return;
}
ball.reset();
}
if (ball.x > 2048 + 20) {
// Left player scores
leftScore++;
leftScoreText.setText(leftScore.toString());
LK.setScore(leftScore);
LK.getSound('score').play();
if (leftScore >= winningScore) {
LK.showYouWin();
return;
}
ball.reset();
}
}
game.update = function () {
if (gameState === 'playing') {
updateAI();
checkBallCollisions();
checkScoring();
}
}; /****
* 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
});
self.velocityX = 8;
self.velocityY = 6;
self.baseSpeed = 8;
self.reset = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.velocityX = 0;
self.velocityY = 0;
startCountdown();
};
self.update = function () {
if (gameState === 'playing') {
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with center line segments
for (var i = 0; i < centerLineSegments.length; i++) {
var segment = centerLineSegments[i];
if (self.intersects(segment)) {
// Increase speed by 0.5
if (self.velocityX > 0) {
self.velocityX += 0.5;
} else {
self.velocityX -= 0.5;
}
if (self.velocityY > 0) {
self.velocityY += 0.5;
} else {
self.velocityY -= 0.5;
}
break;
}
}
// Bounce off top and bottom walls
if (self.y <= 10 || self.y >= 2732 - 10) {
self.velocityY = -self.velocityY;
LK.getSound('wallHit').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.speed = 20;
self.targetY = self.y;
self.moveToTarget = function () {
var diff = self.targetY - self.y;
if (Math.abs(diff) > 2) {
self.y += diff * 0.25;
}
};
self.update = function () {
self.moveToTarget();
// Keep paddle within screen bounds
if (self.y < 60) self.y = 60;
if (self.y > 2732 - 60) self.y = 2732 - 60;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var leftPaddle = game.addChild(new Paddle());
var rightPaddle = game.addChild(new Paddle());
var ball = game.addChild(new Ball());
// Position paddles
leftPaddle.x = 50;
leftPaddle.y = 2732 / 2;
rightPaddle.x = 2048 - 50;
rightPaddle.y = 2732 / 2;
// Score tracking
var leftScore = 0;
var rightScore = 0;
var winningScore = 11;
// Game state
var gameState = 'playing'; // 'countdown' or 'playing'
var countdownNumber = 3;
// Score display
var leftScoreText = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
leftScoreText.anchor.set(0.5, 0);
leftScoreText.x = 2048 / 4;
leftScoreText.y = 100;
LK.gui.addChild(leftScoreText);
var rightScoreText = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
rightScoreText.anchor.set(0.5, 0);
rightScoreText.x = 2048 / 4 * 3;
rightScoreText.y = 100;
LK.gui.addChild(rightScoreText);
// Countdown text
var countdownText = new Text2('', {
size: 300,
fill: 0xFFFFFF
});
countdownText.anchor.set(0.5, 0.5);
countdownText.x = 2048 / 2;
countdownText.y = 2732 / 2;
countdownText.visible = false;
LK.gui.addChild(countdownText);
// Initialize ball
ball.reset();
// Center line (dashed effect with multiple small rectangles)
var centerLineSegments = [];
for (var i = 0; i < 20; i++) {
var segment = LK.getAsset('paddle', {
width: 4,
height: 75,
anchorX: 0.5,
anchorY: 0.5
});
segment.x = 2048 / 2;
segment.y = 100 + i * 70;
game.addChild(segment);
centerLineSegments.push(segment);
}
// Touch controls
var isDragging = false;
var dragStartY = 0;
var paddleStartY = 0;
game.down = function (x, y, obj) {
if (x < 2048 / 2) {
isDragging = true;
dragStartY = y;
paddleStartY = leftPaddle.y;
}
};
game.move = function (x, y, obj) {
if (isDragging && x < 2048 / 2) {
var deltaY = y - dragStartY;
leftPaddle.targetY = paddleStartY + deltaY;
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Improved AI for right paddle
function updateAI() {
var targetY = ball.y;
var paddleCenter = rightPaddle.y;
// Predict ball position when it reaches paddle
if (ball.velocityX > 0) {
var timeToReachPaddle = (rightPaddle.x - ball.x) / ball.velocityX;
targetY = ball.y + ball.velocityY * timeToReachPaddle;
// Account for wall bounces in prediction
if (targetY < 60) {
targetY = 120 - targetY;
} else if (targetY > 2732 - 60) {
targetY = 2 * (2732 - 60) - targetY;
}
}
// Professional AI with perfect tracking and faster response
if (Math.abs(targetY - paddleCenter) > 5) {
var moveDistance = rightPaddle.speed * 1.5;
if (targetY > paddleCenter) {
rightPaddle.targetY = paddleCenter + moveDistance;
} else {
rightPaddle.targetY = paddleCenter - moveDistance;
}
} else {
// Fine adjustment for precise positioning
rightPaddle.targetY = targetY;
}
}
// Countdown function
function startCountdown() {
gameState = 'countdown';
countdownNumber = 3;
ball.velocityX = 0;
ball.velocityY = 0;
function showCountdownNumber() {
if (countdownNumber > 0) {
countdownText.setText(countdownNumber.toString());
countdownText.visible = true;
countdownText.alpha = 0;
countdownText.scaleX = 2;
countdownText.scaleY = 2;
tween(countdownText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(countdownText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
countdownNumber--;
if (countdownNumber > 0) {
showCountdownNumber();
} else {
countdownText.visible = false;
gameState = 'playing';
ball.velocityX = (Math.random() > 0.5 ? 1 : -1) * ball.baseSpeed;
ball.velocityY = (Math.random() - 0.5) * 6;
}
}
});
}
});
} else {
gameState = 'playing';
ball.velocityX = (Math.random() > 0.5 ? 1 : -1) * ball.baseSpeed;
ball.velocityY = (Math.random() - 0.5) * 6;
}
}
showCountdownNumber();
}
// Ball collision detection
function checkBallCollisions() {
// Left paddle collision
if (ball.x <= leftPaddle.x + 10 && ball.x >= leftPaddle.x - 10 && ball.y >= leftPaddle.y - 60 && ball.y <= leftPaddle.y + 60 && ball.velocityX < 0) {
ball.velocityX = -ball.velocityX;
// Add some angle based on where ball hits paddle
var hitPosition = (ball.y - leftPaddle.y) / 60;
ball.velocityY += hitPosition * 3;
// Increase speed by 0.5
if (ball.velocityX > 0) {
ball.velocityX += 0.5;
} else {
ball.velocityX -= 0.5;
}
if (ball.velocityY > 0) {
ball.velocityY += 0.5;
} else {
ball.velocityY -= 0.5;
}
LK.getSound('paddleHit').play();
}
// Right paddle collision
if (ball.x >= rightPaddle.x - 10 && ball.x <= rightPaddle.x + 10 && ball.y >= rightPaddle.y - 60 && ball.y <= rightPaddle.y + 60 && ball.velocityX > 0) {
ball.velocityX = -ball.velocityX;
// Add some angle based on where ball hits paddle
var hitPosition = (ball.y - rightPaddle.y) / 60;
ball.velocityY += hitPosition * 3;
// Increase speed by 0.5
if (ball.velocityX > 0) {
ball.velocityX += 0.5;
} else {
ball.velocityX -= 0.5;
}
if (ball.velocityY > 0) {
ball.velocityY += 0.5;
} else {
ball.velocityY -= 0.5;
}
LK.getSound('paddleHit').play();
}
}
// Scoring
function checkScoring() {
if (ball.x < -20) {
// Right player scores
rightScore++;
rightScoreText.setText(rightScore.toString());
LK.getSound('score').play();
if (rightScore >= winningScore) {
LK.showGameOver();
return;
}
ball.reset();
}
if (ball.x > 2048 + 20) {
// Left player scores
leftScore++;
leftScoreText.setText(leftScore.toString());
LK.setScore(leftScore);
LK.getSound('score').play();
if (leftScore >= winningScore) {
LK.showYouWin();
return;
}
ball.reset();
}
}
game.update = function () {
if (gameState === 'playing') {
updateAI();
checkBallCollisions();
checkScoring();
}
};