diff --git a/game.js b/game.js index bb92dcb..9c2fd35 100644 --- a/game.js +++ b/game.js @@ -9,6 +9,8 @@ const gameWorld = new GameWorld(); let pyodideWorker = startPyodideWorker(); let robots = createInitialRobots(); +console.log(robots); +gameWorld.addRobot(robots["player"]); let paused = false; let scale = 1; // Zoom level @@ -113,11 +115,12 @@ function gameLoop() { ctx.translate(offsetX, offsetY); // Apply panning ctx.scale(scale, scale); // Apply zooming - + gameWorld.update(); gameWorld.draw(ctx); for (let id in robots) { robots[id].update(ctx, gameWorld); + gameWorld.checkAndResolveCollision(robots[id]); } } requestAnimationFrame(gameLoop); diff --git a/gameworld.js b/gameworld.js index 064cbf6..843ff7c 100644 --- a/gameworld.js +++ b/gameworld.js @@ -1,33 +1,67 @@ +import Matter from "https://cdn.jsdelivr.net/npm/matter-js@0.19.0/+esm"; + export class GameWorld { constructor() { - let polygon1 = [ - { x: 80, y: 100 }, // Vertex 1 - { x: 200, y: 100 }, // Vertex 2 - { x: 200, y: 200 }, // Vertex 3 - { x: 100, y: 200 } // Vertex 4 - ]; + this.engine = Matter.Engine.create(); + this.world = this.engine.world; + this.engine.world.gravity.y = 0.01; + Matter.Runner.run(this.engine); - let polygon2 = [ - { x: 300, y: 400 }, // Vertex 1 - { x: 320, y: 450 }, // Vertex 2 - { x: 250, y: 550 }, // Vertex 3 + let ground = Matter.Bodies.rectangle(400, 600, 800, 50, { isStatic: true }); + Matter.World.add(this.world, ground); + + + this.obstacles = []; + this.robots = []; + + this.addObstacle([ + { x: 80 + 100, y: 0 }, // Vertex 1 + { x: 250 + 100, y: 370 }, // Vertex 2 + { x: 200 + 100, y: 370 }, // Vertex 3 + { x: 150 + 100, y: 200 } // Vertex 4 + ]); + + this.addObstacle([ + { x: 300, y: 380 }, // Vertex 1 + { x: 420, y: 380 }, // Vertex 2 + { x: 350, y: 550 }, // Vertex 3 { x: 280, y: 420 } // Vertex 4 - ]; + ]); + - this.obstacles = [ - polygon1, // Example obstacle - polygon2 // Another obstacle - ]; } - // Check if a point (x, y) intersects with any obstacles - isObstacle(x, y) { - // return this.obstacles.some( - // obj => x > obj.x && x < obj.x + obj.width && y > obj.y && y < obj.y + obj.height - // ); - return true; + update() { + // Update the physics simulation + Matter.Engine.update(this.engine); + + + } + + addObstacle(vertices) { + // Convert the polygon points into a Matter.js body + let body = Matter.Bodies.fromVertices(-30, 300, [vertices], { + isStatic: true, // Obstacles shouldn't move + }); + + // Add body to world and store it + Matter.World.add(this.world, body); + this.obstacles.push(body); + } + + addRobot(robot) { + console.log("added robot"); + // Create the robot's Matter.js body + let robotBody = Matter.Bodies.fromVertices(robot.x, robot.y, [robot.hull], { + friction: 0.05, + restitution: 0.2, // Slight bounce + }); + + // Add to the world + Matter.World.add(this.world, robotBody); + this.robots.push(robotBody); } // Return the floor color based on (x, y) coordinates @@ -41,19 +75,28 @@ export class GameWorld { ctx.strokeStyle = "gray"; // Obstacle outline color ctx.lineWidth = 2; // Optional: to make the outline thicker - this.obstacles.forEach(obstacle => { + // Draw obstacles + this.obstacles.forEach(body => { ctx.beginPath(); - ctx.moveTo(obstacle[0].x, obstacle[0].y); // Start at the first vertex - - // Loop through the rest of the vertices and draw lines - for (let i = 1; i < obstacle.length; i++) { - ctx.lineTo(obstacle[i].x, obstacle[i].y); + let vertices = body.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let i = 1; i < vertices.length; i++) { + ctx.lineTo(vertices[i].x, vertices[i].y); } - - // Close the path to form a closed polygon ctx.closePath(); - - ctx.stroke(); // Draw the outline + ctx.stroke(); + }); + + this.robots.forEach(body => { + ctx.strokeStyle = "blue"; + let vertices = body.vertices; + ctx.beginPath(); + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let i = 1; i < vertices.length; i++) { + ctx.lineTo(vertices[i].x, vertices[i].y); + } + ctx.closePath(); + ctx.stroke(); }); } @@ -110,4 +153,112 @@ export class GameWorld { return null; // No intersection with any edge of the polygon } + lineSegmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4) { + let den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + if (den === 0) return null; // Parallel lines + + let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den; + let u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den; + + if (t >= 0 && t <= 1 && u >= 0 && u <= 1) { + return { x: x1 + t * (x2 - x1), y: y1 + t * (y2 - y1) }; + } + return null; + } + + + polygonsIntersect(poly1, poly2) { + for (let i = 0; i < poly1.length; i++) { + let p1 = poly1[i]; + let p2 = poly1[(i + 1) % poly1.length]; // Next point, wrap around + + // Loop through each edge of the second polygon + for (let j = 0; j < poly2.length; j++) { + let p3 = poly2[j]; + let p4 = poly2[(j + 1) % poly2.length]; // Next point, wrap around + + let intersection = this.lineIntersection(p1, p2, p3, p4); + if (intersection) { + // Calculate the normal of the intersecting edge + let normal = this.calculateNormal(p1, p2); + return { intersection, normal }; + } + } + } + return null; // No intersection + } + + + calculateNormal(p1, p2) { + // Vector of the edge (from p1 to p2) + let dx = p2.x - p1.x; + let dy = p2.y - p1.y; + + // The normal is the perpendicular vector to the edge + // We can rotate the vector 90 degrees counterclockwise (for a counterclockwise normal) + let normal = { x: -dy, y: dx }; + + // Normalize the normal vector + let length = Math.sqrt(normal.x * normal.x + normal.y * normal.y); + normal.x /= length; + normal.y /= length; + + return normal; + } + + + polygonLineIntersection(polygon, p1, p2) { + for (let i = 0; i < polygon.length; i++) { + let p3 = polygon[i]; + let p4 = polygon[(i + 1) % polygon.length]; // Next point, wrap around + + let intersection = this.lineIntersection(p1, p2, p3, p4); + if (intersection) { + return intersection; + } + } + return null; + } + + + + checkAndResolveCollision(robot) { + for (let obstacle of this.obstacles) { + let result = this.polygonsIntersect(robot.get_hull(), obstacle); + if (result) { + let { intersection, normal } = result; + //console.log("Intersection point:", intersection); + //console.log("Normal vector:", normal); + + // Resolve the collision by sliding the robot along the normal + this.resolveSlide(robot, normal); + return true; + } + } + return false; + } + + + resolveSlide(robot, normal) { + const radians = (robot.angle * Math.PI) / 180; + let vx = Math.cos(radians) * robot.velocity; + let vy = Math.sin(radians) * robot.velocity; + + + let dot = vx * normal.x + vy * normal.y; + + // Subtract the normal component from velocity to make it slide + vx -= 2 * dot * normal.x; // Reflect velocity along the normal (away from the surface) + vy -= 2 * dot * normal.y; // Reflect velocity along the normal (away from the surface) + + // Apply the adjusted movement + robot.x = robot.prevX; + robot.y = robot.prevY; + robot.x += vx; + robot.y += vy; + //robot.updateHull(); + } + + + } diff --git a/index.html b/index.html index 6ae8923..7f32990 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,8 @@