mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 19:12:31 +08:00
179 lines
4.5 KiB
Dart
179 lines
4.5 KiB
Dart
import 'dart:ui';
|
|
|
|
import 'package:box2d_flame/box2d.dart' hide Timer;
|
|
import 'package:flame/box2d/viewport.dart';
|
|
import 'package:flame/components/component.dart';
|
|
|
|
abstract class Box2DComponent extends Component {
|
|
static const int DEFAULT_WORLD_POOL_SIZE = 100;
|
|
static const int DEFAULT_WORLD_POOL_CONTAINER_SIZE = 10;
|
|
static const double DEFAULT_GRAVITY = -10.0;
|
|
static const int DEFAULT_VELOCITY_ITERATIONS = 10;
|
|
static const int DEFAULT_POSITION_ITERATIONS = 10;
|
|
static const double DEFAULT_SCALE = 1.0;
|
|
|
|
Size dimensions;
|
|
int velocityIterations;
|
|
int positionIterations;
|
|
|
|
World world;
|
|
List<BodyComponent> components = [];
|
|
Viewport viewport;
|
|
|
|
Box2DComponent({
|
|
this.dimensions: null,
|
|
int worldPoolSize: DEFAULT_WORLD_POOL_SIZE,
|
|
int worldPoolContainerSize: DEFAULT_WORLD_POOL_CONTAINER_SIZE,
|
|
double gravity: DEFAULT_GRAVITY,
|
|
this.velocityIterations: DEFAULT_VELOCITY_ITERATIONS,
|
|
this.positionIterations: DEFAULT_POSITION_ITERATIONS,
|
|
double scale: DEFAULT_SCALE,
|
|
}) {
|
|
if (this.dimensions == null) {
|
|
this.dimensions = window.physicalSize;
|
|
}
|
|
final pool = DefaultWorldPool(worldPoolSize, worldPoolContainerSize);
|
|
this.world = World.withPool(Vector2(0.0, gravity), pool);
|
|
this.viewport = Viewport(dimensions, scale);
|
|
}
|
|
|
|
@override
|
|
void update(t) {
|
|
world.stepDt(t, velocityIterations, positionIterations);
|
|
components.forEach((c) {
|
|
c.update(t);
|
|
});
|
|
}
|
|
|
|
@override
|
|
void render(canvas) {
|
|
if (viewport.size == Size(0.0, 0.0)) {
|
|
return;
|
|
}
|
|
components.forEach((c) {
|
|
if (c.body.isActive()) {
|
|
c.render(canvas);
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
void resize(Size size) {
|
|
viewport.resize(size);
|
|
components.forEach((c) {
|
|
c.resize(size);
|
|
});
|
|
}
|
|
|
|
void add(BodyComponent component) {
|
|
components.add(component);
|
|
}
|
|
|
|
void addAll(List<BodyComponent> component) {
|
|
components.addAll(component);
|
|
}
|
|
|
|
void remove(BodyComponent component) {
|
|
components.remove(component);
|
|
world.destroyBody(component.body);
|
|
}
|
|
|
|
void initializeWorld();
|
|
|
|
void cameraFollow(
|
|
BodyComponent component, {
|
|
double horizontal,
|
|
double vertical,
|
|
}) {
|
|
viewport.cameraFollow(
|
|
component,
|
|
horizontal: horizontal,
|
|
vertical: vertical,
|
|
);
|
|
}
|
|
}
|
|
|
|
abstract class BodyComponent extends Component {
|
|
static const MAX_POLYGON_VERTICES = 10;
|
|
|
|
Box2DComponent box;
|
|
|
|
Body body;
|
|
|
|
BodyComponent(this.box);
|
|
|
|
World get world => box.world;
|
|
|
|
Viewport get viewport => box.viewport;
|
|
|
|
@override
|
|
void update(double t) {
|
|
// usually all update will be handled by the world physics
|
|
}
|
|
|
|
@override
|
|
void render(Canvas canvas) {
|
|
body.getFixtureList();
|
|
for (Fixture fixture = body.getFixtureList();
|
|
fixture != null;
|
|
fixture = fixture.getNext()) {
|
|
switch (fixture.getType()) {
|
|
case ShapeType.CHAIN:
|
|
throw Exception('not implemented');
|
|
break;
|
|
case ShapeType.CIRCLE:
|
|
_renderCircle(canvas, fixture);
|
|
break;
|
|
case ShapeType.EDGE:
|
|
throw Exception('not implemented');
|
|
break;
|
|
case ShapeType.POLYGON:
|
|
_renderPolygon(canvas, fixture);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector2 get center => this.body.worldCenter;
|
|
|
|
void _renderCircle(Canvas canvas, Fixture fixture) {
|
|
Vector2 center = Vector2.zero();
|
|
CircleShape circle = fixture.getShape();
|
|
body.getWorldPointToOut(circle.p, center);
|
|
viewport.getWorldToScreen(center, center);
|
|
renderCircle(
|
|
canvas, Offset(center.x, center.y), circle.radius * viewport.scale);
|
|
}
|
|
|
|
void renderCircle(Canvas canvas, Offset center, double radius) {
|
|
final Paint paint = Paint()
|
|
..color = const Color.fromARGB(255, 255, 255, 255);
|
|
canvas.drawCircle(center, radius, paint);
|
|
}
|
|
|
|
void _renderPolygon(Canvas canvas, Fixture fixture) {
|
|
PolygonShape polygon = fixture.getShape();
|
|
assert(polygon.count <= MAX_POLYGON_VERTICES);
|
|
List<Vector2> vertices = Vec2Array().get(polygon.count);
|
|
|
|
for (int i = 0; i < polygon.count; ++i) {
|
|
body.getWorldPointToOut(polygon.vertices[i], vertices[i]);
|
|
viewport.getWorldToScreen(vertices[i], vertices[i]);
|
|
}
|
|
|
|
List<Offset> points = [];
|
|
for (int i = 0; i < polygon.count; i++) {
|
|
points.add(Offset(vertices[i].x, vertices[i].y));
|
|
}
|
|
|
|
renderPolygon(canvas, points);
|
|
}
|
|
|
|
void renderPolygon(Canvas canvas, List<Offset> points) {
|
|
final path = Path()..addPolygon(points, true);
|
|
final Paint paint = Paint()
|
|
..color = const Color.fromARGB(255, 255, 255, 255);
|
|
canvas.drawPath(path, paint);
|
|
}
|
|
}
|