Commit ecd20b6f authored by Viktor Lidholt's avatar Viktor Lidholt

Merge pull request #1541 from vlidholt/master

Adds support for keeping track of contact points in physics
parents 8945e011 333c8f5f
......@@ -45,24 +45,11 @@ main() async {
}
class TestBed extends NodeWithSize {
Sprite _ship;
Sprite _obstacle;
PhysicsNode _physicsNode;
TestBed() : super(new Size(1024.0, 1024.0)) {
PhysicsNode physicsNode = new PhysicsNode(new Offset(0.0, 100.0));
_ship = new Sprite(_spriteSheet["ship.png"]);
_ship.position = new Point(512.0, 512.0);
_ship.size = new Size(64.0, 64.0);
_ship.physicsBody = new PhysicsBody(
new PhysicsShapeGroup([
new PhysicsShapeCircle(Point.origin, 32.0),
new PhysicsShapePolygon([new Point(0.0, 0.0), new Point(50.0, 0.0), new Point(50.0, 50.0), new Point(0.0, 50.0)])
]),
friction: 0.5,
tag: "ship"
);
physicsNode.addChild(_ship);
_physicsNode = new PhysicsNode(new Offset(0.0, 100.0));
_obstacle = new Sprite(_spriteSheet["ship.png"]);
_obstacle.position = new Point(532.0, 800.0);
......@@ -73,23 +60,47 @@ class TestBed extends NodeWithSize {
friction: 0.5,
tag: "obstacle"
);
physicsNode.addChild(_obstacle);
physicsNode.addContactCallback(myCallback, "obstacle", "ship", PhysicsContactType.begin);
_physicsNode.addChild(_obstacle);
_physicsNode.addContactCallback(myCallback, "obstacle", "ship", PhysicsContactType.begin);
addChild(physicsNode);
addChild(_physicsNode);
userInteractionEnabled = true;
}
void myCallback(PhysicsContactType type, PhysicsContact contact) {
print("CONTACT type: $type");
contact.nodeB.removeFromParent();
}
bool handleEvent(SpriteBoxEvent event) {
if (event.type == "pointerdown") {
Point pos = convertPointToNodeSpace(event.boxPosition);
_ship.position = pos;
Sprite shipA;
shipA = new Sprite(_spriteSheet["ship.png"]);
shipA.position = new Point(pos.x - 40.0, pos.y);
shipA.size = new Size(64.0, 64.0);
shipA.physicsBody = new PhysicsBody(new PhysicsShapeCircle(Point.origin, 32.0),
friction: 0.5,
tag: "ship"
);
_physicsNode.addChild(shipA);
shipA.physicsBody.applyLinearImpulse(
new Offset(randomSignedDouble() * 5.0, randomSignedDouble() * 5.0),
shipA.position
);
Sprite shipB;
shipB = new Sprite(_spriteSheet["ship.png"]);
shipB.position = new Point(pos.x + 40.0, pos.y);
shipB.size = new Size(64.0, 64.0);
shipB.physicsBody = new PhysicsBody(new PhysicsShapePolygon([new Point(-25.0, -25.0), new Point(25.0, -25.0), new Point(25.0, 25.0), new Point(-25.0, 25.0)]),
friction: 0.5,
tag: "ship"
);
_physicsNode.addChild(shipB);
new PhysicsJointWeld(shipA.physicsBody, shipB.physicsBody);
}
return true;
}
......
......@@ -230,6 +230,8 @@ class PhysicsBody {
box2d.Body _body;
List<PhysicsJoint> _joints = [];
bool _attached = false;
void applyForce(Offset force, Point worldPoint) {
......@@ -338,6 +340,13 @@ class PhysicsBody {
_node = node;
_attached = true;
// Attach any joints
for (PhysicsJoint joint in _joints) {
if (joint.bodyA._attached && joint.bodyB._attached) {
joint._attach(physicsNode);
}
}
}
void _detach() {
......
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);
}
}
......@@ -83,6 +83,13 @@ class PhysicsNode extends Node {
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();
......@@ -183,7 +190,57 @@ class PhysicsNode extends Node {
}
}
}
canvas.restore();
// Draw contacts
for (box2d.ContactEdge edge = body.getContactList(); edge != null; edge = edge.next) {
box2d.Contact contact = edge.contact;
Vector2 cA = new Vector2.zero();
Vector2 cB = new Vector2.zero();
box2d.Fixture fixtureA = contact.fixtureA;
box2d.Fixture fixtureB = contact.fixtureB;
fixtureA.getAABB(contact.getChildIndexA()).getCenterToOut(cA);
fixtureB.getAABB(contact.getChildIndexB()).getCenterToOut(cB);
Point p1 = new Point(
cA.x * b2WorldToNodeConversionFactor,
cA.y * b2WorldToNodeConversionFactor
);
Point p2 = new Point(
cB.x * b2WorldToNodeConversionFactor,
cB.y * b2WorldToNodeConversionFactor
);
shapePaint.color = new Color(0x33ffffff);
canvas.drawLine(p1, p2, shapePaint);
box2d.WorldManifold worldManifold = new box2d.WorldManifold();
contact.getWorldManifold(worldManifold);
shapePaint.color = new Color(0xffffffff);
for (Vector2 pt in worldManifold.points) {
Point pCenter = new Point(
pt.x * b2WorldToNodeConversionFactor,
pt.y * b2WorldToNodeConversionFactor
);
Offset offset = new Offset(
worldManifold.normal.x * 5.0,
worldManifold.normal.y * 5.0
);
Point p2 = pCenter + offset;
Point p1 = new Point(pCenter.x - offset.dx, pCenter.y - offset.dy);
canvas.drawLine(p1, p2, shapePaint);
canvas.drawCircle(pCenter, 5.0, shapePaint);
}
}
}
}
}
......@@ -195,7 +252,9 @@ class PhysicsContact {
this.shapeA,
this.shapeB,
this.isTouching,
this.isEnabled
this.isEnabled,
this.touchingPoints,
this.touchingNormal
);
final Node nodeA;
......@@ -204,6 +263,8 @@ class PhysicsContact {
final PhysicsShape shapeB;
final isTouching;
bool isEnabled;
final List<Point> touchingPoints;
final Offset touchingNormal;
}
class _ContactCallbackInfo {
......@@ -264,19 +325,35 @@ class _ContactHandler extends box2d.ContactListener {
if (match) {
// We have contact and a matched callback, setup contact info
List<Point> touchingPoints = null;
Offset touchingNormal = null;
// Fetch touching points, if any
if (b2Contact.isTouching()) {
box2d.WorldManifold manifold = new box2d.WorldManifold();
b2Contact.getWorldManifold(manifold);
touchingNormal = new Offset(manifold.normal.x, manifold.normal.y);
touchingPoints = [];
for (Vector2 vec in manifold.points) {
touchingPoints.add(new Point(
vec.x * physicsNode.b2WorldToNodeConversionFactor,
vec.y * physicsNode.b2WorldToNodeConversionFactor
));
}
}
// Create the contact
PhysicsContact contact = new PhysicsContact(
bodyA._node,
bodyB._node,
fixtureA.userData,
fixtureB.userData,
b2Contact.isTouching(),
b2Contact.isEnabled()
b2Contact.isEnabled(),
touchingPoints,
touchingNormal
);
if (type == PhysicsContactType.postSolve) {
}
// Make callback
info.callback(type, contact);
......
......@@ -33,6 +33,7 @@ part 'node3d.dart';
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';
......
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