diff --git a/doc/box2d.md b/doc/box2d.md index 9c7d000b8..7d109b808 100644 --- a/doc/box2d.md +++ b/doc/box2d.md @@ -1,7 +1,16 @@ -# Box2d +# Box2D Although Flame does not implements Box2d itself, it bundles a forked port of the Java Box2d to Dart by Google. The source of the bundled box2d on Flame can be found [here](https://github.com/feroult/box2d.dart). There is also a simple example game that can be used as reference of how to use box2d on Flame [here](https://github.com/feroult/haunt). + +## BaseGame extension + +If you are going to use Box2D in your project it can be a good idea to use the Box2D specific +extension of the `BaseGame` class. It is called `Box2DGame` and it will control the adding and +removal of Box2D's BodyComponents from your root `Box2DComponent`. + +A simple `Box2DGame` implementation example can be seen in the [examples folder](./examples/box2d). + diff --git a/doc/examples/box2d/.gitignore b/doc/examples/box2d/.gitignore new file mode 100644 index 000000000..07488ba61 --- /dev/null +++ b/doc/examples/box2d/.gitignore @@ -0,0 +1,70 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Visual Studio Code related +.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.packages +.pub-cache/ +.pub/ +/build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/doc/examples/box2d/.metadata b/doc/examples/box2d/.metadata new file mode 100644 index 000000000..6a633e088 --- /dev/null +++ b/doc/examples/box2d/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7fc14a55af64462763d28abfb4e610086c6e0f39 + channel: dev + +project_type: app diff --git a/doc/examples/box2d/README.md b/doc/examples/box2d/README.md new file mode 100644 index 000000000..0661cf7d9 --- /dev/null +++ b/doc/examples/box2d/README.md @@ -0,0 +1,3 @@ +# box2d + +A Flame game showcasing how to use box2d together with flame. diff --git a/doc/examples/box2d/lib/main.dart b/doc/examples/box2d/lib/main.dart new file mode 100644 index 000000000..620a5a131 --- /dev/null +++ b/doc/examples/box2d/lib/main.dart @@ -0,0 +1,96 @@ +import 'dart:math' as math; +import 'package:flame/box2d/box2d_component.dart'; +import 'package:flame/box2d/box2d_game.dart'; +import 'package:flame/flame.dart'; +import 'package:flame/palette.dart'; +import 'package:flutter/material.dart'; +import 'package:box2d_flame/box2d.dart'; + +void main() async { + await Flame.util.fullScreen(); + runApp(GameController().widget); +} + +class MyPlanet extends BodyComponent { + double totalTime = 0; + // Creates a BodyComponent that renders a red circle (with a black moving + // pulsating circle on the inside) that can interact with other body + // components that are added to the same Box2DGame/Box2DComponent. + // After 20 seconds the circle will be removed, to show that we don't get + // any concurrent modification exceptions. + MyPlanet(Box2DComponent box) : super(box) { + Vector2 leftCorner = viewport.getScreenToWorld(Vector2.zero()); + _createBody(50.0, leftCorner); + } + + void _createBody(double radius, Vector2 position) { + final CircleShape shape = CircleShape(); + shape.radius = radius; + + final fixtureDef = FixtureDef(); + // To be able to determine object in collision + fixtureDef.setUserData(this); + fixtureDef.shape = shape; + fixtureDef.restitution = 0.0; + fixtureDef.density = 1.0; + fixtureDef.friction = 0.1; + + final bodyDef = BodyDef(); + bodyDef.position = position; + bodyDef.angularVelocity = 4.0; + bodyDef.type = BodyType.DYNAMIC; + + this.body = world.createBody(bodyDef) + ..createFixtureFromFixtureDef(fixtureDef); + } + + @override + bool destroy() { + // Implement your logic for when the component should be removed + return totalTime > 20; + } + + @override + void renderCircle(Canvas c, Offset p, double radius) { + Paint red = PaletteEntry(Colors.red).paint; + Paint black = PaletteEntry(Colors.black).paint; + Paint blue = PaletteEntry(Colors.blue).paint; + c.drawCircle(p, radius, red); + double angle = body.getAngle(); + c.drawCircle(p, math.sin(angle)*radius, black); + Offset lineRotation = Offset(math.sin(angle)*radius, math.cos(angle)*radius); + c.drawLine(p, p+lineRotation, blue); + } + + @override + void update(double t) { + super.update(t); + totalTime += t; + } +} + +class MyGame extends Box2DGame { + MyGame(Box2DComponent box) : super(box) { + add(MyPlanet(box)); + } +} + +class MyBox2D extends Box2DComponent { + MyBox2D() : super(scale: 4.0, gravity: 0.0); + + @override + void initializeWorld() {} +} + +class GameController { + MyGame _game; + + GameController() { + final MyBox2D box = MyBox2D(); + _game = MyGame(box); + _game.add(MyPlanet(box)); + } + + Widget get widget => _game.widget; +} + diff --git a/doc/examples/box2d/pubspec.yaml b/doc/examples/box2d/pubspec.yaml new file mode 100644 index 000000000..0470efc6e --- /dev/null +++ b/doc/examples/box2d/pubspec.yaml @@ -0,0 +1,17 @@ +name: box2d +description: Flame sample game showcasing the structure for box2d games + +version: 1.0.0+1 + +environment: + sdk: ">=2.1.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flame: + path: ../../../ + +dev_dependencies: + flutter_test: + sdk: flutter diff --git a/lib/box2d/box2d_game.dart b/lib/box2d/box2d_game.dart index b57f1fccb..93b8036b9 100644 --- a/lib/box2d/box2d_game.dart +++ b/lib/box2d/box2d_game.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flame/box2d/box2d_component.dart'; import 'package:flame/components/component.dart'; import 'package:flame/game.dart'; @@ -10,27 +12,36 @@ class Box2DGame extends BaseGame { @override void add(Component c) { - super.add(c); if(c is BodyComponent) { box.add(c); + } else { + super.add(c); } } @override void addLater(Component c) { - super.addLater(c); if(c is BodyComponent) { _addLater.add(c); + } else { + super.addLater(c); } } @override void update(double t) { super.update(t); - components - .where((c) => c is BodyComponent && c.destroy()) - .forEach((body) => box.remove(body)); + box.update(t); + box.components + .where((c) => c.destroy()).toList() + .forEach((c) => box.remove(c)); box.addAll(_addLater); _addLater.clear(); } + + @override + void render(Canvas c) { + super.render(c); + box.render(c); + } }