zeroed-some/sprong / 44a9c6e

Browse files

initial physics

Authored by espadonne
SHA
44a9c6eea2e520fd350c5e4dc7efdbd9b067b589
Parents
f5f8b03
Tree
05913e4

1 changed file

StatusFile+-
M Matter.js 364 50
Matter.jsmodified
@@ -1,20 +1,46 @@
11
 // Matter.js module aliases
2
-const Engine = Matter.Engine;
2
+const Body = Matter.Body;
33
 const World = Matter.World;
4
+const Engine = Matter.Engine;
45
 const Bodies = Matter.Bodies;
5
-const Body = Matter.Body;
66
 const Render = Matter.Render;
77
 const Constraint = Matter.Constraint;
88
 
99
 // Game variables
10
-let engine;
10
+let ball;
1111
 let world;
12
-let testBox;
12
+let engine;
13
+
14
+// Spring paddle system components
15
+let boundaries = [];
16
+let leftSupport, leftPaddle, leftSpring;
17
+let rightSupport, rightPaddle, rightSpring;
18
+
19
+// Game state
20
+let leftScore = 0;
21
+let rightScore = 0;
22
+let gameStarted = false;
23
+
24
+// Player input
25
+let keys = {};
1326
 
1427
 // Canvas settings
15
-const CANVAS_WIDTH = 800;
28
+const CANVAS_WIDTH  = 800;
1629
 const CANVAS_HEIGHT = 400;
1730
 
