why camera from my doc is different to the example - flame

I followed a tutorial and code looks like this:
class MyGame extends Forge2DGame{
#override
Future<void> onLoad() async {
camera.toString(); // <= this camera is a Vector2
}
}
But what I learned from another tutorial is like this:
class SpacescapeGame extends FlameGame{
#override
Future<void> onLoad() async {
camera.shake(); // <= this camera is a Camera class
}
}
What I understand is Forge2DGame extends BaseGame, BaseGame extends FlameGame https://pub.dev/documentation/bonfire/latest/base_base_game/BaseGame-class.html
but why I cant use camera.shake as second tutorial showed? thank you for your explanation!

I recommend that you read the Flame documentation instead of the bonfire documentation if you want to write a Flame game that is not involving bonfire.
The camera for Forge2DGame is the same camera as is used for FlameGame, you have most likely added some other variable that is also called camera which is shadowing the real camera.
Also make sure that you are on the latest version of flame (1.4.0) and the latest version of flame_forge2d (0.12.3).

Related

Flutter Flame: proper Viewport to match the screen size

How can I find out the screen size and place my game item according to screen resolution? I want to run my game on the web client.
I want to resize my red game component so fit in the screen and position in center.
LibGdx has some good java class for this concept: Link
You can use the FixedResolutionViewport and set it to the smallest edge if you always want it as a square:
class MyGame extends FlameGame {
Future<void> onLoad() async {
double maxSide = min(size.x, size.y);
camera.viewport = FixedResolutionViewport(Vector2.all(maxSide));
}
}
If you want the game's (viewport) size inside of another component you can add the HasGameRef mixin and use the game's size variable in the same way:
class MyComponent extends Component with HasGameRef {
MyComponent() : super(anchor: Anchor.center);
Future<void> onLoad() async {
final gameSize = gameRef.size;
// To add a position component in the center of the screen for example:
// (when the camera isn't moved)
position = gameSize/2;
}
}
If you want other sizes I recommend to look at game.camera and game.camera.viewport which offers a few other options too.

Why onTap is not working in my flame game (Flutter)?

I am working on a simple game using Flame and Flutter. I am following this tutorial:
Create a Mobile Game with Flutter and Flame – Beginner Tutorial
But after I added some code following tutorial line:
flameUtil.addGestureRecognizer(tapper);
appears underlined and onTap function is not working.
API of flutter-flame library is constantly evolving and the solution from tutorial above might not work anymore.
I had some hard times to re-implement example from this presentation: https://www.youtube.com/watch?v=sFpjEH-ok2s, so you're not alone :-)
Newer versions of flutter-flame like 0.23.0, requires to make it work to add TapDetector mixin to your BaseGame class like below:
class MyBaseGame extends BaseGame with TapDetector, DoubleTapDetector {
#override
void onTapDown(_) {
add(RenderedTimeComponent(Timer(1)..start()));
}
#override
void onDoubleTap() {
add(RenderedTimeComponent(Timer(5)..start()));
}
}
Source: https://github.com/flame-engine/flame/blob/00ad86eb63aa54b411f1d080fec501dd7b671a81/doc/examples/timer/lib/main.dart
Thanks to this your code of BoxGame initialization is just:
BoxGame game = BoxGame();
runApp(game.widget);
without declarations of Util and TapGestureRecognizer.
You can use TapDetector mixin:
class WarriorGirlGame extends FlameGame with TapDetector {
#override
void onTapDown(TapDownInfo info) {
...
}
}

Efficient Canvas rendering in Flutter

