### Pages/Api Conventions/Factories And Definitions ## Factories and Definitions To create a `Body` or a `Joint`, you need to call the factory functions on World: ```js body = world.createBody(bodyDef); joint = world.createJoint(jointDef); ``` And there are corresponding destruction functions: ```js world.destroyBody(body) world.destroyJoint(joint) ``` When you create a body or joint, you need to provide a definition. These definitions contain all the information needed to build the body or joint. By using this approach we can prevent construction errors, keep the number of function parameters small, provide sensible defaults, and reduce the number of accessors. Since fixtures must be parented to a body, they are created and destroyed using a factory method on `Body`: ```js let fixture = body.createFixture(fixtureDef); body.destroyFixture(fixture); ``` There is also a shortcut to create a fixture directly from the shape and density. ```js let fixture = body.createFixture(shape, density); ``` --- ### Pages/Api Conventions/Implicit Destruction ## Implicit Destruction Often when using Planck.js you will create and destroy many bodies, shapes, and joints. Managing these entities is somewhat automated by Planck.js. If you destroy a body then all associated shapes and joints are automatically destroyed. This is called implicit destruction. When you destroy a body, all its attached shapes, joints, and contacts are destroyed. Any body connected to one of those joints and/or contacts is woken. This process is usually convenient. However, you must be aware of one crucial issue: > **Caution**: > When a body is destroyed, all fixtures and joints attached to the body > are automatically destroyed. You must nullify any references you have to > those shapes and joints. Otherwise, your program will die horribly if > you try to use those fixtures or joints later. To help you nullify your references, Planck.js world publishes events (`remove-joint`, `remove-fixture`, `remove-body`) that you can listen to. Then the world object will notify you when an object is going to be implicitly destroyed. ```js world.on('remove-joint', function(joint) { // remove all references to joint. }); world.on('remove-fixture', function(fixture) { // remove all references to fixture. }); world.on('remove-body', function(body) { // bodies are not removed implicitly, // but the world publishes this event if a body is removed }) ``` --- ### Pages/Api Conventions/Units ## Units Planck.js works with floating point numbers and tolerances have to be used to make Planck.js perform well. These tolerances have been tuned to work well with meters-kilogram-second (MKS) units. In particular, Planck.js has been tuned to work well with moving shapes between 0.1 and 10 units (meters). So this means objects between soup cans and buses in size should work well. Static shapes may be up to 50 meters long without trouble. You should try to get your moving objects in the range 0.1 - 10 meters, with 1 meter being the sweet spot. Being a 2D physics engine, it is tempting to use pixels as your units. This could lead to a poor simulation and possibly weird behavior. An object of length 200 pixels would be seen by Planck.js as the size of a 45 story building. If you need to use different length units, you have two options: - Use some scaling system when you render your environment and actors. It is highly recommended to use this approach to keep your physics code portable. The Planck.js testbed does this by using stage.js viewbox transform. - Set Settings.lengthUnitsPerMeter accordingly. For example if a car height which is around 1.6 meter is 80 units (pixels) in in your game, value of lengthUnitsPerMeter should be set to 50 (80 / 1.6). Planck.js uses radians for angles. The body rotation is stored in radians and may grow unbounded. Consider normalizing the angle of your bodies if the magnitude of the angle becomes too large (use body.setAngle). > **Caution**: > Planck.js uses radians, not degrees. --- ### Pages/Api Conventions/User Data ## User Data The `Fixture`, `Body`, and `Joint` classes allow you to attach user data. This is handy to implement game-logic and rendering. For example, it is typical to attach an actor reference to the rigid body on that actor. This sets up a circular reference. If you have the actor, you can get the body. If you have the body, you can get the actor. ```js let actor = gameCreateActor(); actor.body = myWorld.createBody({ userData: actor }); ``` For fixtures you might consider defining a user data structure that lets you store game specific information, such as material type, effects hooks, sound hooks, etc. ```js let fixture = body.createFixture({ shape: someShape, userData: { materialIndex: 2 }, }); ``` Keep in mind that user data is optional and you can put anything in it. However, you should be consistent. For example, if you want to store an actor reference on one body, you should keep an actor reference on all bodies. Don't store an actor reference on one body, and a foo reference on another body. Casting an actor reference to a foo reference may lead to a crash. User data references are null by default. --- ### Pages/Joint/Distance Joint ## Distance Joint One of the simplest joints is a distance joint which says that the distance between two points on two bodies must be constant. When you specify a distance joint the two bodies should already be in place. Then you specify the two anchor points in world coordinates. The first anchor point is connected to body 1, and the second anchor point is connected to body 2. These points imply the length of the distance constraint. Here is an example of a distance joint definition. In this case we decide to allow the bodies to collide. ```js new DistanceJoint({ collideConnected: true, }, myBodyA, myBodyB, worldAnchorOnBodyA, worldAnchorOnBodyB); ``` The distance joint can also be made soft, like a spring-damper connection. See the Web example in the testbed to see how this behaves. Softness is achieved by tuning two constants in the definition: frequency and damping ratio. Think of the frequency as the frequency of a harmonic oscillator (like a guitar string). The frequency is specified in Hertz. Typically the frequency should be less than a half the frequency of the time step. So if you are using a 60Hz time step, the frequency of the distance joint should be less than 30Hz. The reason is related to the Nyquist frequency. The damping ratio is non-dimensional and is typically between 0 and 1, but can be larger. At 1, the damping is critical (all oscillations should vanish). ```js new DistanceJoint({ frequencyHz: 4, dampingRatio: 0.5, collideConnected: true, }, myBodyA, myBodyB, worldAnchorOnBodyA, worldAnchorOnBodyB); ``` --- ### Pages/Joint/Friction Joint ## Friction Joint The friction joint is used for top-down friction. The joint provides 2D translational friction and angular friction. See FrictionJoint.js and ApplyForce.js for details. --- ### Pages/Joint/Gear Joint ## Gear Joint If you want to create a sophisticated mechanical contraption you might want to use gears. In principle you can create gears in Planck.js by using compound shapes to model gear teeth. This is not very efficient and might be tedious to author. You also have to be careful to line up the gears so the teeth mesh smoothly. Planck.js has a simpler method of creating gears: the gear joint. The gear joint can only connect revolute and/or prismatic joints. Like the pulley ratio, you can specify a gear ratio. However, in this case the gear ratio can be negative. Also keep in mind that when one joint is a revolute joint (angular) and the other joint is prismatic (translation), and then the gear ratio will have units of length or one over length. ``` coordinate1 + ratio * coordinate2 == constant ``` Here is an example gear joint. The bodies myBodyA and myBodyB are any bodies from the two joints, as long as they are not the same bodies. ```js new GearJoint({ bodyA: myBodyA, bodyB: myBodyB, joint1: myRevoluteJoint, joint2: myPrismaticJoint, ratio: 2 * Math.PI / myLength, }) ``` Note that the gear joint depends on two other joints. This creates a fragile situation. What happens if those joints are deleted? > **Caution**: > Always delete gear joints before the revolute/prismatic joints on the > gears. Otherwise your code will crash in a bad way due to the orphaned > joint references in the gear joint. You should also delete the gear joint > before you delete any of the bodies involved. --- ### Pages/Joint/Motor Joint ## Motor Joint A motor joint lets you control the motion of a body by specifying target position and rotation offsets. You can set the maximum motor force and torque that will be applied to reach the target position and rotation. If the body is blocked, it will stop and the contact forces will be proportional the maximum motor force and torque. See MotorJoint and MotorJoint.h for details. --- ### Pages/Joint/Mouse Joint ## Mouse Joint The mouse joint is used in the testbed to manipulate bodies with the mouse. It attempts to drive a point on a body towards the current position of the cursor. There is no restriction on rotation. The mouse joint definition has a target point, maximum force, frequency, and damping ratio. The target point initially coincides with the body's anchor point. The maximum force is used to prevent violent reactions when multiple dynamic bodies interact. You can make this as large as you like. The frequency and damping ratio are used to create a spring/damper effect similar to the distance joint. Many users have tried to adapt the mouse joint for game play. Users often want to achieve precise positioning and instantaneous response. The mouse joint doesn't work very well in that context. You may wish to consider using kinematic bodies instead. --- ### Pages/Joint/Prismatic Joint ## Prismatic Joint A prismatic joint allows for relative translation of two bodies along a specified axis. A prismatic joint prevents relative rotation. Therefore, a prismatic joint has a single degree of freedom. The prismatic joint definition is similar to the revolute joint description; just substitute translation for angle, and force for torque. Using this analogy provides an example prismatic joint definition with a joint limit and a friction motor: ```js let worldAxis = new Vec2(1, 0); new PrismaticJoint({ lowerTranslation: -5, upperTranslation: 2.5, enableLimit: true, maxMotorForce: 1, motorSpeed: 0, enableMotor: true, }, myBodyA, myBodyB, myBodyA.getWorldCenter(), worldAxis); ``` The revolute joint has an implicit axis coming out of the screen. The prismatic joint needs an explicit axis parallel to the screen. This axis is fixed in the two bodies and follows their motion. Like the revolute joint, the prismatic joint translation is zero when it is not defined. So be sure zero is between your lower and upper translation limits. Using a prismatic joint is similar to using a revolute joint. Here are the relevant member functions: ```js prismaticJoint.getJointTranslation(); // number prismaticJoint.getJointSpeed(); // number prismaticJoint.getMotorForce(); // number prismaticJoint.setMotorSpeed(speed /*number*/); prismaticJoint.setMotorForce(force /*number*/); ``` --- ### Pages/Joint/Pulley ## Pulley Joint A pulley is used to create an idealized pulley. The pulley connects two bodies to ground and to each other. As one body goes up, the other goes down. The total length of the pulley rope is conserved according to the initial configuration. ``` length1 + length2 == constant ``` You can supply a ratio that simulates a block and tackle. This causes one side of the pulley to extend faster than the other. At the same time the constraint force is smaller on one side than the other. You can use this to create mechanical leverage. ``` length1 + ratio * length2 == constant ``` For example, if the ratio is 2, then `length1` will vary at twice the rate of `length2`. Also the force in the rope attached to `body1` will have half the constraint force as the rope attached to `body2`. Pulleys can be troublesome when one side is fully extended. The rope on the other side will have zero length. At this point the constraint equations become singular (bad). You should configure collision shapes to prevent this. Here is an example pulley definition: ```js let anchor1 = myBody1.getWorldCenter(); let anchor2 = myBody2.getWorldCenter(); let groundAnchor1 = Vec2(p1.x, p1.y + 10); let groundAnchor2 = Vec2(p2.x, p2.y + 12); let ratio = 1; new PulleyJoint({}, myBody1, myBody2, groundAnchor1, groundAnchor2, anchor1, anchor2, ratio); ``` Pulley joints provide the current lengths. ```js pulleyJoint.getLengthA(); // number pulleyJoint.getLengthB(); // number ``` --- ### Pages/Joint/Revolute Joint ## Revolute Joint A revolute joint forces two bodies to share a common anchor point, often called a hinge point. The revolute joint has a single degree of freedom: the relative rotation of the two bodies. This is called the joint angle. To specify a revolute you need to provide two bodies and a single anchor point in world space. The initialization function assumes that the bodies are already in the correct position. In this example, two bodies are connected by a revolute joint at the first body's center of mass. ```js new RevoluteJoint({}, myBodyA, myBodyB, myBodyA.getWorldCenter()); ``` The revolute joint angle is positive when bodyB rotates CCW about the angle point. Like all angles in Planck.js, the revolute angle is measured in radians. By convention the revolute joint angle is zero if it not specified, regardless of the current rotation of the two bodies. In some cases you might wish to control the joint angle. For this, the revolute joint can optionally simulate a joint limit and/or a motor. A joint limit forces the joint angle to remain between a lower and upper bound. The limit will apply as much torque as needed to make this happen. The limit range should include zero, otherwise the joint will lurch when the simulation begins. A joint motor allows you to specify the joint speed (the time derivative of the angle). The speed can be negative or positive. A motor can have infinite force, but this is usually not desirable. Recall the eternal question: > *What happens when an irresistible force meets an immovable object?* I can tell you it's not pretty. So you can provide a maximum torque for the joint motor. The joint motor will maintain the specified speed unless the required torque exceeds the specified maximum. When the maximum torque is exceeded, the joint will slow down and can even reverse. You can use a joint motor to simulate joint friction. Just set the joint speed to zero, and set the maximum torque to some small, but significant value. The motor will try to prevent the joint from rotating, but will yield to a significant load. Here's a revision of the revolute joint definition above; this time the joint has a limit and a motor enabled. The motor is setup to simulate joint friction. ```js new RevoluteJoint({ lowerAngle: -0.5 * Math.PI, // -90 degrees upperAngle: 0.25 * Math.PI, // 45 degrees enableLimit: true, maxMotorTorque: 10, motorSpeed: 0, enableMotor: true, }, myBodyA, myBodyB, myBodyA.getWorldCenter()); ``` You can access a revolute joint's angle, speed, and motor torque. ```js revoluteJoint.getJointAngle(); // number revoluteJoint.getJointSpeed(); // number revoluteJoint.getMotorTorque(); // number ``` You also update the motor parameters each step. ```js revoluteJoint.setMotorSpeed(speed /*number*/); revoluteJoint.setMaxMotorTorque(torque /*number*/); ``` Joint motors have some interesting abilities. You can update the joint speed every time step so you can make the joint move back-and-forth like a sine-wave or according to whatever function you want. ```js // ... Game Loop Begin ... myJoint.setMotorSpeed(Math.cos(0.5 * time)); // ... Game Loop End ... ``` You can also use joint motors to track a desired joint angle. For example: ```js // ... Game Loop Begin ... let angleError = myJoint.getJointAngle() - angleTarget; let gain = 0.1; myJoint.setMotorSpeed(-gain * angleError); // ... Game Loop End ... ``` Generally your gain parameter should not be too large. Otherwise your joint may become unstable. --- ### Pages/Joint/Rope Joint ## Rope Joint The rope joint restricts the maximum distance between two points. This can be useful to prevent chains of bodies from stretching, even under high load. See RopeJoint.js and RopeJoint.js for details. --- ### Pages/Joint/Weld Joint ## Weld Joint The weld joint attempts to constrain all relative motion between two bodies. See the Cantilever.js in the testbed to see how the weld joint behaves. It is tempting to use the weld joint to define breakable structures. However, the Planck.js solver is iterative so the joints are a bit soft. So chains of bodies connected by weld joints will flex. Instead it is better to create breakable bodies starting with a single body with multiple fixtures. When the body breaks, you can destroy a fixture and recreate it on a new body. See the Breakable example in the testbed. --- ### Pages/Joint/Wheel Joint ## Wheel Joint The wheel joint restricts a point on bodyB to a line on bodyA. The wheel joint also provides a suspension spring. See WheelJoint class, and Car example for details. --- ### Pages/Shape/Chain ## Chain Shapes The chain shape provides an efficient way to connect many edges together to construct your static game worlds. Chain shapes automatically eliminate ghost collisions and provide two-sided collision. ```js // This is a chain shape with isolated vertices let vs = [ Vec2(1.7, 0), Vec2(1, 0.25), Vec2(0, 0), Vec2(-1.7, 0.4) ]; let chain = new Chain(vs); ``` You may have a scrolling game world and would like to connect several chains together. You can connect chains together using ghost vertices, like we did with EdgeShape. ```js // Install ghost vertices chain.setPrevVertex(Vec2(3, 1)); chain.setNextVertex(Vec2(-2, 0)); ``` You may also create loops automatically. ```js // Create a loop. The first and last vertices are connected. let chain = new Chain(vs, true); ``` Self-intersection of chain shapes is not supported. It might work, it might not. The code that prevents ghost collisions assumes there are no self-intersections of the chain. Also, very close vertices can cause problems. Make sure all your edges are longer than Settings.linearSlop (5mm). Each edge in the chain is treated as a child shape and can be accessed by index. When a chain shape is connected to a body, each edge gets its own bounding box in the broad-phase collision tree. ```js // Visit each child edge. for (let i = 0; i < chain.getChildCount(); ++i) { let edge = new Edge(); chain.getChildEdge(edge, i); } ``` --- ### Pages/Shape/Circle ## Circle Shapes Circle shapes have a position and radius. Circles are solid. You cannot make a hollow circle using the circle shape. ```js let circle = new Circle(new Vec2(2, 3), 0.5); ``` --- ### Pages/Shape/Edge ## Edge Shapes Edge shapes are line segments. These are provided to assist in making a free-form static environment for your game. A major limitation of edge shapes is that they can collide with circles and polygons but not with themselves. The collision algorithms used by Planck.js require that at least one of two colliding shapes have volume. Edge shapes have no volume, so edge-edge collision is not possible. ```js // This is an edge shape. let edge = new Edge(new Vec2(0, 0), new Vec2(1, 0)); ``` In many cases a game environment is constructed by connecting several edge shapes end-to-end. This can give rise to an unexpected artifact when a polygon slides along the chain of edges. In the figure below we see a box colliding with an internal vertex. These *ghost* collisions are caused when the polygon collides with an internal vertex generating an internal collision normal. If edge1 did not exist this collision would seem fine. With edge1 present, the internal collision seems like a bug. But normally when Planck.js collides two shapes, it views them in isolation. Fortunately, the edge shape provides a mechanism for eliminating ghost collisions by storing the adjacent *ghost* vertices. Planck.js uses these ghost vertices to prevent internal collisions. ```js // This is an edge shape with ghost vertices. let v0 = Vec2(1.7, 0); let v1 = Vec2(1, 0.25); let v2 = Vec2(0, 0); let v3 = Vec2(-1.7, 0.4); let edge = new Edge(v1, v2).setPrevVertex(v0).setNextVertex(v3); ``` In general stitching edges together this way is a bit wasteful and tedious. This brings us to chain shapes. --- ### Pages/Shape/Polygon ## Polygon Shapes Polygon shapes are solid convex polygons. A polygon is convex when all line segments connecting two points in the interior do not cross any edge of the polygon. Polygons are solid and never hollow. A polygon must have 3 or more vertices. Polygon vertices are stored with a counter-clockwise winding (CCW). We must be careful because the notion of CCW is with respect to a right-handed coordinate system with the z-axis pointing out of the plane. This might turn out to be clockwise on your screen, depending on your coordinate system conventions. The initialization functions create normal vectors and perform validation. So you should use initialization functions to create a polygon. You can create a polygon shape by passing in a vertex array. The maximal size of the array is controlled by `Setting.MaxPolygonVertices` which has a default value of 8. This is sufficient to describe most convex polygons. The `PolygonShape.set` function automatically computes the convex hull and establishes the proper winding order. This function is fast when the number of vertices is low. If you increase `MaxPolygonVertices`, then the convex hull computation might become slow. Also note that the convex hull function may eliminate and/or re-order the points you provide. Vertices that are closer than `Settings.linearSlop` may be merged. ```js // This defines a triangle in CCW order. let vertices = [ Vec2(0, 0), Vec2(1, 0), Vec2(0, 1) ]; let polygon = new Polygon(vertices); ``` The polygon shape has some convenience functions to create boxes. ```js new Box(halfWidth, halfHeight); new Box(halfWidth, halfHeight, center, angle); ``` `center` is the local position of the center of the box shape, and `angle` is its rotation. When not provided, center and angle of the box are `{x: 0, y: 0}` and `0` (relative to the body's origin and angle). Polygons inherit a radius from Shape. The radius creates a skin around the polygon. The skin is used in stacking scenarios to keep polygons slightly separated. This allows continuous collision to work against the core polygon. The polygon skin helps prevent tunneling by keeping the polygons separated. This results in small gaps between the shapes. Your visual representation can be larger than the polygon to hide any gaps. --- ### Pages/World/Aabb Query ## AABB Queries Sometimes you want to determine all the shapes in a region. The World class has a fast O(log N) method for this using the broad-phase data structure. You provide an AABB in world coordinates and an implementation of `QueryCallback`. The world calls your class with each fixture whose AABB overlaps the query AABB. Return `true` to continue the query, otherwise return `false`. For example, the following code finds all the fixtures that potentially intersect a specified AABB and wakes up all of the associated bodies. ```js const query = new AABB( new Vec2(-1, -1), new Vec2(1, 1), ); myWorld.queryAABB(query, function(fixture) { let body = fixture.getBody(); body.setAwake(true); // Return true to continue the query. return true; }); ``` You cannot make any assumptions about the order of the callbacks. --- ### Pages/World/Ray Cast ## Ray Casts You can use ray casts to do line-of-sight checks, fire guns, etc. You perform a ray cast by implementing a callback class and providing the start and end points. The world class calls your class with each fixture hit by the ray. Your callback is provided with the fixture, the point of intersection, the unit normal vector, and the fractional distance along the ray. You cannot make any assumptions about the order of the callbacks. You control the continuation of the ray cast by returning a fraction. Returning a fraction of zero indicates the ray cast should be terminated. A fraction of one indicates the ray cast should continue as if no hit occurred. If you return the fraction from the argument list, the ray will be clipped to the current intersection point. So you can ray cast any shape, ray cast all shapes, or ray cast the closest shape by returning the appropriate fraction. You may also return of fraction of -1 to filter the fixture. Then the ray cast will proceed as if the fixture does not exist. Here is an example: ```js // This class captures the closest hit shape. let closest = null; myWorld.rayCast(Vec2(-1, 0), Vec2(3, 1), function(fixture, point, normal, fraction) { closest = { fixture: fixture, point: point, // Vec2 normal: normal, // Vec2 fraction: fraction, // number } // By returning the current fraction, we instruct the calling code to clip the ray and // continue the ray-cast to the next fixture. WARNING: do not assume that fixtures // are reported in order. However, by clipping, we can always get the closest fixture. return fraction; }); ``` > **Caution**: > Due to round-off errors, ray casts can sneak through small cracks > between polygons in your static environment. If this is not acceptable > in your application, try slightly overlapping your polygons. --- ### Pages/World/Simulation ## Simulation The world class is used to drive the simulation. You specify a time step and a velocity and position iteration count. For example: ```js let timeStep = 1 / 60; let velocityIterations = 10; let positionIterations = 8; myWorld.step(timeStep, velocityIterations, positionIterations); ``` After the time step you can examine your bodies and joints for information. Most likely you will grab the position off the bodies so that you can update your actors and render them. You can perform the time step anywhere in your game loop, but you should be aware of the order of things. For example, you must create bodies before the time step if you want to get collision results for the new bodies in that frame. As I discussed above in the HelloWorld tutorial[todo], you should use a fixed time step. By using a larger time step you can improve performance in low frame rate scenarios. But generally you should use a time step no larger than 1/30 seconds. A time step of 1/60 seconds will usually deliver a high quality simulation. The iteration count controls how many times the constraint solver sweeps over all the contacts and joints in the world. More iteration always yields a better simulation. But don't trade a small time step for a large iteration count. 60Hz and 10 iterations is far better than 30Hz and 20 iterations. After stepping, you should clear any forces you have applied to your bodies. This is done with the command `world.clearForces()`. This lets you take multiple sub-steps with the same force field. ```js myWorld.clearForces(); ``` [todo: clean up next section, it is duplicate] ### Simulating the World Planck.js uses a computational algorithm called an integrator. Integrators simulate the physics equations at discrete points of time. This goes along with the traditional game loop where we essentially have a flip book of movement on the screen. So we need to pick a time step for Planck.js. Generally physics engines for games like a time step at least as fast as 60Hz or 1/60 seconds. You can get away with larger time steps, but you will have to be more careful about setting up the definitions for your world. We also don't like the time step to change much. A variable time step produces variable results, which makes it difficult to debug. So don't tie the time step to your frame rate (unless you really, really have to). Without further ado, here is the time step. ```js let timeStep = 1 / 60; ``` In addition to the integrator, Planck.js also uses a larger bit of code called a constraint solver. The constraint solver solves all the constraints in the simulation, one at a time. A single constraint can be solved perfectly. However, when we solve one constraint, we slightly disrupt other constraints. To get a good solution, we need to iterate over all constraints a number of times. There are two phases in the constraint solver: a velocity phase and a position phase. In the velocity phase the solver computes the impulses necessary for the bodies to move correctly. In the position phase the solver adjusts the positions of the bodies to reduce overlap and joint detachment. Each phase has its own iteration count. In addition, the position phase may exit iterations early if the errors are small. The suggested iteration count for Planck.js is 8 for velocity and 3 for position. You can tune this number to your liking, just keep in mind that this has a trade-off between performance and accuracy. Using fewer iterations increases performance but accuracy suffers. Likewise, using more iterations decreases performance but improves the quality of your simulation. For this simple example, we don't need much iteration. Here are our chosen iteration counts. ```js let velocityIterations = 6; let positionIterations = 2; ``` Note that the time step and the iteration count are completely unrelated. An iteration is not a sub-step. One solver iteration is a single pass over all the constraints within a time step. You can have multiple passes over the constraints within a single time step. We are now ready to begin the simulation loop. In your game the simulation loop can be merged with your game loop. In each pass through your game loop you call world.step(). Just one call is usually enough, depending on your frame rate and your physics time step. The Hello World program was designed to be simple, so it has no graphical output. The code prints out the position and rotation of the dynamic body. Here is the simulation loop that simulates 60 time steps for a total of 1 second of simulated time. ```js for (let i = 0; i < 60; ++i) { world.step(timeStep, velocityIterations, positionIterations); let position = body.getPosition(); let angle = body.getAngle(); console.log(position.x, position.y, angle); } ``` The output shows the box falling and landing on the ground box. Your output should look like this: ``` 0.00 4.00 0.00 0.00 3.99 0.00 0.00 3.98 0.00 ... 0.00 1.25 0.00 0.00 1.13 0.00 0.00 1.01 0.00 ``` --- ### Pages/Body ## Body Bodies have position, angle, and velocity. You can apply forces, torques, and impulses to bodies. Bodies can be `static`, `kinematic`, or `dynamic`. Here are the body type definitions: - `static` - A static body does not move under simulation and behaves as if it has infinite mass. Internally, Planck.js stores zero for the mass and the inverse mass. Static bodies can be moved manually by the user. A static body always has zero velocity. Static bodies do not collide with other static or kinematic bodies. - `kinematic` - A kinematic body is like a static body, but can have velocity. You can set kinematic body velocity or move it manually. However, their velocity is not changed in collision or when you apply force. Kinematic bodies do not collide with other kinematic or static bodies. When a kinematic body collides with a dynamic body it behaves as if it has infinite mass. - `dynamic` - A dynamic body is fully simulated. They can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass. If you try to set the mass of a dynamic body to zero, it will automatically acquire a mass of one kilogram and it won't rotate. Bodies are the backbone for fixtures (shapes). Bodies carry fixtures and move them around in the world. Bodies are always rigid bodies in Planck.js. That means that two fixtures attached to the same rigid body never move relative to each other and fixtures attached to the same body don't collide. Fixtures have collision geometry and density. Normally, bodies acquire their mass properties from the fixtures. However, you can override the mass properties after a body is constructed. You usually keep references to all the bodies you create. This way you can query the body positions to update the positions of your graphical entities. You should also keep body references so you can destroy them when you are done with them. ## Body Factory Bodies are created and destroyed using a body factory provided by the `World` class. This lets the world create the body and add the body to the world data structure. ```js let dynamicBody = myWorld.createBody(bodyDef); // ... do stuff ... myWorld.destroyBody(dynamicBody); ``` > **Caution**: > You should never create a body directly using new. The world won't > know about the body and the body won't be properly initialized. Planck.js does not keep a reference to the body definition or any of the data it holds (except user data references). So you can create temporary body definitions and reuse the same body definitions. When you destroy a body, the attached fixtures and joints are automatically destroyed. This has important implications for how you manage shape and joint references. ## Body Definition Let's go over some of the key members of the body definition. ### Body Type As discussed at the beginning of this chapter, there are three different body types: static, kinematic, and dynamic. You should specify the body type at creation because changing the body type later is expensive. ```js world.createBody({ type: 'dynamic' }); ``` Default body type is static, static bodies don't move in simulations. ### Position and Angle The body definition gives you the chance to initialize the position of the body on creation. This has far better performance than creating the body at the world origin and then moving the body. > **Caution**: > Do not create a body at the origin and then move it. If you create > several bodies at the origin, then performance will suffer. A body has two main points of interest. The first point is the body's origin. Fixtures and joints are attached relative to the body's origin. The second point of interest is the center of mass. The center of mass is determined from mass distribution of the attached shapes or is explicitly set with `MassData`. Much of Planck.js's internal computations use the center of mass position. For example `Body` stores the linear velocity for the center of mass. When you are building the body definition, you may not know where the center of mass is located. Therefore you specify the position of the body's origin. You may also specify the body's angle in radians, which is not affected by the position of the center of mass. If you later change the mass properties of the body, then the center of mass may move on the body, but the origin position does not change and the attached shapes and joints do not move. ```js world.createBody({ position: {x: 0, y: 2}, // the body's origin position. angle: 0.25 * Math.PI // the body's angle in radians. }) ``` A rigid body is also a frame of reference. You can define fixtures and joints in that frame. Those fixtures and joint anchors never move in the local frame of the body. ### Damping Damping is used to reduce the world velocity of bodies. Damping is different than friction because friction only occurs with contact. Damping is not a replacement for friction and the two effects should be used together. Damping parameters should be between 0 and infinity, with 0 meaning no damping, and infinity meaning full damping. Normally you will use a damping value between 0 and 0.1. I generally do not use linear damping because it makes bodies look like they are floating. ```js world.createBody({ linearDamping: 0, angularDamping: 0.01 }); ``` Damping is approximated for stability and performance. At small damping values the damping effect is mostly independent of the time step. At larger damping values, the damping effect will vary with the time step. This is not an issue if you use a fixed time step (recommended). ### Gravity Scale You can use the gravity scale to adjust the gravity on a single body. Be careful though, increased gravity can decrease stability. ```js // Set the gravity scale to zero so this body will float world.createBody({ gravityScale: 0 }); ``` ### Sleep Parameters It is expensive to simulate bodies, so the less we have to simulate the better. When a body comes to rest we would like to stop simulating it. When Planck.js determines that a body (or group of bodies) has come to rest, the body enters a sleep state which has very little CPU overhead. If a body is awake and collides with a sleeping body, then the sleeping body wakes up. Bodies will also wake up if a joint or contact attached to them is destroyed. You can also wake a body manually. The body definition lets you specify whether a body can sleep and whether a body is created sleeping. ```js world.createBody({ allowSleep: true, awake: true, }); ``` ### Fixed Rotation You may want a body, such as a character, to have a fixed rotation. Such a body should not rotate, even under load. You can use the fixed rotation setting to achieve this: ```js world.createBody({ fixedRotation: true }); ``` The fixed rotation flag causes the rotational inertia and its inverse to be set to zero. ### Bullets Game simulation usually generates a sequence of images that are played at some frame rate. This is called discrete simulation. In discrete simulation, rigid bodies can move by a large amount in one time step. If a physics engine doesn't account for the large motion, you may see some objects incorrectly pass through each other. This effect is called tunneling. By default, Planck.js uses continuous collision detection (CCD) to prevent dynamic bodies from tunneling through static bodies. This is done by sweeping shapes from their old position to their new positions. The engine looks for new collisions during the sweep and computes the time of impact (TOI) for these collisions. Bodies are moved to their first TOI and then the solver performs a sub-step to complete the full time step. There may be additional TOI events within a sub-step. Normally CCD is not used between dynamic bodies. This is done to keep performance reasonable. In some game scenarios you need dynamic bodies to use CCD. For example, you may want to shoot a high speed bullet at a stack of dynamic bricks. Without CCD, the bullet might tunnel through the bricks. Fast moving objects in Planck.js can be labeled as bullets. Bullets will perform CCD with both static and dynamic bodies. You should decide what bodies should be bullets based on your game design. If you decide a body should be treated as a bullet, use the following setting. ```js world.createBody({ bullet: true, }); ``` The bullet flag only affects dynamic bodies. ### Activation You may wish a body to be created but not participate in collision or dynamics. This state is similar to sleeping except the body will not be woken by other bodies and the body's fixtures will not be placed in the broad-phase. This means the body will not participate in collisions, ray casts, etc. You can create a body in an inactive state and later re-activate it. ```js world.createBody({ active: false, }); ``` Joints may be connected to inactive bodies. These joints will not be simulated. You should be careful when you activate a body that its joints are not distorted. Note that activating a body is almost as expensive as creating the body from scratch. So you should not use activation for streaming worlds. Use creation/destruction for streaming worlds to save memory. ### User Data User data is an untyped reference. This gives you a hook to link your application objects to bodies. ```js world.createBody({ userData: myActor, }); ``` ## Using a Body After creating a body, there are many operations you can perform on the body. These include setting mass properties, accessing position and velocity, applying forces, and transforming points and vectors. ### Mass Data A body has mass (scalar), center of mass (2-vector), and rotational inertia (scalar). For static bodies, the mass and rotational inertia are set to zero. When a body has fixed rotation, its rotational inertia is zero. Normally the mass properties of a body are established automatically when fixtures are added to the body. You can also adjust the mass of a body at run-time. This is usually done when you have special game scenarios that require altering the mass. ```js body.setMassData(massData); ``` After setting a body's mass directly, you may wish to revert to the natural mass dictated by the fixtures. You can do this with: ```js body.resetMassData(); ``` The body's mass data is available through the following functions: ```js body.getMass(); // number body.getInertia(); // number body.getLocalCenter(); // Vec2 body.getMassData(massData); ``` ### State Information There are many aspects to the body's state. You can access this state data efficiently through the following functions: ```js body.setType(bodyType); body.getType(); // string body.setBullet(flag); body.isBullet(); // boolean body.setSleepingAllowed(flag); body.isSleepingAllowed(); // boolean body.setAwake(flag); body.isAwake(); // boolean body.setEnabled(flag); body.isEnabled(); // boolean body.setFixedRotation(flag); body.isFixedRotation(); // boolean ``` ### Position and Velocity You can access the position and rotation of a body. This is common when rendering your associated game actor. You can also set the position and rotation, although this is less common since you will normally use Planck.js to simulate movement. ```js body.setTransform(position, angle); body.getTransform(); // Transform body.setPosition(position); body.getPosition(); // Vec2 body.setAngle(angle); body.getAngle(); // number ``` You can access the center of mass position in local and world coordinates. Much of the internal simulation in Planck.js uses the center of mass. However, you should normally not need to access it. Instead you will usually work with the body transform. For example, you may have a body that is square. The body origin might be a corner of the square, while the center of mass is located at the center of the square. ```js body.getWorldCenter(); // Vec2 body.getLocalCenter(); // Vec2 ``` You can access the linear and angular velocity. The linear velocity is for the center of mass. Therefore, the linear velocity may change if the mass properties change. ### Forces and Impulses You can apply forces, torques (rotational force), and impulses to a body. When you apply a force or an impulse, you provide a world point where the load is applied. This often results in a torque about the center of mass. ```js body.applyForce(force, point); // force: Vec2, point: Vec2 body.applyTorque(torque); body.applyLinearImpulse(impulse, point); // force: Vec2, point: Vec2 body.applyAngularImpulse(impulse); ``` Applying a force, torque, or impulse wakes the body. Sometimes this is undesirable. For example, you may be applying a steady force and want to allow the body to sleep to improve performance. In this case you can use the following code. ```js if (myBody.isAwake()) { myBody.applyForce(myForce, myPoint); } ``` ### Coordinate Transformations The body class has some utility functions to help you transform points and vectors between local and world space. A `localPoint` is a coordinate relative to the body's origin. A `worldPoint` is a coordinate relative to the world's origin. A `localVector` is a vector between two points relative to the body's origin. A `worldVector` is a vector between two points relative to the world's origin. Here "point" means a point's 2D coordinate, "vector" means the vector between two points. In point conversion both position and angle of body are considered, in vector conversion only angle. ```js body.getWorldPoint(localPoint); // Vec2 body.getLocalPoint(worldPoint); // Vec2 body.getWorldVector(localVector); // Vec2 body.getLocalVector(worldVector); // Vec2 ``` ### Accessing Fixtures, Joints, and Contacts You can iterate over a body's fixtures. ```js for (let fixture = body.getFixtureList(); fixture; fixture = fixture.getNext()) { // do something with fixture } ``` You can similarly iterate over the body's joint list. ```js for (var joint = this.getJointList(); joint; joint = joint.getNext()) { // do something with joint } ``` The body also provides a list of associated contacts. You can use this to get information about the current contacts. Be careful, because the contact list may not contain all the contacts that existed during the previous time step. --- ### Pages/Collision ## Collision The Collision classes include shapes and functions that operate on them. The module also contains a dynamic tree and broad-phase to acceleration collision processing of large systems. The collision classes are designed to be usable outside of the dynamic system. For example, you can use the dynamic tree for other aspects of your game besides physics. However, the main purpose of Planck.js is to provide a rigid body physics engine, so the using the collision module by itself may feel limited for some applications. Likewise, I will not make a strong effort to document it or polish the APIs. ### Contact Manifolds Planck.js has functions to compute contact points for overlapping shapes. If we consider circle-circle or circle-polygon, we can only get one contact point and normal. In the case of polygon-polygon we can get two points. These points share the same normal vector so Planck.js groups them into a manifold structure. The contact solver takes advantage of this to improve stacking stability. Normally you don't need to compute contact manifolds directly, however you will likely use the results produced in the simulation. The `Manifold` structure holds a normal vector and up to two contact points. The normal and points are held in local coordinates. As a convenience for the contact solver, each point stores the normal and tangential (friction) impulses. The data stored in `Manifold` is optimized for internal use. If you need this data, it is usually best to use the `WorldManifold` structure to generate the world coordinates of the contact normal and points. You need to provide a `Manifold` and the shape transforms and radii. ```js let worldManifold = manifold.getWorldManifold(null, transformA, shapeA.m_radius, transformB, shapeB.m_radius) for (let i = 0; i < manifold.pointCount; ++i) { let point = worldManifold.points[i]; // Vec2 // ... } ``` Notice that the world manifold uses the point count from the original manifold. During simulation shapes may move and the manifolds may change. Points may be added or removed. You can detect this using `GetPointStates()`. ```js let state1 = []; // [PointState] let state2 = []; // [PointState] getPointStates(state1, state2, manifold1, manifold2); if (state1[0] == PointState.removeState) { // process event } ``` ### Distance The `Distance` function can be used to compute the distance between two shapes. The distance function needs both shapes to be converted into a `DistanceProxy`. There is also some caching used to warm start the distance function for repeated calls. ### Time of Impact If two shapes are moving fast, they may *tunnel* through each other in a single time step. The `TimeOfImpact` function is used to determine the time when two moving shapes collide. This is called the *time of impact* (TOI). The main purpose of `TimeOfImpact` is for tunnel prevention. In particular, it is designed to prevent moving objects from tunneling outside of static level geometry. This function accounts for rotation and translation of both shapes, however if the rotations are large enough, then the function may miss a collision. However the function will still report a non-overlapped time and will capture all translational collisions. The time of impact function identities an initial separating axis and ensures the shapes do not cross on that axis. This might miss collisions that are clear at the final positions. While this approach may miss some collisions, it is very fast and adequate for tunnel prevention. It is difficult to put a restriction on the rotation magnitude. There may be cases where collisions are missed for small rotations. Normally, these missed rotational collisions should not harm game play. They tend to be glancing collisions. The function requires two shapes (converted to `DistanceProxy`) and two `Sweep` structures. The sweep structure defines the initial and final transforms of the shapes. You can use fixed rotations to perform a *shape cast*. In this case, the time of impact function will not miss any collisions. ## Dynamic Tree The `DynamicTree` class is used by Planck.js to organize large numbers of shapes efficiently. The class does not know about shapes. Instead it operates on axis-aligned bounding boxes (AABBs) with user data pointers. The dynamic tree is a hierarchical AABB tree. Each internal node in the tree has two children. A leaf node is a single user AABB. The tree uses rotations to keep the tree balanced, even in the case of degenerate input. The tree structure allows for efficient ray casts and region queries. For example, you may have hundreds of shapes in your scene. You could perform a ray cast against the scene in a brute force manner by ray casting each shape. This would be inefficient because it does not take advantage of shapes being spread out. Instead, you can maintain a dynamic tree and perform ray casts against the tree. This traverses the ray through the tree skipping large numbers of shapes. A region query uses the tree to find all leaf AABBs that overlap a query AABB. This is faster than a brute force approach because many shapes can be skipped. Normally you will not use the dynamic tree directly. Rather you will go through the `World` class for ray casts and region queries. If you plan to instantiate your own dynamic tree, you can learn how to use it by looking at how Planck.js uses it. ## Broad-phase Collision processing in a physics step can be divided into narrow-phase and broad-phase. In the narrow-phase we compute contact points between pairs of shapes. Imagine we have N shapes. Using brute force, we would need to perform the narrow-phase for N*N/2 pairs. The `BroadPhase` class reduces this load by using a dynamic tree for pair management. This greatly reduces the number of narrow-phase calls. Normally you do not interact with the broad-phase directly. Instead, Planck.js creates and manages a broad-phase internally. Also, BroadPhase is designed with Planck.js's simulation loop in mind, so it is likely not suited for other use cases. --- ### Pages/Contacts ## Contacts Contacts are objects created by Planck.js to manage collision between two fixtures. If the fixture has children, such as a chain shape, then a contact exists for each relevant child. There are different kinds of contacts, derived from Contact, for managing contact between different kinds of fixtures. For example there is a contact class for managing polygon-polygon collision and another contact class for managing circle-circle collision. Here is some terminology associated with contacts. #### Contact Point A contact point is a point where two shapes touch. Planck.js approximates contact with a small number of points. #### Contact Normal A contact normal is a unit vector that points from one shape to another. By convention, the normal points from fixtureA to fixtureB. #### Contact Separation Separation is the opposite of penetration. Separation is negative when shapes overlap. It is possible that future versions of Planck.js will create contact points with positive separation, so you may want to check the sign when contact points are reported. #### Contact Manifold Contact between two convex polygons may generate up to 2 contact points. Both of these points use the same normal, so they are grouped into a contact manifold, which is an approximation of a continuous region of contact. #### Normal Impulse The normal force is the force applied at a contact point to prevent the shapes from penetrating. For convenience, Planck.js works with impulses. The normal impulse is just the normal force multiplied by the time step. #### Tangent Impulse The tangent force is generated at a contact point to simulate friction. For convenience, this is stored as an impulse. #### Contact Ids Planck.js tries to re-use the contact force results from a time step as the initial guess for the next time step. Planck.js uses contact ids to match contact points across time steps. The ids contain geometric features indices that help to distinguish one contact point from another. Contacts are created when two fixture's AABBs overlap. Sometimes collision filtering will prevent the creation of contacts. Contacts are destroyed with the AABBs cease to overlap. So you might gather that there may be contacts created for fixtures that are not touching (just their AABBs). Well, this is correct. It's a "chicken or egg" problem. We don't know if we need a contact object until one is created to analyze the collision. We could delete the contact right away if the shapes are not touching, or we can just wait until the AABBs stop overlapping. Planck.js takes the latter approach because it lets the system cache information to improve performance. ## Contact Class As mentioned before, the contact class is created and destroyed by Planck.js. Contact objects are not created by the user. However, you are able to access the contact class and interact with it. You can access the raw contact manifold: ```js let manifold = contact.getManifold(); ``` You can potentially modify the manifold, but this is generally not supported and is for advanced usage. There is a helper function to get the `WorldManifold`: ```js contact.getWorldManifold(worldManifold); ``` This uses the current positions of the bodies to compute world positions of the contact points. Sensors do not create manifolds, so for them use: ```js let touching = sensorContact.isTouching(); ``` This function also works for non-sensors. You can get the fixtures from a contact. From those you can get the bodies. ```js let fixtureA = myContact.getFixtureA(); let bodyA = fixtureA.getBody(); let actorA = bodyA.getUserData(); ``` You can disable a contact. This only works inside the `pre-solve` event, discussed below. ## Accessing Contacts You can get access to contacts in several ways. You can access the contacts directly on the world and body structures. You can also implement a contact listener. You can iterate over all contacts in the world: ```js for (let c = myWorld.getContactList(); c; c = c.getNext()) { // process c } ``` You can also iterate over all the contacts on a body. These are stored in a graph using a contact edge structure. ```js for (let ce = myBody.getContactList(); ce; ce = ce.next) { let c = ce.contact; // process c } ``` You can also access contacts using the contact listener that is described below. > **Caution**: > Accessing contacts off World and Body may miss some transient > contacts that occur in the middle of the time step. Use > ContactListener to get the most accurate results. ## Contact Events You can receive contact data by adding event listeners to world. The World supports several events: begin-contact, end-contact, pre-solve, and post-solve. ```js world.on('begin-contact', function(contact) { /* handle begin event */ }); world.on('end-contact', function(contact) { /* handle end event */ }); world.on('pre-solve', function(contact, oldManifold) { /* handle pre-solve event */ }); world.on('post-solve', function(contact, contactImpulse) { /* handle post-solve event */ }); ``` > **Caution**: > Do not keep a reference to the pointers sent to ContactListener. > Instead make a deep copy of the contact point data into your own buffer. > The example below shows one way of doing this. At run-time you can create an instance of the listener and register it with world.on(). You can remove listener using world.off() function. ### Begin Contact Event This is called when two fixtures begin to overlap. This is called for sensors and non-sensors. This event can only occur inside the time step. ### End Contact Event This is called when two fixtures cease to overlap. This is called for sensors and non-sensors. This may be called when a body is destroyed, so this event can occur outside the time step. ### Pre-Solve Event This is called after collision detection, but before collision resolution. This gives you a chance to disable the contact based on the current configuration. For example, you can implement a one-sided platform using this callback and calling Contact.setEnabled(false). The contact will be re-enabled each time through collision processing, so you will need to disable the contact every time-step. The pre-solve event may be fired multiple times per time-step per contact due to continuous collision detection. ```ts world.on('pre-solve', function(contact: Contact, oldManifold: Manifold) { WorldManifold worldManifold; contact.getWorldManifold(&worldManifold); if (worldManifold.normal.y < -0.5) { contact.setEnabled(false); } }); ``` The pre-solve event is also a good place to determine the point state and the approach velocity of collisions. ```js world.on('pre-solve', function(contact, oldManifold) { let worldManifold = contact.getWorldManifold(); let state1 = []; // [PointState] let state2 = []; // [PointState] getPointStates(state1, state2, oldManifold, contact.getManifold()); if (state2[0] === PointState.addState) { let bodyA = contact.getFixtureA().getBody(); let bodyB = contact.getFixtureB().getBody(); let point = worldManifold.points[0]; let vA = bodyA.getLinearVelocityFromWorldPoint(point); let vB = bodyB.getLinearVelocityFromWorldPoint(point); let approachVelocity = Vec2.dot(vB -- vA, worldManifold.normal); //[todo] if (approachVelocity > 1) { myPlayCollisionSound(); } } }); ``` ### Post-Solve Event The post solve event is where you can gather collision impulse results. If you don't care about the impulses, you should probably just implement the pre-solve event. It is tempting to implement game logic that alters the physics world inside a contact callback. For example, you may have a collision that applies damage and try to destroy the associated actor and its rigid body. However, Planck.js does not allow you to alter the physics world inside a callback because you might destroy objects that Planck.js is currently processing, leading to orphaned pointers. The recommended practice for processing contact points is to buffer all contact data that you care about and process it after the time step. You should always process the contact points immediately after the time step; otherwise some other client code might alter the physics world, invalidating the contact buffer. When you process the contact buffer you can alter the physics world, but you still need to be careful that you don't orphan pointers stored in the contact point buffer. The testbed has example contact point processing that is safe from orphaned pointers. This code from the CollisionProcessing test shows how to handle orphaned bodies when processing the contact buffer. Here is an excerpt. Be sure to read the comments in the listing. This code assumes that all contact points have been buffered in the ContactPoint array m_points. ```js // We are going to destroy some bodies according to contact // points. We must buffer the bodies that should be destroyed // because they may belong to multiple contact points. let nuke = []; // Traverse the contact results. Destroy bodies that // are touching heavier bodies. for (let i = 0; i < points.length && nuke.length < MAX_NUKE; ++i) { let point = points[i]; let body1 = point.fixtureA.getBody(); let body2 = point.fixtureB.getBody(); let mass1 = body1.getMass(); let mass2 = body2.getMass(); if (mass1 > 0.0 && mass2 > 0.0) { if (mass2 > mass1) { nuke.push(body1); } else { nuke.push(body2); } } } for (let i = 0; i < nuke.length; i++) { let b = nuke[i]; world.destroyBody(b); } ``` ## Contact Filtering Often in a game you don't want all objects to collide. For example, you may want to create a door that only certain characters can pass through. This is called contact filtering, because some interactions are filtered out. Planck.js allows you to achieve custom contact filtering by implementing a ContactFilter class. This class requires you to implement a ShouldCollide function that receives two Shape pointers. Your function returns true if the shapes should collide. The default implementation of ShouldCollide uses the filter-data defined in fixtures. ```js Fixture.prototype.shouldCollide = function(that) { if (that.m_filterGroupIndex === this.m_filterGroupIndex && that.m_filterGroupIndex !== 0) { return that.m_filterGroupIndex > 0; } var collideA = (that.m_filterMaskBits & this.m_filterCategoryBits) !== 0; var collideB = (that.m_filterCategoryBits & this.m_filterMaskBits) !== 0; var collide = collideA && collideB; return collide; } ``` You can override it with your contact filter. ```js Fixture.prototype.shouldCollide = function(that) { // should this and that collide? } ``` --- ### Pages/Core Concepts ## Core Concepts Planck.js works with several fundamental concepts and objects. We briefly define these objects here and more details are given later in this document. ### World A physics world is a collection of bodies, fixtures, and constraints that interact together. `World` also manages running simulation. ### Shape A shape is a 2D geometrical object, such as a circle or polygon. ### Rigid Body A chunk of matter that is so strong that the distance between any two bits of matter on the chunk is constant. In the following discussion we use body interchangeably with rigid body. ### Fixture A fixture binds a shape to a body and adds physical properties such as density, friction, and restitution. A fixture puts a shape into the collision system (broad-phase) so that it can collide with other shapes. ### Constraint A constraint is a physical connection that removes degrees of freedom from bodies. A 2D body has 3 degrees of freedom (two position coordinates and one rotation coordinate). If we take a body and pin it to the wall (like a pendulum) we have constrained the body to the wall. At this point the body can only rotate about the pin, so the constraint has removed 2 degrees of freedom. ### Contact Constraint A special constraint designed to prevent penetration of rigid bodies and to simulate friction and restitution. You do not create contact constraints; they are created automatically when two objects might collide. ### Joint This is a constraint used to hold two or more bodies together. There are several joint types implemented in the library: revolute, prismatic, distance, and more. Some joints may have limits and motors. A joint limit restricts the range of motion of a joint. For example, the human elbow only allows a certain range of angles. A joint motor drives the motion of the connected bodies according to the joint's degrees of freedom. For example, you can use a motor to drive the rotation of an elbow. ### Solver The physics world has a solver that is used to advance time and to resolve contact and joint constraints. The Planck.js solver is a high-performance iterative solver that operates in order N time, where N is the number of constraints. ### Continuous Collision The solver advances bodies in time using discrete time steps. Without intervention this can lead to tunneling. Planck.js contains specialized algorithms to deal with tunneling. First, the collision algorithms can interpolate the motion of two bodies to find the first time of impact (TOI). Second, there is a sub-stepping solver that moves bodies to their first time of impact and then resolves the collision. --- ### Pages/Credits #### Credits Box2D is a popular C++ 2D rigid-body physics engine created by Erin Catto. Box2D is used in several popular games, such as Angry Birds, Limbo and Crayon Physics, as well as game development tools and libraries such as Apple's SpriteKit. Planck.js is developed and maintained by Ali Shakiba. TypeScript definitions for planck.js are developed by Oliver Zell. #### License Box2D and Planck.js are released under the MIT license. You can use them for all purposes, including commercial applications for no fee. --- ### Pages/Fixture ## Fixture Shapes only have geometrical coordinates, they don't have physical properties and don't know about the body's transformation, so may be used independently of the physics simulation. The `Fixture` class is used to attach shapes to bodies. A body may have zero or more fixtures. A body with multiple fixtures is sometimes called a *compound body.* Fixtures hold the following: - a single shape - broad-phase proxies - density, friction, and restitution - collision filtering flags - back pointer to the parent body - user data - sensor flag These are described in the following sections. ### Fixture Creation Fixtures are created by initializing a fixture definition and then passing the definition to the parent body. ```js let myFixture = myBody.createFixture({ shape: myShape, density: 1, }); ``` This creates the fixture and attaches it to the body. You do not need to store the fixture pointer since the fixture will automatically be destroyed when the parent body is destroyed. You can create multiple fixtures on a single body. You can destroy a fixture on the parent body. You may do this to model a breakable object. Otherwise you can just leave the fixture alone and let the body destruction take care of destroying the attached fixtures. ```js myBody.destroyFixture(myFixture); ``` ### Density The fixture density is used to compute the mass properties of the parent body. The density can be zero or positive. You should generally use similar densities for all your fixtures. This will improve stacking stability. The mass of a body is not adjusted when you set the density. You must call resetMassData for this to occur. ```js fixture.setDensity(5); body.resetMassData(); ``` ### Friction Friction is used to make objects slide along each other realistically. Planck.js supports static and dynamic friction, but uses the same parameter for both. Friction is simulated accurately in Planck.js and the friction strength is proportional to the normal force (this is called Coulomb friction). The friction parameter is usually set between 0 and 1, but can be any non-negative value. A friction value of 0 turns off friction and a value of 1 makes the friction strong. When the friction force is computed between two shapes, Planck.js must combine the friction parameters of the two parent fixtures. This is done with the geometric mean: ```js function mixFriction(friction1, friction2) { return Math.sqrt(friction1 * friction2); } ``` So if one fixture has zero friction then the contact will have zero friction. You can override the default mixed friction using `contact.setFriction`. This is usually done in the contact listener callback. ### Restitution Restitution is used to make objects bounce. The restitution value is usually set to be between 0 and 1. Consider dropping a ball on a table. A value of zero means the ball won't bounce. This is called an inelastic collision. A value of one means the ball's velocity will be exactly reflected. This is called a perfectly elastic collision. Restitution is combined using the following formula. ```js function mixRestitution(restitution1, restitution2) { return Math.max(restitution1, restitution2); } ``` Restitution is combined this way so that you can have a bouncy super ball without having a bouncy floor. You can override the default mixed restitution using `contact.setRestitution`. This is usually done in the contact listener callback. When a shape develops multiple contacts, restitution is simulated approximately. This is because Planck.js uses an iterative solver. Planck.js also uses inelastic collisions when the collision velocity is small. This is done to prevent jitter. See `Settings.velocityThreshold`. ### Filtering Collision filtering allows you to prevent collision between fixtures. For example, say you make a character that rides a bicycle. You want the bicycle to collide with the terrain and the character to collide with the terrain, but you don't want the character to collide with the bicycle (because they must overlap). Planck.js supports such collision filtering using categories and groups. Planck.js supports 64 [todo?] collision categories. For each fixture you can specify which category it belongs to. You also specify what other categories this fixture can collide with. For example, you could specify in a multiplayer game that all players don't collide with each other and monsters don't collide with each other, but players and monsters should collide. This is done with masking bits. For example: ```js let playerFixtureDef = { filterCategoryBits: parseInt('010', 2), filterMaskBits: parseInt('100', 2), }; let monsterFixtureDef = { filterCategoryBits: parseInt('100', 2), filterMaskBits: parseInt('010', 2), }; ``` Here is the rule for a collision to occur: ```js let catA = fixtureA.filterCategoryBits; let maskA = fixtureA.filterMaskBits; let catB = fixtureB.filterCategoryBits; let maskB = fixtureB.filterMaskBits; if ((catA & maskB) !== 0 && (catB & maskA) !== 0) { // fixtures can collide } ``` Collision groups let you specify an integral group index. You can have all fixtures with the same group index always collide (positive index) or never collide (negative index). Group indices are usually used for things that are somehow related, like the parts of a bicycle. In the following example, `fixture1` and `fixture2` always collide, but `fixture3` and `fixture4` never collide. ```js fixture1Def.filterGroupIndex = 2; fixture2Def.filterGroupIndex = 2; fixture3Def.filterGroupIndex = -8; fixture4Def.filterGroupIndex = -8; ``` Collisions between fixtures of different group indices are filtered according to the category and mask bits. In other words, group filtering has higher precedence than category filtering. Note that additional collision filtering occurs in Planck.js. Here is a list: - A fixture on a static body can only collide with a dynamic body. - A fixture on a kinematic body can only collide with a dynamic body. - Fixtures on the same body never collide with each other. - You can optionally enable/disable collision between fixtures on bodies connected by a joint. Sometimes you might need to change collision filtering after a fixture has already been created. You can get and set the Filter structure on an existing fixture using fixture.getFilterData and fixture.setFilterData. Note that changing the filter data will not add or remove contacts until the next time step (see the World class). ### Sensors Sometimes game logic needs to know when two fixtures overlap yet there should be no collision response. This is done by using sensors. A sensor is a fixture that detects collision but does not produce a response. You can flag any fixture as being a sensor. Sensors may be static, kinematic, or dynamic. Remember that you may have multiple fixtures per body and you can have any mix of sensors and solid fixtures. Also, sensors only form contacts when at least one body is dynamic, so you will not get a contact for kinematic versus kinematic, kinematic versus static, or static versus static. Sensors do not generate contact points. There are two ways to get the state of a sensor: 1. `contact.isTouching()` 2. `begin-contact` and `end-contact` events --- ### Pages/Hello World ## Hello World In this section we will walk through a simple example to set up the physics world, and create a platform and a small box. ### Creating a World Every Planck.js program begins with the creation of a World object. World is the physics hub that manages objects, their physical interactions, and runs simulation. To create a world we simply create an object from the `World` class, and optionally pass gravity. ```js let world = new World({ gravity: {x: 0, y: -10}, }); ``` Now that we have our physics world set up, let's start adding some stuff to it. ### Creating a platform We will create a platform using the following steps: 1. Use the world object to create the body with position. 1. Create a fixture on the body with a shape. For step 1, we pass body properties to the world object to create the platform body. With the body properties we specify the type and initial position of the platform: ```js let platform = world.createBody({ type: "static", position: {x: 0, y: -10}, angle: Math.PI * 0.1 }); ``` Bodies are "static" by default. Static bodies don't collide with other static bodies and are immovable. For step 2, we need to create a `Shape` and add it to body as `Fixture`. ```js platform.createFixture({ shape: new Edge({x: -50, y: 0}, {x: +50, y: 0}), }); ``` Shapes only have geometrical properties (such as vertices or radius), and do not have physical properties. A fixture is used to add a shape to a body, and adds physical properties (such as density, friction, etc.) to a body. A body can have any number of shapes fixed together. `Shape`'s geometrical coordinates are local to the body. A fixture does not have location and angle. So when a body moves, all fixtures/shapes in the body move with the body. However we don't move a shape around on the body. Planck.js is a rigid body engine and many of the assumptions made in Planck.js are based on the rigid body model. A body with morphing shapes is not a rigid body, and if this is violated many things will break. So moving or modifying a shape that is on a body is not supported. Every fixture must have a parent body, even fixtures that are static. However, you can attach all static fixtures to a single static body. A static body has zero mass by definition, so we don't need to specify density in this case. Later we will see how to use a fixture's properties to customize its physical behavior. ### Creating a dynamic box Creating a dynamic box is similar to the platform. The main difference, besides dimensions, is that for a dynamic body we need to specify mass properties. First we create the body using `createBody`. By default bodies are static, so we should set the body's `type` at construction time to make the body dynamic. ```js let body = world.createBody({ type: "dynamic", position: {x: 0, y: 4} }); ``` > **Caution**: > You must set the body `type` to `dynamic` if you want the body to move in response to forces. Next we create and attach a box shape using a fixture definition. ```js body.createFixture({ shape: new Box(1.0, 1.0), density: 1.0, friction: 0.3, }); ``` Notice that we set density to 1. The default density is zero. Setting fixture's density automatically updates the mass of the body. Also, the friction on the shape is set to 0.3. > **Caution**: > A dynamic body should have at least one fixture with a non-zero density. Otherwise you will get strange behavior. You can add as many fixtures as you like to a body. Each one contributes to the total mass. Box dimensions are specified as the **half-width** and **half-height** (like a circler radius). So in this case the ground box is 2 units wide (x-axis) and 2 units tall (y-axis). ### Units Planck.js by default is tuned for meters, kilograms, and seconds. So you can consider the dimensions to be in meters. Planck.js generally works best when objects are the size of typical real world objects. For example, a barrel is about 1 meter tall. Due to the limitations of floating point arithmetic, using Planck.js to model the movement of glaciers or dust particles is not a good idea. If you use a different units for your objects, you can change the value of `Settings.lengthUnitsPerMeter`. For example if you use pixels and a barrel height is 80 pixels set the `lengthUnitsPerMeter` to 80. --- ### Pages/Index ### Planck.js Planck.js is JavaScript/TypeScript rewrite of the [Box2D](https://box2d.org/) C++ physics engine for cross-platform game development. ### Box2D Box2D is a 2D rigid-body physics simulation library for games. You can use it in your games to make objects move in realistic ways and make the game world more interactive. From the game engine's point of view, a physics engine is just a system for procedural animation. Planck.js documentation is based on the Box2D manual with adjustments and additions for JavaScript. Both projects' names are used interchangeably in the documentation. ### Before You Start Planck.js is a physics engine, so to use the library you need to be familiar with basic physics concepts, such as mass, force, torque, and impulses. If not, you can ask ChatGPT to explain them. Since Planck.js is written in JavaScript, you need to be familiar with JavaScript programming. --- ### Pages/Install ## Install Planck can be installed or downloaded from NPM or a CDN. #### NPM First install the package. ```sh npm install planck ``` Then import the library in your code: ```js import { World } from 'planck'; const world = new World(); ``` You can alternatively import planck namespace to access all classes: ```js import planck from 'planck'; const world = new planck.World(); ``` To use testbed you need to import `planck/with-testbed` instead: ```js import { World, Testbed } from 'planck/with-testbed'; const world = new World(); const testbed = Testbed.mount(); testbed.start(world); ``` #### Script tag Planck.js is available on [jsDelivr](https://www.jsdelivr.com/package/npm/planck), [cdnjs](https://cdnjs.com/libraries/planck), and [unpkg](https://unpkg.com/planck/). ```html ``` To use testbed you need to use `planck-with-testbed.min.js` instead: ```html
``` --- ### Pages/Joint ## Joint Joints are used to constrain bodies to the world or to each other. Typical examples in games include ragdolls, teeters, and pulleys. Joints can be combined in many different ways to create interesting motions. Some joints provide limits so you can control the range of motion. Some joints provide motors which can be used to drive the joint at a prescribed speed until a prescribed force/torque is exceeded. Joint motors can be used in many ways. You can use motors to control position by specifying a joint velocity that is proportional to the difference between the actual and desired position. You can also use motors to simulate joint friction: set the joint velocity to zero and provide a small, but significant maximum motor force/torque. Then the motor will attempt to keep the joint from moving until the load becomes too strong. ### Joint Definition Each joint type has a definition that derives from JointDef. All joints are connected between two different bodies. One body may be static. Joints between static and/or kinematic bodies are allowed, but have no effect and use some processing time. You can specify user data for any joint type and you can provide a flag to prevent the attached bodies from colliding with each other. This is actually the default behavior and you must set `collideConnected` to `true` to allow collision between two connected bodies. Many joint definitions require that you provide some geometric data. Often a joint will be defined by anchor points. These are points fixed in the attached bodies. Planck.js requires these points to be specified in local coordinates. This way the joint can be specified even when the current body transforms violate the joint constraint—a common occurrence when a game is saved and reloaded. Additionally, some joint definitions need to know the default relative angle between the bodies. This is necessary to constrain rotation correctly. Initializing the geometric data can be tedious, so many joints have a constructor that uses the current body transforms to remove much of the work. However, these initialization functions should usually only be used for prototyping. Production code should define the geometry directly. This will make joint behavior more robust. The rest of the joint definition data depends on the joint type. We cover these now. ### Joint Factory Joints are created and destroyed using the world factory methods. > **Caution**: > You must create and destroy bodies and joints using the create > and destroy methods of the World class. Here's an example of the lifetime of a revolute joint: ```js let joint = myWorld.createJoint(new RevoluteJoint({ bodyA: myBodyA, bodyB: myBodyB, anchorPoint: myBodyA.getCenterPosition(), })); // ... do stuff ... myWorld.destroyJoint(joint); joint = null; ``` It is always good to nullify your variables after they are destroyed. This will make the program crash in a controlled manner if you try to reuse the variable. The lifetime of a joint is not simple. Heed this warning well: > **Caution**: > Joints are destroyed when an attached body is destroyed. This precaution is not always necessary. You may organize your game engine so that joints are always destroyed before the attached bodies. In this case you don't need to implement the listener class. See the section on Implicit Destruction for details. ### Using Joints Many simulations create the joints and don't access them again until they are destroyed. However, there is a lot of useful data contained in joints that you can use to create a rich simulation. First of all, you can get the bodies, anchor points, and user data from a joint. ```js joint.getBodyA(); joint.getBodyB(); joint.getAnchorA(); joint.getAnchorB(); joint.getUserData(); ``` All joints have a reaction force and torque. This the reaction force applied to body 2 at the anchor point. You can use reaction forces to break joints or trigger other game events. These functions may do some computations, so don't call them if you don't need the result. ```js joint.getReactionForce(inv_dt); // Vec2 joint.getReactionTorque(inv_dt); // number ``` --- ### Pages/Limitations ## Limitations Planck.js uses several approximations to simulate rigid body physics efficiently. This brings some limitations. Here are the current limitations: 1. Stacking heavy bodies on top of much lighter bodies is not stable. Stability degrades as the mass ratio passes 10:1. 2. Chains of bodies connected by joints may stretch if a lighter body is supporting a heavier body. For example, a wrecking ball connect to a chain of light weight bodies may not be stable. Stability degrades as the mass ratio passes 10:1. 3. There is typically around 0.5cm of slop in shape versus shape collision. 4. Continuous collision does not handle joints. So you may see joint stretching on fast moving objects. 5. Planck.js uses the symplectic Euler integration scheme. It does not reproduce parabolic motion of projectiles and has only first-order accuracy. However it is fast and has good stability. 6. Planck.js uses an iterative solver to provide real-time performance. You will not get precisely rigid collisions or pixel perfect accuracy. Increasing the iterations will improve accuracy. ## Accuracy Box2D/Planck.js uses approximate methods for a few reasons. * Performance * Some differential equations don't have known solutions * Some constraints cannot be determined uniquely What this means is that constraints are not perfectly rigid and sometimes you will see some bounce even when the restitution is zero. Box2D/Planck.js uses Gauss-Seidel to approximately solve constraints. Box2D/Planck.js also uses Semi-implicit Euler to approximately solve the differential equations. Box2D/Planck.js also does not have exact collision. Polygons are covered with a thin skin (around 0.5cm thick) to avoid numerical problems. This can sometimes lead to unexpected contact normals. Also, some shapes may begin to overlap and then be pushed apart by the solver. ## Restitution/Friction mixing accuracy A physically correct restitution value must be measured in experiments. But as soon as you change the geometry from the experiment then the value is wrong. Next, adding simultaneous collision makes the answer worse. ## Determinism For the same input, and same javascript runtime, Box2D/Planck.js will reproduce any simulation. Box2D/Planck.js does not use any random numbers nor base any computation on random events (such as timers, etc). However, people often want more stringent determinism. People often want to know if Box2D/Planck.js can produce identical results on different binaries and on different platforms. The answer is no. The reason for this answer has to do with how floating point math is implemented in many compilers and processors. I recommend reading this article if you are curious: http://www.yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html This naturally leads to the question of fixed-point math. Box2D/Planck.js does not support fixed-point math. In the past Box2D was ported to the NDS in fixed-point and apparently it worked okay. Fixed-point math is slower and more tedious to develop, so fixed-point is used for the development of Box2D. ## Making Games ### Worms Clones Making a worms clone requires arbitrarily destructible terrain. This is beyond the scope of Box2D/Planck.js, so you will have to figure out how to do this on your own. ### Tile Based Environment Using many boxes for your terrain may not work well because box-like characters can get snagged on internal corners. A future update to Box2D/Planck.js should allow for smooth motion over edge chains. In general you should avoid using a rectangular character because collision tolerances will still lead to undesirable snagging. ### Asteroid Type Coordinate Systems Box2D/Planck.js does not have any support for coordinate frame wrapping. You would likely need to customize Box2D/Planck.js for this purpose. You may need to use a different broad-phase for this to work. --- ### Pages/References Resources ### References - [Erin Catto's Publications](https://box2d.org/publications/) - Collision Detection in Interactive 3D Environments, Gino van den Bergen, 2004 - Real-Time Collision Detection, Christer Ericson, 2005 Box2D was created as part of a physics tutorial at the Game Developer Conference. You can get these tutorials from the download section of box2d.org. ### More Resources After learning Planck.js basics and how to run your code, there are plenty of Box2D resources available online to learn advanced topics. Here are few great examples: - [iforce2d](https://www.iforce2d.net/b2dtut/) - A collection of helpful Box2D tutorials - Box2D [tutorials](https://www.emanueleferonato.com/category/box2d/) by Emanuele Feronato If are interested in learning about algorithms used in Box2D/Planck.js following resources are helpful. - [dyn4j Blog Posts](http://www.dyn4j.org/category/gamedev/) by William Bittle --- ### Pages/Rendering ## Rendering Planck.js is a physics engine and it can be used with any graphics library or ui framework for rendering. For development and debugging you can use the [Testbed](./testbed) that is provided with the library. For production you can use an existing integration or create a new one. To run simulation and renderer physics objects you need to do the following: - Advance physics simulation by calling `world.step(timeStep)` in each frame - Iterate over world entities to draw or update them - Optionally listen to world events for objects which are removed from simulation and remove them from rendering ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ### Pixels and Coordinate Systems Planck.js uses MKS (meters, kilograms, and seconds) units and radians for angles, however rendering is done in pixels. So you need to transformation world geometry into screen and vice versa. You should consider using MKS units in your game code and find a scale to convert to pixels when you render. This will simplify your game logic and reduce the chance for errors since the rendering conversion can be isolated to a small amount of code. If you use a conversion factor, you should try tweaking it globally to make sure nothing breaks. You can also try adjusting it to improve stability. ### Existing Integration Projects - [notchris/phaser3-planck](https://github.com/notchris/phaser3-planck) Phaser 3 Planck.js Plugin by Chris McGrane - [Phaser 3 with Planck.js](https://www.emanueleferonato.com/2019/10/12/use-box2d-physics-in-your-phaser-3-projects-with-planck-js-javascript-physics-engine/) by Emanuele Feronato - [P5Play](https://p5play.org/) - A game engine based on P5.js and Planck.js, by Quinton Ashley and Paolo Pedercini - [P5.js integration](https://sites.google.com/site/professorcookga/planck-box2d-physics-for-javascript-p5) by Professor Robert Cook - [Modd.io](https://www.modd.io/) - Online io game platform - [KaPlanck](https://github.com/KeSuave/KaPlanck) - Physics extension for KaPlay - [RealPeha/planck-renderer](https://github.com/RealPeha/planck-renderer) --- ### Pages/Run Your Code ## Run Your Code Planck.js is a physics simulation library, and it doesn't draw anything. [Piqnt](https://piqnt.com/) is an online playground for Planck.js. You can explore [examples](https://piqnt.com/planck.js/), inspect and edit them, or create new ones. [Testbed](./testbed) is a simple tool (included in the project repository) to visualize and interact with physics simulation. Testbed is compatible with Piqnt playground. You can use Planck.js with any game engines or frameworks, or use an existing integrations. See [Simulation](./world/simulation) and [Rendering](./rendering) page for more information. --- ### Pages/Shape ## Shape Shapes describe collision geometry and may be used independently of physics simulation. At a minimum, you should understand how to create shapes that can be later attached to rigid bodies. Planck.js shapes implement the Shape base class. The base class defines functions to: - Test a point for overlap with the shape. - Perform a ray cast against the shape. - Compute the shape's AABB. - Compute the mass properties of the shape. In addition, each shape has a type member and a radius. The radius even applies to polygons, as discussed below. Keep in mind that a shape does not know about bodies and stand apart from the dynamics system. In Planck.js shapes are considered immutable. When a shape is attached to a body using a fixture, the shapes move rigidly with the host body. In summary: - When a shape is **not** attached to a body, you can view its vertices as being expressed in world-space. - When a shape is attached to a body, you can view its vertices as being expressed in local coordinates. ### Geometric Queries You can perform a couple geometric queries on a single shape. #### Shape Point Test You can test a point for overlap with a shape. You provide a transform for the shape and a world point. ```js let transform = Transform.identity(); let point = Vec2(5, 2); let hit = shape.testPoint(transform, point); ``` Edge and chain shapes always return false, even if the chain is a loop. #### Shape Ray Cast You can cast a ray at a shape to get the point of first intersection and normal vector. A child index is included for chain shapes because the ray cast will only check a single edge at a time. > **Caution**: > No hit will register if the ray starts inside a convex shape like a circle or > polygon. This is consistent with Planck.js treating convex shapes as solid. > ```js let transform = Transform.identity(); let input = {}; // RayCastInput input.p1 = Vec2(0, 0); input.p2 = Vec2(1, 0); input.maxFraction = 1; let childIndex = 0; let output = {}; // RayCastOutput let hit = shape.RayCast(output, input, transform, childIndex); if (hit) { let hitPoint = Vec2.add( Vec2.mul(1 - output.fraction, input.p1), Vec2.mul(output.fraction, input.p2) ); } ``` #### Pairwise Functions The Collision module contains functions that take a pair of shapes and compute some results. These include: - Overlap - Contact manifolds - Distance - Time of impact #### Overlap You can test two shapes for overlap using this function: ```js Transform xfA = ..., xfB = ...; bool overlap = TestOverlap(shapeA, indexA, shapeB, indexB, xfA, xfB); ``` Again you must provide child indices for the case of chain shapes. --- ### Pages/Testbed ## Testbed Testbed is a debugging tool that is provided with the library. It is useful to get started with Planck.js, and develop and debug physics code, and run examples. Testbed is not required to use Planck.js physics. You can run simulation and render physics world directly, with any rendering library or framework (see [Simulation](./world/simulation) and [Rendering](./rendering) sections for more details). There are multiple way to use testbed: - Use Piqnt online playground - Install from NPM, or from CDN - Run locally from source ### Piqnt onlin playground [Piqnt](https://piqnt.com/) is an online playground to run testbed code. It is useful to quickly try out physics examples, and share them with others. ### Install from NPM To install testbed from NPM, run `npm install planck`. Then import testbed in your code. ```bash npm install planck ``` ```js import { World, Testbed } from 'planck/with-testbed'; ``` ### Script tag and CDN To use testbed from CDN, add the following script tag to your HTML file. ```html ``` ### Run locally from source Running testbed locally is useful if you want to debug or edit the library or testbed code, or if you want to run testbed examples locally. To run testbed from source, clone the repository and run `npm install` and `npm run dev` in the root directory. This will start a local server and open testbed in your browser. ```bash git clone cd planck.js npm install npm run dev ``` ## Testbed Usage and API To use testbed first create a world, then start simulation. ```js // Create a world const world = new World(); // Start simulation const testbed = Testbed.start(world); ``` If you need to access the testbed instance before starting simulation you can mount the testbed first, and later start simulation. ```js // Mount testbed const testbed = Testbed.mount(); // Create a world const world = new World(); // Start simulation testbed.start(world); ``` #### Viewbox You can adjust testbed viewbox, by setting the viewbox center and dimensions. Viewbox center and dimension are in defined in physical units. Testbed will calculate and set rendering scale and offset to match provided dimensions and center. ```js // Viewbox center testbed.x = 0; testbed.y = 0; // Viewbox size testbed.width = 30; testbed.height = 20; ``` #### Game-loop callback You can add a game loop callback to testbed, it will be called in each frame. ```js testbed.step = function() { // Code to run in each game loop }; ``` #### Display text information Testbed has two methods to display information on screen, `info` and `status`. ```js // Use info() to print some text on screen testbed.info('Use arrow keys to move player'); testbed.step = function() { // Use status() to print key-values // Testbed will retain value of keys until they are changed testbed.status('score', score); testbed.status('time', time); }; ``` --- ### Pages/World ## World The `World` class contains the bodies and joints. It manages all aspects of the simulation and allows for asynchronous queries (like AABB queries and ray-casts). Much of your interactions with Planck.js will be with a World object. Creating a world is fairly simple. You just need to provide a gravity vector and a boolean indicating if bodies can sleep. ```js let myWorld = new World({ gravity: {x: 0, y: -10}, allowSleep: true, }); ``` The world class contains factories for creating and destroying bodies and joints. These factories are discussed later in the sections on bodies and joints. There are some other interactions with World that I will cover now. ### Exploring the World The world is a container for bodies, contacts, and joints. You can grab the body, contact, and joint lists off the world and iterate over them. For example, this code wakes up all the bodies in the world: ```js for (let b = myWorld.getBodyList(); b; b = b.getNext()) { b.setAwake(true); } ``` Unfortunately real programs can be more complicated. For example, the following code is broken: ```js for (let b = myWorld.getBodyList(); b; b = b.getNext()) { let myActor = b.getUserData(); if (myActor.isDead()) { myWorld.destroyBody(b); // ERROR: now GetNext returns garbage. } } ``` Everything goes ok until a body is destroyed. Once a body is destroyed, its next pointer becomes invalid. So the call to `body.getNext()` will return garbage. The solution to this is to copy the next pointer before destroying the body. ```js let node = myWorld.getBodyList(); while (node) { let b = node; node = node.getNext(); let myActor = b.getUserData(); if (myActor.isDead()) { myWorld.destroyBody(b); } } ``` This safely destroys the current body. However, you may want to call a game function that may destroy multiple bodies. In this case you need to be very careful. The solution is application specific, but for convenience I'll show one method of solving the problem. ```js let node = myWorld.getBodyList(); while (node) { let b = node; node = node.getNext(); let myActor = b.getUserData(); if (myActor.IsDead()) { let otherBodiesDestroyed = gameCrazyBodyDestroyer(b); if (otherBodiesDestroyed) { node = myWorld.getBodyList(); } } } ``` Obviously to make this work, `gameCrazyBodyDestroyer()` must be honest about what it has destroyed. --- ### CHANGELOG # planck ## 1.5.0 ### Minor Changes - 6ede4f3: Publish add-body, add-fixture, add-joint events ### Patch Changes - 9415379: Add fixedRotation to body serialize - 7791ea1: Remove temp fields from distance-joint serialization - 86c9079: Remove hasVertex0 and hasVertex3 from serialize Edge - 4fc10a7: In polygon and chain \_deserialize use Vec2Value ## 1.4.3 ### Patch Changes - 0bba3b2: Bug fix addVec2 ## 1.4.2 ### Patch Changes - 9343ffc: Fix testbed drawing ## 1.4.1 ### Patch Changes - d12d0cc: Fix shape name alias export ## 1.4.0 ### Minor Changes - 0dcc98b: Split TestbedInterface and Testbed class ### Patch Changes - e1a2717: Mark serialize and deserialize functions as @hidden instead of @internal - 3446820: Fix AABB.rayCast ## 1.3.0 ### Minor Changes - bb9bb87: Testbed rendering rewrite - 56193e7: Add DataDriver (experimental for demo use-case) ### Patch Changes - ce1c486: No pointer interaction when mouseForce===0 ## 1.2.0 ### Minor Changes - f0127f4: Add world.queueUpdate() to queue and defer updates after current simulation step ### Patch Changes - 97bb79e: Improve world.queueUpdate ## 1.1.6 ### Patch Changes - f31114b: Add static Vec2.normalize - bee0e16: Change clampVec2 arg to Vec2Value ## 1.1.5 ### Patch Changes - fbd0021: Un-hidden style field type --- ### README # Planck.js Planck.js is JavaScript/TypeScript rewrite of Box2D physics engine for cross-platform HTML5 game development. #### Motivations - Taking advantage of Box2D's efforts and achievements - Developing readable and editable code in JavaScript/TypeScript - Providing idiomatic JavaScript/TypeScript API - Optimizing the library for web and mobile platforms #### [Documentation](https://piqnt.com/planck.js/docs/) #### [Examples](https://piqnt.com/planck.js/) #### [Discord](https://discord.com/invite/znjh6J7) #### [Made with Planck.js](https://github.com/piqnt/planck.js/wiki/) #### [Report Issues](https://github.com/piqnt/planck.js/issues) To speed up resolving issues, please provide [testbed](https://piqnt.com/planck.js/docs/testbed) code to reproduce the issue. ---