WEBLEB
Home
Editor
Login
Pro
English
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Ragdoll Physics Playground - Matter.js Example
42
iroger7
Open In Editor
Publish Your Code
Recommended
22 August 2024
Portfolio Website-Alex
18 April 2025
Dawid Wróbel - Full Stack Developer Portfolio
27 November 2024
Landing Page - Hotel
HTML
Copy
Ragdoll Physics Playground
Spawn Ragdoll
Spawn Object
Spawn Weapon
Flat Map
Ramps
Obstacles
Change Map
CSS
Copy
/* styles.css */ body { margin: 0; background: linear-gradient(180deg, #b2ebf2, #ffffff 90%); font-family: 'Segoe UI', Arial, sans-serif; display: flex; flex-direction: column; align-items: center; min-height: 100vh; } .ui-panel { background: rgba(255,255,255,0.8); border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin: 1em 0; padding: 1em 2em; display: flex; gap: 1em; align-items: center; } #gameContainer { border-radius: 14px; box-shadow: 0 4px 24px rgba(0,0,0,0.15); overflow: hidden; } canvas#world { background: #e0f7fa; border-radius: 14px; width: 900px; height: 600px; display: block; }
JS
Copy
// script.js // Get HTML elements const canvas = document.getElementById('world'); const spawnRagdollBtn = document.getElementById('spawnRagdollBtn'); const spawnObjectBtn = document.getElementById('spawnObjectBtn'); const spawnWeaponBtn = document.getElementById('spawnWeaponBtn'); const mapSelect = document.getElementById('mapSelect'); const changeMapBtn = document.getElementById('changeMapBtn'); // Matter.js module aliases const Engine = Matter.Engine, Render = Matter.Render, World = Matter.World, Bodies = Matter.Bodies, Body = Matter.Body, Constraint = Matter.Constraint, Composites = Matter.Composites, Composite = Matter.Composite, Mouse = Matter.Mouse, MouseConstraint = Matter.MouseConstraint; // Engine and world const engine = Engine.create(); const world = engine.world; // Canvas size const WIDTH = 900, HEIGHT = 600; // Renderer const render = Render.create({ canvas: canvas, engine: engine, options: { width: WIDTH, height: HEIGHT, wireframes: false, background: '#e0f7fa', pixelRatio: window.devicePixelRatio, showAngleIndicator: false, } }); // Store current map objects to remove on map change let currentMapObjs = []; // Utility: random color for objects function randomColor() { const colors = ['#6ec6ff','#ffb74d','#9575cd','#26a69a','#ec407a','#ffd54f','#8d6e63']; return colors[Math.floor(Math.random() * colors.length)]; } // Ragdoll builder function spawnRagdoll(x = 200, y = 100) { // Limbs and body parts const head = Bodies.circle(x, y, 22, { render: { fillStyle: '#ffd54f' }, density: 0.002 }); const torso = Bodies.rectangle(x, y+45, 25, 60, { render: { fillStyle: '#90caf9' }, density: 0.003 }); const leftArm = Bodies.rectangle(x-30, y+45, 15, 48, { render: { fillStyle: '#b0bec5' }, density: 0.002 }); const rightArm = Bodies.rectangle(x+30, y+45, 15, 48, { render: { fillStyle: '#b0bec5' }, density: 0.002 }); const leftLeg = Bodies.rectangle(x-10, y+110, 16, 50, { render: { fillStyle: '#bcaaa4' }, density: 0.002 }); const rightLeg = Bodies.rectangle(x+10, y+110, 16, 50, { render: { fillStyle: '#bcaaa4' }, density: 0.002 }); // Joints const constraints = [ Constraint.create({ bodyA: head, pointA: {x:0, y:22}, bodyB: torso, pointB: {x:0, y:-30}, stiffness: 0.8 }), Constraint.create({ bodyA: leftArm, pointA: {x:0, y:-24}, bodyB: torso, pointB: {x:-13, y:-24}, stiffness: 0.7 }), Constraint.create({ bodyA: rightArm, pointA: {x:0, y:-24}, bodyB: torso, pointB: {x:13, y:-24}, stiffness: 0.7 }), Constraint.create({ bodyA: leftLeg, pointA: {x:0, y:-25}, bodyB: torso, pointB: {x:-8, y:30}, stiffness: 0.8 }), Constraint.create({ bodyA: rightLeg, pointA: {x:0, y:-25}, bodyB: torso, pointB: {x:8, y:30}, stiffness: 0.8 }), ]; const ragdoll = Composite.create(); Composite.add(ragdoll, [head, torso, leftArm, rightArm, leftLeg, rightLeg, ...constraints]); World.add(world, ragdoll); } // Object spawner: crates, balls, etc function spawnObject(x = 100 + Math.random()*700, y = 50) { const shape = Math.random() > 0.5 ? 'box' : 'circle'; if (shape === 'box') { const box = Bodies.rectangle(x, y, 40, 40, { restitution: 0.4, render: { fillStyle: randomColor() } }); World.add(world, box); } else { const ball = Bodies.circle(x, y, 22, { restitution: 0.7, render: { fillStyle: randomColor() } }); World.add(world, ball); } } // Weapon spawner: simple rectangle or axe shape function spawnWeapon(x = 100 + Math.random()*700, y = 30) { // Weapon: "axe" is a rectangle with a colored head const handle = Bodies.rectangle(x, y, 80, 10, { density: 0.004, render: { fillStyle: '#795548' } }); const head = Bodies.rectangle(x+32, y, 20, 32, { density: 0.007, render: { fillStyle: '#b0bec5' } }); const axe = Composite.create(); Composite.add(axe, [handle, head, Constraint.create({ bodyA: handle, pointA: {x:40, y:0}, bodyB: head, pointB: {x:-10, y:0}, stiffness: 0.9 }) ]); World.add(world, axe); } // MAPS function clearCurrentMap() { for (let obj of currentMapObjs) { World.remove(world, obj); } currentMapObjs = []; } // Map: flat ground only function mapFlat() { clearCurrentMap(); const ground = Bodies.rectangle(WIDTH/2, HEIGHT-25, WIDTH-60, 40, { isStatic: true, render: { fillStyle: '#43a047' } }); currentMapObjs.push(ground); World.add(world, ground); } // Map: ramps function mapRamps() { clearCurrentMap(); const ground = Bodies.rectangle(WIDTH/2, HEIGHT-20, WIDTH-60, 30, { isStatic: true, render: { fillStyle: '#43a047' } }); const ramp1 = Bodies.rectangle(200, HEIGHT-90, 200, 20, { isStatic: true, angle: -Math.PI/7, render: { fillStyle: '#689f38' } }); const ramp2 = Bodies.rectangle(700, HEIGHT-160, 220, 20, { isStatic: true, angle: Math.PI/8, render: { fillStyle: '#33691e' } }); currentMapObjs.push(ground, ramp1, ramp2); World.add(world, [ground, ramp1, ramp2]); } // Map: obstacles function mapObstacles() { clearCurrentMap(); const ground = Bodies.rectangle(WIDTH/2, HEIGHT-25, WIDTH-60, 40, { isStatic: true, render: { fillStyle: '#2e7d32' } }); const box1 = Bodies.rectangle(300, HEIGHT-120, 70, 120, { isStatic: true, render: { fillStyle: '#a1887f' } }); const box2 = Bodies.rectangle(600, HEIGHT-180, 60, 150, { isStatic: true, render: { fillStyle: '#8d6e63' } }); const smallPlat = Bodies.rectangle(WIDTH/2, HEIGHT-220, 120, 14, { isStatic: true, render: { fillStyle: '#607d8b' } }); currentMapObjs.push(ground, box1, box2, smallPlat); World.add(world, [ground, box1, box2, smallPlat]); } // Map dictionary const maps = { flat: mapFlat, ramps: mapRamps, obstacles: mapObstacles }; // Change map function changeMap() { const selected = mapSelect.value; if (maps[selected]) maps[selected](); } // Button handlers spawnRagdollBtn.onclick = () => spawnRagdoll(150 + Math.random()*600, 100 + Math.random()*150); spawnObjectBtn.onclick = () => spawnObject(); spawnWeaponBtn.onclick = () => spawnWeapon(); changeMapBtn.onclick = () => changeMap(); // Mouse drag & drop const mouse = Mouse.create(render.canvas); const mouseConstraint = MouseConstraint.create(engine, { mouse: mouse, constraint: { stiffness: 0.2, render: { visible: false } } }); World.add(world, mouseConstraint); render.mouse = mouse; // Responsive canvas canvas.width = WIDTH; canvas.height = HEIGHT; // Start with flat map mapFlat(); // Start physics engine and render loop Engine.run(engine); Render.run(render);