Code edit (5 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Set the joystick invisible in gameinitialze and show it when game starts
User prompt
Hide the joystick in gameinitialze and show it when game starts
Code edit (13 edits merged)
Please save this source code
User prompt
Move startButton init to the end of gameinitalize
User prompt
In game down handler you can destroy startButton
User prompt
Add a gameStarted = false and a startButton = nulll globals. In gameInitialize, init startButton with a new « startButton » asset. In game.down handler, if !gameStarted, set gameStarted to true and hide startButton else set joystickDrag to true.
Code edit (1 edits merged)
Please save this source code
/**** * 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 : 0xFF6347 // Light blue for player 1, Tomato red 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: "#FFFFFF", 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: "#0000d8", fontWeight: "bold", dropShadow: true }); scoreTxt1.anchor.set(-1, 0); LK.gui.topLeft.addChild(scoreTxt1); scoreTxt2 = new Text2('00', { size: 200, fill: "#FF6347", 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
@@ -501,9 +501,9 @@
joystick = game.addChild(LK.getAsset('joystick', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
- y: 2732 - 270
+ y: 2732 - 330
}));
joystick.visible = false;
joystickBasePosition = {
x: joystick.x,
@@ -527,9 +527,9 @@
player2Debug = false;
player1Touches = 0;
player2Touches = 0;
resetTime = 0; // Global variable to store the reset time
- nextRoundPauseDelay = 1500;
+ nextRoundPauseDelay = 1600;
score1 = 0;
score2 = 0;
scoreTxt1.setText(score1.toString().padStart(2, '0'));
scoreTxt2.setText(score2.toString().padStart(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