I'm playing around with Forge2d on Flutter Flame and created a Ninja which throws Kanuies (a character shooting bullets basically).
If I create Kanuies and the Ninja separately then add them separately to the game world, all will be fine. However, I want the Ninja class to be responsible for login for throwing the Kanui. So I was wondering what is the right way to add a bullet component to a character component in Forge2d.
In my current code, inside Ninja Class, if I call add(Kanui) there will be no graphics shown about Kanuie NOT even with debugMode=true .
However, if use addToParrent(Kanui) it will be fine again.
Below is the link to my code. Please have a look and suggest corrections.
I'll provide some snippets here as well.
https://github.com/bmd007/flame_playground/blob/154cc0a9a99cc4bd5732e8d0c94bfa38093b0298/lib/my_girl.dart#L134
Ninja Class:
class MyGirl extends BodyComponent {
late SpriteAnimationComponent component;
Queue<MyGirlKanui> kanuies = Queue<MyGirlKanui>();
#override
Future<void> onLoad() async {
await super.onLoad();
idleAnimation = await gameRef.loadSpriteAnimation("red_girl/idle_spriteSheet.png", idleAnimationData);
component = SpriteAnimationComponent()
..animation = idleAnimation
..size = Vector2.all(6)
..anchor = Anchor.center;
add(component);
kanuies.add(MyGirlKanui(initialPosition));
}
#override
Body createBody() {
final shape = PolygonShape()..setAsBoxXY(3, 3);
final fixtureDefinition = FixtureDef(shape, density: 2, restitution: 0.1, friction: 2);
final bodyDefinition = BodyDef(position: initialPosition, type: BodyType.dynamic)..fixedRotation = true;
return world.createBody(bodyDefinition)..createFixture(fixtureDefinition);
}
throwKanui() async {
if (kanuies.isNotEmpty) {
var kanui = kanuies.removeFirst();
// await parent?.add(kanui);
await add(kanui);
kanui.component.position = component.position;
kanui.body.linearVelocity.x = 30;
}
}
I call the throw method when a UI button is pressed.
In Forge2D you shouldn't add any bodies as children to other components.
You can add the HasGameRef<Forge2DGame> mixin to the component and then you can add the bullets directly to the game.
Also don't forget to put the body of the bullet to isBullet = true if the "bullet" is moving very fast, otherwise you could end up with tunneling (where the body passes through the object that it is supposed to hit).
Related
Does removeFromParent destroys object? I mean Garbage collects it, I am looking for destroy method but couldn't find.
update:
import 'package:flame/components.dart';
class Enemy extends SpriteAnimationComponent with HasGameRef {
#override
Future<void>? onLoad() async {
position = Vector2(200, 100);
size = Vector2(100, 100);
var spriteAnimationData = SpriteAnimationData.sequenced(
amount: 12,
stepTime: 0.05,
textureSize: Vector2(30, 30),
);
animation =
await gameRef.loadSpriteAnimation('enemy/pig.png', spriteAnimationData);
anchor = Anchor.center;
return super.onLoad();
}
#override
void update(double dt) {
position += Vector2(-2, 0);
if (position.x < -20) {
removeFromParent();
}
super.update(dt);
}
}
Since Dart is a garbage collected language it will garbage collect unused objects automatically once there are no references to the object anymore.
However, Sprites are a bit special since they have a loaded image in them. In Flame 1.0.0 the image cache (or the sprite class) does not call dispose of these images when it is cleared (but on main, and in the next version this will be done).
So to properly free up memory you'll have to call image.dispose() after you have removed the SpriteComponent, you could do sprite.image.dispose() in onRemove of the SpriteComponent for example, or call spriteComponent.sprite.image.dispose() after it has been removed.
EDIT: Since the question is now updated.
To call dispose on all Images loaded into a SpriteAnimationComponent you would have to do something like this:
component.animation.frames.forEach((f) => f.sprite.image.dispose());
Since those images are also loaded into the image cache they will also be removed when you remove an entry (or clear the whole cache) in the next version.
Reference: https://api.flutter.dev/flutter/dart-ui/Image/dispose.html
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).
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.
It is working fine for me for the first time it is rendered.
But, If I change anything over the map or recreate it, its broken.
Here is the screen shot for how it looks.
Here is a screen shot after I changed the results per page value.
This is my code.
#UiField DivElement mapPanel;
private GoogleMap googleMap;
public void loadAllMarkers(final List<LatLng> markers)
{
if(!markers.isEmpty())
{
final MapOptions options = MapOptions.create();
options.setMapTypeId(MapTypeId.ROADMAP);
googleMap = GoogleMap.create(mapPanel, options);
final LatLngBounds latLngBounds = LatLngBounds.create();
for(LatLng latLng : markers)
{
final MarkerOptions markerOptions = MarkerOptions.create();
markerOptions.setPosition(latLng);
markerOptions.setMap(googleMap);
final Marker marker = Marker.create(markerOptions);
latLngBounds.extend(marker.getPosition());
}
googleMap.setCenter(latLngBounds.getCenter());
googleMap.fitBounds(latLngBounds);
}
}
I am calling the loadAllMarkers() method whenever new results needs to be loaded.
Can someone point out what I am doing wrong here.
This seems to come from the following (which I pulled from a Google+ Community - GWT Maps V3 API):
Brandon DonnelsonMar 5, 2013
I've had this happen and forgotten why it is, but
mapwidget.triggerResize() will reset the tiles. This seems to happen
when the onAttach occurs and animation exists meaning that the div
started smaller and increases in side, but the map has already
attached. At the end of the animation, the map doesn't auto resize.
I'v been meaning to investigate auto resize but I haven't had time to
attack it yet.
In your case, you would call googleMap.triggerResize() after you finish your changes. this solved my problem when I had the exact same issue. I know it's a little late, but I hope it helps!
Another answer there was to extend the Map widget with the following:
#Override
protected void onAttach() {
super.onAttach();
Timer timer = new Timer() {
#Override
public void run() {
resize();
}
};
timer.schedule(5);
}
/*
* This method is called to fix the Map loading issue when opening
* multiple instances of maps in different tabs
* Triggers a resize event to be consumed by google api in order to resize view
* after attach.
*
*/
public void resize() {
LatLng center = this.getCenter();
MapHandlerRegistration.trigger(this, MapEventType.RESIZE);
this.setCenter(center);
}
I have a question there.
How i make character with gravity and available to walk properly i.e. what functions i need to use and how do i define fixtures? And do i need box2d physics world(i'm using tiled maps)?
So if you can, please tell me how to do 2d side scrolling platformer like mario with andengine.
My code what i'm trying to do :
// Character:
charactersprite = new Sprite(40, 0, this.character);
charactersprite.setScaleX(0.65f);
this.mScene.setOnSceneTouchListener( this);
// PHYSICS
final FixtureDef characterfictur = PhysicsFactory.createFixtureDef(0, 0f,0.5f);
this.mScene.registerUpdateHandler(this.mPhysicsWorld);
final Body body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, charactersprite, BodyType.DynamicBody, characterfictur);
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(charactersprite, body, true, false));
mScene.attachChild(charactersprite);
createUnwalkableObjects(mTMXTiledMap);
final PhysicsHandler physicsHandler = new PhysicsHandler(charactersprite);
charactersprite.registerUpdateHandler(physicsHandler);
// HUD
HUD my = new HUD();
Sprite forward = new Sprite( 50, CAMERA_HEIGHT - 170, forwardr){
#Override
public boolean onAreaTouched(TouchEvent pEvent, float pX, float pY){
if(!pEvent.isActionUp()){
charactersprite.getTextureRegion().setFlippedHorizontal(false);
body.setLinearVelocity(new Vector2(CHAR_MOVING_SPEED,body.getLinearVelocity().y)); // Don't look at there
//body.applyLinearImpulse(new Vector2(2,0), body.getPosition());
}else{
//body.applyLinearImpulse(new Vector2(0,0), body.getPosition());
physicsHandler.setVelocity(0, 0);
body.setLinearVelocity(new Vector2(0,body.getLinearVelocity().y)); // Don't look at there
}
return false;
}
};
And little forward :
private void createUnwalkableObjects(TMXTiledMap map){
// Loop through the object groups
for(final TMXObjectGroup group: map.getTMXObjectGroups()) {
//if(group.getTMXObjectGroupProperties().containsTMXProperty("Zeme", "true")){
// This is our "wall" layer. Create the boxes from it
for(final TMXObject object : group.getTMXObjects()) {
final Rectangle rect = new Rectangle(object.getX(), object.getY(),object.getWidth(), object.getHeight());
final FixtureDef boxFixtureDef = PhysicsFactory.createFixtureDef(0, 0,1f);
PhysicsFactory.createBoxBody(this.mPhysicsWorld, rect, BodyType.StaticBody, boxFixtureDef);
rect.setVisible(false);
mScene.attachChild(rect);
}
//}
}
}
So it didn't work properly. So what i'm doing wrong? Please help me.
Thank you very much!
Things you will need:
BoundCamera (to make bounds for your map)
chase entity (your player, so camera will follow your entity)
FixtureDef with little elasticity (to prevent player stopping suddenly on the ground)
create box bodies for your static objects (such as walls and so on)
things like jump - simply use setLinearVelocity
Every 'feature' mentioned above, has its example - simply check and engine examples.
In this thread, I provided some more tips about how to code such game: CLICK