へっぽこSEが◯時間でFlutter(iOS、android、web)で2Dゲームアプリを作ってみる(7)

どうも、すえきあおいです。

YouTubeのチュートリアル動画を使った2Dゲーム(テトリス)モバイルアプリ開発が順調に進んでいます。前回はUIレイアウトを完成させるところまでやりました。

今回からは、いよいよブロックの実装に入っていきます。

フルのソースコードはこちらにあるので、面倒な人はダウンロードどうぞ。

 

今回からはチュートリアル動画も進みまして、こちらになります。今日の内容は、こちらの動画の0:00〜9:55までの内容になっています。

 

テトリスの「ブロック」を構成する「サブブロック」を作る

サブブロックというのはこれ。

テトリスのブロックって四つの正方形の並びでいろんな形を作っています。この正方形のことを、今回のチュートリアルでは「サブブロック」と呼んでいます。

それではいってみよー

 

サブブロックを定義するのに必要なのは、この3つ。

  • x座標
  • y座標

サブブロック用に新しいファイルを作り、それぞれを定義してコンストラクタを作ります。

lib/sub_block.dart

import 'package:flutter/material.dart';

class SubBlock {
  int x;
  int y;
  Color color;

  SubBlock(int x, int y, [Color color = Colors.transparent]) {
    this.x = x;
    this.y = y;
    this.color = color;
  }
}

色はデフォルトではtransparent(透明)にしておきます。

 

サブブロックを組み合わせてブロックを作る

次に、サブブロックを組み合わせて作るブロックを作ります。

ブロックに必要なのは、以下3つ。

  • 4つの方向(上下左右)に回転するサブブロック
  • x座標
  • y座標
  • 現在の方向

新しいファイルを作り、またそれぞれを定義してブロッククラスとそのコンストラクタを作ります。

とりあえずソース貼りますね。解説はコメントに書きました。

lib/block.dart

import 'package:flutter/material.dart';
import 'sub_block.dart';

enum BlockMovement {
  UP,
  DOWN,
  LEFT,
  RIGHT,
  ROTATE_CLOCKWISE, //時計回り
  ROTATE_COUNTER_CLOCKWISE //反時計回り
}

class Block {
  // 4つの方向(orientationIndex)と
  // 7つのブロック(Tブロック、Lブロックなど)からなる二次元配列
  List<List<SubBlock>> orientations = List<List<SubBlock>>();
  // 座標
  int x;
  int y;
  // ブロックの現在の向き
  int orientationIndex;

  // コンストラクタ(4つの方向のブロック、色、ブロックの現在の向き)
  Block(this.orientations, Color color, this.orientationIndex) {
    // 落とすブロックを最初に表示する座標
    x = 3;
    y = -height - 1; //最初のy座標は負の高さに設定

    this.color = color;
  }

  //ブロックの色をセットする
  set color(Color color){
    orientations.forEach((orientation) {
      orientation.forEach((subBlock) {
        //ゲームが進むとブロックが消えてバラバラになるので、
        //サブブロックにも色を持たせておく
        subBlock.color = color;
      });
    });
  }

  //ブロックの色を取得する
  get color {
    //最初の向きの最初のサブブロックの色を返す
    return orientations[0][0];
  }

  // 現在の向き(orientationIndex)のサブブロックを取得する
  get subBlocks {
    return orientations[orientationIndex];
  }

  // ブロックの幅を取得
  // ブロックの向きによって幅は変わるので、セッターはない(任意の幅を与えることはできない)
  get width {
    int maxX = 0;
    // 0からスタートし、ブロックに含まれるサブブロックのx座標をループで読む。
    subBlocks.forEach((subBlock){
      // 最大値=ブロックの幅
      if (subBlock.x > maxX) maxX = subBlock.x;
    });
    return maxX + 1; //0からスタートしているので+1する
  }

  // ブロックの高さを取得
  // ブロックの向きによって幅は異なるので、セッターはない
  get height {
    int maxY = 0;
    subBlocks.forEach((subBlock){
      if (subBlock.y > maxY) maxY = subBlock.y;
    });
    return maxY + 1;
  }

  //ブロックの動き(上下左右、回転)で座標や方向をセットする
  void move (BlockMovement blockMovement){
    switch (blockMovement){
      case BlockMovement.UP:
        y -= 1;
        break;
      case BlockMovement.DOWN:
        y += 1;
        break;
      case BlockMovement.LEFT:
        x -= 1;
        break;
      case BlockMovement.RIGHT:
        x += 1;
        break;
      case BlockMovement.ROTATE_CLOCKWISE:
        orientationIndex = ++orientationIndex % 4; //4で割った余り(0,1,2,3)
        break;
      case BlockMovement.ROTATE_COUNTER_CLOCKWISE:
        orientationIndex = (orientationIndex + 3) % 4;
        break;

    }

  }
}

 

いろいろありつつここまでなんとか動画チュートリアルみながら実装したのですが、正直難しかったw

ポイント① ブロックを回転させるか、内部的に4つの方向のリストを持っておくか

テトリスでブロックが回転する動きを描画する時、回転させると幅や高さが変わるので、枠からはみ出してしまわないように制御する必要があります。

その方法には2つのパターンがあります。

  1. ブロックは一つのオブジェクトとしておき、それを90度ずつ回して表示を変えていく(都度計算して、はみ出す場合は逆回転にする)
  2. 1つのブロックを内部的に4つのオブジェクト(方向が違うだけ)として持っておき、幅と高さも予め持っておく。で、回転と同時に表示するオブジェクトを変える

このチュートリアルでは、2の方法で実装しています。

これが分かってないと、たぶんコード読んでも意味不明になると思います。

 

ポイント② List<List<SubBlock>> orientations が何を表しているのか?

ズバリ、これです。

つまり、各ブロックの向き(4つ)×ブロックの形(7つ)の二次元配列です。

ブロックのサブブロックを取得すると、4つの向きのサブブロックが取れるんですね。

 

・List<SubBlock>:subBlockの塊=ブロック。どの形かはまだ決まってない

・ブロックの向き(4つ)=orientation

 

ポイント② ブロックの幅と高さは、ブロックの向きによって変わる

上の図は回転させると幅2になります。なので、セッター(任意の値をセットする)は作りません。

幅と高さは、ブロックに含まれるサブブロックを0からループさせて取得します。なので、幅はx座標の最大値と同値になります。

 

 

まぁ、いろいろ説明しましたが、ここまではさらっと目を通すだけでいいかもしれません。

あんまり長々と時間かけてもしょうがないので、次回、とりあえず一つブロック作ってみて、動かしながら理解していきましょう。

次回はチュートリアル動画の9:57からスタートです。

それでは!

末岐 碧衣
  • 末岐 碧衣
  • フリーランス のシステムエンジニア。独立後、一度も営業せずに月収 96 万円を達成。1986年大阪生まれ。早稲田大学理工学部卒。システムエンジニア歴 12年。
    2009年、ITコンサルティング企業に入社。3年目でコミュ障が爆発し人間関係が崩壊。うつにより休職するも、復帰後はコミュ障の自覚を持ち、「チームプレイ」を徹底的に避け、会社組織内においても「一人でできる仕事」に専念。社内外から評価を得た。
    無理に「チームプレイ」するよりも「一人でできる仕事」に専念した方が自分も周囲も幸せにできることを確信し、2015年フリーランスとして独立。