Flame

一个极简的Flutter游戏引擎。(A minimalist Flutter game engine)

Github stars Tracking Chart

Flame

本篇为社区版翻译, 目前针对版本: 0.21.0

一款极简的 Flutter 游戏引擎。

问题互助

我们在 Fireslime 的 Discord 上有一个互助频道, 点击加入

我们也有 FAQ, 所以提问前请先在这里看看你有没有需要的答案.

国内用户欢迎加入 Flame QQ交流群(非官方, 针对国内用户的讨论群, 较新)

项目目标

这个项目的目的是为使用 Flutter 进行开发的每个游戏都会遇到的常见问题提供一套完整的解决方案.

目前它提供了:

  • 游戏循环(game loop)
  • 组件/对象系统
  • 内置物理引擎(box2d)
  • 音频支持
  • 特效与粒子效果
  • 手势和输入支持
  • 图片, 精灵(sprite)以及精灵组
  • 基础 Rive 支持
  • 和一些简化开发的实用工具类

你可以按需使用它们, 因为它们在某种程度上是相互独立的。

立即开始

简体中文教程 Alekhin Tutorial Series (Simplified Chinese) by HarrisonQI

我们还在 awesome-flame 项目上提供了一些精选的游戏, 库和文章.

文档&说明

你可以在这里看到完整的开发文档

你也可以在这里看到许多不同功能的示例(文档/demo), 点击此处来查看一个入门 demo.

Flame 的官方网站(其中包含文档), 请点击此处

感谢

Main metrics

Overview
Name With Ownerflame-engine/flame
Primary LanguageDart
Program languageDart (Language Count: 5)
PlatformLinux, Mac, Windows
License:MIT License
所有者活动
Created At2017-10-22 03:01:50
Pushed At2025-06-14 15:32:25
Last Commit At
Release Count940
Last Release Name1.29.0 (Posted on )
First Release Name0.6.0 (Posted on )
用户参与
Stargazers Count10k
Watchers Count139
Fork Count1k
Commits Count3.6k
Has Issues Enabled
Issues Count1180
Issue Open Count108
Pull Requests Count2204
Pull Requests Open Count6
Pull Requests Close Count159
项目设置
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private

Pub Build Status - Travis Discord

:fire: flame

A minimalist Flutter game engine.

Any help is appreciated! Comment, suggestions, issues, PR's! Give us a star to help!

Help

We have a Flame help channel on Fireslime's Discord, join it here. Also we now have a FAQ, so please search your questions there first.

Goals

The goal of this project is to provided a complete set of out-of-the-way solutions for the common problems every game developed in Flutter will share.

Currently it provides you with: a few utilities, images/sprites/sprite sheets, audio, a game loop and a component/object system.

You can use whatever ones you want, as they are all somewhat independent.

Support

Support us by becoming a patron on Patreon

Patreon

Or making a single donation buying us a coffee:

Buy Me A Coffee

You can also show support by showing on your repository that your game is made with Flame by using one of the following badges:

Powered by Flame
Powered by Flame
Powered by Flame

