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

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

YouTubeのチュートリアル動画を使った2Dゲーム(テトリス)モバイルアプリ開発が順調に進んでいます。前回は画面の枠を作るとこまで行きましたので、今回はいよいよゲームの中身の部分を作っていきます。

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

 

チュートリアル動画はこちら

 

Game部分の枠を作る

Game部分の実装用に新しいファイルを作成します。

lib/game.dart

import 'package:flutter/material.dart';

const BLOCKS_X = 10;
const BLOCKS_Y = 20;

class Game extends StatefulWidget {
  @override
  State createState() => _GameState();
}

class _GameState extends State {
  @override
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: BLOCKS_X / BLOCKS_Y, //高さに対する幅の比率
      child: Container(
        decoration: BoxDecoration(
          color: Colors.indigo[800],
          border: Border.all(
            width: 2.0,
            color: Colors.indigoAccent
          ),
          borderRadius: BorderRadius.all(Radius.circular(10.0)),
        ),
      ),
    );
  }
}

 

AspectRatioというのは、高さに対する幅の比率です。デバイスによって高さや幅は異なるので、比率で指定しておきます。ここでは10:20にしてあります。(1:2でもいいかもしれん。けど後で使うのかもしれんからそのまま残す)

 

次に、main.dartに、この作成したGame クラスWedgetを配置します。

lib/main.dart

            Expanded(
              child:Row(
                children: [
                  Flexible(
                    flex: 3,
                    child: Padding(
                      padding: EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 10.0),
                      child: Game()//ゲームwidgetに置き換える
                    ),
                  ),
                  Flexible(
                    flex: 1,
                    child: Padding(
                      padding: EdgeInsets.fromLTRB(5.0, 10.0, 10.0, 10.0),
                      child:Container(
                          color: Colors.green,
                      ),
                    ),
                  ),
                ],
              ),
            ),

これだけですね。Container(…)の部分をGame()に置き換えました。

実行すると。

はい、OKですね。左の赤かった部分が、青い四角に置き換わりました。

 

次のブロックを表示する部分の枠を作る

右の緑色の部分に、次に落ちてくるブロックを表示する枠を作っていきます。

実装用に新しいファイルを作成します。

lib/next_block.dart

import 'package:flutter/material.dart';

class NextBlock extends StatefulWidget {
  @override
  State createState() => _NextBlockState();
}
class _NextBlockState extends State {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(5),
        color: Colors.white,
      ),
      width: double.infinity, //使用可能な最大幅
      padding: EdgeInsets.all(5.0),
      child: Column( //さらに領域を縦に3つに分ける
        children: [
          Text(  //1つ目の領域
            'Next',
            style: TextStyle(
              fontWeight: FontWeight.bold,
            ),
          ),
          SizedBox(height: 5,), //2つ目の領域(1つ目と3つ目のスキマ)
          AspectRatio( //3つ目の領域(ここに次のブロックを表示する
            aspectRatio: 1, //正方形にする
            child: Container(
              color: Colors.indigo[600],
            ),
          ),
        ],
      )
    );
  }
}

次に、main.dartに、この作成したNextBlock クラスWedgetを配置します。

lib/main.dart

            Expanded(
              child:Row(
                children: [
                  Flexible(
                    flex: 3,
                    child: Padding(
                      padding: EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 10.0),
                      child: Game()//ゲームwidgetに置き換える
                    ),
                  ),
                  Flexible(
                    flex: 1,
                    child: Padding(
                      padding: EdgeInsets.fromLTRB(5.0, 10.0, 10.0, 10.0),
                      child:NextBlock(),//次のブロック用widgetに置き換える
                    ),
                  ),
                ],
              ),
            ),

実行してみます。

いい感じですね。

レイアウトを調整する

mainAxisSizeを使って軸方向にサイズを最小化する

今のままでは、白い部分がビヨーンて上下に伸びちゃってて、左のGameエリアと高さが揃っていません。かっこ悪いので直します。

lib/next_block.dart

      child: Column(
        mainAxisSize: MainAxisSize.min, //軸方向のサイズを最小に指定する
        children: [
          Text(
            'Next',
            style: TextStyle(
              fontWeight: FontWeight.bold,
            ),
          ),
          SizedBox(height: 5,), //余白
          AspectRatio(
            aspectRatio: 1, //正方形にする
            child: Container(
              color: Colors.indigo[600],
            ),
          ),
        ],
      )

ちなみに、mainAxisSizeにおける「軸の方向」というのは、Columnだったら縦、Rowだったら横です。今回はColumnなので、縦方向に最小化されました。

こんな感じ。

crossAxisAlignmentで上揃えにする

次に、Gameエリアと次のブロックエリアが中央揃えになっているので、上に揃えます。

lib/main.dart

            Expanded(
              child:Row(
                crossAxisAlignment: CrossAxisAlignment.start, //軸方向の開始位置に揃える
                children: [
                  Flexible(
                    flex: 3,
                    child: Padding(
                      padding: EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 10.0),
                      child: Game()//ゲームwidgetに置き換える
                    ),
                  ),
                  Flexible(
                    flex: 1,
                    child: Padding(
                      padding: EdgeInsets.fromLTRB(5.0, 10.0, 10.0, 10.0),
                      child:NextBlock(),
                    ),
                  ),
                ],
              ),
            ),

CrossAxisAlignment.startはColumnなら右寄せ、Rowなら上揃えになります。今回はRowなので上揃えになります。

こんな感じ。

 

行全体を中央寄せする

次に、Gameエリア次のブロックエリアが乗っている行全体を中央寄せします。

lib/main.dart

            Expanded(
              child: Center(
                child:Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Flexible(
                      flex: 3,
                      child: Padding(
                          padding: EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 10.0),
                          child: Game()//ゲームwidgetに置き換える
                      ),
                    ),
                    Flexible(
                      flex: 1,
                      child: Padding(
                        padding: EdgeInsets.fromLTRB(5.0, 10.0, 10.0, 10.0),
                        child:NextBlock(),
                      ),
                    ),
                  ],
                ),
              ),
            ),

Row Widget全体をCenter Widgetでラップしました。

実行するとこんな感じ。

 

いいですね。これで枠はほぼ完成かな?

長くなっちゃったので次回へ続く!

それでは!

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