I want to detect when an object goes outside of the screen in Flame Flutter. I think there is two way to accomplish this either with Collidable mixin or with Forge2D. If possible explain it in both of them.
Flame version: flame: 1.0.0-releasecandidate.18
It is way overkill to use Forge2D for this (that complicates a lot of other things too). But you can use the built-in collision detection system, or you can check in the update-loop whether it is within the screen or not (this would be the most efficient).
By using the collision detection system we can use the built-in ScreenCollidable and you can do something like this:
class ExampleGame extends FlameGame with HasCollidables {
...
#override
Future<void> onLoad() async {
await super.onLoad();
add(ScreenCollidable());
}
}
class YourComponent extends PositionComponent with HasHitboxes, Collidable {
#override
Future<void> onLoad() async {
await super.onLoad();
// Change this if you want the components to collide with each other
// and not only the screen.
collidableType = CollidableType.passive;
addHitbox(HitboxRectangle());
}
// Do note that this doesn't work if the component starts
// to go outside of the screen but then comes back.
#override
void onCollisionEnd(Collidable other) {
if (other is ScreenCollidable) {
removeFromParent();
}
}
}
and by just calculating it in the update-loop:
class YourComponent extends PositionComponent with HasGameRef {
#override
void update(double dt) {
final topLeft = absoluteTopLeftPosition;
final gameSize = gameRef.size;
if(topLeft.x > gameSize.x || topLeft.y > gameSize.y) {
removeFromParent();
return;
}
final bottomRight = absolutePositionOfAnchor(Anchor.bottomRight);
if(bottomRight.x < 0 || bottomRight.y < 0) {
removeFromParent();
return;
}
}
}
I also recommend that you update to Flame 1.0.0 now when it is released. :)
There is no longer a Collidable mixin.
See the Flame documentation
Related
I want to update the gravity according to some events in the game, but I can't find a way to change it after I initialise it in the super of the class Forge2DGame.
You have to use setGravity to change it.
For example:
class MyGame extends Forge2DGame with TapDetector {
#override
void onTap() {
world.setGravity(Vector2(0, 10));
}
}
I'm building a game using the flame game engine. I encountered an issue with the map I built using Tiled - which generates a .tmx file that is called by the code using the following commands:
var demoMap = await TiledComponent.load("demoMap.tmx", Vector2(128, 128));
add(demoMap);
mapHeight = 128.0 * demoMap.tileMap.map.height;
mapWidth = 128.0 * demoMap.tileMap.map.width;
final List<TiledObject> obstacles =
demoMap.tileMap.getLayer<ObjectGroup>('Collisions')!.objects;
for (final TiledObject obstacle in obstacles) {
add(Obstacle(obstacle));
}
these lines will load the Tile layers and object layers. but not the image layer. the background appears black no matter what I do.
Any help will be greatly appreciated! =)
I had the same problem before. I hope the following 2 methods will help.
You can give background to ".tmx" file.
Use ParallaxComponent. Here's the code I used on my project.
class GameBackground extends ParallaxComponent<HorseRiderGame> {
double velocity = 0.0;
#override
Future<void>? onLoad() async {
await super.onLoad();
Vector2 gameSize = gameRef.size;
gameSize.y;
parallax = await gameRef.loadParallax(
[
ParallaxImageData('background/sky.png'),
ParallaxImageData('background/jungle_bg.png'),
ParallaxImageData('background/trees_bushes.png'),
ParallaxImageData('background/grasses.png'),
],
repeat: ImageRepeat.repeatX,
baseVelocity: Vector2(velocity, 0.0),
velocityMultiplierDelta: Vector2(2, 0.0),
);
}
}
Then add this class to the FlameGame class.
#override
Future<void>? onLoad() async {
await super.onLoad();
add(GameBackground());
}
I followed the documentation on the Flame site to create a Klondike game skeleton, and now am trying to apply the same ideas towards creating a board game. I like using the CameraComponent because it allows for easily resizing the game area as the browser size changes, but I've found that when mixed with Tappable Components the tap events tend to "miss", or not line up with what's visible on the screen. Here's a minimal example:
import 'package:flame/components.dart';
import 'package:flame/experimental.dart';
import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
void main() {
final game = MyFlameGame();
runApp(GameWidget(game: game));
}
class MyFlameGame extends FlameGame with HasTappables {
#override
Future<void> onLoad() async {
final myComponent = MyComponent()
..size = Vector2(50, 50)
..position = Vector2(250, 250);
final world = World();
world.add(myComponent);
add(world);
final camera = CameraComponent(world: world)
..viewfinder.visibleGameSize = Vector2(1000, 1000)
..viewfinder.position = Vector2(500, 0)
..viewfinder.anchor = Anchor.topCenter;
add(camera);
}
}
class MyComponent extends PositionComponent with Tappable {
#override
bool get debugMode => true;
#override
bool onTapUp(TapUpInfo info) {
print('tap up');
return true;
}
#override
bool onTapDown(TapDownInfo info) {
print('tap down');
return true;
}
}
If you resize the window and click within the box, I'd expect to see console logs but they sometimes don't appear. Occasionally I'm able to find the real Tappable position in the black area which will trigger the console messages. Is there a way to configure the Tappable components to line up with what's visible through the CameraComponent viewport?
This is a known bug in the CameraComponent, or maybe not a bug, it just hasn't been implemented yet. This is one of the reasons why the CameraComponent is still classified as experimental.
The work is ongoing though, and you can get updated by following this issue.
I have two sprites in the game e.g Player and Enemy. After collision detection why remove(Component) method is not working?
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/geometry.dart';
import 'package:flutter/material.dart';
void main() async {
runApp(GameWidget(
game: CGame(),
));
}
class CGame extends FlameGame with HasCollidables {
#override
Future<void>? onLoad() async {
add(Enemy());
add(Player());
return super.onLoad();
}
}
class Enemy extends SpriteComponent with HasGameRef, HasHitboxes, Collidable {
Enemy() : super(priority: 1);
#override
Future<void>? onLoad() async {
sprite = await Sprite.load('crate.png');
size = Vector2(100, 100);
position = Vector2(200, gameRef.size.y / 3 * 2);
anchor = Anchor.center;
addHitbox(HitboxRectangle());
return super.onLoad();
}
}
class Player extends SpriteComponent with HasGameRef, HasHitboxes, Collidable {
#override
Future<void>? onLoad() async {
sprite = await Sprite.load('player.png');
size = Vector2(100, 100);
position = Vector2(0, gameRef.size.y / 3 * 2);
anchor = Anchor.center;
addHitbox(HitboxRectangle());
return super.onLoad();
}
#override
void update(double dt) {
position += Vector2(1, 0);
}
#override
void onCollision(Set<Vector2> intersectionPoints, Collidable other) {
if (other is Enemy) {
print('Player hit the Enemy!');
remove(Enemy()); //<== Why this line is not working?
}
}
}
You are trying to remove a new instance of an enemy component, you have to remove the specific one that you collided with, which in this case is other.
You also want to remove other from the game where it is added, if you do remove on the component you are trying to remove a subcomponent.
#override
void onCollision(Set<Vector2> intersectionPoints, Collidable other) {
if (other is Enemy) {
other.removeFromParent();
// It can also be done like this since you have the `HasGameRef` mixin
// gameRef.remove(other);
}
}
}
I'm Migrating to flame v1.0.0-rc8 from flame v0.29.4
and I can't find a good roadmap of how to get the initialDimensions, how to get the engine widget via engine.widget, how to init the Sprite object (Previously via Sprite('path_to_asset_file')), and how to set the width and height for SpriteComponent (Previously via SpriteComponent.rectangle).
These are several questions, so I'll give several answers:
How to get the inititialDimensions?
inititialDimensions is no longer needed, onGameResize is called before onLoad which will give you the size of the game. You can also get the size of the game by adding the HasGameRef mixin to your Components and call gameRef.size.
How to get the flutter widget?
You now wrap your game inside of a GameWidget instead of using .widget:
import 'package:flutter/material.dart';
import 'package:flame/game.dart';
void main() {
final myGame = MyGame();
runApp(
GameWidget(
game: myGame,
),
);
}
How to initialize a Sprite?
You usually want a SpriteComponent, and not a raw Sprite.
To create a Sprite:
class MyGame extends FlameGame {
Sprite player;
#override
Future<void> onLoad() async {
player = Sprite.load('player.png');
}
}
To create a SpriteComponent:
class MyGame extends FlameGame {
SpriteComponent player;
#override
Future<void> onLoad() async {
final sprite = await loadSprite('player.png');
player = SpriteComponent(sprite: sprite);
// And you usually want to add your component to the game too.
add(player);
}
}
How to set the size of a component?
Simply do component.size = Vector2(width, height); or component.width = width; + component.height = height