mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
Sprite physics, first iteration
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<box2d.Shape> b2Shapes = [];
|
||||
List<PhysicsShape> 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<box2d.Shape> b2Shapes, List<PhysicsShape> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Point> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<Point> points;
|
||||
|
||||
box2d.Shape _createB2Shape(PhysicsNode node) {
|
||||
List<Vector2> 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<PhysicsShape> shapes;
|
||||
|
||||
box2d.Shape _createB2Shape(PhysicsNode node) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -76,6 +76,8 @@ class SpriteBox extends RenderBox {
|
||||
|
||||
List<Node> _constrainedNodes;
|
||||
|
||||
List<PhysicsNode> _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<ActionController> 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 = [];
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user