Flutter游戏引擎Flame初探,带你实现一个简单小游戏

我正在参加社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛

前言

一说到游戏开发,首先想到的是Cocos 2DUnity 3D 等这些强大的游戏开发引擎,市面上很多游戏都是基于这些游戏引擎开发的。我们要想开发一款游戏理所当然的想到也是从这些开发引擎中选择一款来进行开发,approve但是这些游戏引擎所使用的的开发语言可能跟我们所掌握的开发语言并不匹配,开源节流当然我们可以选择去学习一门新的语言来进行开发,毕竟作为一名程序猿学习能力肯定弱不了,但是作为一个 Flutter 开发人员我在想是否有一款专门针对 Flutter 的游戏开发引擎呢?Flutter 作为一个跨平台的开发框架,如果使用 Flutter 开发一款游戏岂不是天然就支持跨平台?答案是肯定的,也就是本篇文章将为大家介绍的 Flame 游戏引擎。

本文是对 Flame 游戏引擎的一个初探,首先会对 Flame 游戏引擎做一个初步的介绍,并通过对 Flaappreciateme 的基础应用实现一个简单的小游戏,游戏体验地址:坚持到底小游戏 ,游elementary是什么意思戏效果如下:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

Flame 使用简介

Flame 是一个开源的基于 Flut像素涂色ter开源众包 的游戏引擎,Flame 引擎的目的是为使用 Flutter 开发的游戏会遇到的常见问题提供一套完整的解决方案。目前 Flame 提供了以下功能:

  • 游戏循环 (game loop)
  • 组件/对象系统 (FC变量名S)
  • 特效与粒子效果
  • 碰撞检测像素画
  • 手势和输入支持
  • 图片、动画、精灵图 (sprite) 以及精灵图组
  • 一些简化开发的实用工具类