I have been trying to develop a drawing app and all the example codes including the one's from The Boring Flutter Development Show don't translate well into real-world usage.
The main problem being that CustomPaint's paint operation is too expensive and re-draws every point, every frame. And as the points increase the flutter app's render time per frame increases significantly
From the time I spent finding a solution to this problem, I found these
RepaintBoundary : Rasterizes layers
Custom Widget using SingleChildRenderObjectWidget and RenderProxyBox : must implement a paint method and no way of passing a controller
I don't think any of the above solutions work well for my needs: smooth canvas drawing operations without re-paint. I even tried simplifying the points and that didn't work either because of the inherent mechanism of CustomPaint
If there was a way to pass a canvas as a widget and attaching a controller it'll be easy to store the captured points and use basic canvas operations like canvas.drawPath() or canvas.drawLine() much efficiently
Any suggestion would be helpful. Thank you!
This is the Controller class
class DrawingController extends ChangeNotifier {
List<Offset> _points = []
.... // Perform operations on data points
....
void add(Offset point) {
_points.add(point);
notifyListeners();
}
}
This is the CustomPaint class
class Painter extends CustomPainter {
DrawingController controller;
Painter(this.controller) : super(repaint: controller); //This is important
#override
void paint(Canvas canvas, Size size) {
//Paint function
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
And in the Gesture detector simply call controller.add(point) to add new points as you grab them
I am not sure this is the most efficient way to paint but it reduced the paint times down to 13ms on a 60hz screen and 9ms on a 120Hz screen. One noticeable drawback is that a single stroke (onTapdownEvent to an onDragEndEvent) renders quite slowly(up to 18ms) when the stroke has many points. This problem disappears as soon as you start a new stroke. I tried asking on several forums and this is the best I could come up with after digging through the flutter source code.
I profiled this functionality on a snapdragon 855 device so the processor is not a bottleneck. If anyone finds a better solution please post it.
A helpful tip is to add a Timer.run(setState()). This way it only updates when it can. Instead of loading update1 update2 update3...etc. It loads update1 then if it can it loads update2 but if update1 took to long it goes to update3. In general it just looks smoother but doesn't necessarily speed it up. Sample:
scaleStart(ScaleStartDetails d) => startPoint = d.focalPoint - _offset;
scaleUpdate(ScaleUpdateDetails d) {
_offset = d.focalPoint - startPoint;
scale = min(max(1, scale * (d.scale + 13) / 14), 128);
Timer.run(() => setState(() {}));
}

How to detect a swipe in a game with flutter/flame

I want to create a game in flutter with flame. For this game I want to detect swipes.
I could implement a tap recognition with the help of a tutorial. But I could not implement it with swipe detection.
my main with Taprecognition looks like this:
My main function is
void main() async{
Util flameUtil = Util();
await flameUtil.fullScreen();
await flameUtil.setOrientation(DeviceOrientation.portraitUp);
GameManager game = GameManager();
runApp(game.widget);
TapGestureRecognizer tapper = TapGestureRecognizer();
tapper.onTapDown = game.onTapDown;
flameUtil.addGestureRecognizer(tapper);
}
In my GameManager class I do have:
class GameMAnager extends Game{
// a few methods like update, render and constructor
void onTapDown(TapDownDetails d) {
if (bgRect.contains(d.globalPosition)) { //bgRect is the background rectangle, so the tap works on the whole screen
player.onTapDown();
}
}
And my player class contains:
void onTapDown(){
rotate();
}
Now I want to change this to rotate in the direction of the swipe instead of onTapDown.
I tried to somehow add
GestureDetector swiper = GestureDetector();
swiper.onPanUpdate = game.onPanUpdate;
to my main and
void onPanUpdate() {
}
to my gameManager class. But I cannot find anything similar to TapDownDetails for panning.
Any suggestions on this?
I saw some help for this to wrap the widget in a GestureDetector and use it like this:
GestureDetector(onPanUpdate: (details) {
if (details.delta.dx > 0) {
// swiping in right direction
}
});
But I couldn't make it work on my project.
You can use the HorizontalDragGestureDetector (or PanGestureRecognizer if you need both axes)
use the following in your main method
HorizontalDragGestureRecognizer tapper = HorizontalDragGestureRecognizer();
tapper.onUpdate = game.dragUpdate;
and then the following in your GameManager
void dragUpdate(DragUpdateDetails d) {
// using d.delta you can then track the movement and implement your rotation updade here
}
That should do the trick :D
If you need both axes, you can use PanGestureRecognizer (as #Marco Papula said).
Use this in your main method:
PanGestureRecognizer panGestureRecognizer = PanGestureRecognizer();
panGestureRecognizer.onEnd = game.onPanUpdate;
flameUtil.addGestureRecognizer(panGestureRecognizer);
and your onPanUpdate method:
void onPanUpdate(DragEndDeatils d) {
if(d.velocity.pixelsPerSecond.dx.abs()>d.velocity.pixelsPerSecond.dy.abs()) {
// X Axis
snake.velocity = d.velocity.pixelsPerSecond.dx<0 ? LeftSwipe : RightSwipe;
} else {
// Y Axis
snake.velocity = d.velocity.pixelsPerSecond.dy<0 ? UpSwipe : DownSwipe;
}
}
If you are using a newer (v1) version of Flame, you no longer need to wrap it in your own GestureDetector (thought you can). Now Flame has built in wrappers for all events, including panning! You can mix your game with:
class MyGame extends BaseGame with PanDetector {
// ...
}
And implement onPanUpdate to get the desired behaviour; or use a myriad of any other detectors (check the documentation for more options and details on how to use it).

Looking for a synchronous alternative to Flutter's .toImage() method

At the moment I'm experimenting with Flutter and the Flame game engine.
To do so I'm extending Flame's BaseGame class and do some heavy processing inside it's constructor.
The heavy processing includes composing an Image out of other images and ultimatively drawing it onto a temporary Canvas and the result is stored in a Picture object.
ui.PictureRecorder rec = new ui.PictureRecorder();
Canvas tempCanvas = new Canvas(rec, bgRect);
// left out picture operations
ui.Picture pic = rec.endRecording();
To finally get an Image object, I need to call the asynchronous .toData() method which returns a Future.
I'm wrapping the call in an async method getImage()
getImage(ui.Picture pic, Rect bgRect) async {
background = await pic.toImage(bgRect.width.toInt(), bgRect.height.toInt());
done = true;
}
(background is a class variable of type Image which is used inside the render() method of the BaseGame class)
Problem is, because it's asynchronous the rest of my statements inside the game's constructor get executed and after it finishes, the render() method fires but the background might not be available yet.
To workaround, I added a class variable done of type bool which gets set to true inside the getImage() method.
Now I modified the render() to wait for done to be true.
void render(Canvas canvas) {
if (done) {
canvas.drawImage(background, new Offset(0.0, 0.0), new Paint());
}
}
Of course this ain't to elegant.
Is there a way to wait for the .toImage() method to finish inside the constructor function of the extended BaseGame class?
I tried making the constructor async like:
class TheGame extends BaseGame {
Image background;
bool done = false;
TheGame(DeviceOrientation orientation) async {
}
}
but this gives me the error:
The modifier 'async' can't be applied to the body of a constructor
What else could I try to make it 'synchronous'?
If you really need the image before the first frame is rendered, you can just create a static method which is responsible for creating TheGame
class TheGame extends BaseGame {
final Image background;
TheGame._(DeviceOrientation orientation, this.background);
static Future<TheGame> create(DeviceOrientation orientation) async {
return TheGame._(orientation, await generateImage());
}
}
but I assume it doesn't really hurt if you render a few frames without a background image, then I would suggest you simply check background != null instead of the done property, which feels a bit redundant.