flame
一个极简的 Flutter 游戏引擎。
任何帮助都将不胜感激!评论、建议、问题、PR!请给我们点赞支持!
目标
本项目的目标是为 Flutter 中每个游戏开发都会遇到的常见问题提供一套完整的、不碍事的解决方案。
目前它为您提供了:一些工具,图片/精灵/精灵表,音频,一个游戏循环和一个组件/对象系统。
您可以随意使用它们,因为它们都相对独立。
用法
只需将其添加到您的 pubspec.yaml 文件中
dependencies:
flame: ^0.9.5
然后开始使用它!
下面是一个概述,足以帮助您构建一个简单的游戏,并在此基础上继续前进。
flame-example 游戏已更新,以在新分支上使用较新的 API (0.8.2)。
有一个非常好的 0.6.1 版本的快速入门教程 在此。API 变化很大,因此请参考此文档以获取更新信息。我计划很快发布一个更新的教程。
结构
唯一需要遵守的结构是 assets 文件夹,其中包含两个子文件夹:audio 和 images。
一个例子
Flame.audio.play('explosion.mp3');
Flame.images.load('player.png');
Flame.images.load('enemy.png');
文件结构将是
.
└── assets
├── audio
│ └── explosion.mp3
└── images
├── enemy.png
└── player.png
别忘了将这些文件添加到您的 pubspec.yaml 文件中
flutter:
assets:
- assets/audio/explosion.mp3
- assets/images/player.png
- assets/images/enemy.png
模块
模块化方法允许您独立使用、组合使用或根据需要使用这些模块。
音频
要播放音频,只需使用 Flame.audio.play 方法
import 'package:flame/flame.dart';
Flame.audio.play('explosion.mp3');
您可以在开始时预加载音频,并通过 loadAll 方法避免延迟
// in a async prepare function for your game
await Flame.audio.loadAll(['explosion.mp3']);
图片
如果您正在使用组件模块并且做一些简单的事情,您可能不需要使用这些类;而是使用 SpriteComponent 或 AnimationComponent。
如果您想加载一张图片并在 Canvas 上渲染它,您可以使用 Sprite 类
import 'package:flame/sprite.dart';
Sprite sprite = new Sprite('player.png');
// in your render loop
sprite.render(canvas, width, height);
请注意,在图像加载完成之前,render 方法将不起任何作用;您可以使用 loaded 方法检查完成状态。
组件
此类表示屏幕上的单个对象,例如浮动矩形或旋转的精灵。
基类抽象类具有 update 和 render 的通用预期方法,待您实现。
中间继承类 PositionComponent 为您的组件添加了 x、y、width、height 和 angle,以及一些有用的方法,如 distance 和 angleBetween。
最常用的实现 SpriteComponent,可以使用 Sprite 创建
import 'package:flame/components/component.dart';
Sprite sprite = new Sprite('player.png');
const size = 128.0;
var player = new SpriteComponent.fromSprite(size, size, sprite); // width, height, sprite
// screen coordinates
player.x = ... // 0 by default
player.y = ... // 0 by default
player.angle = ... // 0 by default
player.render(canvas); // it will render only if the image is loaded and the x, y, width and height parameters are not null
每个 Component 都有一些其他可选的方法可以实现,它们由 BaseGame 类使用。如果您不使用基础游戏,则可以改用自己的游戏循环中的这些方法。
resize 方法在屏幕调整大小时被调用,并且在组件通过 add 方法添加时在开始时被调用一次。您需要在此应用对组件的 x、y、width 和 height 的任何更改,或由于屏幕调整大小而导致的任何其他更改。您可以在此处开始设置这些变量,因为在所有设置完成之前,精灵都不会被渲染。
destroy 方法可以实现为返回 true 并警告 BaseGame 您的对象已被标记为销毁,它将在当前更新循环后被移除。然后它将不再被渲染或更新。
isHUD 方法可以实现为返回 true (默认为 false),以便 BaseGame 忽略此元素的 camera。
还有其他实现
AnimationComponent接受一个Animation对象并渲染一个循环动画精灵(有关动画的更多详细信息,请参见 此处)。ParallaxComponent可以渲染一个具有多个帧的视差背景Box2DComponent,它内置了一个物理引擎(使用了 Dart 的 Box2D 端口)
游戏循环
游戏循环模块是游戏循环概念的一个简单抽象。基本上,大多数游戏都建立在两个方法之上
- render 方法接收已准备好绘制游戏当前状态的画布。
- update 方法接收自上次更新以来的毫秒时间差,并允许您移动到下一个状态。
Game 类可以被子类化,并提供这两个方法供您实现。作为回报,它将提供一个 widget 属性,该属性返回游戏小部件,可以在您的应用程序中进行渲染。
您可以直接在 runApp 中渲染它,或者您可以拥有一个更大的结构,包含您的游戏的路由、其他屏幕和菜单。
要开始,只需将您的游戏小部件直接添加到您的 runApp 中,如下所示
main() {
Game game = new MyGameImpl();
runApp(game.widget);
}
与其实现低级别的 Game 类,不如使用功能更全面的 BaseGame 类。
BaseGame 为您实现了一个基于 Component 的 Game;基本上它有一个 Component 列表,并将 update 和 render 调用适当地转交给。您仍然可以扩展这些方法以添加自定义行为,并且您将免费获得其他一些功能,例如转交 resize 方法(每次屏幕调整大小时,信息都会传递给所有组件的 resize 方法)以及基本相机功能(它将翻译所有非 HUD 组件以将它们居中于您指定的相机)。
下面是一个非常简单的 BaseGame 实现示例
class MyCrate extends SpriteComponent {
// creates a component that renders the crate.png sprite, with size 16 x 16
MyCrate() : SpriteComponent.fromSprite(16.0, 16.0, new Sprite('crate.png'));
@override
void resize(Size size) {
// we don't need to set the x and y in the constructor, we can set then here
this.x = (size.width - this.width)/ 2;
this.y = (size.height - this.height) / 2;
}
}
class MyGame extends BaseGame {
MyGame() {
add(new MyCrate()); // this will call resize the first time as well
}
}
输入
为了处理用户输入,您可以使用 Flutter 为常规应用程序提供的库:手势识别器。
但是,为了绑定它们,请使用 Flame.util.addGestureRecognizer 方法;通过这样做,您将确保它们在游戏小部件未渲染时被正确解除绑定,因此您其余的屏幕将正常工作。
例如,添加一个点击侦听器(“on click”)
Flame.util.addGestureRecognizer(new TapGestureRecognizer()
..onTapDown = (TapDownDetails evt) => game.handleInput(evt.globalPosition.dx, evt.globalPosition.dy));
其中 game 是您的游戏对象的引用,handleInput 是您在游戏中处理输入的自定义方法。
如果您的游戏没有其他屏幕,只需在 runApp 调用之后,在 main 方法中调用此方法。