From b3dc369bd0887769b9bbabdd8ca916becbc68ad0 Mon Sep 17 00:00:00 2001 From: Viktor Lidholt Date: Mon, 28 Sep 2015 15:44:47 -0700 Subject: [PATCH] Sprite physics, first iteration --- examples/game/pubspec.yaml | 3 + examples/game/test_bed.dart | 59 +++++++ examples/game/test_physics.dart | 104 +++++++++++ skysprites/lib/node.dart | 46 +++++ skysprites/lib/physics_body.dart | 138 +++++++++++++++ skysprites/lib/physics_node.dart | 283 ++++++++++++++++++++++++++++++ skysprites/lib/physics_shape.dart | 65 +++++++ skysprites/lib/skysprites.dart | 4 + skysprites/lib/sprite_box.dart | 34 +++- skysprites/pubspec.yaml | 3 + 10 files changed, 732 insertions(+), 7 deletions(-) create mode 100644 examples/game/test_bed.dart create mode 100644 examples/game/test_physics.dart create mode 100644 skysprites/lib/physics_body.dart create mode 100644 skysprites/lib/physics_node.dart create mode 100644 skysprites/lib/physics_shape.dart diff --git a/examples/game/pubspec.yaml b/examples/game/pubspec.yaml index 69c481bc3..07125ee96 100644 --- a/examples/game/pubspec.yaml +++ b/examples/game/pubspec.yaml @@ -3,6 +3,7 @@ dependencies: sky: any sky_tools: any skysprites: any + box2d: any dependency_overrides: material_design_icons: path: ../../sky/packages/material_design_icons @@ -10,3 +11,5 @@ dependency_overrides: path: ../../sky/packages/sky skysprites: path: ../../skysprites + box2d: + path: ../../../../box2d.dart diff --git a/examples/game/test_bed.dart b/examples/game/test_bed.dart new file mode 100644 index 000000000..653a566c0 --- /dev/null +++ b/examples/game/test_bed.dart @@ -0,0 +1,59 @@ +import 'dart:sky'; + +import 'package:sky/material.dart'; +import 'package:sky/rendering.dart'; +import 'package:sky/services.dart'; +import 'package:sky/widgets.dart'; +import 'package:skysprites/skysprites.dart'; + +AssetBundle _initBundle() { + if (rootBundle != null) + return rootBundle; + return new NetworkAssetBundle(Uri.base); +} + +final AssetBundle _bundle = _initBundle(); + +ImageMap _images; +SpriteSheet _spriteSheet; +TestBedApp _app; + +main() async { + _images = new ImageMap(_bundle); + + await _images.load([ + 'assets/sprites.png' + ]); + + String json = await _bundle.loadString('assets/sprites.json'); + _spriteSheet = new SpriteSheet(_images['assets/sprites.png'], json); + + _app = new TestBedApp(); + runApp(_app); +} + +class TestBedApp extends App { + + Widget build() { + ThemeData theme = new ThemeData( + brightness: ThemeBrightness.light, + primarySwatch: Colors.purple + ); + + return new Theme( + data: theme, + child: new Title( + title: 'Test Bed', + child: new SpriteWidget( + new TestBed(), + SpriteBoxTransformMode.letterbox + ) + ) + ); + } +} + +class TestBed extends NodeWithSize { + TestBed() : super(new Size(1024.0, 1024.0)) { + } +} diff --git a/examples/game/test_physics.dart b/examples/game/test_physics.dart new file mode 100644 index 000000000..1ba81aa33 --- /dev/null +++ b/examples/game/test_physics.dart @@ -0,0 +1,104 @@ +import 'dart:sky'; + +import 'package:sky/material.dart'; +import 'package:sky/rendering.dart'; +import 'package:sky/services.dart'; +import 'package:sky/widgets.dart'; +import 'package:skysprites/skysprites.dart'; + +AssetBundle _initBundle() { + if (rootBundle != null) + return rootBundle; + return new NetworkAssetBundle(Uri.base); +} + +final AssetBundle _bundle = _initBundle(); + +ImageMap _images; +SpriteSheet _spriteSheet; +TestBedApp _app; + +main() async { + _images = new ImageMap(_bundle); + + await _images.load([ + 'assets/sprites.png' + ]); + + String json = await _bundle.loadString('assets/sprites.json'); + _spriteSheet = new SpriteSheet(_images['assets/sprites.png'], json); + + _app = new TestBedApp(); + runApp(_app); +} + +class TestBedApp extends App { + + Widget build() { + ThemeData theme = new ThemeData( + brightness: ThemeBrightness.light, + primarySwatch: Colors.purple + ); + + return new Theme( + data: theme, + child: new Title( + title: 'Test Physics', + child: new SpriteWidget( + new TestBed(), + SpriteBoxTransformMode.letterbox + ) + ) + ); + } +} + +class TestBed extends NodeWithSize { + Sprite _ship; + Sprite _obstacle; + + 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); + + _obstacle = new Sprite(_spriteSheet["ship.png"]); + _obstacle.position = new Point(532.0, 800.0); + _obstacle.size = new Size(64.0, 64.0); + _obstacle.physicsBody = new PhysicsBody( + new PhysicsShapeCircle(Point.origin, 32.0), + type: PhysicsBodyType.static, + friction: 0.5, + tag: "obstacle" + ); + physicsNode.addChild(_obstacle); + physicsNode.addContactCallback(myCallback, "obstacle", "ship", PhysicsContactType.begin); + + addChild(physicsNode); + + userInteractionEnabled = true; + } + + void myCallback(PhysicsContactType type, PhysicsContact contact) { + print("CONTACT type: $type"); + } + + bool handleEvent(SpriteBoxEvent event) { + if (event.type == "pointerdown") { + Point pos = convertPointToNodeSpace(event.boxPosition); + _ship.position = pos; + } + return true; + } +} diff --git a/skysprites/lib/node.dart b/skysprites/lib/node.dart index 9d51195a9..0896b9c12 100644 --- a/skysprites/lib/node.dart +++ b/skysprites/lib/node.dart @@ -131,6 +131,19 @@ class Node { void set rotation(double rotation) { assert(rotation != null); + + if (_physicsBody != null && parent is PhysicsNode) { + PhysicsNode physicsNode = parent; + physicsNode._updateRotation(this.physicsBody, rotation); + return; + } + + _rotation = rotation; + invalidateTransformMatrix(); + } + + void _setRotationFromPhysics(double rotation) { + assert(rotation != null); _rotation = rotation; invalidateTransformMatrix(); } @@ -142,6 +155,19 @@ class Node { void set position(Point position) { assert(position != null); + + if (_physicsBody != null && parent is PhysicsNode) { + PhysicsNode physicsNode = parent; + physicsNode._updatePosition(this.physicsBody, position); + return; + } + + _position = position; + invalidateTransformMatrix(); + } + + void _setPositionFromPhysics(Point position) { + assert(position != null); _position = position; invalidateTransformMatrix(); } @@ -609,4 +635,24 @@ class Node { bool handleEvent(SpriteBoxEvent event) { return false; } + + // Physics + + PhysicsBody _physicsBody; + + PhysicsBody get physicsBody => _physicsBody; + + set physicsBody(PhysicsBody physicsBody) { + if (parent != null) { + assert(parent is PhysicsNode); + + if (physicsBody == null) { + physicsBody._detach(); + } else { + physicsBody._attach(parent, this); + } + } + + _physicsBody = physicsBody; + } } diff --git a/skysprites/lib/physics_body.dart b/skysprites/lib/physics_body.dart new file mode 100644 index 000000000..99c8ea426 --- /dev/null +++ b/skysprites/lib/physics_body.dart @@ -0,0 +1,138 @@ +part of skysprites; + +enum PhysicsBodyType { + static, + dynamic +} + +class PhysicsBody { + PhysicsBody(this.shape, { + this.tag: null, + this.type: PhysicsBodyType.dynamic, + this.density: 1.0, + this.friction: 0.0, + this.restitution: 0.0, + this.isSensor: false, + this.linearVelocity: Offset.zero, + this.angularVelocity: 0.0, + this.linearDampening: 0.0, + this.angularDampening: 0.0, + this.allowSleep: true, + this.awake: true, + this.fixedRotation: false, + this.bullet: false, + this.active: true, + this.gravityScale: 1.0 + }); + + Object tag; + + PhysicsShape shape; + + PhysicsBodyType type; + + double density; + double friction; + double restitution; + bool isSensor; + + Offset linearVelocity; + double angularVelocity; + + double linearDampening; + + double angularDampening; + + bool allowSleep; + + bool awake; + + bool fixedRotation; + + bool bullet; + + bool active; + + double gravityScale; + + PhysicsNode _physicsNode; + Node _node; + + box2d.Body _body; + + bool _attached = false; + + void _attach(PhysicsNode physicsNode, Node node) { + assert(_attached == false); + + // Create BodyDef + box2d.BodyDef bodyDef = new box2d.BodyDef(); + bodyDef.linearVelocity = new Vector2(linearVelocity.dx, linearVelocity.dy); + bodyDef.angularVelocity = angularVelocity; + bodyDef.linearDamping = linearDampening; + bodyDef.angularDamping = angularDampening; + bodyDef.allowSleep = allowSleep; + bodyDef.awake = awake; + bodyDef.fixedRotation = fixedRotation; + bodyDef.bullet = bullet; + bodyDef.active = active; + bodyDef.gravityScale = gravityScale; + if (type == PhysicsBodyType.dynamic) + bodyDef.type = box2d.BodyType.DYNAMIC; + else + bodyDef.type = box2d.BodyType.STATIC; + + double conv = physicsNode.b2WorldToNodeConversionFactor; + bodyDef.position = new Vector2(node.position.x / conv, node.position.y / conv); + bodyDef.angle = radians(node.rotation); + + // Create Body + _body = physicsNode.b2World.createBody(bodyDef); + + // Create FixtureDef + box2d.FixtureDef fixtureDef = new box2d.FixtureDef(); + fixtureDef.friction = friction; + fixtureDef.restitution = restitution; + fixtureDef.density = density; + fixtureDef.isSensor = isSensor; + + // Get shapes + List b2Shapes = []; + List physicsShapes = []; + _addB2Shapes(physicsNode, shape, b2Shapes, physicsShapes); + + // Create fixtures + for (int i = 0; i < b2Shapes.length; i++) { + box2d.Shape b2Shape = b2Shapes[i]; + PhysicsShape physicsShape = physicsShapes[i]; + + fixtureDef.shape = b2Shape; + box2d.Fixture fixture = _body.createFixtureFromFixtureDef(fixtureDef); + fixture.userData = physicsShape; + } + _body.userData = this; + + _physicsNode = physicsNode; + _node = node; + + _attached = true; + } + + void _detach() { + if (_attached) { + _physicsNode.b2World.destroyBody(_body); + _attached = false; + } + } + + void _addB2Shapes(PhysicsNode physicsNode, PhysicsShape shape, List b2Shapes, List physicsShapes) { + if (shape is PhysicsShapeGroup) { + for (PhysicsShape child in shape.shapes) { + _addB2Shapes(physicsNode, child, b2Shapes, physicsShapes); + } + } else { + b2Shapes.add(shape.getB2Shape(physicsNode)); + physicsShapes.add(shape); + } + } +} diff --git a/skysprites/lib/physics_node.dart b/skysprites/lib/physics_node.dart new file mode 100644 index 000000000..bc19c6adb --- /dev/null +++ b/skysprites/lib/physics_node.dart @@ -0,0 +1,283 @@ +part of skysprites; + +enum PhysicsContactType { + preSolve, + postSolve, + begin, + end +} + +typedef void PhysicsContactCallback(PhysicsContactType type, PhysicsContact contact); + +class PhysicsNode extends Node { + PhysicsNode(Offset gravity) { + b2World = new box2d.World.withGravity( + new Vector2( + gravity.dx / b2WorldToNodeConversionFactor, + gravity.dy / b2WorldToNodeConversionFactor)); + _init(); + } + + PhysicsNode.fromB2World(this.b2World, this.b2WorldToNodeConversionFactor) { + _init(); + } + + void _init() { + _contactHandler = new _ContactHandler(this); + b2World.setContactListener(_contactHandler); + } + + box2d.World b2World; + + _ContactHandler _contactHandler; + + double b2WorldToNodeConversionFactor = 500.0; + + Offset get gravity { + Vector2 g = b2World.getGravity(); + return new Offset(g.x, g.y); + } + + set gravity(Offset gravity) { + // Convert from points/s^2 to m/s^2 + b2World.setGravity(new Vector2(gravity.dx / b2WorldToNodeConversionFactor, + gravity.dy / b2WorldToNodeConversionFactor)); + } + + bool get allowSleep => b2World.isAllowSleep(); + + set allowSleep(bool allowSleep) { + b2World.setAllowSleep(allowSleep); + } + + bool get subStepping => b2World.isSubStepping(); + + set subStepping(bool subStepping) { + b2World.setSubStepping(subStepping); + } + + void _stepPhysics(double dt) { + // Calculate a step in the simulation + b2World.stepDt(dt, 10, 10); + + // Iterate over the bodies + for (box2d.Body b2Body = b2World.bodyList; b2Body != null; b2Body = b2Body.getNext()) { + // Update visual position and rotation + PhysicsBody body = b2Body.userData; + body._node._setPositionFromPhysics(new Point( + b2Body.position.x * b2WorldToNodeConversionFactor, + b2Body.position.y * b2WorldToNodeConversionFactor + )); + + body._node._setRotationFromPhysics(degrees(b2Body.getAngle())); + } + } + + void _updatePosition(PhysicsBody body, Point position) { + Vector2 newPos = new Vector2( + position.x / b2WorldToNodeConversionFactor, + position.y / b2WorldToNodeConversionFactor + ); + double angle = body._body.getAngle(); + body._body.setTransform(newPos, angle); + body._body.setAwake(true); + } + + void _updateRotation(PhysicsBody body, double rotation) { + Vector2 pos = body._body.position; + double newAngle = radians(rotation); + body._body.setTransform(pos, newAngle); + body._body.setAwake(true); + } + + void addChild(Node node) { + super.addChild(node); + if (node.physicsBody != null) { + node.physicsBody._attach(this, node); + } + } + + void removeChild(Node node) { + super.removeChild(node); + if (node.physicsBody != null) { + node.physicsBody._detach(); + } + } + + void addContactCallback(PhysicsContactCallback callback, Object tagA, Object tagB, [PhysicsContactType type]) { + _contactHandler.addContactCallback(callback, tagA, tagB, type); + } + + void paint(PaintingCanvas canvas) { + super.paint(canvas); + paintDebug(canvas); + } + + void paintDebug(PaintingCanvas canvas) { + Paint shapePaint = new Paint(); + shapePaint.setStyle(sky.PaintingStyle.stroke); + shapePaint.strokeWidth = 1.0; + + for (box2d.Body body = b2World.bodyList; body != null; body = body.getNext()) { + canvas.save(); + + Point point = new Point( + body.position.x * b2WorldToNodeConversionFactor, + body.position.y * b2WorldToNodeConversionFactor); + + canvas.translate(point.x, point.y); + canvas.rotate(body.getAngle()); + + if (body.getType() == box2d.BodyType.DYNAMIC) { + if (body.isAwake()) + shapePaint.color = new Color(0xff00ff00); + else + shapePaint.color = new Color(0xff666666); + } + else if (body.getType() == box2d.BodyType.STATIC) + shapePaint.color = new Color(0xffff0000); + else if (body.getType() == box2d.BodyType.KINEMATIC) + shapePaint.color = new Color(0xffff9900); + + for (box2d.Fixture fixture = body.getFixtureList(); fixture != null; fixture = fixture.getNext()) { + box2d.Shape shape = fixture.getShape(); + + if (shape.shapeType == box2d.ShapeType.CIRCLE) { + box2d.CircleShape circle = shape; + Point cp = new Point( + circle.p.x * b2WorldToNodeConversionFactor, + circle.p.y * b2WorldToNodeConversionFactor + ); + double radius = circle.radius * b2WorldToNodeConversionFactor; + canvas.drawCircle(cp, radius, shapePaint); + } else if (shape.shapeType == box2d.ShapeType.POLYGON) { + box2d.PolygonShape poly = shape; + List points = []; + for (int i = 0; i < poly.getVertexCount(); i++) { + Vector2 vertex = poly.getVertex(i); + Point pt = new Point( + vertex.x * b2WorldToNodeConversionFactor, + vertex.y * b2WorldToNodeConversionFactor + ); + points.add(pt); + } + if (points.length >= 2) { + for (int i = 0; i < points.length; i++) { + canvas.drawLine(points[i], points[(i + 1) % points.length], shapePaint); + } + } + } + } + canvas.restore(); + } + } +} + +class PhysicsContact { + PhysicsContact( + this.nodeA, + this.nodeB, + this.shapeA, + this.shapeB, + this.isTouching, + this.isEnabled + ); + + final Node nodeA; + final Node nodeB; + final PhysicsShape shapeA; + final PhysicsShape shapeB; + final isTouching; + bool isEnabled; +} + +class _ContactCallbackInfo { + _ContactCallbackInfo(this.callback, this.tagA, this.tagB, this.type); + + PhysicsContactCallback callback; + Object tagA; + Object tagB; + PhysicsContactType type; +} + +class _ContactHandler extends box2d.ContactListener { + _ContactHandler(this.physicsNode); + + PhysicsNode physicsNode; + + List<_ContactCallbackInfo> callbackInfos = []; + + void addContactCallback(PhysicsContactCallback callback, Object tagA, Object tagB, PhysicsContactType type) { + callbackInfos.add(new _ContactCallbackInfo(callback, tagA, tagB, type)); + } + + void handleCallback(PhysicsContactType type, box2d.Contact b2Contact, box2d.Manifold oldManifold, box2d.ContactImpulse impulse) { + // Get info about the contact + PhysicsBody bodyA = b2Contact.fixtureA.getBody().userData; + PhysicsBody bodyB = b2Contact.fixtureB.getBody().userData; + box2d.Fixture fixtureA = b2Contact.fixtureA; + box2d.Fixture fixtureB = b2Contact.fixtureB; + + // Match callback with added callbacks + for (_ContactCallbackInfo info in callbackInfos) { + // Check that type is matching + if (info.type != null && info.type != type) + continue; + + // Check if there is a match + bool matchA = (info.tagA == null) || info.tagA == bodyA.tag; + bool matchB = (info.tagB == null) || info.tagB == bodyB.tag; + + bool match = (matchA && matchB); + if (!match) { + // Check if there is a match if we swap a & b + bool matchA = (info.tagA == null) || info.tagA == bodyB.tag; + bool matchB = (info.tagB == null) || info.tagB == bodyA.tag; + + match = (matchA && matchB); + if (match) { + // Swap a & b + PhysicsBody tempBody = bodyA; + bodyA = bodyB; + bodyB = tempBody; + + box2d.Fixture tempFixture = fixtureA; + fixtureA = fixtureB; + fixtureB = tempFixture; + } + } + + if (match) { + // We have contact and a matched callback, setup contact info + PhysicsContact contact = new PhysicsContact( + bodyA._node, + bodyB._node, + fixtureA.userData, + fixtureB.userData, + b2Contact.isTouching(), + b2Contact.isEnabled() + ); + // Make callback + info.callback(type, contact); + + // Update Box2D contact + b2Contact.setEnabled(contact.isEnabled); + } + } + } + + void beginContact(box2d.Contact contact) { + handleCallback(PhysicsContactType.begin, contact, null, null); + } + + void endContact(box2d.Contact contact) { + handleCallback(PhysicsContactType.end, contact, null, null); + } + + void preSolve(box2d.Contact contact, box2d.Manifold oldManifold) { + handleCallback(PhysicsContactType.preSolve, contact, oldManifold, null); + } + void postSolve(box2d.Contact contact, box2d.ContactImpulse impulse) { + handleCallback(PhysicsContactType.postSolve, contact, null, impulse); + } +} diff --git a/skysprites/lib/physics_shape.dart b/skysprites/lib/physics_shape.dart new file mode 100644 index 000000000..657b85d1e --- /dev/null +++ b/skysprites/lib/physics_shape.dart @@ -0,0 +1,65 @@ +part of skysprites; + +abstract class PhysicsShape { + + box2d.Shape _b2Shape; + + Object userObject; + + box2d.Shape getB2Shape(PhysicsNode node) { + if (_b2Shape == null) { + _b2Shape = _createB2Shape(node); + } + return _b2Shape; + } + + box2d.Shape _createB2Shape(PhysicsNode node); +} + +class PhysicsShapeCircle extends PhysicsShape { + PhysicsShapeCircle(this.point, this.radius); + + final Point point; + final double radius; + + box2d.Shape _createB2Shape(PhysicsNode node) { + box2d.CircleShape shape = new box2d.CircleShape(); + shape.p.x = point.x / node.b2WorldToNodeConversionFactor; + shape.p.y = point.y / node.b2WorldToNodeConversionFactor; + shape.radius = radius / node.b2WorldToNodeConversionFactor; + return shape; + } +} + +class PhysicsShapePolygon extends PhysicsShape { + + PhysicsShapePolygon(this.points); + + final List points; + + box2d.Shape _createB2Shape(PhysicsNode node) { + List vectors = []; + for (Point point in points) { + Vector2 vec = new Vector2( + point.x / node.b2WorldToNodeConversionFactor, + point.y / node.b2WorldToNodeConversionFactor + ); + vectors.add(vec); + } + + box2d.PolygonShape shape = new box2d.PolygonShape(); + shape.set(vectors, vectors.length); + return shape; + } +} + +class PhysicsShapeGroup extends PhysicsShape { + + PhysicsShapeGroup(this.shapes); + + final List shapes; + + box2d.Shape _createB2Shape(PhysicsNode node) { + return null; + } +} diff --git a/skysprites/lib/skysprites.dart b/skysprites/lib/skysprites.dart index 08fd7cc2d..ad662c65d 100644 --- a/skysprites/lib/skysprites.dart +++ b/skysprites/lib/skysprites.dart @@ -10,6 +10,7 @@ import 'dart:math' as math; import 'dart:typed_data'; import 'dart:sky' as sky; +import 'package:box2d/box2d.dart' as box2d; import 'package:mojo/core.dart'; import 'package:sky_services/media/media.mojom.dart'; import 'package:sky/animation.dart'; @@ -31,6 +32,9 @@ part 'node.dart'; part 'node3d.dart'; part 'node_with_size.dart'; part 'particle_system.dart'; +part 'physics_body.dart'; +part 'physics_node.dart'; +part 'physics_shape.dart'; part 'sound.dart'; part 'sound_manager.dart'; part 'sprite.dart'; diff --git a/skysprites/lib/sprite_box.dart b/skysprites/lib/sprite_box.dart index aeecfd016..07e6d317d 100644 --- a/skysprites/lib/sprite_box.dart +++ b/skysprites/lib/sprite_box.dart @@ -76,6 +76,8 @@ class SpriteBox extends RenderBox { List _constrainedNodes; + List _physicsNodes; + Rect _visibleArea; Rect get visibleArea { @@ -138,15 +140,17 @@ class SpriteBox extends RenderBox { // Adding and removing nodes - _registerNode(Node node) { + void _registerNode(Node node) { _actionControllers = null; _eventTargets = null; + _physicsNodes = null; if (node == null || node.constraints != null) _constrainedNodes = null; } - _deregisterNode(Node node) { + void _deregisterNode(Node node) { _actionControllers = null; _eventTargets = null; + _physicsNodes = null; if (node == null || node.constraints != null) _constrainedNodes = null; } @@ -359,6 +363,7 @@ class SpriteBox extends RenderBox { _callConstraintsPreUpdate(delta); _runActions(delta); _callUpdate(_rootNode, delta); + _callStepPhysics(delta); _callConstraintsConstrain(delta); // Schedule next update @@ -370,20 +375,26 @@ class SpriteBox extends RenderBox { void _runActions(double dt) { if (_actionControllers == null) { - _actionControllers = []; - _addActionControllers(_rootNode, _actionControllers); + _rebuildActionControllersAndPhysicsNodes(); } for (ActionController actions in _actionControllers) { actions.step(dt); } } - void _addActionControllers(Node node, List controllers) { - if (node._actions != null) controllers.add(node._actions); + void _rebuildActionControllersAndPhysicsNodes() { + _actionControllers = []; + _physicsNodes = []; + _addActionControllersAndPhysicsNodes(_rootNode); + } + + void _addActionControllersAndPhysicsNodes(Node node) { + if (node._actions != null) _actionControllers.add(node._actions); + if (node is PhysicsNode) _physicsNodes.add(node); for (int i = node.children.length - 1; i >= 0; i--) { Node child = node.children[i]; - _addActionControllers(child, controllers); + _addActionControllersAndPhysicsNodes(child); } } @@ -397,6 +408,15 @@ class SpriteBox extends RenderBox { } } + void _callStepPhysics(double dt) { + if (_physicsNodes == null) + _rebuildActionControllersAndPhysicsNodes(); + + for (PhysicsNode physicsNode in _physicsNodes) { + physicsNode._stepPhysics(dt); + } + } + void _callConstraintsPreUpdate(double dt) { if (_constrainedNodes == null) { _constrainedNodes = []; diff --git a/skysprites/pubspec.yaml b/skysprites/pubspec.yaml index a557cc8dd..418a5eb9d 100644 --- a/skysprites/pubspec.yaml +++ b/skysprites/pubspec.yaml @@ -6,6 +6,9 @@ homepage: http://flutter.io dependencies: sky: ">=0.0.36 < 0.1.0" sky_tools: ">=0.0.10 < 0.1.0" + box2d: any dependency_overrides: sky: path: ../sky/packages/sky + box2d: + path: ../../../box2d.dart