[![Powered by Flame](https://img.shields.io/badge/Powered%20by-%F0%9F%94%A5-orange.svg)](https://flame-engine.org)
[![Powered by Flame](https://img.shields.io/badge/Powered%20by-%F0%9F%94%A5-orange.svg?style=flat-square)](https://flame-engine.org)
[![Powered by Flame](https://img.shields.io/badge/Powered%20by-%F0%9F%94%A5-orange.svg?style=for-the-badge)](https://flame-engine.org)

Contributing

Found a bug on Flame and want to contribute with a PR? PRs are always very welcome, just be sure to create your PR from the develop branch.

External Modules

Flame is modular, and you can always pick and choose. Some modules are extracted to separate plugins; some are bundled with flame, and some must be added separately.

  • audioplayers is the audio engine behind flame. It's included.

  • tiled adds support for parsing and using TMX files from Tiled. It's included.

  • box2d adds wrappers over Box2D for the physics engine. It's included.

  • flame_gamepad adds support to gamepad. Android only. It's not included, add to your pubspec as desired.

  • play_games integrates to Google Play Games Services (GPGS). Adds login, achievements, saved games and leaderboard. Android only. It's not included, add to your pubspec as desired. Be sure to check the instructions on how to configure, as it's not trivial.

Usage

Just drop it in your pubspec.yaml:

dependencies:
  flame: ^0.17.4

And start using it!

Important

We strive to keep Flame working on the Flutter's stable channel, currently on version v1.7.8+hotfix.2, be sure to check which channel are you using if you encounter any trouble.

Documentation

The complete documentation can be found here.

A very cool docs site can be found here.

Getting started

Check out this great series of articles/tutorials written by Alekhin

We also offer a curated list of Games, Libraries and Articles over at awesome-flame.

Structure

The only structure you are required to comply is a assets folder with two sub folders: audio and images.

An example:

  Flame.audio.play('explosion.mp3');

  Flame.images.load('player.png');
  Flame.images.load('enemy.png');

The file structure would have to be:

.
└── assets
    ├── audio
    │   └── explosion.mp3
    └── images
        ├── enemy.png
        └── player.png

Don't forget to add these files to your pubspec.yaml file:

flutter:
  assets:
    - assets/audio/explosion.mp3
    - assets/images/player.png
    - assets/images/enemy.png

Modules

The modular approach allows you to use any of these modules independently, or together, or as you wish.

Audio

To play an audio, just use the Flame.audio.play method:

    import 'package:flame/flame.dart';

    Flame.audio.play('explosion.mp3');

You can pre-load your audios in the beginning and avoid delays with the loadAll method:

    // in a async prepare function for your game
    await Flame.audio.loadAll(['explosion.mp3']);

Complete Audio Guide

Looping Background Music Guide

Images

If you are using the Component module and doing something simple, you probably won't need to use these classes; use SpriteComponent or AnimationComponent instead.

If you want to load an image and render it on the Canvas, you can use the Sprite class:

    import 'package:flame/sprite.dart';

    Sprite sprite = Sprite('player.png');

    // in your render loop
    sprite.render(canvas, width, height);

Note that the render method will do nothing while the image has not been loaded; you can check for completion using the loaded method.

Complete Images Guide

Component

This class represent a single object on the screen, being a floating rectangle or a rotating sprite.

The base abstract class has the common expected methods update and render to be implemented.

The intermediate inheritance PositionComponent adds x, y, width, height and angle to your Components, as well as some useful methods like distance and angleBetween.

The most commonly used implementation, SpriteComponent, can be created with a Sprite:

    import 'package:flame/components/component.dart';

    // on your constructor or init logic
    Sprite sprite = Sprite('player.png');

    const size = 128.0;
    final player = 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

    // on your render method...
    player.render(canvas); // it will render only if the image is loaded and the x, y, width and height parameters are not null

Every Component has a few other methods that you can optionally implement, that are used by the BaseGame class. If you are not using the base game, you can alternatively use these methods on your own game loop.

The resize method is called whenever the screen is resized, and in the beginning once when the component is added via the add method. You need to apply here any changes to the x, y, width and height of your component, or any other changes, due to the screen resizing. You can start these variables here, as the sprite won't be rendered until everything is set.

The destroy method can be implemented to return true and warn the BaseGame that your object is marked for destruction, and it will be remove after the current update loop. It will then no longer be rendered or updated.

The isHUD method can be implemented to return true (default false) to make the BaseGame ignore the camera for this element.

There are also other implementations:

  • The AnimationComponent takes an Animation object and renders a cyclic animated sprite (more details about Animations here)
  • The ParallaxComponent can render a parallax background with several frames
  • The Box2DComponent, that has a physics engine built-in (using the Box2D port for Dart)

Complete Components Guide

Game Loop

The Game Loop module is a simple abstraction over the game loop concept. Basically most games are built upon two methods:

  • The render method takes the canvas ready for drawing the current state of the game.
  • The update method receives the delta time in milliseconds since last update and allows you to move the next state.

The class Game can be subclassed and will provide both these methods for you to implement. In return it will provide you with a widget property that returns the game widget, that can be rendered in your app.

You can either render it directly in your runApp, or you can have a bigger structure, with routing, other screens and menus for your game.

To start, just add your game widget directly to your runApp, like so:

    main() {
        Game game = MyGameImpl();
        runApp(game.widget);
    }

Instead of implementing the low level Game class, you should probably use the more full-featured BaseGame class.

The BaseGame implements a Component based Game for you; basically it has a list of Components and repasses the update and render calls appropriately. You can still extend those methods to add custom behavior, and you will get a few other features for free, like the repassing of resize methods (every time the screen is resized the information will be passed to the resize methods of all your components) and also a basic camera feature (that will translate all your non-HUD components in order to center in the camera you specified).

A very simple BaseGame implementation example can be seen below:

    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
        }
    }

Input

Inside package:flame/gestures.dart you can find a whole set of mixin which can be included on your game class instance to be able to receive touch input events

Example

class MyGame extends Game with TapDetector {
  // Other methods ommited

  @override
  void onTapDown(TapDownDetails details) {
    print("Player tap down on ${details.globalPosition.dx} - ${details.globalPosition.dy}");
  }

  @override
  void onTapUp(TapUpDetails details) {
    print("Player tap up on ${details.globalPosition.dx} - ${details.globalPosition.dy}");
  }
}

Complete Input Guide

Credits

  • All the friendly contributors and people who are helping in the community.
  • My own audioplayers lib, which in turn is forked from rxlabz's.
  • The Dart port of Box2D.
  • inu-no-policemen's post on reddit, which helped me a lot with the basics
  • Everyone who answered my beginner's questions on Stack Overflow