mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge pull request #1541 from vlidholt/master
Adds support for keeping track of contact points in physics
This commit is contained in:
commit
98eb54a53c
@ -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() {
|
||||
|
||||
90
skysprites/lib/physics_joint.dart
Normal file
90
skysprites/lib/physics_joint.dart
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user