Commit 856ee978 authored by Viktor Lidholt's avatar Viktor Lidholt

Merge pull request #1863 from vlidholt/master

Adds API documentation to sprite physics
parents 5b3b3dfb 567b0cf2
......@@ -786,6 +786,13 @@ class Node {
PhysicsBody _physicsBody;
/// The physics body associated with this node. If a physics body is assigned,
/// and the node is a child of a [PhysicsWorld] or a [PhysicsGroup] the
/// node's position and rotation will be controlled by the body.
///
/// myNode.physicsBody = new PhysicsBody(
/// new PhysicsShapeCircle(Point.zero, 20.0)
/// );
PhysicsBody get physicsBody => _physicsBody;
set physicsBody(PhysicsBody physicsBody) {
......
......@@ -5,6 +5,18 @@ enum PhysicsBodyType {
dynamic
}
/// A physics body can be assigned to any node to make it simulated by physics.
/// The body has a shape, and physical properties such as density, friction,
/// and velocity.
///
/// Bodies can be either dynamic or static. Dynamic bodies will move and rotate
/// the nodes that are associated with it. Static bodies can be moved by moving
/// or animating the node associated with them.
///
/// For a body to be simulated it needs to be associated with a [Node], through
/// the node's physicsBody property. The node also need to be a child of either
/// a [PhysicsWorld] or a [PhysicsGroup] (which in turn is a child of a
/// [PhysicsWorld] or a [Physics Group]).
class PhysicsBody {
PhysicsBody(this.shape, {
this.tag: null,
......@@ -52,14 +64,31 @@ class PhysicsBody {
double _scale;
/// An object associated with this body, normally used for detecting
/// collisions.
///
/// myBody.tag = "SpaceShip";
Object tag;
/// The shape of this physics body. The shape cannot be modified once the
/// body is created. If the shape is required to change, create a new body.
///
/// myShape = myBody.shape;
final PhysicsShape shape;
/// The type of the body. This is either [PhysicsBodyType.dynamic] or
/// [PhysicsBodyType.static]. Dynamic bodies are simulated by the physics,
/// static objects affect the physics but are not moved by the physics.
///
/// myBody.type = PhysicsBodyType.static;
PhysicsBodyType type;
double _density;
/// The density of the body, default value is 1.0. The density has no specific
/// unit, instead densities are relative to each other.
///
/// myBody.density = 0.5;
double get density => _density;
set density(double density) {
......@@ -74,6 +103,10 @@ class PhysicsBody {
double _friction;
/// The fricion of the body, the default is 0.0 and the value should be in
/// the range of 0.0 to 1.0.
///
/// myBody.friction = 0.4;
double get friction => _friction;
set friction(double friction) {
......@@ -90,6 +123,10 @@ class PhysicsBody {
double get restitution => _restitution;
/// The restitution of the body, the default is 0.0 and the value should be in
/// the range of 0.0 to 1.0.
///
/// myBody.restitution = 0.5;
set restitution(double restitution) {
_restitution = restitution;
......@@ -102,6 +139,11 @@ class PhysicsBody {
bool _isSensor;
/// True if the body is a sensor. Sensors doesn't collide with other bodies,
/// but will return collision callbacks. Use a sensor body to detect if two
/// bodies are overlapping.
///
/// myBody.isSensor = true;
bool get isSensor => _isSensor;
set isSensor(bool isSensor) {
......@@ -116,12 +158,15 @@ class PhysicsBody {
Offset _linearVelocity;
/// The current linear velocity of the body in points / second.
///
/// myBody.velocity = Offset.zero;
Offset get linearVelocity {
if (_body == null)
return _linearVelocity;
else {
double dx = _body.linearVelocity.x * _physicsNode.b2WorldToNodeConversionFactor;
double dy = _body.linearVelocity.y * _physicsNode.b2WorldToNodeConversionFactor;
double dx = _body.linearVelocity.x * _physicsWorld.b2WorldToNodeConversionFactor;
double dy = _body.linearVelocity.y * _physicsWorld.b2WorldToNodeConversionFactor;
return new Offset(dx, dy);
}
}
......@@ -131,8 +176,8 @@ class PhysicsBody {
if (_body != null) {
Vector2 vec = new Vector2(
linearVelocity.dx / _physicsNode.b2WorldToNodeConversionFactor,
linearVelocity.dy / _physicsNode.b2WorldToNodeConversionFactor
linearVelocity.dx / _physicsWorld.b2WorldToNodeConversionFactor,
linearVelocity.dy / _physicsWorld.b2WorldToNodeConversionFactor
);
_body.linearVelocity = vec;
}
......@@ -140,6 +185,9 @@ class PhysicsBody {
double _angularVelocity;
/// The angular velocity of the body in degrees / second.
///
/// myBody.angularVelocity = 0.0;
double get angularVelocity {
if (_body == null)
return _angularVelocity;
......@@ -156,10 +204,17 @@ class PhysicsBody {
}
// TODO: Should this be editable in box2d.Body ?
/// Linear dampening, in the 0.0 to 1.0 range, default is 0.0.
///
/// double dampening = myBody.linearDampening;
final double linearDampening;
double _angularDampening;
/// Angular dampening, in the 0.0 to 1.0 range, default is 0.0.
///
/// myBody.angularDampening = 0.1;
double get angularDampening => _angularDampening;
set angularDampening(double angularDampening) {
......@@ -171,6 +226,9 @@ class PhysicsBody {
bool _allowSleep;
/// Allows the body to sleep if it hasn't moved.
///
/// myBody.allowSleep = false;
bool get allowSleep => _allowSleep;
set allowSleep(bool allowSleep) {
......@@ -182,6 +240,9 @@ class PhysicsBody {
bool _awake;
/// True if the body is currently awake.
///
/// bool isAwake = myBody.awake;
bool get awake {
if (_body != null)
return _body.isAwake();
......@@ -198,6 +259,9 @@ class PhysicsBody {
bool _fixedRotation;
/// If true, the body cannot be rotated by the physics simulation.
///
/// myBody.fixedRotation = true;
bool get fixedRotation => _fixedRotation;
set fixedRotation(bool fixedRotation) {
......@@ -211,6 +275,11 @@ class PhysicsBody {
bool get bullet => _bullet;
/// If true, the body cannot pass through other objects when moved at high
/// speed. Bullet bodies are slower to simulate, so only use this option
/// if neccessary.
///
/// myBody.bullet = true;
set bullet(bool bullet) {
_bullet = bullet;
......@@ -221,6 +290,10 @@ class PhysicsBody {
bool _active;
/// An active body is used in the physics simulation. Set this to false if
/// you want to temporarily exclude a body from the simulation.
///
/// myBody.active = false;
bool get active {
if (_body != null)
return _body.isActive();
......@@ -239,6 +312,11 @@ class PhysicsBody {
Object _collisionCategory = null;
/// The collision category assigned to this body. The default value is
/// "Default". The body will only collide with bodies that have the either
/// the [collisionMask] set to null or has the category in the mask.
///
/// myBody.collisionCategory = "Air";
Object get collisionCategory {
return _collisionCategory;
}
......@@ -250,6 +328,10 @@ class PhysicsBody {
List<Object> _collisionMask = null;
/// A list of collision categories that this object will collide with. If set
/// to null (the default value) the body will collide with all other bodies.
///
/// myBody.collisionMask = ["Air", "Ground"];
List<Object> get collisionMask => _collisionMask;
set collisionMask(List<Object> collisionMask) {
......@@ -258,10 +340,10 @@ class PhysicsBody {
}
box2d.Filter get _b2Filter {
print("_physicsNode: $_physicsNode groups: ${_physicsNode._collisionGroups}");
print("_physicsNode: $_physicsWorld groups: ${_physicsWorld._collisionGroups}");
box2d.Filter f = new box2d.Filter();
f.categoryBits = _physicsNode._collisionGroups.getBitmaskForKeys([_collisionCategory]);
f.maskBits = _physicsNode._collisionGroups.getBitmaskForKeys(_collisionMask);
f.categoryBits = _physicsWorld._collisionGroups.getBitmaskForKeys([_collisionCategory]);
f.maskBits = _physicsWorld._collisionGroups.getBitmaskForKeys(_collisionMask);
print("Filter: $f category: ${f.categoryBits} mask: ${f.maskBits}");
......@@ -277,7 +359,7 @@ class PhysicsBody {
}
}
PhysicsWorld _physicsNode;
PhysicsWorld _physicsWorld;
Node _node;
box2d.Body _body;
......@@ -286,62 +368,79 @@ class PhysicsBody {
bool _attached = false;
/// Applies a force to the body at the [worldPoint] position in world
/// cordinates.
///
/// myBody.applyForce(new Offset(0.0, 100.0), myNode.position);
void applyForce(Offset force, Point worldPoint) {
assert(_body != null);
Vector2 b2Force = new Vector2(
force.dx / _physicsNode.b2WorldToNodeConversionFactor,
force.dy / _physicsNode.b2WorldToNodeConversionFactor);
force.dx / _physicsWorld.b2WorldToNodeConversionFactor,
force.dy / _physicsWorld.b2WorldToNodeConversionFactor);
Vector2 b2Point = new Vector2(
worldPoint.x / _physicsNode.b2WorldToNodeConversionFactor,
worldPoint.y / _physicsNode.b2WorldToNodeConversionFactor
worldPoint.x / _physicsWorld.b2WorldToNodeConversionFactor,
worldPoint.y / _physicsWorld.b2WorldToNodeConversionFactor
);
_body.applyForce(b2Force, b2Point);
}
/// Applice a force to the body at the its center of gravity.
///
/// myBody.applyForce(new Offset(0.0, 100.0));
void applyForceToCenter(Offset force) {
assert(_body != null);
Vector2 b2Force = new Vector2(
force.dx / _physicsNode.b2WorldToNodeConversionFactor,
force.dy / _physicsNode.b2WorldToNodeConversionFactor);
force.dx / _physicsWorld.b2WorldToNodeConversionFactor,
force.dy / _physicsWorld.b2WorldToNodeConversionFactor);
_body.applyForceToCenter(b2Force);
}
/// Applies a torque to the body.
///
/// myBody.applyTorque(10.0);
void applyTorque(double torque) {
assert(_body != null);
_body.applyTorque(torque / _physicsNode.b2WorldToNodeConversionFactor);
_body.applyTorque(torque / _physicsWorld.b2WorldToNodeConversionFactor);
}
/// Applies a linear impulse to the body at the [worldPoint] position in world
/// cordinates.
///
/// myBody.applyLinearImpulse(new Offset(0.0, 100.0), myNode.position);
void applyLinearImpulse(Offset impulse, Point worldPoint, [bool wake = true]) {
assert(_body != null);
Vector2 b2Impulse = new Vector2(
impulse.dx / _physicsNode.b2WorldToNodeConversionFactor,
impulse.dy / _physicsNode.b2WorldToNodeConversionFactor);
impulse.dx / _physicsWorld.b2WorldToNodeConversionFactor,
impulse.dy / _physicsWorld.b2WorldToNodeConversionFactor);
Vector2 b2Point = new Vector2(
worldPoint.x / _physicsNode.b2WorldToNodeConversionFactor,
worldPoint.y / _physicsNode.b2WorldToNodeConversionFactor
worldPoint.x / _physicsWorld.b2WorldToNodeConversionFactor,
worldPoint.y / _physicsWorld.b2WorldToNodeConversionFactor
);
_body.applyLinearImpulse(b2Impulse, b2Point, wake);
}
/// Applies an angular impulse to the body.
///
/// myBody.applyAngularImpulse(20.0);
void applyAngularImpulse(double impulse) {
assert(_body != null);
_body.applyAngularImpulse(impulse / _physicsNode.b2WorldToNodeConversionFactor);
_body.applyAngularImpulse(impulse / _physicsWorld.b2WorldToNodeConversionFactor);
}
void _attach(PhysicsWorld physicsNode, Node node) {
assert(_attached == false);
_physicsNode = physicsNode;
_physicsWorld = physicsNode;
// Account for physics groups
Point positionWorld = node._positionToPhysics(node.position, node.parent);
......@@ -419,7 +518,7 @@ class PhysicsBody {
void _detach() {
if (_attached) {
_physicsNode._bodiesScheduledForDestruction.add(_body);
_physicsWorld._bodiesScheduledForDestruction.add(_body);
_attached = false;
}
}
......
......@@ -3,7 +3,7 @@ part of flutter_sprites;
class _PhysicsDebugDraw extends box2d.DebugDraw {
_PhysicsDebugDraw(
box2d.ViewportTransform transform,
this.physicsNode
this.physicsWorld
) : super(transform) {
appendFlags(
box2d.DebugDraw.JOINT_BIT |
......@@ -12,7 +12,7 @@ class _PhysicsDebugDraw extends box2d.DebugDraw {
);
}
PhysicsWorld physicsNode;
PhysicsWorld physicsWorld;
PaintingCanvas canvas;
......@@ -93,12 +93,12 @@ class _PhysicsDebugDraw extends box2d.DebugDraw {
Point _toPoint(Vector2 vec) {
return new Point(
vec.x * physicsNode.b2WorldToNodeConversionFactor,
vec.y * physicsNode.b2WorldToNodeConversionFactor
vec.x * physicsWorld.b2WorldToNodeConversionFactor,
vec.y * physicsWorld.b2WorldToNodeConversionFactor
);
}
double _scale(double value) {
return value * physicsNode.b2WorldToNodeConversionFactor;
return value * physicsWorld.b2WorldToNodeConversionFactor;
}
}
part of flutter_sprites;
/// A [Node] that acts as a middle layer between a [PhysicsWorld] and a node
/// with an assigned [PhysicsBody]. The group's transformations are limited to
/// [position], [rotation], and uniform [scale].
///
/// PhysicsGroup group = new PhysicsGroup();
/// myWorld.addChild(group);
/// group.addChild(myNode);
class PhysicsGroup extends Node {
set scaleX(double scaleX) {
......
......@@ -2,6 +2,9 @@ part of flutter_sprites;
typedef void PhysicsJointBreakCallback(PhysicsJoint joint);
/// A joint connects two physics bodies and restricts their movements. Some
/// types of joints also support motors that adds forces to the connected
/// bodies.
abstract class PhysicsJoint {
PhysicsJoint(this._bodyA, this._bodyB, this.breakingForce, this.breakCallback) {
bodyA._joints.add(this);
......@@ -10,12 +13,20 @@ abstract class PhysicsJoint {
PhysicsBody _bodyA;
/// The first body connected to the joint.
///
/// PhysicsBody body = myJoint.bodyA;
PhysicsBody get bodyA => _bodyA;
PhysicsBody _bodyB;
/// The second body connected to the joint.
///
/// PhysicsBody body = myJoint.bodyB;
PhysicsBody get bodyB => _bodyB;
/// The maximum force the joint can handle before it breaks. If set to null,
/// the joint will never break.
final double breakingForce;
final PhysicsJointBreakCallback breakCallback;
......@@ -23,33 +34,35 @@ abstract class PhysicsJoint {
bool _active = true;
box2d.Joint _joint;
PhysicsWorld _physicsNode;
PhysicsWorld _physicsWorld;
void _completeCreation() {
if (bodyA._attached && bodyB._attached) {
_attach(bodyA._physicsNode);
_attach(bodyA._physicsWorld);
}
}
void _attach(PhysicsWorld physicsNode) {
if (_joint == null) {
_physicsNode = physicsNode;
_physicsWorld = physicsNode;
_joint = _createB2Joint(physicsNode);
_physicsNode._joints.add(this);
_physicsWorld._joints.add(this);
}
}
void _detach() {
if (_joint != null && _active) {
_physicsNode.b2World.destroyJoint(_joint);
_physicsWorld.b2World.destroyJoint(_joint);
_joint = null;
_physicsNode._joints.remove(this);
_physicsWorld._joints.remove(this);
}
_active = false;
}
box2d.Joint _createB2Joint(PhysicsWorld physicsNode);
/// If the joint is no longer needed, call the the [destroy] method to detach
/// if from its connected bodies.
void destroy() {
_detach();
}
......@@ -73,6 +86,26 @@ abstract class PhysicsJoint {
}
}
/// The revolute joint can be thought of as a hinge, a pin, or an axle.
/// An anchor point is defined in global space.
///
/// Revolute joints can be given limits so that the bodies can rotate only to a
/// certain point using [lowerAngle], [upperAngle], and [enableLimit].
/// They can also be given a motor using [enableMotore] together with
/// [motorSpeed] and [maxMotorTorque] so that the bodies will try
/// to rotate at a given speed, with a given torque.
///
/// Common uses for revolute joints include:
/// - wheels or rollers
/// - chains or swingbridges (using multiple revolute joints)
/// - rag-doll joints
/// - rotating doors, catapults, levers
///
/// new PhysicsJointRevolute(
/// nodeA.physicsBody,
/// nodeB.physicsBody,
/// nodeB.position
/// );
class PhysicsJointRevolute extends PhysicsJoint {
PhysicsJointRevolute(
PhysicsBody bodyA,
......@@ -94,12 +127,24 @@ class PhysicsJointRevolute extends PhysicsJoint {
}
final Point _worldAnchor;
/// The lower angle of the limits of this joint, only used if [enableLimit]
/// is set to true.
final double lowerAngle;
/// The upper angle of the limits of this joint, only used if [enableLimit]
/// is set to true.
final double upperAngle;
/// If set to true, the rotation will be limited to a value between
/// [lowerAngle] and [upperAngle].
final bool enableLimit;
bool _enableMotor;
/// By setting enableMotor to true, the joint will automatically rotate, e.g.
/// this can be used for creating an engine for a wheel. For this to be
/// useful you also need to set [motorSpeed] and [maxMotorTorque].
bool get enableMotor => _enableMotor;
set enableMotor(bool enableMotor) {
......@@ -112,6 +157,8 @@ class PhysicsJointRevolute extends PhysicsJoint {
double _motorSpeed;
/// Sets the motor speed of this joint, will only work if [enableMotor] is
/// set to true and [maxMotorTorque] is set to a non zero value.
double get motorSpeed => _motorSpeed;
set motorSpeed(double motorSpeed) {
......@@ -126,6 +173,8 @@ class PhysicsJointRevolute extends PhysicsJoint {
double get maxMotorTorque => _maxMotorTorque;
/// Sets the motor torque of this joint, will only work if [enableMotor] is
/// set to true and [motorSpeed] is set to a non zero value.
set maxMotorTorque(double maxMotorTorque) {
_maxMotorTorque = maxMotorTorque;
if (_joint != null) {
......@@ -156,6 +205,25 @@ class PhysicsJointRevolute extends PhysicsJoint {
}
}
/// The prismatic joint is probably more commonly known as a slider joint.
/// The two joined bodies have their rotation held fixed relative to each
/// other, and they can only move along a specified axis.
///
/// Prismatic joints can be given limits so that the bodies can only move
/// along the axis within a specific range. They can also be given a motor so
/// that the bodies will try to move at a given speed, with a given force.
///
/// Common uses for prismatic joints include:
/// - elevators
/// - moving platforms
/// - sliding doors
/// - pistons
///
/// new PhysicsJointPrismatic(
/// nodeA.physicsBody,
/// nodeB.physicsBody,
/// new Offset(0.0, 1.0)
/// );
class PhysicsJointPrismatic extends PhysicsJoint {
PhysicsJointPrismatic(
PhysicsBody bodyA,
......@@ -174,10 +242,14 @@ class PhysicsJointPrismatic extends PhysicsJoint {
_completeCreation();
}
/// Axis that the movement is restricted to (in global space at the time of
/// creation)
final Offset axis;
bool _enableMotor;
/// For the motor to be effective you also need to set [motorSpeed] and
/// [maxMotorForce].
bool get enableMotor => _enableMotor;
set enableMotor(bool enableMotor) {
......@@ -190,25 +262,29 @@ class PhysicsJointPrismatic extends PhysicsJoint {
double _motorSpeed;
/// Sets the motor speed of this joint, will only work if [enableMotor] is
/// set to true and [maxMotorForce] is set to a non zero value.
double get motorSpeed => _motorSpeed;
set motorSpeed(double motorSpeed) {
_motorSpeed = motorSpeed;
if (_joint != null) {
box2d.PrismaticJoint prismaticJoint = _joint;
prismaticJoint.setMotorSpeed(motorSpeed / _physicsNode.b2WorldToNodeConversionFactor);
prismaticJoint.setMotorSpeed(motorSpeed / _physicsWorld.b2WorldToNodeConversionFactor);
}
}
double _maxMotorForce;
/// Sets the motor force of this joint, will only work if [enableMotor] is
/// set to true and [motorSpeed] is set to a non zero value.
double get maxMotorForce => _maxMotorForce;
set maxMotorForce(double maxMotorForce) {
_maxMotorForce = maxMotorForce;
if (_joint != null) {
box2d.PrismaticJoint prismaticJoint = _joint;
prismaticJoint.setMaxMotorForce(maxMotorForce / _physicsNode.b2WorldToNodeConversionFactor);
prismaticJoint.setMaxMotorForce(maxMotorForce / _physicsWorld.b2WorldToNodeConversionFactor);
}
}
......@@ -223,6 +299,9 @@ class PhysicsJointPrismatic extends PhysicsJoint {
}
}
/// The weld joint attempts to constrain all relative motion between two bodies.
///
/// new PhysicsJointWeld(bodyA.physicsJoint, bodyB.physicsJoint)
class PhysicsJointWeld extends PhysicsJoint {
PhysicsJointWeld(
PhysicsBody bodyA,
......@@ -252,6 +331,22 @@ class PhysicsJointWeld extends PhysicsJoint {
}
}
/// 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.
///
/// new PhysicsJointPulley(
/// nodeA.physicsBody,
/// nodeB.physicsBody,
/// new Point(0.0, 100.0),
/// new Point(100.0, 100.0),
/// nodeA.position,
/// nodeB.position,
/// 1.0
/// );
class PhysicsJointPulley extends PhysicsJoint {
PhysicsJointPulley(
PhysicsBody bodyA,
......@@ -289,18 +384,27 @@ class PhysicsJointPulley extends PhysicsJoint {
}
}
/// 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.
///
/// new PhysicsJointGear(nodeA.physicsBody, nodeB.physicsBody);
class PhysicsJointGear extends PhysicsJoint {
PhysicsJointGear(
PhysicsBody bodyA,
PhysicsBody bodyB, {
double breakingForce,
PhysicsJointBreakCallback breakCallback,
this.ratio: 0.0
this.ratio: 1.0
}
) : super(bodyA, bodyB, breakingForce, breakCallback) {
_completeCreation();
}
/// The ratio of the rotation for bodyA relative bodyB.
final double ratio;
box2d.Joint _createB2Joint(PhysicsWorld physicsNode) {
......@@ -313,6 +417,8 @@ class PhysicsJointGear extends PhysicsJoint {
}
}
/// Keeps a fixed distance between two bodies, [anchorA] and [anchorB] are
/// defined in world coordinates.
class PhysicsJointDistance extends PhysicsJoint {
PhysicsJointDistance(
PhysicsBody bodyA,
......@@ -329,10 +435,21 @@ class PhysicsJointDistance extends PhysicsJoint {
_completeCreation();
}
/// The anchor of bodyA in world coordinates at the time of creation.
final Point anchorA;
/// The anchor of bodyB in world coordinates at the time of creation.
final Point anchorB;
/// The desired distance between the joints, if not passed in at creation
/// it will be set automatically to the distance between the anchors at the
/// time of creation.
final double length;
/// Dampening factor.
final double dampening;
/// Dampening frequency.
final double frequency;
box2d.Joint _createB2Joint(PhysicsWorld physicsNode) {
......@@ -352,6 +469,8 @@ class PhysicsJointDistance extends PhysicsJoint {
}
}
/// The wheel joint restricts a point on bodyB to a line on bodyA. The wheel
/// joint also optionally provides a suspension spring.
class PhysicsJointWheel extends PhysicsJoint {
PhysicsJointWheel(
PhysicsBody bodyA,
......@@ -367,9 +486,16 @@ class PhysicsJointWheel extends PhysicsJoint {
_completeCreation();
}
/// The rotational point in global space at the time of creation.
final Point anchor;
/// The axis which to restrict the movement to.
final Offset axis;
/// Dampening factor.
final double dampening;
/// Dampening frequency.
final double frequency;
box2d.Joint _createB2Joint(PhysicsWorld physicsNode) {
......@@ -387,6 +513,8 @@ class PhysicsJointWheel extends PhysicsJoint {
}
}
/// The friction joint is used for top-down friction. The joint provides 2D
/// translational friction and angular friction.
class PhysicsJointFriction extends PhysicsJoint {
PhysicsJointFriction(
PhysicsBody bodyA,
......
part of flutter_sprites;
/// Defines the shape of a [PhysicsBody].
abstract class PhysicsShape {
box2d.Shape _b2Shape;
......@@ -20,6 +21,9 @@ abstract class PhysicsShape {
}
}
/// Defines a circle shape with a given center [point] and [radius].
///
/// var shape = PhysicsShapeCircle(Point.origin, 20.0);
class PhysicsShapeCircle extends PhysicsShape {
PhysicsShapeCircle(this.point, this.radius);
......@@ -35,6 +39,14 @@ class PhysicsShapeCircle extends PhysicsShape {
}
}
/// Defines a polygon shape from a list of [points];
///
/// var points = [
/// new Point(-10.0, 0.0),
/// new Point(0.0, 10.0),
/// new Point(10.0, 0.0)
/// ];
/// var shape = new PhysicsShapePolygon(points);
class PhysicsShapePolygon extends PhysicsShape {
PhysicsShapePolygon(this.points);
......@@ -56,6 +68,9 @@ class PhysicsShapePolygon extends PhysicsShape {
}
}
/// Defines a box shape from a [width] and [height].
///
/// var shape = new PhysicsShapeBox(50.0, 100.0);
class PhysicsShapeBox extends PhysicsShape {
PhysicsShapeBox(
this.width,
......@@ -84,6 +99,15 @@ class PhysicsShapeBox extends PhysicsShape {
}
}
/// Defines a chain shape from a set of [points]. This can be used to create
/// a continuous chain of edges or, if [loop] is set to true, concave polygons.
///
/// var points = [
/// new Point(-10.0, 0.0),
/// new Point(0.0, 10.0),
/// new Point(10.0, 0.0)
/// ];
/// var shape = new PhysicsShapeChain(points);
class PhysicsShapeChain extends PhysicsShape {
PhysicsShapeChain(this.points, [this.loop=false]);
......@@ -109,6 +133,12 @@ class PhysicsShapeChain extends PhysicsShape {
}
}
/// Defines a single edge line shape from [pointA] to [pointB].
///
/// var shape = new PhysicsShapeEdge(
/// new Point(20.0, 20.0),
/// new Point(50.0, 20.0)
/// );
class PhysicsShapeEdge extends PhysicsShape {
PhysicsShapeEdge(this.pointA, this.pointB);
......@@ -131,6 +161,11 @@ class PhysicsShapeEdge extends PhysicsShape {
}
}
/// A group combines several [shapes] into a single shape.
///
/// var s0 = new PhysicsShapeCircle(new Point(-10.0, 0.0), 20.0);
/// var s1 = new PhysicsShapeCircle(new Point(10.0, 0.0), 20.0);
/// var shape = new PhysicsShapeGroup([s0, s1]);
class PhysicsShapeGroup extends PhysicsShape {
PhysicsShapeGroup(this.shapes);
......
......@@ -9,6 +9,14 @@ enum PhysicsContactType {
typedef void PhysicsContactCallback(PhysicsContactType type, PhysicsContact contact);
/// A [Node] that performs a 2D physics simulation on any children with a
/// [PhysicsBody] attached. To simulate grand children, they need to be placed
/// in a [PhysicsGroup].
///
/// The PhysicsWorld uses Box2D.dart to perform the actual simulation, but
/// wraps its behavior in a way that is more integrated with the sprite node
/// tree. If needed, you can still access the Box2D world through the [b2World]
/// property.
class PhysicsWorld extends Node {
PhysicsWorld(Offset gravity) {
b2World = new box2d.World.withGravity(
......@@ -35,6 +43,7 @@ class PhysicsWorld extends Node {
b2World.debugDraw = _debugDraw;
}
/// The Box2D world used to perform the physics simulations.
box2d.World b2World;
_ContactHandler _contactHandler;
......@@ -47,14 +56,19 @@ class PhysicsWorld extends Node {
List<PhysicsBody> _bodiesScheduledForUpdate = <PhysicsBody>[];
/// If set to true, a debug image of all physics shapes and joints will
/// be drawn on top of the [SpriteBox].
bool drawDebug = false;
Matrix4 _debugDrawTransform ;
_PhysicsDebugDraw _debugDraw;
/// The conversion factor that is used to convert points in the physics world
/// node to points in the Box2D physics simulation.
double b2WorldToNodeConversionFactor = 10.0;
/// The gravity vector used in the simulation.
Offset get gravity {
Vector2 g = b2World.getGravity();
return new Offset(g.x, g.y);
......@@ -66,12 +80,14 @@ class PhysicsWorld extends Node {
gravity.dy / b2WorldToNodeConversionFactor));
}
/// If set to true, objects can fall asleep if the haven't moved in a while.
bool get allowSleep => b2World.isAllowSleep();
set allowSleep(bool allowSleep) {
b2World.setAllowSleep(allowSleep);
}
/// True if sub stepping should be used in the simulation.
bool get subStepping => b2World.isSubStepping();
set subStepping(bool subStepping) {
......@@ -227,6 +243,26 @@ class PhysicsWorld extends Node {
}
}
/// Adds a contact callback, the callback will be invoked when bodies collide
/// in the world.
///
/// To match specific sets bodies, use the [tagA] and [tagB]
/// which will be matched to the tag property that is set on the
/// [PhysicsBody]. If [tagA] or [tagB] is set to null, it will match any
/// body.
///
/// By default, callbacks are made at four different times during a
/// collision; preSolve, postSolve, begin, and end. If you are only interested
/// in one of these events you can pass in a [type].
///
/// myWorld.addContactCallback(
/// (PhysicsContactType type, PhysicsContact contact) {
/// print("Collision between ship and asteroid");
/// },
/// "Ship",
/// "Asteroid",
/// PhysicsContactType.begin
/// );
void addContactCallback(PhysicsContactCallback callback, Object tagA, Object tagB, [PhysicsContactType type]) {
_contactHandler.addContactCallback(callback, tagA, tagB, type);
}
......@@ -238,12 +274,21 @@ class PhysicsWorld extends Node {
super.paint(canvas);
}
/// Draws the debug data of the physics world, normally this method isn't
/// invoked directly. Instead, set the [drawDebug] property to true.
void paintDebug(PaintingCanvas canvas) {
_debugDraw.canvas = canvas;
b2World.drawDebugData();
}
}
/// Contains information about a physics collision and is normally passed back
/// in callbacks from the [PhysicsWorld].
///
/// void myCallback(PhysicsContactType type, PhysicsContact contact) {
/// if (contact.isTouching)
/// print("Bodies are touching");
/// }
class PhysicsContact {
PhysicsContact(
this.nodeA,
......@@ -256,13 +301,29 @@ class PhysicsContact {
this.touchingNormal
);
/// The first node as matched in the rules set when adding the callback.
final Node nodeA;
/// The second node as matched in the rules set when adding the callback.
final Node nodeB;
/// The first shape as matched in the rules set when adding the callback.
final PhysicsShape shapeA;
/// The second shape as matched in the rules set when adding the callback.
final PhysicsShape shapeB;
/// True if the two nodes are touching.
final isTouching;
/// To ignore the collision to take place, you can set isEnabled to false
/// during the preSolve phase.
bool isEnabled;
/// List of points that are touching, in world coordinates.
final List<Point> touchingPoints;
/// The normal from [shapeA] to [shapeB] at the touchingPoint.
final Offset touchingNormal;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment