mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 10:38:17 +08:00
v0.2.0, audio loop and rectangle textures
This commit is contained in:
@ -1,2 +1,6 @@
|
||||
## [0.2.0]
|
||||
- Added a loop method for playing audio on loop
|
||||
- Added the option to make rectangular SpriteComponents, not just squares
|
||||
|
||||
## [0.1.0]
|
||||
- First release, basic utilities
|
||||
|
||||
34
README.md
34
README.md
@ -4,7 +4,7 @@ A minimalist Flutter game engine.
|
||||
|
||||
## WIP
|
||||
|
||||
Audio doesn't work on iOS; the rest should (not tested).
|
||||
Audio does not work on iOS; the rest should (not tested).
|
||||
|
||||
Help is appreciated, check the Audio section for more details.
|
||||
|
||||
@ -90,7 +90,19 @@ You must have an appropriate folder structure and add the files to the `pubspec.
|
||||
It has to be an MP3 file.
|
||||
|
||||
This uses the [audioplayers](https://github.com/luanpotter/audioplayer) lib, in order to allow playing multiple sounds simultaneously (crucial in a game).
|
||||
Therefore, it doesn't work on iOS yet; check their README for more details on that.
|
||||
Therefore, it does not work on iOS yet; check their README for more details on that.
|
||||
|
||||
If you want to play indefinitely, just use loop:
|
||||
|
||||
```
|
||||
Flame.audio.loop('music.mp3');
|
||||
```
|
||||
|
||||
Beware: in order to use loop or any platform binding callbacks, you need to call this utility function first thing on your application code:
|
||||
|
||||
```
|
||||
Flame.util.enableEvents();
|
||||
```
|
||||
|
||||
### Images
|
||||
|
||||
@ -112,13 +124,14 @@ Just use:
|
||||
canvas.drawImageRect(image, rect, rect, paint);
|
||||
});
|
||||
```
|
||||
|
||||
Similarly to Audio, you can instantiate your own copy of Image:
|
||||
|
||||
```
|
||||
Image image = await new Images().load('asd');
|
||||
```
|
||||
|
||||
If you are using the Component module, you probably shouldn't use this one; use SpriteComponent instead!
|
||||
If you are using the Component module, you probably should not use this one; use SpriteComponent instead!
|
||||
|
||||
You must have an appropriate folder structure and add the files to the `pubspec.yaml` file, as explained above.
|
||||
|
||||
@ -137,7 +150,7 @@ But you can use the default implementation, `SpriteComponent`, which makes rende
|
||||
|
||||
const size = 128.0; // size that will be drawn on the screen
|
||||
// it will resize the image according
|
||||
SpriteComponent player = new SpriteComponent(size, 'player.png');
|
||||
SpriteComponent player = new SpriteComponent.square(size, 'player.png');
|
||||
// the image sprite will be loaded by the Images module
|
||||
|
||||
// screen coordinates
|
||||
@ -149,6 +162,12 @@ But you can use the default implementation, `SpriteComponent`, which makes rende
|
||||
player.render(canvas); // it will render if the image is ready
|
||||
```
|
||||
|
||||
You can also use the rectangle constructor if you want a non-square sprite:
|
||||
|
||||
```
|
||||
var object = new SpriteComponent.rectangle(width, height, imagePath);
|
||||
```
|
||||
|
||||
### Game Loop
|
||||
|
||||
The Game Loop module is a simple abstraction over the game loop concept.
|
||||
@ -163,11 +182,11 @@ Extend the abstract class Game and just implement render and update; they will b
|
||||
List<Component> objs = new List();
|
||||
|
||||
update(double t) {
|
||||
components.forEach((Component obj) { obj.update(t); });
|
||||
components.forEach((Component obj) => obj.update(t));
|
||||
}
|
||||
|
||||
render(Canvas canvas) {
|
||||
components.forEach((Component obj) { obj.render(canvas); });
|
||||
components.forEach((Component obj) => obj.render(canvas));
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,9 +201,10 @@ The update method receives the delta time in milliseconds since last update and
|
||||
|
||||
### Util
|
||||
|
||||
This module will incorporate a few utility functions that are good to have in any game environment. For now, there is only one:
|
||||
This module will incorporate a few utility functions that are good to have in any game environment. For now, there is only two:
|
||||
|
||||
* initialDimensions : returns a Future with the dimension (Size) of the screen. This has to be done in a hacky way because of the reasons described in the code.
|
||||
* enableEvents : this is also a hack that allows you to use the Service bindings with platform specific code callbacks. Normally they would only work if you called runApp with a widget, since we draw on canvas for the game, that's never called. This makes sure it works.
|
||||
|
||||
Ideas are appreciated!
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ import 'package:audioplayers/audioplayer.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class Audio {
|
||||
|
||||
Map<String, File> loadedFiles = new Map();
|
||||
|
||||
Future<ByteData> _loadAsset(String fileName) async {
|
||||
@ -21,10 +20,21 @@ class Audio {
|
||||
}
|
||||
|
||||
Future<int> play(String fileName) async {
|
||||
File file = await assertLoaded(fileName);
|
||||
return await new AudioPlayer().play(file.path, isLocal: true);
|
||||
}
|
||||
|
||||
Future<int> loop(String fileName) async {
|
||||
File file = await assertLoaded(fileName);
|
||||
AudioPlayer player = new AudioPlayer();
|
||||
player.setCompletionHandler(() => player.play(file.path, isLocal: true));
|
||||
return await player.play(file.path, isLocal: true);
|
||||
}
|
||||
|
||||
Future<File> assertLoaded(String fileName) async {
|
||||
if (!loadedFiles.containsKey(fileName)) {
|
||||
loadedFiles[fileName] = await load(fileName);
|
||||
}
|
||||
AudioPlayer audioPlayer = new AudioPlayer();
|
||||
return await audioPlayer.play(loadedFiles[fileName].path, isLocal: true);
|
||||
return loadedFiles[fileName];
|
||||
}
|
||||
}
|
||||
@ -14,22 +14,24 @@ abstract class SpriteComponent extends Component {
|
||||
|
||||
double x, y, angle;
|
||||
|
||||
double size;
|
||||
double width, height;
|
||||
Image image;
|
||||
|
||||
final Paint paint = new Paint()..color = new Color(0xffffffff);
|
||||
|
||||
SpriteComponent(this.size, String imagePath) {
|
||||
SpriteComponent.square(double size, String imagePath) : this.rectangle(size, size, imagePath);
|
||||
|
||||
SpriteComponent.rectangle(this.width, this.height, String imagePath) {
|
||||
Flame.images.load(imagePath).then((image) { this.image = image; });
|
||||
}
|
||||
|
||||
render(Canvas canvas) {
|
||||
canvas.translate(x, y);
|
||||
canvas.rotate(PI /2 + angle);
|
||||
canvas.translate(-size/2, -size/2);
|
||||
canvas.translate(-width/2, -height/2);
|
||||
if (image != null) {
|
||||
Rect src = new Rect.fromLTWH(0.0, 0.0, image.width.toDouble(), image.height.toDouble());
|
||||
Rect dst = new Rect.fromLTWH(0.0, 0.0, size, size);
|
||||
Rect dst = new Rect.fromLTWH(0.0, 0.0, width, height);
|
||||
canvas.drawImageRect(image, src, dst, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
class Util {
|
||||
|
||||
Future<Size> initialDimensions() async {
|
||||
// https://github.com/flutter/flutter/issues/5259
|
||||
// "In release mode we start off at 0x0 but we don't in debug mode"
|
||||
@ -21,4 +22,11 @@ class Util {
|
||||
return window.physicalSize;
|
||||
});
|
||||
}
|
||||
|
||||
void enableEvents() {
|
||||
new CustomBinder();
|
||||
}
|
||||
}
|
||||
|
||||
class CustomBinder extends BindingBase with ServicesBinding {
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
name: flame
|
||||
description: A minimalist Flutter game engine
|
||||
version: 0.1.0
|
||||
version: 0.2.0
|
||||
author: Luan Nico <luannico27@gmail.com>
|
||||
homepage: https://github.com/luanpotter/flame
|
||||
|
||||
|
||||
Reference in New Issue
Block a user