へっぽこ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でラップしました。
実行するとこんな感じ。
いいですね。これで枠はほぼ完成かな?
長くなっちゃったので次回へ続く!
それでは!