/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 10
});
self.addChild(ballGraphics);
// Ball properties
self.vx = BALL_INITIAL_SPEED;
self.vy = BALL_INITIAL_SPEED;
self.speed = BALL_INITIAL_SPEED;
// Initialize tracking variables
self.lastX = 0;
self.lastY = 0;
self.lastLeftIntersect = false;
self.lastRightIntersect = false;
// Reset ball to center with random direction
self.reset = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.speed = BALL_INITIAL_SPEED;
// Randomize direction but keep it horizontal
var angle = Math.random() * Math.PI / 4 - Math.PI / 8;
if (Math.random() > 0.5) {
angle += Math.PI;
}
self.vx = Math.cos(angle) * self.speed;
self.vy = Math.sin(angle) * self.speed;
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
self.update = function () {
// Move ball
self.x += self.vx;
self.y += self.vy;
// Check top/bottom boundaries
if (self.lastY >= 0 && self.y < 0 || self.lastY <= 2732 && self.y > 2732) {
self.vy = -self.vy;
self.y = self.y < 0 ? 0 : 2732;
}
// Check paddle collisions
var leftIntersect = self.intersects(leftPaddle);
var rightIntersect = self.intersects(rightPaddle);
// Left paddle collision
if (!self.lastLeftIntersect && leftIntersect) {
// Play bounce sound on left paddle collision
LK.getSound('bounce').play();
self.vx = -self.vx;
// Increase speed
self.speed += BALL_SPEED_INCREMENT;
self.vx = self.vx > 0 ? self.speed : -self.speed;
// Adjust angle based on where ball hit paddle
var relativeY = (self.y - leftPaddle.y) / 200;
self.vy = relativeY * self.speed;
}
// Right paddle collision
if (!self.lastRightIntersect && rightIntersect) {
// Play bounce sound on right paddle collision
LK.getSound('bounce').play();
self.vx = -self.vx;
// Increase speed
self.speed += BALL_SPEED_INCREMENT;
self.vx = self.vx > 0 ? self.speed : -self.speed;
// Adjust angle based on where ball hit paddle
var relativeY = (self.y - rightPaddle.y) / 200;
self.vy = relativeY * self.speed;
}
// Check if ball passed the paddles (scoring)
if (self.lastX >= 0 && self.x < 0) {
// Right player scored
LK.getSound('point').play();
rightScore++;
rightScoreText.setText(rightScore);
// Check win condition
if (rightScore >= WINNING_SCORE) {
startRainbowEffect(rightScoreText);
LK.showYouWin();
} else {
self.reset();
}
} else if (self.lastX <= 2048 && self.x > 2048) {
// Left player scored
LK.getSound('point').play();
leftScore++;
leftScoreText.setText(leftScore);
// Check win condition
if (leftScore >= WINNING_SCORE) {
startRainbowEffect(leftScoreText);
LK.showYouWin();
} else {
self.reset();
}
}
// Update last states
self.lastX = self.x;
self.lastY = self.y;
self.lastLeftIntersect = leftIntersect;
self.lastRightIntersect = rightIntersect;
};
return self;
});
// Create center line
var CenterLine = Container.expand(function () {
var self = Container.call(this);
// Create dashed line segments
var lineCount = 20;
var segmentHeight = 2732 / (lineCount * 2);
for (var i = 0; i < lineCount; i++) {
var segment = LK.getAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5
});
segment.y = i * segmentHeight * 2 + segmentHeight;
self.addChild(segment); // Correctly using the method to add children
}
return self;
});
// Initialize shapes for paddles and ball
// Paddle class
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = LK.getAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(paddleGraphics);
// Initialize tracking variables
self.lastY = 0;
// Boundary checking
self.update = function () {
// Keep paddle within screen bounds
// New paddle visual height is 240 (asset width 240, orientation 1). Half height is 120.
if (self.y < 120) self.y = 120;
if (self.y > 2732 - 120) self.y = 2732 - 120;
// Update last position
self.lastY = self.y;
};
self.down = function (x, y, obj) {
activePaddle = self;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Rainbow animation variables and functions
var rainbowColors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x9400D3]; // Classic rainbow
var rainbowCurrentColorIndex = 0;
var rainbowTargetText = null; // Stores the Text2 object currently being animated
function _animateToNextRainbowColor() {
// Ensure the target text object is still valid and part of the game scene
if (!rainbowTargetText || !rainbowTargetText.parent) {
rainbowTargetText = null; // Clear target if it's no longer valid
return;
}
rainbowCurrentColorIndex = (rainbowCurrentColorIndex + 1) % rainbowColors.length;
var nextColor = rainbowColors[rainbowCurrentColorIndex];
tween(rainbowTargetText, {
fill: nextColor
}, {
duration: 300,
// Duration for each color transition in milliseconds
easing: tween.linear,
// A smooth, linear transition
onFinish: function onFinish() {
// Continue the animation cycle if the target is still valid
if (rainbowTargetText && rainbowTargetText.parent) {
_animateToNextRainbowColor();
} else {
rainbowTargetText = null; // Clear target if it became invalid during tween
}
}
});
}
function startRainbowEffect(textObject) {
if (typeof tween === 'undefined') {
console.log("Ava here: Tween plugin not available for rainbow effect. Skipping.");
return; // Tween plugin isn't loaded
}
// If another text is already doing the rainbow, stop it.
if (rainbowTargetText && rainbowTargetText !== textObject) {
tween.stop(rainbowTargetText, {
fill: true
});
}
rainbowTargetText = textObject;
var currentFill = textObject.fill;
var startIndex = rainbowColors.indexOf(currentFill);
if (startIndex !== -1) {
// Current color is already in our rainbow, start cycling from there
rainbowCurrentColorIndex = startIndex;
_animateToNextRainbowColor(); // Start the cycle
} else {
// Current color is not a rainbow color (e.g., white).
// Tween to the first rainbow color, then start cycling.
rainbowCurrentColorIndex = 0; // Target the first color in the rainbow array
tween(rainbowTargetText, {
fill: rainbowColors[rainbowCurrentColorIndex]
}, {
duration: 150,
// Quick transition to the first rainbow color
easing: tween.linear,
onFinish: function onFinish() {
if (rainbowTargetText && rainbowTargetText.parent) {
_animateToNextRainbowColor(); // Start the main cycle
} else {
rainbowTargetText = null;
}
}
});
}
}
// Game constants
var WINNING_SCORE = 10;
var PADDLE_SPEED = 15;
var BALL_INITIAL_SPEED = 8;
var BALL_SPEED_INCREMENT = 0.5;
// Game variables
var leftScore = 0;
var rightScore = 0;
var leftScoreText, rightScoreText;
var leftPaddle, rightPaddle;
var ball;
var activePaddle = null;
// Initialize shapes for paddles and ball
// Create paddles
leftPaddle = new Paddle();
leftPaddle.x = 100;
leftPaddle.y = 2732 / 2;
game.addChild(leftPaddle);
rightPaddle = new Paddle();
rightPaddle.x = 2048 - 100;
rightPaddle.y = 2732 / 2;
game.addChild(rightPaddle);
// Create ball
ball = new Ball();
ball.reset();
game.addChild(ball);
// Create score displays
leftScoreText = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
leftScoreText.anchor.set(0.5, 0);
leftScoreText.x = 2048 / 4;
leftScoreText.y = 50;
game.addChild(leftScoreText);
rightScoreText = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
rightScoreText.anchor.set(0.5, 0);
rightScoreText.x = 2048 * 3 / 4;
rightScoreText.y = 50;
game.addChild(rightScoreText);
var centerLine = new CenterLine();
// Initialize center line segments
centerLine.x = 2048 / 2;
game.addChild(centerLine);
// Game event handlers
game.move = function (x, y, obj) {
if (activePaddle) {
activePaddle.y = y;
}
};
game.up = function (x, y, obj) {
activePaddle = null;
};
game.down = function (x, y, obj) {
// Determine which paddle to control based on which side of the screen was touched
if (x < 2048 / 2) {
activePaddle = leftPaddle;
} else {
activePaddle = rightPaddle;
}
// Move the paddle to the touch position
if (activePaddle) {
activePaddle.y = y;
}
};
// Game update loop
game.update = function () {
// Update game objects
ball.update();
leftPaddle.update();
rightPaddle.update();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 10
});
self.addChild(ballGraphics);
// Ball properties
self.vx = BALL_INITIAL_SPEED;
self.vy = BALL_INITIAL_SPEED;
self.speed = BALL_INITIAL_SPEED;
// Initialize tracking variables
self.lastX = 0;
self.lastY = 0;
self.lastLeftIntersect = false;
self.lastRightIntersect = false;
// Reset ball to center with random direction
self.reset = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.speed = BALL_INITIAL_SPEED;
// Randomize direction but keep it horizontal
var angle = Math.random() * Math.PI / 4 - Math.PI / 8;
if (Math.random() > 0.5) {
angle += Math.PI;
}
self.vx = Math.cos(angle) * self.speed;
self.vy = Math.sin(angle) * self.speed;
// Update last positions
self.lastX = self.x;
self.lastY = self.y;
};
self.update = function () {
// Move ball
self.x += self.vx;
self.y += self.vy;
// Check top/bottom boundaries
if (self.lastY >= 0 && self.y < 0 || self.lastY <= 2732 && self.y > 2732) {
self.vy = -self.vy;
self.y = self.y < 0 ? 0 : 2732;
}
// Check paddle collisions
var leftIntersect = self.intersects(leftPaddle);
var rightIntersect = self.intersects(rightPaddle);
// Left paddle collision
if (!self.lastLeftIntersect && leftIntersect) {
// Play bounce sound on left paddle collision
LK.getSound('bounce').play();
self.vx = -self.vx;
// Increase speed
self.speed += BALL_SPEED_INCREMENT;
self.vx = self.vx > 0 ? self.speed : -self.speed;
// Adjust angle based on where ball hit paddle
var relativeY = (self.y - leftPaddle.y) / 200;
self.vy = relativeY * self.speed;
}
// Right paddle collision
if (!self.lastRightIntersect && rightIntersect) {
// Play bounce sound on right paddle collision
LK.getSound('bounce').play();
self.vx = -self.vx;
// Increase speed
self.speed += BALL_SPEED_INCREMENT;
self.vx = self.vx > 0 ? self.speed : -self.speed;
// Adjust angle based on where ball hit paddle
var relativeY = (self.y - rightPaddle.y) / 200;
self.vy = relativeY * self.speed;
}
// Check if ball passed the paddles (scoring)
if (self.lastX >= 0 && self.x < 0) {
// Right player scored
LK.getSound('point').play();
rightScore++;
rightScoreText.setText(rightScore);
// Check win condition
if (rightScore >= WINNING_SCORE) {
startRainbowEffect(rightScoreText);
LK.showYouWin();
} else {
self.reset();
}
} else if (self.lastX <= 2048 && self.x > 2048) {
// Left player scored
LK.getSound('point').play();
leftScore++;
leftScoreText.setText(leftScore);
// Check win condition
if (leftScore >= WINNING_SCORE) {
startRainbowEffect(leftScoreText);
LK.showYouWin();
} else {
self.reset();
}
}
// Update last states
self.lastX = self.x;
self.lastY = self.y;
self.lastLeftIntersect = leftIntersect;
self.lastRightIntersect = rightIntersect;
};
return self;
});
// Create center line
var CenterLine = Container.expand(function () {
var self = Container.call(this);
// Create dashed line segments
var lineCount = 20;
var segmentHeight = 2732 / (lineCount * 2);
for (var i = 0; i < lineCount; i++) {
var segment = LK.getAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5
});
segment.y = i * segmentHeight * 2 + segmentHeight;
self.addChild(segment); // Correctly using the method to add children
}
return self;
});
// Initialize shapes for paddles and ball
// Paddle class
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = LK.getAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(paddleGraphics);
// Initialize tracking variables
self.lastY = 0;
// Boundary checking
self.update = function () {
// Keep paddle within screen bounds
// New paddle visual height is 240 (asset width 240, orientation 1). Half height is 120.
if (self.y < 120) self.y = 120;
if (self.y > 2732 - 120) self.y = 2732 - 120;
// Update last position
self.lastY = self.y;
};
self.down = function (x, y, obj) {
activePaddle = self;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Rainbow animation variables and functions
var rainbowColors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x9400D3]; // Classic rainbow
var rainbowCurrentColorIndex = 0;
var rainbowTargetText = null; // Stores the Text2 object currently being animated
function _animateToNextRainbowColor() {
// Ensure the target text object is still valid and part of the game scene
if (!rainbowTargetText || !rainbowTargetText.parent) {
rainbowTargetText = null; // Clear target if it's no longer valid
return;
}
rainbowCurrentColorIndex = (rainbowCurrentColorIndex + 1) % rainbowColors.length;
var nextColor = rainbowColors[rainbowCurrentColorIndex];
tween(rainbowTargetText, {
fill: nextColor
}, {
duration: 300,
// Duration for each color transition in milliseconds
easing: tween.linear,
// A smooth, linear transition
onFinish: function onFinish() {
// Continue the animation cycle if the target is still valid
if (rainbowTargetText && rainbowTargetText.parent) {
_animateToNextRainbowColor();
} else {
rainbowTargetText = null; // Clear target if it became invalid during tween
}
}
});
}
function startRainbowEffect(textObject) {
if (typeof tween === 'undefined') {
console.log("Ava here: Tween plugin not available for rainbow effect. Skipping.");
return; // Tween plugin isn't loaded
}
// If another text is already doing the rainbow, stop it.
if (rainbowTargetText && rainbowTargetText !== textObject) {
tween.stop(rainbowTargetText, {
fill: true
});
}
rainbowTargetText = textObject;
var currentFill = textObject.fill;
var startIndex = rainbowColors.indexOf(currentFill);
if (startIndex !== -1) {
// Current color is already in our rainbow, start cycling from there
rainbowCurrentColorIndex = startIndex;
_animateToNextRainbowColor(); // Start the cycle
} else {
// Current color is not a rainbow color (e.g., white).
// Tween to the first rainbow color, then start cycling.
rainbowCurrentColorIndex = 0; // Target the first color in the rainbow array
tween(rainbowTargetText, {
fill: rainbowColors[rainbowCurrentColorIndex]
}, {
duration: 150,
// Quick transition to the first rainbow color
easing: tween.linear,
onFinish: function onFinish() {
if (rainbowTargetText && rainbowTargetText.parent) {
_animateToNextRainbowColor(); // Start the main cycle
} else {
rainbowTargetText = null;
}
}
});
}
}
// Game constants
var WINNING_SCORE = 10;
var PADDLE_SPEED = 15;
var BALL_INITIAL_SPEED = 8;
var BALL_SPEED_INCREMENT = 0.5;
// Game variables
var leftScore = 0;
var rightScore = 0;
var leftScoreText, rightScoreText;
var leftPaddle, rightPaddle;
var ball;
var activePaddle = null;
// Initialize shapes for paddles and ball
// Create paddles
leftPaddle = new Paddle();
leftPaddle.x = 100;
leftPaddle.y = 2732 / 2;
game.addChild(leftPaddle);
rightPaddle = new Paddle();
rightPaddle.x = 2048 - 100;
rightPaddle.y = 2732 / 2;
game.addChild(rightPaddle);
// Create ball
ball = new Ball();
ball.reset();
game.addChild(ball);
// Create score displays
leftScoreText = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
leftScoreText.anchor.set(0.5, 0);
leftScoreText.x = 2048 / 4;
leftScoreText.y = 50;
game.addChild(leftScoreText);
rightScoreText = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
rightScoreText.anchor.set(0.5, 0);
rightScoreText.x = 2048 * 3 / 4;
rightScoreText.y = 50;
game.addChild(rightScoreText);
var centerLine = new CenterLine();
// Initialize center line segments
centerLine.x = 2048 / 2;
game.addChild(centerLine);
// Game event handlers
game.move = function (x, y, obj) {
if (activePaddle) {
activePaddle.y = y;
}
};
game.up = function (x, y, obj) {
activePaddle = null;
};
game.down = function (x, y, obj) {
// Determine which paddle to control based on which side of the screen was touched
if (x < 2048 / 2) {
activePaddle = leftPaddle;
} else {
activePaddle = rightPaddle;
}
// Move the paddle to the touch position
if (activePaddle) {
activePaddle.y = y;
}
};
// Game update loop
game.update = function () {
// Update game objects
ball.update();
leftPaddle.update();
rightPaddle.update();
};