Adds basic support for joints in sprite physics
diff --git a/packages/flutter_sprites/lib/physics_body.dart b/packages/flutter_sprites/lib/physics_body.dart
index 178b1ac..40ee242 100644
--- a/packages/flutter_sprites/lib/physics_body.dart
+++ b/packages/flutter_sprites/lib/physics_body.dart
@@ -230,6 +230,8 @@
 
   box2d.Body _body;
 
+  List<PhysicsJoint> _joints = [];
+
   bool _attached = false;
 
   void applyForce(Offset force, Point worldPoint) {
@@ -338,6 +340,13 @@
     _node = node;
 
     _attached = true;
+
+    // Attach any joints
+    for (PhysicsJoint joint in _joints) {
+      if (joint.bodyA._attached && joint.bodyB._attached) {
+        joint._attach(physicsNode);
+      }
+    }
   }
 
   void _detach() {
diff --git a/packages/flutter_sprites/lib/physics_joint.dart b/packages/flutter_sprites/lib/physics_joint.dart
new file mode 100644
index 0000000..cecdd5c
--- /dev/null
+++ b/packages/flutter_sprites/lib/physics_joint.dart
@@ -0,0 +1,90 @@
+part of skysprites;
+
+abstract class PhysicsJoint {
+  PhysicsJoint(this.bodyA, this.bodyB) {
+    bodyA._joints.add(this);
+    bodyB._joints.add(this);
+
+    if (bodyA._attached && bodyB._attached) {
+      _attach(bodyA._physicsNode);
+    }
+  }
+
+  PhysicsBody bodyA;
+  PhysicsBody bodyB;
+
+  bool _active = true;
+  box2d.Joint _joint;
+
+  PhysicsNode _physicsNode;
+
+  void _attach(PhysicsNode physicsNode) {
+    if (_joint == null) {
+      _physicsNode = physicsNode;
+      _joint = _createB2Joint(physicsNode);
+    }
+  }
+
+  void _detach() {
+    if (_joint != null && _active) {
+      _physicsNode.b2World.destroyJoint(_joint);
+      _joint = null;
+    }
+    _active = false;
+  }
+
+  box2d.Joint _createB2Joint(PhysicsNode physicsNode);
+}
+
+class PhysicsJointRevolute extends PhysicsJoint {
+  PhysicsJointRevolute(
+    PhysicsBody bodyA,
+    PhysicsBody bodyB,
+    this.anchorWorld, {
+      double lowerAngle: 0.0,
+      double upperAngle: 0.0,
+      bool enableLimit: false
+    }) : super(bodyA, bodyB) {
+    this.lowerAngle = lowerAngle;
+    this.upperAngle = upperAngle;
+    this.enableLimit = enableLimit;
+  }
+
+  Point anchorWorld;
+  double lowerAngle;
+  double upperAngle;
+  bool enableLimit;
+
+  box2d.Joint _createB2Joint(PhysicsNode physicsNode) {
+    // Create Joint Definition
+    Vector2 vecAnchor = new Vector2(
+      anchorWorld.x / physicsNode.b2WorldToNodeConversionFactor,
+      anchorWorld.y / physicsNode.b2WorldToNodeConversionFactor
+    );
+
+    box2d.RevoluteJointDef b2Def = new box2d.RevoluteJointDef();
+    b2Def.initialize(bodyA._body, bodyB._body, vecAnchor);
+    b2Def.enableLimit = enableLimit;
+    b2Def.lowerAngle = lowerAngle;
+    b2Def.upperAngle = upperAngle;
+
+    // Create joint
+    return physicsNode.b2World.createJoint(b2Def);
+  }
+}
+
+class PhysicsJointWeld extends PhysicsJoint {
+  PhysicsJointWeld(
+    PhysicsBody bodyA,
+    PhysicsBody bodyB) : super(bodyA, bodyB);
+
+    box2d.Joint _createB2Joint(PhysicsNode physicsNode) {
+      box2d.WeldJointDef b2Def = new box2d.WeldJointDef();
+      Vector2 middle = new Vector2(
+        (bodyA._body.position.x + bodyB._body.position.x) / 2.0,
+        (bodyA._body.position.y + bodyB._body.position.y) / 2.0
+      );
+      b2Def.initialize(bodyA._body, bodyB._body, middle);
+      return physicsNode.b2World.createJoint(b2Def);
+    }
+}
diff --git a/packages/flutter_sprites/lib/physics_node.dart b/packages/flutter_sprites/lib/physics_node.dart
index 4510289..69d7482 100644
--- a/packages/flutter_sprites/lib/physics_node.dart
+++ b/packages/flutter_sprites/lib/physics_node.dart
@@ -83,6 +83,13 @@
 
   void _removeBodiesScheduledForDestruction() {
     for (box2d.Body b2Body in _bodiesScheduledForDestruction) {
+      // Destroy any joints before destroying the body
+      PhysicsBody body = b2Body.userData;
+      for (PhysicsJoint joint in body._joints) {
+        joint._detach();
+      }
+
+      // Destroy the body
       b2World.destroyBody(b2Body);
     }
     _bodiesScheduledForDestruction.clear();
diff --git a/packages/flutter_sprites/lib/skysprites.dart b/packages/flutter_sprites/lib/skysprites.dart
index ad662c6..6ffd26d 100644
--- a/packages/flutter_sprites/lib/skysprites.dart
+++ b/packages/flutter_sprites/lib/skysprites.dart
@@ -33,6 +33,7 @@
 part 'node_with_size.dart';
 part 'particle_system.dart';
 part 'physics_body.dart';
+part 'physics_joint.dart';
 part 'physics_node.dart';
 part 'physics_shape.dart';
 part 'sound.dart';