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']);

图片

如果您正在使用组件模块并且做一些简单的事情,您可能不需要使用这些类;而是使用 SpriteComponentAnimationComponent

如果您想加载一张图片并在 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 为您的组件添加了 xywidthheightangle,以及一些有用的方法,如 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 为您实现了一个基于 ComponentGame;基本上它有一个 Component 列表,并将 updaterender 调用适当地转交给。您仍然可以扩展这些方法以添加自定义行为,并且您将免费获得其他一些功能,例如转交 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 方法中调用此方法。

GitHub

https://github.com/luanpotter/flame