From 6d38f162dc80cad7934425899c0747ee78cf75b8 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Fri, 1 May 2020 00:43:13 +0200 Subject: [PATCH] Example showcasing how to use the ContactCallback --- .gitignore | 2 + .../box2d/contact_callbacks/.gitignore | 70 ++++++++++ .../box2d/contact_callbacks/.metadata | 10 ++ .../box2d/contact_callbacks/README.md | 4 + .../contact_callbacks/lib/boundaries.dart | 57 ++++++++ .../box2d/contact_callbacks/lib/main.dart | 122 ++++++++++++++++++ .../box2d/contact_callbacks/pubspec.yaml | 17 +++ 7 files changed, 282 insertions(+) create mode 100644 doc/examples/box2d/contact_callbacks/.gitignore create mode 100644 doc/examples/box2d/contact_callbacks/.metadata create mode 100644 doc/examples/box2d/contact_callbacks/README.md create mode 100644 doc/examples/box2d/contact_callbacks/lib/boundaries.dart create mode 100644 doc/examples/box2d/contact_callbacks/lib/main.dart create mode 100644 doc/examples/box2d/contact_callbacks/pubspec.yaml diff --git a/.gitignore b/.gitignore index 50c69cbc7..07e32a4b8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ pubspec.lock doc/api/ .flutter-plugins .vscode/ +**/.flutter-plugins +**/.flutter-plugins-dependencies android/ ios/ diff --git a/doc/examples/box2d/contact_callbacks/.gitignore b/doc/examples/box2d/contact_callbacks/.gitignore new file mode 100644 index 000000000..07488ba61 --- /dev/null +++ b/doc/examples/box2d/contact_callbacks/.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/contact_callbacks/.metadata b/doc/examples/box2d/contact_callbacks/.metadata new file mode 100644 index 000000000..6a633e088 --- /dev/null +++ b/doc/examples/box2d/contact_callbacks/.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/contact_callbacks/README.md b/doc/examples/box2d/contact_callbacks/README.md new file mode 100644 index 000000000..43ee686ad --- /dev/null +++ b/doc/examples/box2d/contact_callbacks/README.md @@ -0,0 +1,4 @@ +# box2d contact callbacks + +A Flame game showcasing how to use box2d_game's contact callbacks. +Balls will bounce around and change colour when they come in contact. diff --git a/doc/examples/box2d/contact_callbacks/lib/boundaries.dart b/doc/examples/box2d/contact_callbacks/lib/boundaries.dart new file mode 100644 index 000000000..ac8703967 --- /dev/null +++ b/doc/examples/box2d/contact_callbacks/lib/boundaries.dart @@ -0,0 +1,57 @@ +import 'dart:ui'; + +import 'package:box2d_flame/box2d.dart'; +import 'package:flame/box2d/box2d_component.dart'; +import 'package:flame/box2d/viewport.dart'; +import 'package:flame/palette.dart'; + +List createBoundaries(Box2DComponent box) { + final Viewport viewport = box.viewport; + final Vector2 screenSize = Vector2(viewport.width, viewport.height) + *viewport.scale; + final Vector2 topLeft = viewport.getScreenToWorld(Vector2.zero()); + final Vector2 bottomRight = viewport.getScreenToWorld(screenSize); + final Vector2 topRight = Vector2(bottomRight.x, topLeft.y); + final Vector2 bottomLeft = Vector2(topLeft.x, bottomRight.y); + + return [ + Wall(topLeft, topRight, box), + Wall(topRight, bottomRight, box), + Wall(bottomRight, bottomLeft, box), + Wall(bottomLeft, topLeft, box), + ]; +} + +class Wall extends BodyComponent { + Paint paint = BasicPalette.white.paint; + final Vector2 start; + final Vector2 end; + + Wall(this.start, this.end, Box2DComponent box) : super(box) { + _createBody(start, end); + } + + @override + void renderPolygon(Canvas canvas, List coordinates) { + Offset start = coordinates[0]; + Offset end = coordinates[1]; + canvas.drawLine(start, end, paint); + } + + void _createBody(Vector2 start, Vector2 end) { + final PolygonShape shape = PolygonShape(); + shape.setAsEdge(start, end); + + final fixtureDef = FixtureDef(); + fixtureDef.setUserData(this); // To be able to determine object in collision + fixtureDef.shape = shape; + fixtureDef.restitution = 0.0; + fixtureDef.friction = 0.1; + + final bodyDef = BodyDef(); + bodyDef.position = Vector2.zero(); + bodyDef.type = BodyType.STATIC; + + body = world.createBody(bodyDef)..createFixtureFromFixtureDef(fixtureDef); + } +} diff --git a/doc/examples/box2d/contact_callbacks/lib/main.dart b/doc/examples/box2d/contact_callbacks/lib/main.dart new file mode 100644 index 000000000..9301f25c8 --- /dev/null +++ b/doc/examples/box2d/contact_callbacks/lib/main.dart @@ -0,0 +1,122 @@ +import 'dart:math' as math; +import 'package:box2d_contact_callbacks/boundaries.dart'; +import 'package:flame/box2d/box2d_component.dart'; +import 'package:flame/box2d/box2d_game.dart'; +import 'package:flame/box2d/contact_listeners.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 { + WidgetsFlutterBinding.ensureInitialized(); + await Flame.util.fullScreen(); + runApp(GameController().widget); +} + +class Ball extends BodyComponent { + Paint originalPaint, currentPaint; + + Ball(Vector2 position, Box2DComponent box) : super(box) { + originalPaint = _randomPaint(); + currentPaint = originalPaint; + Vector2 worldPosition = viewport.getScreenToWorld(position); + _createBody(5.0, worldPosition); + } + + Paint _randomPaint() { + math.Random rng = math.Random(); + return PaletteEntry(Color.fromARGB(rng.nextInt(255), rng.nextInt(255), + rng.nextInt(255), rng.nextInt(255))).paint; + } + + 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 = 1.0; + fixtureDef.density = 1.0; + fixtureDef.friction = 0.1; + + final bodyDef = BodyDef(); + bodyDef.position = position; + bodyDef.type = BodyType.DYNAMIC; + + body = world.createBody(bodyDef)..createFixtureFromFixtureDef(fixtureDef); + } + + @override + bool destroy() { + // Implement your logic for when the component should be removed + return false; + } + + @override + void renderCircle(Canvas c, Offset p, double radius) { + Paint blue = PaletteEntry(Colors.blue).paint; + c.drawCircle(p, radius, currentPaint); + double angle = body.getAngle(); + Offset lineRotation = + Offset(math.sin(angle) * radius, math.cos(angle) * radius); + c.drawLine(p, p + lineRotation, blue); + } +} + +class BallContactCallback implements ContactCallback { + @override + List objects = [Ball, Ball]; + + BallContactCallback(); + + @override + void begin(Object contact1, Object contact2) { + Ball ball1 = contact1 as Ball; + Ball ball2 = contact2 as Ball; + if (ball1.currentPaint != ball1.originalPaint) { + ball1.currentPaint = ball2.currentPaint; + } else { + ball2.currentPaint = ball1.currentPaint; + } + } + + @override + void end(Object ship, Object material) {} +} + +class MyGame extends Box2DGame { + MyGame(Box2DComponent box) : super(box) { + final List boundaries = createBoundaries(box); + boundaries.forEach(add); + addContactCallback(BallContactCallback()); + } + + @override + void onTapDown(TapDownDetails details) { + super.onTapDown(details); + final Vector2 position = + Vector2(details.globalPosition.dx, details.globalPosition.dy); + add(Ball(position, box)); + } +} + +class MyBox2D extends Box2DComponent { + MyBox2D() : super(scale: 4.0, gravity: -10.0); + + @override + void initializeWorld() {} +} + +class GameController { + MyGame _game; + + GameController() { + final MyBox2D box = MyBox2D(); + _game = MyGame(box); + } + + Widget get widget => _game.widget; +} diff --git a/doc/examples/box2d/contact_callbacks/pubspec.yaml b/doc/examples/box2d/contact_callbacks/pubspec.yaml new file mode 100644 index 000000000..4929f45d1 --- /dev/null +++ b/doc/examples/box2d/contact_callbacks/pubspec.yaml @@ -0,0 +1,17 @@ +name: box2d_contact_callbacks +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