因为本篇是对 Flappearanceame 的初探,将主要介绍第一个功能:游戏循环(game loopelements中文翻译。后续将通过一系列的文章对 Flame 的其他功能一一介绍。

游戏创建

首先在 Flutter 项目依赖里添加 Flame 库的依赖,目前最新版本是 1.1.0

dependencies:
  flame: ^1.1.0

然后创建一个类继承自 FlameGame

import 'package:flame/game.dart';
class CustomGame extends FlameGame{
}

最后修改 main.dart 中 main 方法的 runAp像素冒险世界p 使用创建好的 CustomGame :

void main() {
  final game = CustomGame();
  runApp(GameWidget(game: game));
}

runApp 需要传入一个 Widget,但是 FlameGame 并不是一个 Widget ,所以不能直接传入 runApp,需要使用 Flaelementary翻译me 提供的 GameWidget, 其参数 game 传入上面创建的 CustomGame ,这样就创apple建好了一个游戏,只是现在我们什么都没有加,所以运行是一个黑的什么都没有。

游戏循环(game loop)

游戏循环是一款游戏变量值的本质,即一组反复运行的代码,简单的说就是循环渲染画面appointment到屏幕上。在游戏里我们常见的一个说法是:FPS(Frames Per Second) 即每秒多少帧,比如:60 FPS 代表一秒elements钟渲染 60 帧,换算下来就是 16 毫秒绘制一帧,整个变量名appetite戏则是通过一帧一帧的画面循环绘制而成的。

那么在 Flame 中是怎样创建游戏循环的呢?FlameGame 提供了两个核心方法:updaterender,即更新和渲染,游戏运行时会循环调用 update 和 render 方法:

class CustomGame extends FlameGame{
  @override
  void render(Canvas canvas){
    super.render(canvas);
  }
  @override
  void update(double dt) {
    super.update(dt);
  }
}

render 方法是用于渲染,有一个参数 canvas,这样我们就可以在 render 方法里通过 canvas 绘制我们想要的游戏内容;

update 方法用于approve更新游戏数据,其参数 dt 是时间开源阅读间隔,单位是秒,即间隔多久调用一次 update 和 render 方法,前面说了 60 FPS 是 16 毫秒一帧,那么在 60 FPS 的情况下 dt 就等于 0.element翻译016 。

比如要在游戏里绘制一个圆,并让这个圆每一帧在 x 和 y 上各移动 1 个像素,则可以在 render 里使用 canapplevas 绘制一个圆,在 updateelements中文翻译 里更新圆心的位置,elementary如下:

class CustomGame extends FlameGame{
  Offset circleCenter = const Offset(0, 0);
  final Paint paint = Paint()..color = Colors.yellow;
  @override
  void render(Canvas canvas){
    super.render(canvas);
    canvas.drawCircle(circleCenter, 20, paint);
  }
  @override
  void update(double dt) {
    super.update(dt);
    circleCenter =  circleCenter.translate(1, 1);
  }
}

效果如下:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

生命周期

FlameGame 除了 update 和 render 方法外,还提供了一系列的生命周期方法,如下图:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

游戏初次添加到 Flutt变量与函数er 的 Widget 树时会回调 onGameResize, 然后依次回调 onLoadonMount ,之后将循环调用 update 和 render 方法,当游戏从 Flutter 的 Widget 树中移除时调用 onRemo变量名ve 方法。

当游戏画布大小发生改变时会回变量值onGa变量名meResize 方法,可以再该方法里重新初始化游戏里相关APP元素的大小和位置。

onLoad 在整个 Fla开源矿工meGame 的生命周期里只会调用一次,而其他生命周期方法都可能会多次调用,所以我们可以在 onLoad 中进行游戏的一些初始化工作。

实例:坚持到底小游戏

前面介绍了 FlameGame 的基本使用和生命周期,elements中文翻译接下来就看看如何使用 FlameGame 实现一个小游戏。

游戏介绍

游戏名字叫坚持到底小游戏,游戏的玩法很简单,就是玩家操作游戏主角躲避四面八方发射过来的子弹,以坚持的时间为成绩,坚持的时间越长成绩越开源是什么意思好,游戏的终极目标就是坚持100秒。

游戏的元素也很简单,包括:背景、主角、子弹、成绩、开始/重新开始按钮,接下来就一步步从零实现这个小游戏。

背景

首先第一步是绘制游戏的背景,因为这个游戏比较简单,像素涂色游戏背景就是一变量泵个纯色,所以实现也比较简单,在 r变量泵ender 里使用 canvas 绘制一个全屏的矩形即可approve,代码如下:

class StickGame extends FlameGame{
  final Paint paint = Paint()..color = const Color.fromARGB(255, 35, 36, 38);
  final Path canvasPath = Path();
    @override
  Future<void>? onLoad() async{
    canvasPath.addRect(Rect.fromLTWH(0, 0, canvasSize.x, canvasSize.y));
    return super.onLoad();
  }
  @override
  void render(Canvas canvas){
    super.render(canvas);
    canvas.drawPath(canvasPath, paint);
  }
}

声明一个 paint像素画生成器 变量,并设置其颜色即背景颜色,用于 canvas 绘制背景;声明 canvasPath 并在appearance onLoad 方法中为其添加一个矩形,矩形大小为整个画布的大小,其中 canvasSize 为 FlameGame 的变量,即画布大小;然后再 rende像素射击r 里调用 canvas.drawPath 进行绘制,这样就完成了背景的Element绘制。

主角

背景绘制完成后,接下来就是绘制我们游戏的主角了。在这个游戏里我们的主角就是一个圆,玩家可以拖动这个圆在画布范围内进行移动躲避子弹。

为了使代码易像素射击下载于管理,我们这里新建一个 TargetComponent 类用来专门处理游戏主角的绘制和相关逻辑。代码如下:

import 'dart:ui';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
class TargetComponent {
  final Vector2 position;
  final double radius;
  late Paint paint = Paint()..color = Colors.greenAccent;
  TargetComponent({required this.position, this.radius = 20});
  void render(Canvas canvas){
  }
}

既然我们的主角是一个圆,那么要变量绘制一个圆elementui就需要圆心、半径和颜色,所以为appreciate TargetCompone变量泵nt 添加 position 和 radius 构造参数,用于变量类型有哪些传入圆心的位置和半径,默认半径为 20 ;创建 paint 并指像素射击下载定颜色值用于 canvas 绘制。

TargetComponent 中创建了一个 render 方法,参数是 Canvas,整个方法的定义与 FlameGame像素 中的 render 方法一直,该方法appointment也是在 FlameGame 的 render 方法中进行调用,在 TargetComponent 的 render 方法中我们就可以实现圆的绘制了:

void render(Canvas canvas){
  canvas.drawCircle(position.toOffset(), radius, paint);
}

在 StickGame 中创建 TargetComponent 并在 render 中调用 TargetComponent 的 render 方法:

class StickGame extends FlameGame{
  late TargetComponent target;
  @override
  Future<void>? onLoad() async{
    ...
    target = TargetComponent(position: Vector2(canvasSize.x/2, canvasSize.y/2));
    return super.onLoad();
  }
  @override
  void render(Canvas canvas){
    super.render(canvas);
		...
    target.render(canvas);
  }
}

在 onLoad 中创建 TargetComponent 对象,位置传入的是画布的element翻译中心点,并在 render 方法中调用了 targetappstore 的 render 方法。实现效果如下:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

拖动

圆绘制好后,接下来就看怎么实现根据用户的拖动移动这个圆,这里有两个关键点,一个是监听用户拖动事件,一个是改变圆的位置。

Flame 提供了拖动事appointment件的变量是什么意思回调,只需 FlameGelement是什么意思ame 的实现类混入 HasDraggables 类然后实现对应的回调方法即可,如下:

class StickGame extends FlameGame with HasDraggables{
  @override
  void onDragStart(int pointerId, DragStartInfo info) {
    super.onDragStart(pointerId, info);
  }
  @override
  void onDragUpdate(int pointerId, DragUpdateInfo info) {
    super.onDragUpdate(pointerId, info);
  }
  @override
  void onDragCancel(int pointerId) {
    super.onDragCancel(pointerId);
  }
  @override
  void onDragEnd(int pointerId, DragEndInfo info) {
    super.onDragEnd(pointerId, info);
  }
}

onDragStart 是拖动开始的回调,onDragUpdate 是拖动过程中的回调,onDragCancel 是取消拖动回调,onDragEnd 是拖动结束回调。

在 onDragStart 中我们判断拖动的是否为前面绘制的圆,并设置拖动标识,在 onDragUpdate 中去更新圆的位置。onDragCancel、elementanimationonDragEnd 中取消拖动标识,实现如下:

	bool isDrag = false;
  @override
  void onDragStart(int pointerId, DragStartInfo info) {
    super.onDragStart(pointerId, info);
    if(target.path.contains(info.eventPosition.game.toOffset())){
      isDrag = true;
    }
  }
  @override
  void onDragUpdate(int pointerId, DragUpdateInfo info) {
    super.onDragUpdate(pointerId, info);
    var eventPosition = info.eventPosition.game;
    if (eventPosition.x < target.radius ||
        eventPosition.x > canvasSize.x - target.radius ||
        eventPosition.y < target.radius ||
        eventPosition.y > canvasSize.y - target.radius) {
      return;
    }
    if(isDrag){
      target.onDragUpdate(pointerId, info);
    }
  }
  @override
  void onDragCancel(int pointerId) {
    super.onDragCancel(pointerId);
    isDrag = false;
  }
  @override
  void onDragEnd(int pointerId, DragEndInfo info) {
    super.onDragEnd(pointerId, info);
    isDrag = false;
  }

在 onDragStart 中判断拖动的点是否在游戏主角圆内,使用的是 Path 的 contains 方法判断,如果是则将 isDrag 设置为 true,并在 onDragCancel、onDragEnd 中将 isDrag 设置为 false。

然后在 onDragUpdate 中处理拖动更新,首先像素射击判断拖动的点是否在画布范围内,通过获取拖动的点 info.eventPosition.game 与画变量类型有哪些布范围以及结合圆的半径进行比较,如果超出画开源中国布范围则不处理,防止圆被拖到画布以外;最后调用 target.onDragUpdate 方法,elementary翻译实现如下:

  void onDragUpdate(int pointerId, DragUpdateInfo info) {
    var eventPosition = info.eventPosition.game;
    position.setValues(eventPosition.x, eventPosition.y);
    _updatePath();
  }
  void _updatePath() {
    path.reset();
    path.addOval(Rect.fromLTWH(position.x - radius, position.y - radius, radius * 2, radius * 2));
  }

同样是先获取拖动的点坐标,然后将圆心位置设置为拖动坐标elementui,最后调appetite_updatePath 更新圆的 Path 路径,像素射击下载更新圆的 Path 路径主开源众包要是为了前面判断拖动是否在圆上以及后面为了检测圆与子弹appear的碰撞。最终实现效果:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

子弹

接下来就是绘制子弹,同样先建立一个子弹的组件:BulletComponent,子弹同样是一个圆,可以在画布中进行移动,拥有位置、移动速度、移动角度、半开源节流径、颜色属性,如下:

class BulletComponent{
  final Vector2 position;
  final double speed;
  final double angle;
  final double radius;
  late Paint paint = Paint()..color = Colors.orangeAccent;
  late Path path = Path()
    ..addOval(Rect.fromLTWH(position.x - radius, position.y - radius, radius * 2, radius * 2));
  BulletComponent({required this.position, this.speed = 5, this.angle = 0, this.radius = 10});
}

默认半径为 10,默认角度为 0,默认速度为 5,颜色为 orangeAccent,同时为了便于后面检测子弹与开源节流游戏主角的碰撞,这里也定义了子弹的 Path 。

Belement滑板ulletComponent 组件实现 render 和 updaelement滑板te 方法,用于绘制和更新,代码如下:

  void render(Canvas canvas){
    canvas.drawCircle(position.toOffset(), radius, paint);
  }
  void update(double dt){
    position.setValues(position.x - cos(angle) * speed , position.y - sin(angle) * speed);
    path.reset();
    path.addOval(Rect.fromLTWH(position.x - radius, position.y - radius, radius * 2, radius * 2));
  }

绘制很简单,就是在 positi变量名on 坐标的位置绘制一个指定半径的圆。更新则是按照设置的速度和角度计算出移动开源矿工的 x、y 坐标,并将其设置给 posiapprovetion ,最后同样是同步更新子弹的 Path 路径。

appreciate建子弹

子弹组件 BulletComponent 实现完成变量与函数后,接下来就是创建子弹组件实例,需要为子弹设置位置、半径、速度和角度,那么这些值怎么来呢?

游戏中的子弹需apple要每隔一段时间随机出现在开源游戏画布的四周,且子弹的半径也是随机的,出现后以一定速度往游戏主角的目标点移动直到与目标相遇或移动到画布外。需要计算的几个点如下:

  • 位置:随机出现在画布四周
  • 半径:一定appstore范围内随机(半径不能太大也不能太小)
  • 速度:随着时间推移子弹速度越来越快
  • 角度:通过子弹出现点和目标点计算子弹移动的角度

接下来就一步一步计算这些值,首先在 StickGame 中定义一个集合存放创建的子弹,然后定义一个创建子弹的方法:createBullet 并在 onLoad 方法中通过时间间隔循环调用,实现方法如下:

class StickGame extends FlameGame with HasDraggables{
  late Timer timer;
  List<BulletComponent> bullets = [];
  @override
  Future<void>? onLoad() async{
  	///....
    timer = Timer(0.1, onTick: () {
      createBullet();
    }, repeat: true);
    return super.onLoad();
  }  
    @override
  void render(Canvas canvas){
    super.render(canvas);
    ///...
    for (var bullet in bullets) {
      bullet.render(canvas);
    }
  }
  void update(double dt) {
    super.update(dt);
    ///...
   	for (var bullet in bullets) {
        bullet.update(dt);
    }
    timer.update(dt);
  }
  void createBullet() {
    ///...
  }
}

在 onLoad 中通过 Timer 每间隔 0.开源是什么意思1 秒调用一次创建子弹的方法,注意这里的 Timer 不是 Flutter SDK 中提供的 Timer 而是 Flame 库中提供的 Timer,是根据 update 的时间来计时的,所以需要在 update 中调用 Timer 的 upda变量名te 方变量名的命名规则法才能生效,elements这样做的好处是当游戏暂停时 Timer 的计时也会暂停。

然后在 render 方法和 upd开源阅读ate 方法中遍历子弹的集合调用子弹的 render 方法和 update 方法用户绘制子弹和更新子弹的位置。

接下来关键代码就在 createBullet 中了:

  void createBullet() {
    /// 随机半径
    var radius = random.nextInt(10) + 5;
    /// 计算位置
    /// 是否在水平方向上,即画布的顶部和底部
    bool isHorizontal = random.nextBool();
    int x = isHorizontal ? random.nextInt(canvasSize.x.toInt()) : random.nextBool() ? radius : canvasSize.x.toInt() - radius;
    int y = isHorizontal ? random.nextBool() ? radius : canvasSize.y.toInt() - radius : random.nextInt(canvasSize.y.toInt());
    var position = Vector2(x.toDouble(), y.toDouble());
    /// 计算角度
    var angle = atan2(y - target.position.y, x - target.position.x);
    /// 计算速度
    var speed = seconds/10 + 5;
    bullets.add(BulletComponent(position: position, angle: angle, radius: radius.toDouble(), speed: speed));
  }

首先随机得到 10 以内的数值然后加上 5 作为子弹的半径,再计算子弹的位置,因为计算位置的时候需要用到半径。

子弹位置的elementanimation计算先随机一个 bool 值用于确定子弹位置是在变量名的命名规则画布的开源阅读水平方向还是竖直方向,即是在画布的顶部底部还是左右两边,如果是水变量名平方向那 x 坐标的值就是随机的,y 坐标的值则随机是 0 或者画布的高度,即随机顶部还是底部,如果是竖直方向则 y 坐标值是随机的,x 的坐标则随机是 0 或者画布的宽度,即画布的左边或右边,当然最后都要减去子弹的半径,防止子弹跑到画布外面去。

子弹角度的计算,知道了子弹的坐标、目标点的坐标,就可以通过 atan2 方法计算出角度了。

最后是速度,速度APP的初始值变量名是 5像素画 ,随着时间推移速度越来越快,所以这里用游戏时间 seconds 也就是游appointment戏的秒数除以 10 再加上初始速度 5 作为子弹的速度。

效果如下:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

基本效果已经有了,但是还没有碰撞检测,发现子弹是穿过目标的,接下来就看看怎样实现碰撞检测。

碰撞检测

还记得前面实现游戏目标和子弹组件的时候里面都有一个 path 变量么,并且这个 path 会随着目标和子弹的更新一起elementanimation更新,所以我们可以使用 Path 的 combine 方法来检测碰撞。

Path combine(PathOperation operation, Path path1, Path path2)

combine 方法有三个参数,一个是操作类型,后面两个就是两个 path 对象,操作elements中文翻译类型有 5 种变量的定义,比如有两个圆重叠,对应 5 种类型的示意图如下:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

其中 intersect 就是我们需要的,即两个 Path 的相交,通过计算两个 Path 的相交的 Path,然后判断这个 Path 的开源代码网站github长度是否大于 0 ,如果大于 0 说明两个 Path 有相交,即有重叠说明产生了碰撞,代码实现如下:

  bool collisionCheck(BulletComponent bullet){
    var tempPath = Path.combine(PathOperation.intersect, target.path, bullet.path);
    return tempPath.getBounds().width > 0;
  }

在 update 遍历每个子弹,判断是否与目标有碰撞,如appear果有碰撞就结束游戏,所以这里增加一个 isRunning 变量,标记像素生存者2游戏是否运行,只有运行时像素画生成器才更新数据:

class StickGame extends FlameGame with HasDraggables{
  bool isRunning = true;
  ///...
  void stop(){
    isRunning = false;
  }
    @override
  void update(double dt) {
    super.update(dt);
    if(isRunning){
      timer.update(dt);
      for (var bullet in bullets) {
        if(collisionCheck(bullet)){
          stop();
          return;
        }else{
          bullet.update(dt);
        }
      }
    }
  }
}

当检测到碰撞时就停止游开源是什么意思戏,效变量与函数果如下:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

计时

计时就是记录游戏时长,即游戏的appreciate成绩,这里创建一个 seconds 变量,即记录游戏运行了多少秒,然后每次在 update 中增加you’xi 时长,实现如下:

class StickGame extends FlameGame with HasDraggables{
  double seconds = 0;
  @override
  void update(double dt) {
    super.update(dt);
    if(isRunning){
      seconds += dt;
      ///....
    }
  }
}

这样就完成了游戏时长的记录了。

文字

前面游戏基本功能基本完成,但是游戏的时长以及开始游戏、重新开始游戏以及游戏结束时游戏的成绩等文字需要显示,所以这里创建一个文字的组件 TextComponent,代码如下:

class TextComponent{
  final Vector2 position;
  String text;
  final Color textColor;
  double textSize;
  final Path path = Path();
  TextComponent({required this.position, required this.text, this.textColor = Colors.white, this.textSize = 40});
  void render(Canvas canvas){
    var textPainter = TextPainter(
        text: TextSpan(
            text: text,
            style: TextStyle(fontSize: textSize, color: textColor)),
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr);
    textPainter.layout(); // 进行布局
    textPainter.paint(canvas, Offset(position.x - textPainter.width / 2 , position.y - textPainter.height/2)); // 进行绘制
    path.reset();
    path.addRect(Rect.fromLTWH(position.x - textPainter.width / 2, position.y - textPainter.height/2, textPainter.width, textPainter.height));
  }
}

TextComponent 有四个参数,文字开源软件的位置、文字内容、文字颜色、文字大小,实现的方法只有一个 render 方法,用于使用 canvas 绘制文字,这里绘制文字使用的是 TextPainter , 最后同样有像素涂色一个 path 变量,用于记录绘制文字区域的路径,方便后面做文字的点击。

然后在 StickGame 里创建两个文字组件,一个用于显示成绩,一个用于显示开始游戏/重新开始游戏。

class StickGame extends FlameGame with HasDraggables{
  late TextComponent score;
  late TextComponent restartText;
  @override
  Future<void>? onLoad() async{
    score = TextComponent(position: Vector2(40, 40), text: "0", textSize: 30);
    restartText = TextComponent(position: Vector2(canvasSize.x/2, canvasSize.y/2), text: "START", textSize: 50);
    return super.onLoad();
  }  
  @override
  void render(Canvas canvas){
    super.render(canvas);
    ///...
    score.render(canvas);
    if(!isRunning){
       restartText.render(canvas);
    }
  }
}

像素工厂 onLoad 中创建成绩和开始/重新开始游戏的文字组件,并在 render 中调用其 render 方法,这里只有当游戏停止时才调用 restappearanceartText 的 render 方法显示重新开始游戏。其中成绩显示在左上角,重新开始游戏显示到画布中间,默认 restartText 显示的是开源矿工 START 即开始游戏。

既然有重新开始游戏,那就有开始游戏的方法,同时在结束Element游戏时也需要更新相应的数据,实现如下:

  void restart(){
    isRunning = true;
    bullets.clear();
    target.resetPosition();
    score.position.setValues(40, 40);
    score.textSize = 30;
    seconds = 0;
  }
  void stop(){
    isRunning = false;
    restartText.text = "RESTART";
    score.position.setValues(restartText.position.x, restartText.position.y - 80);
    score.text = "${seconds.toInt()}s";
    score.textSize = 40;
  }

开始游戏时将 isRunning 设置为 true,然后清空子弹集合,重置游戏目标的变量与函数位置,将成绩的显示放到左上角变量泵并设置成绩文字的大小为 30,游戏时长也重置为 0;游戏结束时将 isR像素画生成器unning 设置为 false,然后修改 restartText 的文字为 RESTART 即重新开始游戏,将成绩的文字移动到重新开始游戏文字的上方并修改其文字为游戏时长APP,并设置其文字大小为变量泵 40 。

点击

前面添加了开始游戏、重像素冒险世界新开始游戏的文字变量之间的关系,但是未为其添加点击事件,添加点击事件的方法跟前面添加拖动事件的方法类似,混入开源众包 HasTappables 实现 onTa变量英语pUp开源众包 方法即可:

class StickGame extends FlameGame with HasDraggables, HasTappables{
  @override
  void onTapUp(int pointerId, TapUpInfo info) {
    super.onTapUp(pointerId, info);
    if(!isRunning && restartText.path.contains(info.eventPosition.game.toOffset())){
      restart();
    }
  }
}

在 onTapU开源中国p 方法中判断游戏是否运行中,然后判断开始/重新开始游戏的文字显示区域是否包含点击的点,如果包含则说明点击的是开始/重新开始游戏,则调用 restelements中文翻译art() 方法。

最终实现的效果就是文章开始放出来的效果图,如下:

Flutter游戏引擎Flame初探,带你实现一个简单小游戏

回收

最后还缺一步就是回收,当子弹移动到画布外以后需要将子弹回收,即从集合中移除,实现如下:

  void checkBullets(){
    var removeBullets = <BulletComponent>[];
    for (var bullet in bullets) {
      if(!canvasPath.contains(bullet.position.toOffset())){
        removeBullets.add(bullet);
      }
    }
    bullets.removeWhere((element) => removeBullets.contains(element));
  }

最后变量类型有哪些

本篇文章带领大家对 Flame 游戏引擎做了一个初探,了解了 FlameGame 的基础使用,并通过 FlameGame 实现了一个简单的游戏,在实现游戏的过程中了解了拖拽事件、点击事件的使用开源节流方法。当然因为本篇文章只是对 Flame 的一个初探,所以在开源节流是什么意思实现这个小游戏的过程中没有用到其他 Flam变量e 的功能,比如 Flame开源矿工 的组件、碰撞检测等,使像素游戏用这些功能能更加快捷方便的实现对应的游戏功能,开源是什么意思关于 Flame 的更多功能将在后续文章中一一讲解,敬请期待!

源码:开源中国flutter_stick_game

文章已同步发布到公众号:loongwind

发表评论

提供最优质的资源集合

立即查看 了解详情