Flutter Flame遊戲開發上手(1)

BestNevermore發表於2020-06-19

背景

收到一個遊戲開發的需求,因為雙端上線所以需要支援iOS Android兩端.

3個Android RD,沒有接觸過遊戲開發,快速學習上手Flutter支援雙端.

計劃遊戲使用Flame框架,先大題熟悉下環境然後在開發的過程中學習和解決問題.

知識點

GameLoop是什麼

遊戲迴圈就是搭建一個遊戲的腳手架.大部分遊戲只需要2個方法即:

  1. render方法 用來繪製
  2. update方法 用來更新繪製引數. update當前幀和上一幀的差異.

比如表現一個正在下墜的圓 圓的座標用y表示. render方法負責拿著y值去繪製圓, update方法負責更新y的變化. 互相配合完成了一個圓的下墜動畫

如何把一個Flutter元件展示在Frame引擎上

利用 HasWidgetsOverlay 介面.

  1. removeWidgetOverlay方法移出Flutter元件
  2. addWidgetOverlay方法新增Flutter元件
class MyGame extends BaseGame with HasWidgetsOverlay, TapDetector {
  Size size;
  SpriteC spriteC;
  bool isPaused = false;

  MyGame(this.size) {
    spriteC = SpriteC.create();
  }

  @override
  void onTap() {
    super.onTap();
    if (isPaused) {
      removeWidgetOverlay('pausemenu');
      isPaused = false;
    } else {
      addWidgetOverlay(
          'pausemenu',
          Center(
            child: Container(
              width: 100,
              height: 100,
              color: Colors.blue[300],
              child: const Center(child: const Text('Pause')),
            ),
          ));
      isPaused = true;
    }
  }
}
複製程式碼

如何把遊戲展示在Flutter的一個元件上

Game提供一個Widget物件,可以直接獲取最為Flutter的一個Widget使用.

  final game = MyGame(size);
  runApp(game.widget);
複製程式碼

Debug功能是什麼

  @override
  bool debugMode() => true;

  @override
  bool recordFps() => true;
複製程式碼

開啟Debug.

Flutter Flame遊戲開發上手(1)

Debug就是允許除錯,會自動列印元件的位置.

recordFps就是輸出當前執行遊戲的FPS.

元件(Components)

必要性: 就跟在Android/iOS上糊頁面一樣. 所有的頁面,動畫,特效都可以用Canvas來畫, 實際上開發一般情況下用的卻是ImageView,TextView,ListView這樣的元件來完成. 沒有人喜歡刀耕火種

元件重要方法:

除了render 和 update這種通用方法.

  // 初始化
  @override
  void onMount() {
    super.onMount();
  }

  // 回收前
  @override
  void onDestroy() {
    super.onDestroy();
  }
  
  // 還沒搞清楚...?  好像在說繪製的位置跟裝置之間的關係
  @override
  bool isHud() {
    return true;
  }

  // 標記可回收
  @override
  bool destroy() {
    return false;
  }
複製程式碼

怎麼畫幀動畫

即把

Flutter Flame遊戲開發上手(1)
變成
Flutter Flame遊戲開發上手(1)

遊戲裡面的人都是會動的,所以這是一個重要知識點.

在遊戲裡面展示 AnimationComponent

    const textureWidth = 96.0;
    const textureHeight = 96.0;
    add(AnimationComponent.sequenced(
      textureWidth * 2,
      textureHeight * 2,
      'minotaur.png',
      19,
      textureWidth: textureWidth,
      textureHeight: textureHeight,
      loop: true,
      stepTime: 0.15,
    ));
複製程式碼

其中函式 AnimationComponent.sequenced 的引數說明如下:

    AnimationComponent.sequenced(
        double width, // 展示的寬度
        double height, // 展示的高度
        String imagePath,
        int amount, { // Sprit Sheet的幀數
      int amountPerRow, // 多行切圖 兩行之間的間隔
      double textureX = 0.0, // 展示的偏移值
      double textureY = 0.0,
      double textureWidth, // 切圖一幀的寬度
      double textureHeight, // 切圖一幀的高度
      double stepTime, // 播放間隔時間
      bool loop = true, // 是否迴圈播放
      this.destroyOnFinish = false, // 播放完了是否銷燬
    })
複製程式碼

提供給Flutter元件展示 AnimationWidget

  await Flame.images.load('minotaur.png');
  final _animationSpriteSheet = SpriteSheet(
    imageName: 'minotaur.png',
    columns: 19,
    rows: 1,
    textureWidth: 96,
    textureHeight: 96,
  );
  _animation = _animationSpriteSheet.createAnimation(
    0,
    stepTime: 0.2,
    to: 5,
  );
  
  // 使用
       Container(
              width: 200,
              height: 200,
              child: AnimationWidget(animation: _animation),
            ),
