UNPKG

planck-js

Version:

2D JavaScript/TypeScript physics engine for cross-platform HTML5 game development

415 lines (326 loc) 14.3 kB
## 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: Vec2(0, 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.