31
+// Game constants
32
+const BALL_SPEED    = 8;
33
+const BALL_RADIUS   = 12;
34
+const PADDLE_WIDTH  = 20;
35
+const PADDLE_HEIGHT = 80;
36
+const SUPPORT_SPEED = 4;
37
+
38
+// Spring physics constants
39
+const PADDLE_MASS       = 0.8;
40
+const SPRING_LENGTH     = 40;
41
+const SPRING_DAMPING    = 0.8;
42
+const SPRING_STIFFNESS  = 0.02;
43
+
1844
 function setup() {
1945
     // Create p5.js canvas
2046
     let canvas = createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
@@ -28,77 +54,365 @@ function setup() {
2854
     engine.world.gravity.y = 0;
2955
     engine.world.gravity.x = 0;
3056
     
31
-    // Create a test rectangle to confirm everything is working
32
-    testBox = Bodies.rectangle(width/2, height/2, 60, 60);
57
+    // Create game boundaries (top and bottom walls)
58
+    let topWall = Bodies.rectangle(width/2, -10, width, 20, { isStatic: true });
59
+    let bottomWall = Bodies.rectangle(width/2, height + 10, width, 20, { isStatic: true });
60
+    boundaries.push(topWall, bottomWall);
61
+    
62
+    // Create spring paddle systems
63
+    createSpringPaddleSystem('left');
64
+    createSpringPaddleSystem('right');
65
+    
66
+    // Create ball
67
+    resetBall();
3368
     
34
-    // Add the test box to the world
35
-    World.add(world, testBox);
69
+    // Add everything to the world
70
+    World.add(world, [
71
+        ...boundaries, 
72
+        ball,
73
+        leftSupport, leftPaddle, leftSpring,
74
+        rightSupport, rightPaddle, rightSpring
75
+    ]);
76
+}
77
+
78
+function createSpringPaddleSystem(side) {
79
+    let supportX = side === 'left' ? 60 : width - 60;
80
+    let paddleX = side === 'left' ? 60 + SPRING_LENGTH : width - 60 - SPRING_LENGTH;
81
+    let startY = height / 2;
82
+    
83
+    if (side === 'left') {
84
+        // Left support (invisible anchor point controlled by player)
85
+        leftSupport = Bodies.rectangle(supportX, startY, 10, 10, {
86
+            isStatic: true,
87
+            render: { visible: false }
88
+        });
89
+        
90
+        // Left paddle (the actual hitting surface)
91
+        leftPaddle = Bodies.rectangle(paddleX, startY, PADDLE_WIDTH, PADDLE_HEIGHT, {
92
+            mass: PADDLE_MASS,
93
+            restitution: 1.2,
94
+            friction: 0,
95
+            frictionAir: 0.01
96
+        });
97
+        
98
+        // Spring constraint connecting support to paddle
99
+        leftSpring = Constraint.create({
100
+            bodyA: leftSupport,
101
+            bodyB: leftPaddle,
102
+            length: SPRING_LENGTH,
103
+            stiffness: SPRING_STIFFNESS,
104
+            damping: SPRING_DAMPING
105
+        });
106
+    } else {
107
+        // Right support (invisible anchor point controlled by player/AI)
108
+        rightSupport = Bodies.rectangle(supportX, startY, 10, 10, {
109
+            isStatic: true,
110
+            render: { visible: false }
111
+        });
112
+        
113
+        // Right paddle (the actual hitting surface)
114
+        rightPaddle = Bodies.rectangle(paddleX, startY, PADDLE_WIDTH, PADDLE_HEIGHT, {
115
+            mass: PADDLE_MASS,
116
+            restitution: 1.2,
117
+            friction: 0,
118
+            frictionAir: 0.01
119
+        });
120
+        
121
+        // Spring constraint connecting support to paddle
122
+        rightSpring = Constraint.create({
123
+            bodyA: rightSupport,
124
+            bodyB: rightPaddle,
125
+            length: SPRING_LENGTH,
126
+            stiffness: SPRING_STIFFNESS,
127
+            damping: SPRING_DAMPING
128
+        });
129
+    }
36130
 }
37131
 
38132
 function draw() {
39133
     // Update physics
40134
     Engine.update(engine);
41135
     
42
-    // Clear canvas with dark background
43
-    background(26, 26, 26);
136
+    // Handle player input
137
+    handleInput();
44138
     
45
-    // Draw test box
46
-    push();
47
-    fill(0, 255, 136); // Bright green
139
+    // Check for scoring
140
+    checkBallPosition();
141
+    
142
+    // Clear canvas
143
+    background(10, 10, 10);
144
+    
145
+    // Draw game objects
146
+    drawSpringPaddleSystems();
147
+    drawBall();
148
+    drawBoundaries();
149
+    drawCenterLine();
150
+    
151
+    // Draw debug info
152
+    drawDebugInfo();
153
+    
154
+    // Start message
155
+    if (!gameStarted) {
156
+        drawStartMessage();
157
+    }
158
+}
159
+
160
+function drawSpringPaddleSystems() {
161
+    // Draw springs first (behind paddles)
162
+    drawSprings();
163
+    
164
+    // Draw paddles
165
+    fill(0, 255, 136);
48166
     stroke(0, 255, 136);
49167
     strokeWeight(2);
50168
     
51
-    // Get the test box position and angle from Matter.js
52
-    let pos = testBox.position;
53
-    let angle = testBox.angle;
169
+    // Left paddle
170
+    let leftPos = leftPaddle.position;
171
+    let leftAngle = leftPaddle.angle;
172
+    push();
173
+    translate(leftPos.x, leftPos.y);
174
+    rotate(leftAngle);
175
+    rectMode(CENTER);
176
+    rect(0, 0, PADDLE_WIDTH, PADDLE_HEIGHT);
177
+    pop();
54178
     
55
-    // Draw the box at its physics position
56
-    translate(pos.x, pos.y);
57
-    rotate(angle);
179
+    // Right paddle
180
+    let rightPos = rightPaddle.position;
181
+    let rightAngle = rightPaddle.angle;
182
+    push();
183
+    translate(rightPos.x, rightPos.y);
184
+    rotate(rightAngle);
58185
     rectMode(CENTER);
59
-    rect(0, 0, 60, 60);
186
+    rect(0, 0, PADDLE_WIDTH, PADDLE_HEIGHT);
60187
     pop();
61188
     
62
-    // Draw some helpful debug info
63
-    fill(255);
189
+    // Draw support points (small indicators)
190
+    fill(0, 255, 136, 100);
191
+    noStroke();
192
+    ellipse(leftSupport.position.x, leftSupport.position.y, 8, 8);
193
+    ellipse(rightSupport.position.x, rightSupport.position.y, 8, 8);
194
+}
195
+
196
+function drawSprings() {
197
+    stroke(0, 255, 136, 150);
198
+    strokeWeight(3);
199
+    
200
+    // Left spring
201
+    let leftSupportPos = leftSupport.position;
202
+    let leftPaddlePos = leftPaddle.position;
203
+    
204
+    // Draw spring as a zigzag line
205
+    drawSpringLine(leftSupportPos, leftPaddlePos, 'left');
206
+    
207
+    // Right spring
208
+    let rightSupportPos = rightSupport.position;
209
+    let rightPaddlePos = rightPaddle.position;
210
+    
211
+    drawSpringLine(rightSupportPos, rightPaddlePos, 'right');
212
+}
213
+
214
+function drawSpringLine(startPos, endPos, side) {
215
+    let segments = 8;
216
+    let amplitude = 8;
217
+    
218
+    // Calculate spring compression (affects visual amplitude)
219
+    let currentLength = dist(startPos.x, startPos.y, endPos.x, endPos.y);
220
+    let compression = SPRING_LENGTH / currentLength;
221
+    amplitude *= compression;
222
+    
223
+    stroke(0, 255, 136, 150 + compression * 50); // Brighter when compressed
224
+    strokeWeight(2 + compression);
225
+    
226
+    for (let i = 0; i <= segments; i++) {
227
+        let t = i / segments;
228
+        let x = lerp(startPos.x, endPos.x, t);
229
+        let y = lerp(startPos.y, endPos.y, t);
230
+        
231
+        // Add zigzag offset
232
+        if (i > 0 && i < segments) {
233
+            let perpX = -(endPos.y - startPos.y) / currentLength;
234
+            let perpY = (endPos.x - startPos.x) / currentLength;
235
+            let offset = sin(i * PI) * amplitude;
236
+            x += perpX * offset;
237
+            y += perpY * offset;
238
+        }
239
+        
240
+        if (i === 0) {
241
+            beginShape();
242
+            vertex(x, y);
243
+        } else {
244
+            vertex(x, y);
245
+            if (i === segments) {
246
+                endShape();
247
+            }
248
+        }
249
+    }
250
+}
251
+
252
+function drawBall() {
253
+    fill(255, 100, 100);
254
+    stroke(255, 150, 150);
255
+    strokeWeight(2);
256
+    
257
+    let ballPos = ball.position;
258
+    ellipse(ballPos.x, ballPos.y, BALL_RADIUS * 2, BALL_RADIUS * 2);
259
+    
260
+    // Ball trail effect
261
+    fill(255, 100, 100, 50);
262
+    noStroke();
263
+    ellipse(ballPos.x, ballPos.y, BALL_RADIUS * 3, BALL_RADIUS * 3);
264
+}
265
+
266
+function drawBoundaries() {
267
+    stroke(0, 255, 136, 30);
268
+    strokeWeight(1);
269
+    noFill();
270
+    line(0, 0, width, 0);
271
+    line(0, height, width, height);
272
+}
273
+
274
+function drawCenterLine() {
275
+    stroke(0, 255, 136, 50);
276
+    strokeWeight(2);
277
+    
278
+    for (let y = 0; y < height; y += 20) {
279
+        line(width/2, y, width/2, y + 10);
280
+    }
281
+}
282
+
283
+function drawDebugInfo() {
284
+    fill(255, 100);
64285
     textAlign(LEFT);
286
+    textSize(12);
65287
     text(`FPS: ${Math.round(frameRate())}`, 10, 20);
66
-    text(`Physics Bodies: ${world.bodies.length}`, 10, 40);
67
-    text(`Test Box Position: (${Math.round(pos.x)}, ${Math.round(pos.y)})`, 10, 60);
288
+    text(`Ball Speed: ${Math.round(getBallSpeed())}`, 10, 35);
289
+    
290
+    // Spring info
291
+    let leftSpringLength = dist(leftSupport.position.x, leftSupport.position.y, 
292
+                               leftPaddle.position.x, leftPaddle.position.y);
293
+    let rightSpringLength = dist(rightSupport.position.x, rightSupport.position.y, 
294
+                                rightPaddle.position.x, rightPaddle.position.y);
295
+    
296
+    text(`Left Spring: ${Math.round(leftSpringLength)}px`, 10, 50);
297
+    text(`Right Spring: ${Math.round(rightSpringLength)}px`, 10, 65);
298
+    text(`Spring Rest Length: ${SPRING_LENGTH}px`, 10, 80);
299
+}
300
+
301
+function drawStartMessage() {
302
+    fill(0, 255, 136, 200);
303
+    textAlign(CENTER);
304
+    textSize(20);
305
+    text("Press any key to start!", width/2, height/2 + 100);
306
+    textSize(14);
307
+    text("Watch the springs compress and extend!", width/2, height/2 + 125);
308
+}
309
+
310
+function handleInput() {
311
+    // Left paddle (W/S keys) - move the support point
312
+    if (keys['w'] || keys['W']) {
313
+        moveSupport(leftSupport, -SUPPORT_SPEED);
314
+    }
315
+    if (keys['s'] || keys['S']) {
316
+        moveSupport(leftSupport, SUPPORT_SPEED);
317
+    }
318
+    
319
+    // Right paddle (Arrow keys) - move the support point
320
+    if (keys['ArrowUp']) {
321
+        moveSupport(rightSupport, -SUPPORT_SPEED);
322
+    }
323
+    if (keys['ArrowDown']) {
324
+        moveSupport(rightSupport, SUPPORT_SPEED);
325
+    }
68326
 }
69327
 
70
-// Add some interactivity - click to move the test box
71
-function mousePressed() {
72
-    if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
73
-        // Set the test box position to mouse position
74
-        Body.setPosition(testBox, { x: mouseX, y: mouseY });
328
+function moveSupport(support, deltaY) {
329
+    let newY = support.position.y + deltaY;
330
+    
331
+    // Keep support within reasonable bounds
332
+    newY = constrain(newY, 50, height - 50);
333
+    
334
+    Body.setPosition(support, { x: support.position.x, y: newY });
335
+}
336
+
337
+function resetBall() {
338
+    if (ball) {
339
+        World.remove(world, ball);
340
+    }
341
+    
342
+    // Create new ball at center
343
+    ball = Bodies.circle(width/2, height/2, BALL_RADIUS, {
344
+        restitution: 1,
345
+        friction: 0,
346
+        frictionAir: 0
347
+    });
348
+    
349
+    if (world) {
350
+        World.add(world, ball);
351
+    }
352
+    
353
+    // Start ball moving after a short delay
354
+    setTimeout(() => {
355
+        let direction = random() > 0.5 ? 1 : -1;
356
+        let angle = random(-PI/6, PI/6);
75357
         
76
-        // Add a little spin for fun
77
-        Body.setAngularVelocity(testBox, random(-0.1, 0.1));
358
+        Body.setVelocity(ball, {
359
+            x: direction * BALL_SPEED * cos(angle),
360
+            y: BALL_SPEED * sin(angle)
361
+        });
78362
         
79
-        console.log(`🎯 Test box moved to (${mouseX}, ${mouseY})`);
363
+        gameStarted = true;
364
+    }, 1000);
365
+}
366
+
367
+function checkBallPosition() {
368
+    let ballX = ball.position.x;
369
+    
370
+    if (ballX < -BALL_RADIUS) {
371
+        rightScore++;
372
+        updateScore();
373
+        resetBall();
374
+        gameStarted = false;
80375
     }
376
+    
377
+    if (ballX > width + BALL_RADIUS) {
378
+        leftScore++;
379
+        updateScore();
380
+        resetBall();
381
+        gameStarted = false;
382
+    }
383
+}
384
+
385
+function updateScore() {
386
+    document.getElementById('leftScore').textContent = leftScore;
387
+    document.getElementById('rightScore').textContent = rightScore;
81388
 }
82389
 
83
-// Keyboard interaction for testing
390
+function getBallSpeed() {
391
+    let velocity = ball.velocity;
392
+    return Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
393
+}
394
+
395
+// Input handling
84396
 function keyPressed() {
85
-    if (key === ' ') {
86
-        // Spacebar: reset test box to center
87
-        Body.setPosition(testBox, { x: width/2, y: height/2 });
88
-        Body.setAngle(testBox, 0);
89
-        Body.setVelocity(testBox, { x: 0, y: 0 });
90
-        Body.setAngularVelocity(testBox, 0);
91
-        console.log("🔄 Test box reset to center");
397
+    keys[key] = true;
398
+    keys[keyCode] = true;
399
+    
400
+    if (!gameStarted && key !== ' ') {
401
+        gameStarted = true;
92402
     }
93403
     
94
-    if (key === 'g' || key === 'G') {
95
-        // Toggle gravity for fun
96
-        if (engine.world.gravity.y === 0) {
97
-            engine.world.gravity.y = 0.8;
98
-            console.log("🌍 Gravity enabled");
99
-        } else {
100
-            engine.world.gravity.y = 0;
101
-            console.log("🚀 Gravity disabled");
102
-        }
404
+    // Reset game with spacebar
405
+    if (key === ' ') {
406
+        leftScore = 0;
407
+        rightScore = 0;
408
+        updateScore();
409
+        resetBall();
410
+        gameStarted = false;
411
+        console.log("🔄 Game reset!");
103412
     }
413
+}
414
+
415
+function keyReleased() {
416
+    keys[key] = false;
417
+    keys[keyCode] = false;
104418
 }