複製程式碼

怎麼畫SVG動畫 SvgComponent

    Svg svg = Svg('android.svg');
    SvgComponent android = SvgComponent.fromSvg(100, 100, svg);
    android.x = 100;
    android.y = 100;
複製程式碼

怎麼畫組合控制元件 ComposedComponent

和原生組合控制元件相似的做法.

class GameOverPanel extends PositionComponent
    with Resizable, HasGameRef, Tapable, ComposedComponent {

  GameOverPanel(Image spriteImage) : super() {
    gameOverText = GameOverText(spriteImage);
    gameOverRestart = GameOverRestart(spriteImage);

    components..add(gameOverText)..add(gameOverRestart);
  }

  bool visible = false;

  GameOverText gameOverText;
  GameOverRestart gameOverRestart;

  @override
  void render(Canvas canvas) {
    if (visible) {
      super.render(canvas);
    }
  }
}

class GameOverText extends SpriteComponent with Resizable {
  GameOverText(Image spriteImage)
      : super.fromSprite(
    GameOverConfig.textWidth,
    GameOverConfig.textHeight,
    Sprite.fromImage(
      spriteImage,
      x: 955.0,
      y: 26.0,
      width: GameOverConfig.textWidth,
      height: GameOverConfig.textHeight,
    ),
  );

  @override
  void resize(Size size) {
    if (width > size.width * 0.8) {
      width = size.width * 0.8;
    }
    y = size.height * .25;
    x = (size.width / 2) - width / 2;
  }
}

class GameOverRestart extends SpriteComponent with Resizable {
  GameOverRestart(Image spriteImage)
      : super.fromSprite(
    GameOverConfig.restartWidth,
    GameOverConfig.restartHeight,
    Sprite.fromImage(
      spriteImage,
      x: 2.0,
      y: 2.0,
      width: GameOverConfig.restartWidth,
      height: GameOverConfig.restartHeight,
    ),
  );

  @override
  void resize(Size size) {
    y = size.height * .75;
    x = (size.width / 2) - GameOverConfig.restartWidth / 2;
![](https://user-gold-cdn.xitu.io/2020/6/19/172c89c8d5d4aff0?w=304&h=640&f=gif&s=3257550)
  }
}
複製程式碼

怎麼繪製視差動畫 ParallaxComponent

簡單來說就是怎麼把

Flutter Flame遊戲開發上手(1)
Flutter Flame遊戲開發上手(1)

Flutter Flame遊戲開發上手(1)

Flutter Flame遊戲開發上手(1)

Flutter Flame遊戲開發上手(1)

組合起來生成

Flutter Flame遊戲開發上手(1)

  final images = [
      ParallaxImage("bg.png"),
      ParallaxImage("mountain-far.png"),
      ParallaxImage("mountains.png"),
      ParallaxImage("trees.png"),
      ParallaxImage("foreground-trees.png"),
    ];

    final parallaxComponent = ParallaxComponent(images,
        baseSpeed: const Offset(20, 0), layerDelta: const Offset(100, 0));
複製程式碼

月亮&天空離我們遠所以移動得慢一些

樹木離我們近所以移動得快一些

組合起來很神奇,這個效果我很喜歡.TOT

怎麼畫可拉伸圖片(Android的.9.png圖) NineTileBox

如下例:

Flutter Flame遊戲開發上手(1)

// 定義
  final sprite = Sprite('nine-box.png');
    nineTileBox = NineTileBox(sprite, tileSize: 8, destTileSize: 24);
    
// 使用
 nineTileBox.draw(canvas, x, y, 200, 500);
複製程式碼

兩個角各佔4個畫素,合起來就是8個畫素, 圖片總共24個畫素. 這就是上面得引數得含義.

UE切圖的話需要保持圖片尺寸是3的倍數,另外這裡比起.9.png有個缺點是他必須保證4個角是對稱的. 對於非對稱,比如上面8個畫素 下面只需要4個畫素的圖片不支援.

物理引擎 Box2DComponent

Flutter Flame遊戲開發上手(1)

Flame支援Box2D.即元件 Box2DComponent.我理解就是可以實現物理體積 和 碰撞的能力.

這是一個很Powerful得元件,應該會有些深度,需要後面花時間仔細研究下.這裡先熟悉他的作用範圍就行.

網站:box2d.org/

Box2D是一款免費的開源二維物理引擎,由Erin Catto使用C++編寫,在zlib授權下發布。它已被用於蠟筆物理學、憤怒的小鳥、地獄邊境、Rolando、Fantastic ...

相關文章