Flutter で将棋ゲームを作ろう #1 将棋盤を実装しよう
はじめに
こんにちは!水瀬ひろと申します。
趣味で Flutter を楽しんでいるエンジニアです。
僕はソシャゲ(特にHoYoverse作品)が好きで、自分でもゲームを作ってみたいと思い、まずは将棋ゲームに挑戦してみました。
Flame などのライブラリは使用せず、自前実装のみでおこなったので学びが多かったです。
将棋ゲームの制作を通じて、以下のようなことを学ぶことができたので、シリーズ化して残しておこうと思います。
- 将棋ゲームの作り方やルールの理解
- 応用としてボードゲーム系アプリの作成手法の習得
- ゲームの基本的な動作原理についての理解
このシリーズでは、実際に行った実装内容を細かく解説していきます。
最終的には以下のような将棋ゲームが完成する予定です!
まずは準備を
実際に将棋盤を実装する前にいくつか確認しておきます。
実際の将棋盤はこちらですね。
縦9マス、横9マスの計81マスの正方形が作れれば良さそうです。
コードを書く前のプロジェクトの状態はこちら。
Flutter プロジェクトを作ったばかりの状態から不要なものを取り除いています。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false, // デバッグラベル非表示
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
body: Center(),
);
}
}
この状態だとまだ画面は真っ白です。
擬似将棋版となる配列を作る
実装に入ります。
まずは 9 × 9 = 81 マスを管理するための配列を用意します。
class _MyHomePageState extends State<MyHomePage> {
late List<List<dynamic>> shogiBoard; // 盤面管理用の配列
Widget build(BuildContext context) {
return Scaffold(
body: Center(),
);
}
}
そしてアプリの初期化時に、配列も初期化されるように書いていきます。
class _MyHomePageState extends State<MyHomePage> {
late List<List<dynamic>> shogiBoard; // 盤面管理用の配列
void initState() {
super.initState();
initializeBoard(); // ゲーム開始時に盤面を初期化する
}
// 盤面の初期化
void initializeBoard() {
// 9 × 9 の配列
List<List<dynamic>> newBoard = List.generate(9, (index) => List.generate(9, (index) => null));
// いずれここで駒の初期配置などをおこなう
shogiBoard = newBoard;
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(),
);
}
}
重要な部分を抜き出します。
List<List<dynamic>> newBoard = List.generate(9, (index) => List.generate(9, (index) => null));
これにより以下のような配列の枠が作られます。
まるで将棋盤ですね。
今はすべてに null
が入っていますが、
そに駒となるオブジェクトを入れ、移動させることで将棋を実装していきます。
擬似将棋盤を表示する
将棋盤のようなものを表示させるには GridView を使います。
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GridView.builder(
shrinkWrap: true, // GridView のサイズを itemBuilder で表示するコンテンツによって決定
itemCount: 9 * 9,
physics: const NeverScrollableScrollPhysics(), // スクロールさせない
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 9), // 9列で表示
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 1.0),
),
child: Center(
child: Text(index.toString()),
)
);
},
),
],
)
);
}
こんな表示になりますね。
デザインに関わるコードも増えていますが、ここで重要なのは以下。
itemCount: 9 * 9
81マスのグリッドを表示するために定義しています。
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 9)
一列を9マスにするために定義しています。
もっと将棋版っぽくしてみる
今のままだとただのグリッド表示なので、見た目を少し将棋盤に近づけて今回は終わりにしたいと思います。
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GridView.builder(
shrinkWrap: true, // GridView のサイズを itemBuilder で表示するコンテンツによって決定
itemCount: 9 * 9,
physics: const NeverScrollableScrollPhysics(), // スクロールさせない
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 9), // 9列で表示
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.orange[100], // 背景を薄めのオレンジ色に
border: Border.all(color: Colors.black, width: 1.0),
),
child: Center(
child: Text(""), // インデックス表示を削除
)
);
},
),
],
)
);
}
色をつけただけですが、将棋盤に見えなくもないですね。
終わりに
ここまでお疲れさまでした〜!
今回は複雑なロジックもなく、比較的簡単な内容だったのではないでしょうか。
質問などあれば気軽にコメントください!
次回は駒を配置していきます。
その後も以下のような記事を準備しているのでお付き合いいただけると嬉しいです。
- 駒を動かす
- 相手の駒を取る
- 持ち駒を盤面に出す
- 「成り」の実装
- 王手の判定
- 詰みの判定
- CPUを実装する
- 「二歩」の禁止
- 棋譜を記録する
Threads
Discussion