I try to make a mobile game by using Flutter and Flame, but the controller is not showing at the front of the layer even if I set the priority to 10 and the level of map priority to under 10.
Here's a screenshot :
Here this joystick code:
import 'dart:async';
import 'dart:io';
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/game.dart';
import 'package:flutter/painting.dart';
import 'package:pixel_adventure/components/player.dart';
import 'package:pixel_adventure/components/level.dart';
class PixelAdventure extends FlameGame
with HasKeyboardHandlerComponents, DragCallbacks {
final Player _player = Player();
late final CameraComponent _cam;
late final JoystickComponent _joystick;
@override
Color backgroundColor() => const Color(0xFF211F30);
@override
FutureOr<void> onLoad() async {
await images.loadAllImages();
final world = Level(levelName: 'level-01', player: _player);
_cam = CameraComponent.withFixedResolution(
world: world,
width: 640,
height: 360,
);
_cam.viewfinder.anchor = Anchor.topLeft;
addAll([_cam, world]);
if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
_addJoystick();
}
return super.onLoad();
}
@override
void update(double dt) {
if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
_updateJoystick();
}
super.update(dt);
}
void _addJoystick() {
_joystick = JoystickComponent(
priority: 10,
knob: SpriteComponent(
sprite: Sprite(
images.fromCache('HUD/knob.png'),
),
),
background: SpriteComponent(
sprite: Sprite(
images.fromCache('HUD/joystick.png'),
),
),
margin: const EdgeInsets.only(left: 32, bottom: 32),
);
add(_joystick);
}
void _updateJoystick() {
switch (_joystick.direction) {
case JoystickDirection.left:
case JoystickDirection.downLeft:
case JoystickDirection.upLeft:
_player.horizontalMovement = -1;
break;
case JoystickDirection.right:
case JoystickDirection.downRight:
case JoystickDirection.upRight:
_player.horizontalMovement = 1;
break;
default:
_player.horizontalMovement = 0;
break;
}
}
}
and this level of map
import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame_tiled/flame_tiled.dart';
import 'package:pixel_adventure/components/background_tile.dart';
import 'package:pixel_adventure/components/collision_block.dart';
import 'package:pixel_adventure/components/player.dart';
import 'package:pixel_adventure/pixel_adventure.dart';
class Level extends World with HasGameRef<PixelAdventure> {
final String levelName;
final Player player;
Level({required this.player, required this.levelName});
late TiledComponent level;
List<CollisionBlock> collisionBlocks = [];
@override
FutureOr<void> onLoad() async {
priority = -1;
level = await TiledComponent.load(
'$levelName.tmx',
Vector2.all(16),
);
add(level);
_scrollingObject();
_spawningObject();
_addCollisions();
player.collisionBlocks = collisionBlocks;
return super.onLoad();
}
void _scrollingObject() {
final Layer? backgroundLayer = level.tileMap.getLayer('background');
const int tileSize = 65;
final int numTilesY = (game.size.y / tileSize).floor();
final int numTilesX = (game.size.x / tileSize).floor();
if (backgroundLayer != null) {
final backgroundColor =
backgroundLayer.properties.getValue('BackgroundColor');
for (double y = 0; y < numTilesY; y++) {
for (double x = 0; x < numTilesX; x++) {
final backgroundTile = BackgroundTile(
color: backgroundColor ?? 'Gray',
position: Vector2(x * tileSize, y * tileSize - tileSize));
add(backgroundTile);
}
}
}
}
void _spawningObject() {
final ObjectGroup? spawnPointsLayer =
level.tileMap.getLayer<ObjectGroup>('Spawnpoints');
if (spawnPointsLayer != null) {
for (final TiledObject spawnPoint in spawnPointsLayer.objects) {
switch (spawnPoint.class_) {
case 'Player':
player.position = Vector2(
spawnPoint.x,
spawnPoint.y,
);
add(player);
break;
default:
}
}
}
}
void _addCollisions() {
final ObjectGroup? collisionsLayer =
level.tileMap.getLayer<ObjectGroup>('Collisions');
if (collisionsLayer != null) {
for (final TiledObject collisions in collisionsLayer.objects) {
switch (collisions.class_) {
case 'Platform':
final platform = CollisionBlock(
position: Vector2(collisions.x, collisions.y),
size: Vector2(collisions.width, collisions.height),
isPlatform: true,
);
collisionBlocks.add(platform);
add(platform);
break;
default:
final block = CollisionBlock(
position: Vector2(collisions.x, collisions.y),
size: Vector2(collisions.width, collisions.height),
);
collisionBlocks.add(block);
add(block);
}
}
}
}
}
and this gray background
import 'dart:async';
import 'package:flame/components.dart';
import 'package:pixel_adventure/pixel_adventure.dart';
class BackgroundTile extends SpriteComponent with HasGameRef<PixelAdventure> {
final String color;
BackgroundTile({this.color = 'Gray', position}) : super(position: position);
@override
FutureOr<void> onLoad() {
priority = -2;
size = Vector2.all(65.6);
sprite = Sprite(
game.images.fromCache('Background/$color.png'),
);
super.onLoad();
}
}
I tried to make a priority in the controller layer, but it's doesn't work.
Your joystick is added to the game
, which is underneath the world.
Setting the priority
only makes a difference if you set it between sibling components. I would add the joystick to the viewport instead, to make sure that it is treated as a HUD and always visible.
camera.viewport.add(joystick);
You also have some other issues in your code, you have two cameras (there is already one built-in to FlameGame
).
Do this instead to use the built-in camera:
@override
FutureOr<void> onLoad() async {
await images.loadAllImages();
camera = CameraComponent.withFixedResolution(
world: world,
width: 640,
height: 360,
);
world = Level(levelName: 'level-01', player: _player);
camera.viewfinder.anchor = Anchor.topLeft;
if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
_addJoystick(); // Make sure this adds to `camera.viewport` instead.
}
return super.onLoad();
}
and remove all references to _cam
and use camera
intead.