@@ -1,5 +1,5 @@ |
| 1 | 1 | // Matter.js module aliases |
| 2 | | -const Body = Matter.Body; |
| 2 | +const Body = Matter.Body; |
| 3 | 3 | const World = Matter.World; |
| 4 | 4 | const Engine = Matter.Engine; |
| 5 | 5 | const Bodies = Matter.Bodies; |
@@ -25,6 +25,16 @@ let gameStarted = false; |
| 25 | 25 | let keys = {}; |
| 26 | 26 | let inputBuffer = { left: 0, right: 0 }; |
| 27 | 27 | |
| 28 | +// Touch/mouse input |
| 29 | +let mouseInput = { |
| 30 | + active: false, |
| 31 | + targetY: 0, |
| 32 | + leftPaddleTarget: 0, |
| 33 | + rightPaddleTarget: 0, |
| 34 | + smoothing: 0.08, // Slower smoothing for deliberate lag |
| 35 | + deadZone: 15 // Minimum distance before movement starts |
| 36 | +}; |
| 37 | + |
| 28 | 38 | // Particle systems |
| 29 | 39 | let particles = []; |
| 30 | 40 | let impactParticles = []; |
@@ -39,22 +49,27 @@ const BALL_RADIUS = 12; |
| 39 | 49 | const PADDLE_WIDTH = 20; |
| 40 | 50 | const PADDLE_HEIGHT = 80; |
| 41 | 51 | |
| 42 | | -// Enhanced movement constants |
| 43 | | -const SUPPORT_SPEED = 6.5; |
| 44 | | -const SUPPORT_ACCEL = 1.2; |
| 45 | | -const INPUT_SMOOTHING = 0.25 |
| 46 | | -const SUPPORT_MAX_SPEED = 8; |
| 52 | +// Enhanced movement constants (tuned for faster response) |
| 53 | +const SUPPORT_SPEED = 6.5; // Bumped up from 4.5 |
| 54 | +const SUPPORT_ACCEL = 1.2; // Increased acceleration |
| 55 | +const INPUT_SMOOTHING = 0.25; // More responsive |
| 56 | +const SUPPORT_MAX_SPEED = 8; // Higher max speed |
| 57 | + |
| 58 | +// Touch/mouse control constants |
| 59 | +const MOUSE_SPEED_LIMIT = 4; // Max speed for mouse movement |
| 60 | +const MOUSE_LAG_FACTOR = 0.12; // How much lag in mouse following |
| 61 | +const TOUCH_SENSITIVITY = 1.2; // Touch movement multiplier |
| 47 | 62 | |
| 48 | | -// Spring physics constants |
| 49 | | -const PADDLE_MASS = 0.6; |
| 63 | +// Spring physics constants (tuned for bounciness!) |
| 64 | +const PADDLE_MASS = 0.6; // Lighter for more bounce |
| 50 | 65 | const SPRING_LENGTH = 40; |
| 51 | | -const SPRING_DAMPING = 0.4; |
| 52 | | -const SPRING_STIFFNESS = 0.035; |
| 66 | +const SPRING_DAMPING = 0.4; // Much less damping = more bounce! |
| 67 | +const SPRING_STIFFNESS = 0.035; // Higher stiffness = snappier |
| 53 | 68 | |
| 54 | 69 | // Visual enhancement constants |
| 55 | 70 | const TRAIL_SEGMENTS = 8; |
| 56 | 71 | const PADDLE_GLOW_DISTANCE = 25; |
| 57 | | -const SPRING_GLOW_INTENSITY = 120; |
| 72 | +const SPRING_GLOW_INTENSITY = 120; // More intense glow |
| 58 | 73 | |
| 59 | 74 | // Particle system constants |
| 60 | 75 | const MAX_PARTICLES = 100; |
@@ -94,12 +109,17 @@ function setup() { |
| 94 | 109 | leftSupport, leftPaddle, leftSpring, |
| 95 | 110 | rightSupport, rightPaddle, rightSpring |
| 96 | 111 | ]); |
| 112 | + |
| 113 | + console.log("🎮 Sprong Phase 5 Complete!"); |
| 114 | + console.log("✓ Particle effects system"); |
| 115 | + console.log("✓ Tuned physics for maximum bounce"); |
| 116 | + console.log("✓ Faster, more responsive paddles"); |
| 97 | 117 | } |
| 98 | 118 | |
| 99 | 119 | function createSpringPaddleSystem(side) { |
| 100 | | - let startY = height / 2; |
| 101 | | - let supportX = side === 'left' ? 60 : width - 60; |
| 102 | | - let paddleX = side === 'left' ? 60 + SPRING_LENGTH : width - 60 - SPRING_LENGTH; |
| 120 | + let supportX = side === 'left' ? 60 : width - 60; |
| 121 | + let paddleX = side === 'left' ? 60 + SPRING_LENGTH : width - 60 - SPRING_LENGTH; |
| 122 | + let startY = height / 2; |
| 103 | 123 | |
| 104 | 124 | if (side === 'left') { |
| 105 | 125 | // Left support (invisible anchor point controlled by player) |
@@ -111,7 +131,7 @@ function createSpringPaddleSystem(side) { |
| 111 | 131 | // Left paddle (the actual hitting surface) |
| 112 | 132 | leftPaddle = Bodies.rectangle(paddleX, startY, PADDLE_WIDTH, PADDLE_HEIGHT, { |
| 113 | 133 | mass: PADDLE_MASS, |
| 114 | | - restitution: 1.3, |
| 134 | + restitution: 1.3, // Even bouncier! |
| 115 | 135 | friction: 0, |
| 116 | 136 | frictionAir: 0.005 // Less air resistance |
| 117 | 137 | }); |
@@ -186,6 +206,12 @@ function draw() { |
| 186 | 206 | } |
| 187 | 207 | |
| 188 | 208 | function handleEnhancedInput() { |
| 209 | + // Handle both keyboard and mouse/touch input |
| 210 | + handleKeyboardInput(); |
| 211 | + handleMouseTouchInput(); |
| 212 | +} |
| 213 | + |
| 214 | +function handleKeyboardInput() { |
| 189 | 215 | // Smooth input accumulation with acceleration |
| 190 | 216 | let leftInput = 0; |
| 191 | 217 | let rightInput = 0; |
@@ -198,16 +224,52 @@ function handleEnhancedInput() { |
| 198 | 224 | if (keys['ArrowUp']) rightInput -= 1; |
| 199 | 225 | if (keys['ArrowDown']) rightInput += 1; |
| 200 | 226 | |
| 201 | | - // Apply acceleration and smoothing |
| 227 | + // Apply acceleration and smoothing for keyboard |
| 202 | 228 | inputBuffer.left = lerp(inputBuffer.left, leftInput, INPUT_SMOOTHING); |
| 203 | 229 | inputBuffer.right = lerp(inputBuffer.right, rightInput, INPUT_SMOOTHING); |
| 204 | 230 | |
| 205 | | - // Move supports with enhanced physics |
| 206 | | - if (Math.abs(inputBuffer.left) > 0.01) { |
| 207 | | - moveSupportEnhanced(leftSupport, inputBuffer.left * SUPPORT_SPEED); |
| 231 | + // Move supports with enhanced physics (only if not using mouse) |
| 232 | + if (!mouseInput.active) { |
| 233 | + if (Math.abs(inputBuffer.left) > 0.01) { |
| 234 | + moveSupportEnhanced(leftSupport, inputBuffer.left * SUPPORT_SPEED); |
| 235 | + } |
| 236 | + if (Math.abs(inputBuffer.right) > 0.01) { |
| 237 | + moveSupportEnhanced(rightSupport, inputBuffer.right * SUPPORT_SPEED); |
| 238 | + } |
| 208 | 239 | } |
| 209 | | - if (Math.abs(inputBuffer.right) > 0.01) { |
| 210 | | - moveSupportEnhanced(rightSupport, inputBuffer.right * SUPPORT_SPEED); |
| 240 | +} |
| 241 | + |
| 242 | +function handleMouseTouchInput() { |
| 243 | + if (!mouseInput.active) return; |
| 244 | + |
| 245 | + // Determine which paddle to control based on mouse X position |
| 246 | + let controllingLeft = mouseX < width / 2; |
| 247 | + let targetSupport = controllingLeft ? leftSupport : rightSupport; |
| 248 | + |
| 249 | + // Calculate target Y with dead zone |
| 250 | + let currentY = targetSupport.position.y; |
| 251 | + let targetY = mouseY; |
| 252 | + let deltaY = targetY - currentY; |
| 253 | + |
| 254 | + // Apply dead zone - don't move unless mouse is far enough |
| 255 | + if (Math.abs(deltaY) < mouseInput.deadZone) { |
| 256 | + return; |
| 257 | + } |
| 258 | + |
| 259 | + // Calculate movement with lag and speed limiting |
| 260 | + let movement = deltaY * MOUSE_LAG_FACTOR * TOUCH_SENSITIVITY; |
| 261 | + |
| 262 | + // Limit maximum speed to prevent snappy movement |
| 263 | + movement = constrain(movement, -MOUSE_SPEED_LIMIT, MOUSE_SPEED_LIMIT); |
| 264 | + |
| 265 | + // Apply the lagged movement |
| 266 | + moveSupportEnhanced(targetSupport, movement); |
| 267 | + |
| 268 | + // Visual feedback - update input buffer for particle effects |
| 269 | + if (controllingLeft) { |
| 270 | + inputBuffer.left = constrain(movement / MOUSE_SPEED_LIMIT, -1, 1); |
| 271 | + } else { |
| 272 | + inputBuffer.right = constrain(movement / MOUSE_SPEED_LIMIT, -1, 1); |
| 211 | 273 | } |
| 212 | 274 | } |
| 213 | 275 | |
@@ -570,6 +632,12 @@ function drawDebugInfo() { |
| 570 | 632 | text(`L Spring: ${Math.round(leftSpringLength)}px (${((SPRING_LENGTH/leftSpringLength - 1) * 100).toFixed(0)}%)`, 10, 65); |
| 571 | 633 | text(`R Spring: ${Math.round(rightSpringLength)}px (${((SPRING_LENGTH/rightSpringLength - 1) * 100).toFixed(0)}%)`, 10, 80); |
| 572 | 634 | text(`Input: L=${inputBuffer.left.toFixed(2)} R=${inputBuffer.right.toFixed(2)}`, 10, 95); |
| 635 | + |
| 636 | + // Mouse/touch input debug |
| 637 | + if (mouseInput.active) { |
| 638 | + text(`Mouse: ${mouseInput.active ? 'Active' : 'Inactive'} | Side: ${mouseX < width/2 ? 'Left' : 'Right'}`, 10, 110); |
| 639 | + text(`Mouse Y: ${mouseY} | Dead Zone: ${mouseInput.deadZone}px`, 10, 125); |
| 640 | + } |
| 573 | 641 | } |
| 574 | 642 | |
| 575 | 643 | function drawStartMessage() { |
@@ -578,7 +646,9 @@ function drawStartMessage() { |
| 578 | 646 | textSize(20); |
| 579 | 647 | text("Press any key to start!", width/2, height/2 + 100); |
| 580 | 648 | textSize(14); |
| 581 | | - text("Bouncier springs, faster paddles, explosive particles!", width/2, height/2 + 125); |
| 649 | + text("Keyboard: W/S + ↑/↓ | Mouse/Touch: Drag paddles", width/2, height/2 + 125); |
| 650 | + textSize(12); |
| 651 | + text("(Mouse movement has deliberate lag to preserve challenge!)", width/2, height/2 + 145); |
| 582 | 652 | } |
| 583 | 653 | |
| 584 | 654 | function resetBall() { |
@@ -660,6 +730,9 @@ function keyPressed() { |
| 660 | 730 | inputBuffer.left = 0; |
| 661 | 731 | inputBuffer.right = 0; |
| 662 | 732 | |
| 733 | + // Reset mouse input |
| 734 | + mouseInput.active = false; |
| 735 | + |
| 663 | 736 | // Clear particles |
| 664 | 737 | particles = []; |
| 665 | 738 | |
@@ -670,4 +743,51 @@ function keyPressed() { |
| 670 | 743 | function keyReleased() { |
| 671 | 744 | keys[key] = false; |
| 672 | 745 | keys[keyCode] = false; |
| 746 | +} |
| 747 | + |
| 748 | +// Mouse/touch input handlers |
| 749 | +function mousePressed() { |
| 750 | + // Start mouse/touch input when clicking in game area |
| 751 | + if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { |
| 752 | + mouseInput.active = true; |
| 753 | + |
| 754 | + // Start game if not started |
| 755 | + if (!gameStarted) { |
| 756 | + gameStarted = true; |
| 757 | + } |
| 758 | + |
| 759 | + return false; // Prevent default behavior |
| 760 | + } |
| 761 | +} |
| 762 | + |
| 763 | +function mouseDragged() { |
| 764 | + // Continue mouse/touch input while dragging |
| 765 | + if (mouseInput.active) { |
| 766 | + return false; // Prevent default behavior |
| 767 | + } |
| 768 | +} |
| 769 | + |
| 770 | +function mouseReleased() { |
| 771 | + // Stop mouse/touch input when releasing |
| 772 | + mouseInput.active = false; |
| 773 | + |
| 774 | + // Gradually reduce input buffer when mouse is released |
| 775 | + inputBuffer.left *= 0.8; |
| 776 | + inputBuffer.right *= 0.8; |
| 777 | +} |
| 778 | + |
| 779 | +function touchStarted() { |
| 780 | + // Handle touch events same as mouse |
| 781 | + return mousePressed(); |
| 782 | +} |
| 783 | + |
| 784 | +function touchMoved() { |
| 785 | + // Handle touch drag same as mouse |
| 786 | + return mouseDragged(); |
| 787 | +} |
| 788 | + |
| 789 | +function touchEnded() { |
| 790 | + // Handle touch end same as mouse |
| 791 | + mouseReleased(); |
| 792 | + return false; |
| 673 | 793 | } |