mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge pull request #1863 from vlidholt/master
Adds API documentation to sprite physics
This commit is contained in:
commit
856ee97892
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
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,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user