Adding images on Game, auto clear on game detach, fixing some examples

This commit is contained in:
Erick Zanardo
2020-09-23 22:21:56 -03:00
parent 585b440d0b
commit 2ea5001506
8 changed files with 66 additions and 87 deletions

View File

@ -5,14 +5,16 @@ import 'package:flame/components/sprite_component.dart';
import 'package:flame/components/mixins/resizable.dart'; import 'package:flame/components/mixins/resizable.dart';
import 'package:flame/text_config.dart'; import 'package:flame/text_config.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart' hide Image;
import 'dart:ui';
void main() async { void main() async {
Flame.initializeWidget();
await Flame.util.initialDimensions(); await Flame.util.initialDimensions();
final myGame = MyGame(); final myGame = MyGame();
runApp(myGame.widget); runApp(myGame.widget);
myGame.start();
} }
class AndroidComponent extends SpriteComponent with Resizable { class AndroidComponent extends SpriteComponent with Resizable {
@ -20,7 +22,7 @@ class AndroidComponent extends SpriteComponent with Resizable {
int xDirection = 1; int xDirection = 1;
int yDirection = 1; int yDirection = 1;
AndroidComponent() : super.square(100, 'android.png'); AndroidComponent(Image image) : super.square(100, image);
@override @override
void update(double dt) { void update(double dt) {
@ -56,17 +58,20 @@ class MyGame extends BaseGame {
@override @override
bool recordFps() => true; bool recordFps() => true;
void start() { @override
final android = AndroidComponent(); Future<void> onLoad() async {
final androidImage = await images.load('android.png');
final android = AndroidComponent(androidImage);
android.x = 100; android.x = 100;
android.y = 400; android.y = 400;
final android2 = AndroidComponent(); final android2 = AndroidComponent(androidImage);
android2.x = 100; android2.x = 100;
android2.y = 400; android2.y = 400;
android2.yDirection = -1; android2.yDirection = -1;
final android3 = AndroidComponent(); final android3 = AndroidComponent(androidImage);
android3.x = 100; android3.x = 100;
android3.y = 400; android3.y = 400;
android3.xDirection = -1; android3.xDirection = -1;

View File

@ -7,15 +7,11 @@ import 'package:flame/flame.dart';
import 'dart:ui'; import 'dart:ui';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); Flame.initializeWidget();
await Flame.util.fullScreen(); await Flame.util.fullScreen();
final playerSprite = await Sprite.loadSprite('player.png'); runApp(LayerGame().widget);
final enemySprite = await Sprite.loadSprite('enemy.png');
final backgroundSprite = await Sprite.loadSprite('background.png');
runApp(LayerGame(playerSprite, enemySprite, backgroundSprite).widget);
} }
class GameLayer extends DynamicLayer { class GameLayer extends DynamicLayer {
@ -56,14 +52,15 @@ class BackgroundLayer extends PreRenderedLayer {
} }
class LayerGame extends Game { class LayerGame extends Game {
Sprite playerSprite;
Sprite enemySprite;
Sprite backgroundSprite;
Layer gameLayer; Layer gameLayer;
Layer backgroundLayer; Layer backgroundLayer;
LayerGame(this.playerSprite, this.enemySprite, this.backgroundSprite) { @override
Future<void> onLoad() async {
final playerSprite = Sprite(await images.load('player.png'));
final enemySprite = Sprite(await images.load('enemy.png'));
final backgroundSprite = Sprite(await images.load('background.png'));
gameLayer = GameLayer(playerSprite, enemySprite); gameLayer = GameLayer(playerSprite, enemySprite);
backgroundLayer = BackgroundLayer(backgroundSprite); backgroundLayer = BackgroundLayer(backgroundSprite);
} }

View File

@ -17,8 +17,11 @@ class MyGame extends Game {
Size size; Size size;
NineTileBox nineTileBox; NineTileBox nineTileBox;
MyGame(this.size) { MyGame(this.size);
final sprite = Sprite('nine-box.png');
@override
Future<void> onLoad() async {
final sprite = Sprite(await images.load('nine-box.png'));
nineTileBox = NineTileBox(sprite, tileSize: 8, destTileSize: 24); nineTileBox = NineTileBox(sprite, tileSize: 8, destTileSize: 24);
} }

View File

@ -65,6 +65,12 @@ class MyGame extends BaseGame {
Timer.periodic(sceneDuration, (_) => spawnParticles()); Timer.periodic(sceneDuration, (_) => spawnParticles());
} }
@override
Future<void> onLoad() async {
await images.load('zap.png');
await images.load('boom3.png');
}
/// Showcases various different uses of [Particle] /// Showcases various different uses of [Particle]
/// and its derivatives /// and its derivatives
void spawnParticles() { void spawnParticles() {
@ -295,7 +301,7 @@ class MyGame extends BaseGame {
Particle imageParticle() { Particle imageParticle() {
return ImageParticle( return ImageParticle(
size: const Size.square(24), size: const Size.square(24),
image: Flame.images.loadedFiles['zap.png'].loadedImage, image: images.fromCache('zap.png'),
); );
} }
@ -385,7 +391,7 @@ class MyGame extends BaseGame {
/// Flame's [Sprite] into the effect. /// Flame's [Sprite] into the effect.
Particle spriteParticle() { Particle spriteParticle() {
return SpriteParticle( return SpriteParticle(
sprite: Sprite('zap.png'), sprite: Sprite(images.fromCache('zap.png')),
size: Position.fromOffset(cellSize * .5), size: Position.fromOffset(cellSize * .5),
); );
} }
@ -524,12 +530,11 @@ class MyGame extends BaseGame {
const columns = 8; const columns = 8;
const rows = 8; const rows = 8;
const frames = columns * rows; const frames = columns * rows;
const imagePath = 'boom3.png'; final spriteImage = images.fromCache('boom3.png');
final spriteImage = Flame.images.loadedFiles[imagePath].loadedImage;
final spritesheet = SpriteSheet( final spritesheet = SpriteSheet(
rows: rows, rows: rows,
columns: columns, columns: columns,
imageName: imagePath, image: spriteImage,
textureWidth: spriteImage.width ~/ columns, textureWidth: spriteImage.width ~/ columns,
textureHeight: spriteImage.height ~/ rows, textureHeight: spriteImage.height ~/ rows,
); );
@ -543,19 +548,8 @@ class MyGame extends BaseGame {
} }
Future<BaseGame> loadGame() async { Future<BaseGame> loadGame() async {
Size gameSize; Flame.initializeWidget();
WidgetsFlutterBinding.ensureInitialized(); final gameSize = await Flame.util.initialDimensions();
await Future.wait([
Flame.util.initialDimensions().then((size) => gameSize = size),
Flame.images.loadAll(const [
'zap.png',
/// Credits to Stumpy Strust from
/// https://opengameart.org/content/explosion-sheet
'boom3.png',
]),
]);
return MyGame(screenSize: gameSize); return MyGame(screenSize: gameSize);
} }

View File

@ -6,14 +6,21 @@ import 'dart:convert' show base64;
import 'package:flame/flame.dart'; import 'package:flame/flame.dart';
class Images { class Images {
Map<String, ImageAssetLoader> loadedFiles = {}; final Map<String, _ImageAssetLoader> _loadedFiles = {};
void clear(String fileName) { void clear(String fileName) {
loadedFiles.remove(fileName); _loadedFiles.remove(fileName);
} }
void clearCache() { void clearCache() {
loadedFiles.clear(); _loadedFiles.clear();
}
Image fromCache(String fileName) {
final image = _loadedFiles[fileName];
assert(image?.loadedImage != null,
'Tried to access an inexistent entry on cache "$fileName"');
return image.loadedImage;
} }
Future<List<Image>> loadAll(List<String> fileNames) async { Future<List<Image>> loadAll(List<String> fileNames) async {
@ -21,17 +28,17 @@ class Images {
} }
Future<Image> load(String fileName) async { Future<Image> load(String fileName) async {
if (!loadedFiles.containsKey(fileName)) { if (!_loadedFiles.containsKey(fileName)) {
loadedFiles[fileName] = ImageAssetLoader(_fetchToMemory(fileName)); _loadedFiles[fileName] = _ImageAssetLoader(_fetchToMemory(fileName));
} }
return await loadedFiles[fileName].retreive(); return await _loadedFiles[fileName].retreive();
} }
Future<Image> fromBase64(String fileName, String base64) async { Future<Image> fromBase64(String fileName, String base64) async {
if (!loadedFiles.containsKey(fileName)) { if (!_loadedFiles.containsKey(fileName)) {
loadedFiles[fileName] = ImageAssetLoader(_fetchFromBase64(base64)); _loadedFiles[fileName] = _ImageAssetLoader(_fetchFromBase64(base64));
} }
return await loadedFiles[fileName].retreive(); return await _loadedFiles[fileName].retreive();
} }
Future<Image> _fetchFromBase64(String base64Data) async { Future<Image> _fetchFromBase64(String base64Data) async {
@ -53,8 +60,8 @@ class Images {
} }
} }
class ImageAssetLoader { class _ImageAssetLoader {
ImageAssetLoader(this.future); _ImageAssetLoader(this.future);
Image loadedImage; Image loadedImage;
Future<Image> future; Future<Image> future;

View File

@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart';
import 'flame_audio.dart'; import 'flame_audio.dart';
import 'bgm.dart'; import 'bgm.dart';
import 'images.dart'; import 'assets/images.dart';
import 'assets_cache.dart'; import 'assets_cache.dart';
import 'util.dart'; import 'util.dart';

View File

@ -7,10 +7,11 @@ import 'package:flutter/widgets.dart' hide WidgetBuilder;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../keyboard.dart';
import 'widget_builder.dart'; import 'widget_builder.dart';
import '../keyboard.dart';
import '../assets/images.dart';
/// Represents a generic game. /// Represents a generic game.
/// ///
/// Subclass this to implement the [update] and [render] methods. /// Subclass this to implement the [update] and [render] methods.
@ -19,6 +20,8 @@ abstract class Game {
// Widget Builder for this Game // Widget Builder for this Game
final builder = WidgetBuilder(); final builder = WidgetBuilder();
final images = Images();
/// Returns the game background color. /// Returns the game background color.
/// By default it will return a black color. /// By default it will return a black color.
/// It cannot be changed at runtime, because the game widget does not get rebuild when this value changes. /// It cannot be changed at runtime, because the game widget does not get rebuild when this value changes.
@ -78,6 +81,8 @@ abstract class Game {
if (this is KeyboardEvents) { if (this is KeyboardEvents) {
RawKeyboard.instance.removeListener(_handleKeyEvent); RawKeyboard.instance.removeListener(_handleKeyEvent);
} }
images.clearCache();
} }
/// Flag to tell the game loop if it should start running upon creation /// Flag to tell the game loop if it should start running upon creation

View File

@ -14,38 +14,6 @@ class SpriteSheet {
List<List<Sprite>> _sprites; List<List<Sprite>> _sprites;
SpriteSheet({ SpriteSheet({
@required String imageName,
@required this.textureWidth,
@required this.textureHeight,
@required this.columns,
@required this.rows,
}) {
_sprites = List.generate(
rows,
(y) => List.generate(
columns,
(x) => _mapImagePath(imageName, textureWidth, textureHeight, x, y),
),
);
}
Sprite _mapImagePath(
String imageName,
int textureWidth,
int textureHeight,
int x,
int y,
) {
return Sprite(
imageName,
x: (x * textureWidth).toDouble(),
y: (y * textureHeight).toDouble(),
width: textureWidth.toDouble(),
height: textureHeight.toDouble(),
);
}
SpriteSheet.fromImage({
@required Image image, @required Image image,
@required this.textureWidth, @required this.textureWidth,
@required this.textureHeight, @required this.textureHeight,
@ -56,19 +24,19 @@ class SpriteSheet {
rows, rows,
(y) => List.generate( (y) => List.generate(
columns, columns,
(x) => _mapImage(image, textureWidth, textureHeight, x, y), (x) => _mapImagePath(image, textureWidth, textureHeight, x, y),
), ),
); );
} }
Sprite _mapImage( Sprite _mapImagePath(
Image image, Image image,
int textureWidth, int textureWidth,
int textureHeight, int textureHeight,
int x, int x,
int y, int y,
) { ) {
return Sprite.fromImage( return Sprite(
image, image,
x: (x * textureWidth).toDouble(), x: (x * textureWidth).toDouble(),
y: (y * textureHeight).toDouble(), y: (y * textureHeight).toDouble(),