UNPKG

cannon-es-control

Version:

A lightweight 3D physics engine written in JavaScript with control system tools

383 lines (341 loc) 13.3 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>cannon.js - ragdoll demo</title> <link rel="stylesheet" href="css/style.css" type="text/css" /> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" /> </head> <body> <script type="module"> import * as CANNON from '../dist/cannon-es-control.js' import { Demo } from './js/Demo.js' const demo = new Demo() demo.addScene('No cone joints', () => { const world = setupWorld(demo) // Add a sphere to land on const sphereBody = createStaticSphere() world.addBody(sphereBody) demo.addVisual(sphereBody) // Create the ragdoll // It returns an array of body parts and their constraints const { bodies, constraints } = createRagdoll({ scale: 3, angle: Math.PI, angleShoulders: Math.PI, twistAngle: Math.PI, }) bodies.forEach((body) => { // Move the ragdoll up const position = new CANNON.Vec3(0, 10, 0) body.position.vadd(position, body.position) world.addBody(body) demo.addVisual(body) }) constraints.forEach((constraint) => { world.addConstraint(constraint) }) }) demo.addScene('Normal cone joints', () => { const world = setupWorld(demo) // Add a sphere to land on const sphereBody = createStaticSphere() world.addBody(sphereBody) demo.addVisual(sphereBody) // Create the ragdoll // It returns an array of body parts and their constraints const { bodies, constraints } = createRagdoll({ scale: 3, angle: Math.PI / 4, angleShoulders: Math.PI / 3, twistAngle: Math.PI / 8, }) bodies.forEach((body) => { // Move the ragdoll up const position = new CANNON.Vec3(0, 10, 0) body.position.vadd(position, body.position) world.addBody(body) demo.addVisual(body) }) constraints.forEach((constraint) => { world.addConstraint(constraint) }) }) demo.addScene('Thin cone joints', () => { const world = setupWorld(demo) // Add a sphere to land on const sphereBody = createStaticSphere() world.addBody(sphereBody) demo.addVisual(sphereBody) // Create the ragdoll // It returns an array of body parts and their constraints const { bodies, constraints } = createRagdoll({ scale: 3, angle: 0, angleShoulders: 0, twistAngle: 0, }) bodies.forEach((body) => { // Move the ragdoll up const position = new CANNON.Vec3(0, 10, 0) body.position.vadd(position, body.position) world.addBody(body) demo.addVisual(body) }) constraints.forEach((constraint) => { world.addConstraint(constraint) }) }) demo.start() function createRagdoll({ scale = 1, angle, angleShoulders, twistAngle }) { const bodies = [] const constraints = [] const shouldersDistance = 0.5 * scale const upperArmLength = 0.4 * scale const lowerArmLength = 0.4 * scale const upperArmSize = 0.2 * scale const lowerArmSize = 0.2 * scale const neckLength = 0.1 * scale const headRadius = 0.25 * scale const upperBodyLength = 0.6 * scale const pelvisLength = 0.4 * scale const upperLegLength = 0.5 * scale const upperLegSize = 0.2 * scale const lowerLegSize = 0.2 * scale const lowerLegLength = 0.5 * scale const headShape = new CANNON.Sphere(headRadius) const upperArmShape = new CANNON.Box( new CANNON.Vec3(upperArmLength * 0.5, upperArmSize * 0.5, upperArmSize * 0.5) ) const lowerArmShape = new CANNON.Box( new CANNON.Vec3(lowerArmLength * 0.5, lowerArmSize * 0.5, lowerArmSize * 0.5) ) const upperBodyShape = new CANNON.Box( new CANNON.Vec3(shouldersDistance * 0.5, lowerArmSize * 0.5, upperBodyLength * 0.5) ) const pelvisShape = new CANNON.Box( new CANNON.Vec3(shouldersDistance * 0.5, lowerArmSize * 0.5, pelvisLength * 0.5) ) const upperLegShape = new CANNON.Box( new CANNON.Vec3(upperLegSize * 0.5, lowerArmSize * 0.5, upperLegLength * 0.5) ) const lowerLegShape = new CANNON.Box( new CANNON.Vec3(lowerLegSize * 0.5, lowerArmSize * 0.5, lowerLegLength * 0.5) ) // Lower legs const lowerLeftLeg = new CANNON.Body({ mass: 1, position: new CANNON.Vec3(shouldersDistance / 2, 0, lowerLegLength / 2), }) const lowerRightLeg = new CANNON.Body({ mass: 1, position: new CANNON.Vec3(-shouldersDistance / 2, 0, lowerLegLength / 2), }) lowerLeftLeg.addShape(lowerLegShape) lowerRightLeg.addShape(lowerLegShape) bodies.push(lowerLeftLeg) bodies.push(lowerRightLeg) // Upper legs const upperLeftLeg = new CANNON.Body({ mass: 1, position: new CANNON.Vec3( shouldersDistance / 2, 0, lowerLeftLeg.position.z + lowerLegLength / 2 + upperLegLength / 2 ), }) const upperRightLeg = new CANNON.Body({ mass: 1, position: new CANNON.Vec3( -shouldersDistance / 2, 0, lowerRightLeg.position.z + lowerLegLength / 2 + upperLegLength / 2 ), }) upperLeftLeg.addShape(upperLegShape) upperRightLeg.addShape(upperLegShape) bodies.push(upperLeftLeg) bodies.push(upperRightLeg) // Pelvis const pelvis = new CANNON.Body({ mass: 1, position: new CANNON.Vec3(0, 0, upperLeftLeg.position.z + upperLegLength / 2 + pelvisLength / 2), }) pelvis.addShape(pelvisShape) bodies.push(pelvis) // Upper body const upperBody = new CANNON.Body({ mass: 1, position: new CANNON.Vec3(0, 0, pelvis.position.z + pelvisLength / 2 + upperBodyLength / 2), }) upperBody.addShape(upperBodyShape) bodies.push(upperBody) // Head const head = new CANNON.Body({ mass: 1, position: new CANNON.Vec3(0, 0, upperBody.position.z + upperBodyLength / 2 + headRadius + neckLength), }) head.addShape(headShape) bodies.push(head) // Upper arms const upperLeftArm = new CANNON.Body({ mass: 1, position: new CANNON.Vec3( shouldersDistance / 2 + upperArmLength / 2, 0, upperBody.position.z + upperBodyLength / 2 ), }) const upperRightArm = new CANNON.Body({ mass: 1, position: new CANNON.Vec3( -shouldersDistance / 2 - upperArmLength / 2, 0, upperBody.position.z + upperBodyLength / 2 ), }) upperLeftArm.addShape(upperArmShape) upperRightArm.addShape(upperArmShape) bodies.push(upperLeftArm) bodies.push(upperRightArm) // Lower arms const lowerLeftArm = new CANNON.Body({ mass: 1, position: new CANNON.Vec3( upperLeftArm.position.x + lowerArmLength / 2 + upperArmLength / 2, 0, upperLeftArm.position.z ), }) const lowerRightArm = new CANNON.Body({ mass: 1, position: new CANNON.Vec3( upperRightArm.position.x - lowerArmLength / 2 - upperArmLength / 2, 0, upperRightArm.position.z ), }) lowerLeftArm.addShape(lowerArmShape) lowerRightArm.addShape(lowerArmShape) bodies.push(lowerLeftArm) bodies.push(lowerRightArm) // Neck joint const neckJoint = new CANNON.ConeTwistConstraint(head, upperBody, { pivotA: new CANNON.Vec3(0, 0, -headRadius - neckLength / 2), pivotB: new CANNON.Vec3(0, 0, upperBodyLength / 2), axisA: CANNON.Vec3.UNIT_Z, axisB: CANNON.Vec3.UNIT_Z, angle, twistAngle, }) constraints.push(neckJoint) // Knee joints const leftKneeJoint = new CANNON.ConeTwistConstraint(lowerLeftLeg, upperLeftLeg, { pivotA: new CANNON.Vec3(0, 0, lowerLegLength / 2), pivotB: new CANNON.Vec3(0, 0, -upperLegLength / 2), axisA: CANNON.Vec3.UNIT_Z, axisB: CANNON.Vec3.UNIT_Z, angle, twistAngle, }) const rightKneeJoint = new CANNON.ConeTwistConstraint(lowerRightLeg, upperRightLeg, { pivotA: new CANNON.Vec3(0, 0, lowerLegLength / 2), pivotB: new CANNON.Vec3(0, 0, -upperLegLength / 2), axisA: CANNON.Vec3.UNIT_Z, axisB: CANNON.Vec3.UNIT_Z, angle, twistAngle, }) constraints.push(leftKneeJoint) constraints.push(rightKneeJoint) // Hip joints const leftHipJoint = new CANNON.ConeTwistConstraint(upperLeftLeg, pelvis, { pivotA: new CANNON.Vec3(0, 0, upperLegLength / 2), pivotB: new CANNON.Vec3(shouldersDistance / 2, 0, -pelvisLength / 2), axisA: CANNON.Vec3.UNIT_Z, axisB: CANNON.Vec3.UNIT_Z, angle, twistAngle, }) const rightHipJoint = new CANNON.ConeTwistConstraint(upperRightLeg, pelvis, { pivotA: new CANNON.Vec3(0, 0, upperLegLength / 2), pivotB: new CANNON.Vec3(-shouldersDistance / 2, 0, -pelvisLength / 2), axisA: CANNON.Vec3.UNIT_Z, axisB: CANNON.Vec3.UNIT_Z, angle, twistAngle, }) constraints.push(leftHipJoint) constraints.push(rightHipJoint) // Spine const spineJoint = new CANNON.ConeTwistConstraint(pelvis, upperBody, { pivotA: new CANNON.Vec3(0, 0, pelvisLength / 2), pivotB: new CANNON.Vec3(0, 0, -upperBodyLength / 2), axisA: CANNON.Vec3.UNIT_Z, axisB: CANNON.Vec3.UNIT_Z, angle, twistAngle, }) constraints.push(spineJoint) // Shoulders const leftShoulder = new CANNON.ConeTwistConstraint(upperBody, upperLeftArm, { pivotA: new CANNON.Vec3(shouldersDistance / 2, 0, upperBodyLength / 2), pivotB: new CANNON.Vec3(-upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle: angleShoulders, }) const rightShoulder = new CANNON.ConeTwistConstraint(upperBody, upperRightArm, { pivotA: new CANNON.Vec3(-shouldersDistance / 2, 0, upperBodyLength / 2), pivotB: new CANNON.Vec3(upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle: angleShoulders, twistAngle, }) constraints.push(leftShoulder) constraints.push(rightShoulder) // Elbow joint const leftElbowJoint = new CANNON.ConeTwistConstraint(lowerLeftArm, upperLeftArm, { pivotA: new CANNON.Vec3(-lowerArmLength / 2, 0, 0), pivotB: new CANNON.Vec3(upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle, twistAngle, }) const rightElbowJoint = new CANNON.ConeTwistConstraint(lowerRightArm, upperRightArm, { pivotA: new CANNON.Vec3(lowerArmLength / 2, 0, 0), pivotB: new CANNON.Vec3(-upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle, twistAngle, }) constraints.push(leftElbowJoint) constraints.push(rightElbowJoint) return { bodies, constraints } } function setupWorld(demo) { const world = demo.getWorld() world.gravity.set(0, -5, 0) // Static ground plane const groundShape = new CANNON.Plane() const groundBody = new CANNON.Body({ mass: 0 }) groundBody.addShape(groundShape) groundBody.position.set(0, -1, 0) groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) world.addBody(groundBody) demo.addVisual(groundBody) return world } function createStaticSphere() { const sphereShape = new CANNON.Sphere(4) const sphereBody = new CANNON.Body({ mass: 0 }) sphereBody.addShape(sphereShape) sphereBody.position.set(0, -1, 0) return sphereBody } </script> </body> </html>