/**** * Classes ****/ // Initialize rotation speed //<Assets used in the game will automatically appear here> // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.rotationSpeed = 0; var half = self.width / 2; self.speedX = 3; self.speedY = 3; self.accelerationY = 0.5; // Gravity self.friction = 0.99; // Friction to slow down the ball over time self.lastCollisionTime = 0; // Initialize last collision time self.update = function (net, player1, player2) { if (!ballCanMove) { return; } // Apply gravity self.speedY += self.accelerationY; // Apply friction self.speedX *= self.friction; self.speedY *= self.friction; // Apply speed limit self.speedX = Math.sign(self.speedX) * Math.min(Math.abs(self.speedX), SPEED_LIMIT); self.speedY = Math.sign(self.speedY) * Math.min(Math.abs(self.speedY), SPEED_LIMIT); // Update ball position self.x += self.speedX; self.y += self.speedY; self.rotation += self.rotationSpeed * (self.speedX > 0 ? 1 : -1); // Update ball rotation based on direction self.rotationSpeed *= 0.98; // Gradually decrease rotation speed // Check for out of bounds if (self.y + half > 2000) { self.y = 2000 - half; self.speedY = -Math.abs(self.speedY) * 0.7; // Make the ball bounce up with more reduced speed self.speedX *= 0.9; // Apply slight energy loss on horizontal speed playSound('whistle', 1000); // Play whistle sound when ball touches the ground with a cooldown of 1 second if (self.x < 1024 && servingPlayer === 2) { updateScore(2); resetBall(); // Reset the ball after scoring } else if (self.x >= 1024 && servingPlayer === 1) { updateScore(1); resetBall(); // Reset the ball after scoring } else { servingPlayer = servingPlayer == 1 ? 2 : 1; resetBall(); // Reset the ball after scoring } player1Touches = 0; player2Touches = 0; } else if (self.y - half < 0) { self.y = half; self.speedY = Math.abs(self.speedY) * 0.8; // Make the ball bounce down with reduced speed } // Check for x screen limits if (self.x - half < 0) { self.x = half; self.speedX = Math.abs(self.speedX); // Make the ball bounce to the right } else if (self.x + half > 2048) { self.x = 2048 - half; self.speedX = -Math.abs(self.speedX); // Make the ball bounce to the left } // Collision detection logic moved to game update }; }); // Net class var Net = Container.expand(function () { var self = Container.call(this); var netGraphics = self.attachAsset('net', { anchorX: 0.5, anchorY: 1, alpha: isDebug ? 1 : 0 }); }); // Player class var Player = Container.expand(function (index) { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 1, alpha: 0.98, scaleX: index === 2 ? -1 : 1, tint: index === 1 ? 0xADD8E6 : 0x1089f4 // Light blue for player 1, Blue for player 2 }); self.index = index; self.scale.set(1, 1); // Initialize player scale to normal self.jumping = false; self.falling = false; self.speedX = 0; // Initialize horizontal speed var collidSize = 245; self.collisionBody = LK.getAsset('collisionBody', { anchorX: 0.5, anchorY: 0.5, alpha: isDebug ? 0.6 : 0, width: collidSize, height: collidSize, x: 0, y: -200 }); self.addChild(self.collisionBody); self.update = function () { var prevX = self.x; // Store previous x position var prevY = self.y; // Store previous y position if (self.jumping && !self.falling) { self.y -= 20; // Increase jump speed self.scale.set(1, 1 + 0.25 * (PLAYER_INITIAL_Y - self.y) / 700); // Progressive scaling when jumping if (self.y <= 1300) { self.falling = true; } } if (self.falling && self.y < PLAYER_INITIAL_Y) { self.y += 20; // Increase fall speed for smoother jump if (self.y > PLAYER_INITIAL_Y - 256) { self.scale.set(1 + 0.15 * (PLAYER_INITIAL_Y - self.y) / 700, 1 - 0.15 * (PLAYER_INITIAL_Y - self.y) / 700); // Progressive scaling when falling } } if (self.y >= PLAYER_INITIAL_Y) { self.jumping = false; self.falling = false; self.y = PLAYER_INITIAL_Y; // Ensure player lands on the ground // Reset scale to normal when player stops moving self.scale.set(1, 1); } if (self.y < 0) { self.y = 0; // Prevent player1 from moving above the window } if (self.index === 2 && self.x > 1024 && self.x < 1024 + self.width / 2) { self.x = 1024 + self.width / 2; // Prevent player2 from moving past the net } self.speedX = self.x - prevX; // Calculate horizontal speed based on movement self.speedY = self.y - prevY; // Calculate vertical speed based on movement }; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue background }); /**** * Game Code ****/ var PLAYER1_INITIAL_X = 256; var PLAYER_INITIAL_Y = 2000; var PLAYER2_INITIAL_X = 1792; var BALL1_INITIAL_X = 640; var BALL_INITIAL_Y = 1256; var BALL2_INITIAL_X = 1408; var gameStarted = false; var startButton = null; var joystick; var joystickBasePosition; var joystickDrag; var touchTime; // Global variable to store the touch time var prevMouseY; var lastPlayedTime; var ballCanMove; var SPEED_LIMIT; // Define a global speed limit var isDebug = false; var player2Debug; var player1Touches; var player2Touches; var resetTime; // Global variable to store the reset time var nextRoundPauseDelay; var score1; var score2; var finalScore = 15; // Define the final score for the game var servingPlayer = 1; // Initialize serving player to player 1 var ballShadow; var player1Shadow; var player2Shadow; var beachBall = null; function playSound(soundId, cooldown) { var currentTime = Date.now(); if (!lastPlayedTime[soundId] || currentTime - lastPlayedTime[soundId] > cooldown) { LK.getSound(soundId).play(); lastPlayedTime[soundId] = currentTime; } } function updateScore(index) { if (index === 1) { score1 += 1; scoreTxt1.setText(score1.toString().padStart(2, '0')); } else if (index === 2) { score2 += 1; scoreTxt2.setText(score2.toString().padStart(2, '0')); } // Check for game over condition if ((score1 >= finalScore || score2 >= finalScore) && Math.abs(score1 - score2) >= 2) { var resultText = new Text2(score1 > score2 ? 'VICTORY!' : 'DEFEAT!', { size: 300, fill: 0xFFFFFF, fontWeight: "bold", dropShadow: true }); resultText.anchor.set(0.5, 2); //resultText.x = 0; //resultText.y = -512; LK.gui.center.addChild(resultText); LK.setTimeout(function () { LK.showGameOver(); }, 2000); } } function handleJoystickLimits(x, y) { if (joystickDrag) { var dx = x - joystickBasePosition.x; var dy = y - joystickBasePosition.y; var distance = Math.sqrt(dx * dx + dy * dy); var maxDistance = joystick.width / 3; // Max distance joystick can move from center if (distance > maxDistance) { var angle = Math.atan2(dy, dx); dx = Math.cos(angle) * maxDistance; dy = Math.sin(angle) * maxDistance; } joystick.x = joystickBasePosition.x + dx; joystick.y = joystickBasePosition.y + dy; } } function aiUpdate() { if (ball.x > 1024) { if (ball.x > player2.x + player2.width / 2) { player2.x += 5; // Move right } else if (ball.x < player2.x - player2.width / 2) { player2.x -= 5; // Move left } } if (!player2.jumping && player2.y >= PLAYER_INITIAL_Y && ball.y < player2.y && Math.abs(ball.x - player2.x) < player2.width) { player2.jumping = true; } } function resetBall() { ball.y = BALL_INITIAL_Y; // Set the ball's initial vertical position using BALL_INITIAL_Y ball.speedX = 3; ball.speedY = 3; // Reset speed ball.accelerationY = 0.5; // Reset gravity ball.friction = 0.99; // Reset friction ballCanMove = false; // Reset ball movement flag ball.rotation = 0; // Reset rotation angle resetTime = Date.now(); // Update reset time player1Touches = 0; player2Touches = 0; ball.alpha = 0; joystick.alpha = 0.; ball.x = servingPlayer === 1 ? BALL1_INITIAL_X : BALL2_INITIAL_X; // Move players progressively to their initial X positions var movePlayerToInitialX = function movePlayerToInitialX(player, initialX) { var moveInterval = LK.setInterval(function () { updateShadows(); if (Math.abs(player.x - initialX) < 5) { player.x = initialX; LK.clearInterval(moveInterval); } else { player.x += (initialX - player.x) * 0.1; } }, 16); // Move players every 16ms (approximately 60 FPS) }; movePlayerToInitialX(player1, PLAYER1_INITIAL_X); movePlayerToInitialX(player2, PLAYER2_INITIAL_X); } function customBoxCircleIntersect(box, circle) { var circleX = circle.x; var circleY = circle.y; if (circle.parent) { circleX += circle.parent.x; circleY += circle.parent.y; } var boxX = box.x; var boxY = box.y; var halfBoxWidth = box.width / 2; var halfCircleWidth = circle.width / 2; var netBuffer = 2; // Adjust netBuffer to control how far the ball bounces from the net var left = boxX - halfBoxWidth; var right = boxX + halfBoxWidth; var top = boxY - box.height; var bottom = boxY; // Check if the circle intersects with the box (considering entire ball) return circleX + halfCircleWidth > left && circleX - halfCircleWidth < right && circleY + halfCircleWidth > top + netBuffer && circleY - halfCircleWidth < bottom; } function customIntersect(circle1, circle2) { //console.log("customIntersect ", circle1, circle2.parent); var circle2X = circle2.x; var circle2Y = circle2.y; if (circle2.parent) { circle2X += circle2.parent.x; circle2Y += circle2.parent.y; } var dx = circle1.x - circle2X; var dy = circle1.y - circle2Y; var distance = Math.sqrt(dx * dx + dy * dy); var radiusSum = circle1.width / 2 + circle2.width / 2; //console.log("customIntersect ", distance.toFixed(0), radiusSum); return distance < radiusSum; } var background; gameInitialize(); game.update = function () { if (Date.now() - resetTime < nextRoundPauseDelay) { updateShadows(); return; } ball.alpha = 1; joystick.alpha = 1; if (!ballCanMove && (customIntersect(ball, player1.collisionBody) || customIntersect(ball, player2.collisionBody))) { ///|| player2.collisionBody && customIntersect(ball, player2.collisionBody) || customIntersect(ball, player1) || customIntersect(ball, player2))) { ballCanMove = true; //console.log("Player collision detected 1"); } // Check for collision with the net if (customBoxCircleIntersect(net, ball)) { // Check if enough time has passed since the last collision if (Date.now() - ball.lastCollisionTime > 0) { playSound('bump', 500); // Play bump sound on collision with a cooldown of 0.5 seconds // Reverse ball's horizontal direction and apply some energy loss // Handle y reaction when intersecting from the top var newSpeedX = ball.speedX * 1.15 + (Math.random() - 0.5) * 0.05; var newSpeedY = ball.speedY * 1.15 + (Math.random() - 0.5) * 0.05; // Check if the ball is intersecting the net from the top //console.log("TOP chceck", ball.y + ball.height / 2, net.y - net.height); if (ball.y + ball.height / 2 <= net.y - net.height + 100) { //console.log("TOP collision detected ", ball.y + ball.height / 2, net.y - net.height); // Top collision: Reverse the horizontal direction and limit the speed newSpeedX = Math.sign(ball.speedX) + ball.speedX * 1.25; // Top collision: Reverse the vertical direction and limit the speed newSpeedY = -Math.min(Math.abs(newSpeedY), SPEED_LIMIT); } else { // Side collision: Reverse the horizontal direction and limit the speed newSpeedX = -Math.sign(newSpeedX) * Math.min(Math.abs(newSpeedX), SPEED_LIMIT); newSpeedY = ball.speedY; } ball.speedX = newSpeedX; ball.speedY = newSpeedY; // Update the last collision time ball.lastCollisionTime = Date.now(); } } // Check for collisions with players var touchIndex = 0; if (ball.x + ball.width / 2 < 1024) { player2Touches = 0; } else if (ball.x - ball.width / 2 > 1024) { player1Touches = 0; } if (ball.x < 1024 && customIntersect(ball, player1.collisionBody)) { touchIndex = 1; } else if (ball.x > 1024 && customIntersect(ball, player2.collisionBody)) { touchIndex = 2; } if (touchIndex) { playSound('bump', 500); // Play bump sound on collision with a cooldown of 0.5 seconds // Increment touch counters if (touchIndex === 1) { if (Date.now() - touchTime >= 300) { player1Touches++; } player2Touches = 0; // Reset opponent's touch counter } else if (touchIndex === 2) { if (Date.now() - touchTime >= 300) { player2Touches++; } player1Touches = 0; // Reset opponent's touch counter } touchTime = Date.now(); // Update touchTime when a player touches the ball // Check for touch limit if (player1Touches >= 4) { if (servingPlayer == 1) { servingPlayer = 2; } else { updateScore(2); } } else if (player2Touches >= 4) { if (servingPlayer == 2) { servingPlayer = 1; } else { updateScore(1); } } if (player1Touches >= 4 || player2Touches >= 4) { playSound('whistle', 1000); // Play whistle sound resetBall(); } //console.log("Player collision detected 2"); var player = touchIndex === 1 ? player1 : player2; var collisionAngle = Math.atan2(ball.y - player.y, ball.x - player.x); var speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY); var playerSpeed = Math.sqrt(player.speedX * player.speedX + player.speedY * player.speedY); var newSpeedX = Math.sign(speed * Math.cos(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.cos(collisionAngle)) * Math.min(Math.abs(speed * Math.cos(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.cos(collisionAngle)), SPEED_LIMIT); var newSpeedY = Math.sign(speed * Math.sin(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.sin(collisionAngle)) * Math.min(Math.abs(speed * Math.sin(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.sin(collisionAngle)), SPEED_LIMIT); // Add a bit of randomness to the ball's speed to avoid infinite stuck ball.speedX = newSpeedX * 1.20 + (Math.random() - 0.5) * 0.05; ball.speedY = newSpeedY * 1.20 + (Math.random() - 0.5) * 0.05; } ball.rotationSpeed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) * 0.005; // Update rotation speed based on collision aiUpdate(); if (joystickDrag) { var dx = joystick.x - joystickBasePosition.x; var dy = joystick.y - joystickBasePosition.y; var movementRatio = 0.25; // Adjust this ratio to make the movement smoother player1.x = Math.max(player1.width / 2, Math.min(player1.x + dx * movementRatio, 1024 - player1.width / 2 - net.width / 2)); player1.speedX = dx * movementRatio; if (!player1.jumping && dy < -80 && Date.now() - resetTime >= nextRoundPauseDelay) { player1.jumping = true; } //console.log("MOVE joystickDrag dx: ".concat(dx.toFixed(0), ", player1.speedX: ").concat(player1.speedX.toFixed(0), ", joystickDrag dy: ").concat(dy.toFixed(0))); } updateShadows(); }; game.down = function (x, y, obj) { if (!gameStarted) { gameStarted = true; startButton.destroy(); joystick.visible = true; } else { joystickDrag = true; } }; game.move = function (x, y, obj) { if (Date.now() - resetTime < nextRoundPauseDelay) { return; } handleJoystickLimits(x, y); }; game.up = function (x, y, obj) { joystickDrag = false; //console.log("UP joystickDrag state:", joystickDrag); joystick.x = joystickBasePosition.x; joystick.y = joystickBasePosition.y; }; function gameInitialize() { background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(background); initShadows(); var parasol = LK.getAsset('parasol', { anchorX: 0.5, anchorY: 1, x: 1848, y: 1450 }); game.addChild(parasol); var chair = LK.getAsset('chair', { anchorX: 0.5, anchorY: 1, x: 1948, y: 1500 }); game.addChild(chair); var towel = LK.getAsset('towel', { anchorX: 0.5, anchorY: 1, x: 10, y: 1540 }); game.addChild(towel); beachBall = LK.getAsset('beachBall', { anchorX: 0.5, anchorY: 1, x: 120, y: 1465 }); game.addChild(beachBall); player1 = new Player(1); player2 = new Player(2); game.addChild(player1); game.addChild(player2); player1.x = PLAYER1_INITIAL_X; player1.y = PLAYER_INITIAL_Y; player2.y = PLAYER_INITIAL_Y; player2.x = PLAYER2_INITIAL_X; ball = new Ball(); ball.x = servingPlayer === 1 ? BALL1_INITIAL_X : BALL2_INITIAL_X; ball.y = BALL_INITIAL_Y; // Set the ball's initial vertical position using BALL_INITIAL_Y ball.rotationSpeed = 0; game.addChild(ball); scoreTxt1 = new Text2('00', { size: 200, fill: 0x0000D8, fontWeight: "bold", dropShadow: true }); scoreTxt1.anchor.set(-1, 0); LK.gui.topLeft.addChild(scoreTxt1); scoreTxt2 = new Text2('00', { size: 200, fill: 0xFF6347, fontWeight: "bold", dropShadow: true }); scoreTxt2.anchor.set(2, 0); LK.gui.topRight.addChild(scoreTxt2); joystick = game.addChild(LK.getAsset('joystick', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 - 330 })); joystick.visible = false; joystickBasePosition = { x: joystick.x, y: joystick.y }; joystickBasePosition = { x: joystick.x, y: joystick.y }; joystickDrag = false; net = new Net(); net.x = 2048 / 2; net.y = 2000; game.addChild(net); touchTime = 0; // Global variable to store the touch time prevMouseY = null; lastPlayedTime = {}; ballCanMove = false; SPEED_LIMIT = 50; // Define a global speed limit isDebug = false; player2Debug = false; player1Touches = 0; player2Touches = 0; resetTime = 0; // Global variable to store the reset time nextRoundPauseDelay = 1600; score1 = 0; score2 = 0; scoreTxt1.setText(score1.toString().padStart(2, '0')); scoreTxt2.setText(score2.toString().padStart(2, '0')); startButton = LK.getAsset('startButton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(startButton); } function initShadows() { ballShadow = LK.getAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.33, scaleX: 2.2, scaleY: 0.6 }); player1Shadow = LK.getAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.33, scaleX: 2.7, scaleY: 0.7 }); player2Shadow = LK.getAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.33, scaleX: 2.7, scaleY: 0.7 }); var parasolShadow = LK.getAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.33, scaleX: 3.5, scaleY: 0.65, x: 1900 + 10, y: 1445 + 10 }); var beachBallShadow = LK.getAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.33, scaleX: 0.75, scaleY: 0.35, x: 120 + 20, y: 1465 }); ballShadow.x = BALL1_INITIAL_X; ballShadow.y = PLAYER_INITIAL_Y; player1Shadow.x = PLAYER1_INITIAL_X; player1Shadow.y = PLAYER_INITIAL_Y; player2Shadow.x = PLAYER2_INITIAL_X; player2Shadow.y = PLAYER_INITIAL_Y; game.addChild(parasolShadow); game.addChild(beachBallShadow); game.addChild(ballShadow); game.addChild(player1Shadow); game.addChild(player2Shadow); } function updateShadows() { // Update ball shadow position and size ballShadow.visible = ball.alpha != 0; if (ballShadow.visible) { ballShadow.x = ball.x; ballShadow.scale.x = 2.2 * (1 + (ball.y - PLAYER_INITIAL_Y) / 3000); ballShadow.scale.y = 0.6 * (1 + (ball.y - PLAYER_INITIAL_Y) / 3000); } // Update player1 shadow position and size player1Shadow.x = player1.x; player1Shadow.scale.x = 2.7 * (1 + (player1.y - PLAYER_INITIAL_Y) / 1500); player1Shadow.scale.y = 0.7 * (1 + (player1.y - PLAYER_INITIAL_Y) / 1500); // Update player2 shadow position and size player2Shadow.x = player2.x; player2Shadow.scale.x = 2.7 * (1 + (player2.y - PLAYER_INITIAL_Y) / 1500); player2Shadow.scale.y = 0.7 * (1 + (player2.y - PLAYER_INITIAL_Y) / 1500); }
===================================================================
--- original.js
+++ change.js
@@ -83,9 +83,9 @@
anchorX: 0.5,
anchorY: 1,
alpha: 0.98,
scaleX: index === 2 ? -1 : 1,
- tint: index === 1 ? 0xADD8E6 : 0xFF6347 // Light blue for player 1, Tomato red for player 2
+ tint: index === 1 ? 0xADD8E6 : 0x1089f4 // Light blue for player 1, Blue for player 2
});
self.index = index;
self.scale.set(1, 1); // Initialize player scale to normal
self.jumping = false;
@@ -194,9 +194,9 @@
// Check for game over condition
if ((score1 >= finalScore || score2 >= finalScore) && Math.abs(score1 - score2) >= 2) {
var resultText = new Text2(score1 > score2 ? 'VICTORY!' : 'DEFEAT!', {
size: 300,
- fill: "#FFFFFF",
+ fill: 0xFFFFFF,
fontWeight: "bold",
dropShadow: true
});
resultText.anchor.set(0.5, 2);
@@ -483,17 +483,17 @@
ball.rotationSpeed = 0;
game.addChild(ball);
scoreTxt1 = new Text2('00', {
size: 200,
- fill: "#0000d8",
+ fill: 0x0000D8,
fontWeight: "bold",
dropShadow: true
});
scoreTxt1.anchor.set(-1, 0);
LK.gui.topLeft.addChild(scoreTxt1);
scoreTxt2 = new Text2('00', {
size: 200,
- fill: "#FF6347",
+ fill: 0xFF6347,
fontWeight: "bold",
dropShadow: true
});
scoreTxt2.anchor.set(2, 0);
white volley ball.
top view of a concave blue (0xADD8E6) plastic button. 4 small black directionnal chevrons engraved : right, left, top , bottom.. Photorealistic
Beach ball. photo
full view of a Beach white towel with colored infinte logo. placed on the sand. photo
Start button in the shape of a white beach volleyball with « START » written on it in black. Photo
amangus. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat