Flutterアプリケーション開発概論

ゲームで使うデータを整理しよう

6人生ゲームを作る。サイコロ処理・イベント分岐・プレイヤー管理・データ設計・アニメーション
FlutteriOSAndroidMacOSWindows基礎から学ぶ開発アプリ開発

忙しい方はここだけ見て

この章で見るのは、ゲームの「材料」です。

今回の人生ゲームは、主にこのデータでできています。

BoardTile      人生のマス
PlayerState    プレイヤー情報
PropertyOwner  物件の所有者
GameLog        ゲームの記録
GameResult     最終結果・順位
EventInfo      ランダムイベント

コードでは、このあたりを見ます。

class BoardTile
class PlayerState
class PropertyOwner
class GameLog
class GameResult
class EventInfo

まずは、こう覚えればOKです。

ゲームは、データを用意して、そのデータを画面に表示している

この章でやること

この章では、人生ゲームで使うデータを整理します。

ゲームアプリは、見た目だけでできているわけではありません。

裏側には、次のような情報があります。

今、誰のターンか
プレイヤーがどこにいるか
所持金はいくらか
どんなマスがあるか
物件を誰が持っているか
イベントでお金が増えるか減るか

これらをコードの中で整理しているのが、データクラスです。


今日のゴール

この章のゴールは、次の3つです。

1. ゲームにはデータが必要だと分かる
2. BoardTileとPlayerStateの役割が分かる
3. classは情報をまとめる箱だと分かる

全部を暗記しなくて大丈夫です。

まずは、「どのclassが何を担当しているか」だけ分かればOKです。


Step 1:データとは何かを知ろう

ゲームでいうデータとは、ゲームを動かすための情報です。

例えば、プレイヤーには次の情報が必要です。

名前
色
現在地
所持金
持っている物件
ゴールしたかどうか

人生のマスにも情報が必要です。

何歳のマスか
マスの名前
説明文
給料マスなのか
物件マスなのか
イベントマスなのか

このような情報をまとめておくことで、ゲームを動かせます。


Step 2:classは情報をまとめる箱

Dartでは、情報をまとめるときに class を使います。

例えば、人生のマスは BoardTile というclassでまとめています。

class BoardTile {
  const BoardTile({
    required this.index,
    required this.age,
    required this.stage,
    required this.label,
    required this.description,
    required this.type,
    this.price = 0,
    this.rent = 0,
  });

  final int index;
  final int age;
  final String stage;
  final String label;
  final String description;
  final TileType type;
  final int price;
  final int rent;
}

最初は、こう考えればOKです。

BoardTile = 人生のマス情報を入れる箱

Step 3:BoardTileを理解しよう

BoardTile は、人生ゲームの1マス分の情報です。

例えば、コードにはこのようなマスがあります。

BoardTile(
  index: 2,
  age: 23,
  stage: '社会人スタート期',
  label: '小さなカフェ',
  description: '小さなカフェに投資できます。',
  type: TileType.property,
  price: 300,
  rent: 80,
),

これは、次の意味です。

項目意味
index何番目のマスか
age何歳のマスか
stage人生の時期
labelマスの名前
description説明文
typeマスの種類
price物件価格
rent通行料

物件マスなので、pricerent があります。


Step 4:TileTypeを理解しよう

マスの種類は、TileType で決めています。

enum TileType {
  start,
  normal,
  payday,
  event,
  property,
  tax,
  goal,
}

それぞれの意味はこちらです。

種類意味
startスタート
normal通常マス
payday給料マス
eventイベントマス
property物件マス
tax税金マス
goalゴール

enum は、選択肢をまとめるためのものです。

この場合は、

マスの種類を決める選択肢

として使っています。


Step 5:PlayerStateを理解しよう

次に、プレイヤーの情報です。

class PlayerState {
  const PlayerState({
    required this.id,
    required this.name,
    required this.color,
    required this.position,
    required this.cash,
    required this.properties,
    required this.isFinished,
  });

  final int id;
  final String name;
  final Color color;
  final int position;
  final int cash;
  final Set<int> properties;
  final bool isFinished;
}

これは、プレイヤー1人分の情報をまとめています。

PlayerState = プレイヤー情報を入れる箱

Step 6:PlayerStateの中身を見る

プレイヤーには、次の情報があります。

項目意味
idプレイヤー番号
nameプレイヤー名
colorコマの色
position今いるマス
cash所持金
properties持っている物件
isFinishedゴールしたか

例えば、最初のプレイヤーはこう作られています。

PlayerState(
  id: 0,
  name: 'Player 1',
  color: AppColors.red,
  position: 0,
  cash: 1500,
  properties: <int>{},
  isFinished: false,
),

意味はこうです。

Player 1
赤いコマ
0番目のマスにいる
所持金は1500
物件はまだ持っていない
ゴールしていない

Step 7:copyWithを理解しよう

PlayerState の中には、copyWith という関数があります。

PlayerState copyWith({
  int? position,
  int? cash,
  Set<int>? properties,
  bool? isFinished,
}) {
  return PlayerState(
    id: id,
    name: name,
    color: color,
    position: position ?? this.position,
    cash: cash ?? this.cash,
    properties: properties ?? this.properties,
    isFinished: isFinished ?? this.isFinished,
  );
}

これは、プレイヤー情報の一部だけを変えるための便利な関数です。

例えば、所持金だけ変えたいときに使います。

player.copyWith(cash: player.cash + 300)

これは、

今のプレイヤー情報をもとにして
所持金だけ300増やした新しいデータを作る

という意味です。


Step 8:PropertyOwnerを理解しよう

物件の所有者は、PropertyOwner で管理します。

class PropertyOwner {
  const PropertyOwner({
    required this.tileIndex,
    required this.ownerPlayerId,
  });

  final int tileIndex;
  final int ownerPlayerId;
}

これは、次の情報を持っています。

項目意味
tileIndexどのマスの物件か
ownerPlayerId誰が所有しているか

例えば、

2番目のマスを、Player 1が持っている

という情報を保存できます。


Step 9:GameLogを理解しよう

ゲーム中の記録は、GameLog で管理します。

class GameLog {
  const GameLog(this.message);

  final String message;
}

例えば、こういう記録です。

Player 1 が 3 を出しました。
Player 2 は小さなカフェを購入しました。
Player 1 は通行料 80 を支払いました。

ゲームログがあると、何が起きたか分かりやすくなります。


Step 10:GameResultを理解しよう

ゲーム終了後の順位は、GameResult で管理します。

class GameResult {
  const GameResult({
    required this.player,
    required this.totalAssets,
  });

  final PlayerState player;
  final int totalAssets;
}

これは、次の情報を持っています。

項目意味
playerどのプレイヤーか
totalAssets最終資産

最終資産は、この考え方で計算します。

最終資産 = 現金 + 持っている物件の価格

Step 11:EventInfoを理解しよう

ランダムイベントは、EventInfo で管理します。

class EventInfo {
  const EventInfo({
    required this.title,
    required this.description,
    required this.cashChange,
    required this.category,
  });

  final String title;
  final String description;
  final int cashChange;
  final EventCategory category;
}

例えば、こういうイベントです。

EventInfo(
  title: '副業が大成功',
  description: '週末に始めた小さな副業が話題になりました。',
  cashChange: 300,
  category: EventCategory.work,
),

意味はこうです。

イベント名:副業が大成功
説明文:週末の副業が話題になった
お金の変化:+300
カテゴリ:仕事

Step 12:EventCategoryを理解しよう

イベントの種類は、EventCategory で分けています。

enum EventCategory {
  income,
  expense,
  work,
  health,
  family,
  investment,
  chance,
  trouble,
}

意味はこちらです。

種類意味
income収入
expense出費
work仕事
health健康
family家族
investment投資
chanceチャンス
troubleトラブル

イベントをカテゴリ分けしておくと、色やアイコンを変えやすくなります。


Step 13:GamePhaseを理解しよう

ゲームの状態は、GamePhase で管理しています。

enum GamePhase {
  waitingRoll,
  rolling,
  showingModal,
  choosingProperty,
  gameOver,
}

意味はこちらです。

状態意味
waitingRollサイコロ待ち
rollingサイコロ中
showingModalイベント画面表示中
choosingProperty物件購入を選択中
gameOverゲーム終了

これがあることで、

今はサイコロを押してよいか
購入ボタンを出すべきか
ゲーム終了画面を出すべきか

を判断できます。


Step 14:データの関係をざっくり見る

今回のゲームは、だいたいこのような関係です。

BoardTile
↓
人生のマス

PlayerState
↓
プレイヤーの状態

EventInfo
↓
ランダムイベント

PropertyOwner
↓
物件の所有者

GameResult
↓
最終結果

GameLog
↓
ゲームの記録

このデータを使って、画面を作ったり、ゲームを進めたりしています。


Step 15:どこを編集しやすい?

初心者が最初に編集しやすいのは、この3つです。

編集するもの場所
マスの内容_createTiles()
プレイヤー名_createInitialPlayers()
イベント内容_createEventPool()

例えば、プレイヤー名を変えるならここです。

name: 'Player 1',

これを変えます。

name: '太郎',

まずは文字を変えるところから始めると、壊れにくいです。


触ってみよう

今回は、プレイヤー名だけ変えてみましょう。

_createInitialPlayers() を探します。

変更前です。

name: 'Player 1',

変更後です。

name: '太郎',

もう1人も変えてみます。

name: '花子',

保存して、アプリを再読み込みします。

r

画面に「太郎」「花子」と表示されたら成功です。


よくあるエラーと直し方

1. カンマを消してしまった

Dartでは、項目の区切りにカンマが必要です。

悪い例です。

name: '太郎'
color: AppColors.red,

正しくはこちらです。

name: '太郎',
color: AppColors.red,

文字を変えるときは、カンマを消さないようにしましょう。


2. クォーテーションを消してしまった

文字は ' で囲みます。

悪い例です。

name: 太郎,

正しくはこちらです。

name: '太郎',

文字を変えるときは、前後の ' を残します。


3. 数字に文字を入れてしまった

cash は数字です。

悪い例です。

cash: '1500',

正しくはこちらです。

cash: 1500,

数字は ' で囲みません。


4. typeを適当に変えてしまった

type は、決まった選択肢から選びます。

正しい例です。

type: TileType.event,

悪い例です。

type: 'event',

TileType.event のように書きます。


この章で覚えること

この章で覚えることは、3つだけです。

1. classは情報をまとめる箱
2. enumは選択肢をまとめるもの
3. ゲームはデータを使って動いている

まずはこれだけで大丈夫です。


やる気を維持するコツ

ゲーム開発は、データが分かると一気に楽しくなります。

なぜなら、文字を変えるだけで別のゲームにできるからです。

人生ゲーム
↓
投資ゲーム
↓
キャリアゲーム
↓
学校生活ゲーム
↓
店舗経営ゲーム

最初は難しい処理を書かなくても大丈夫です。

まずは、マスの名前やイベント名を変えて、自分だけのゲームに近づけていきましょう。


チェックリスト

□ BoardTileが人生のマスだと分かった
□ PlayerStateがプレイヤー情報だと分かった
□ PropertyOwnerが物件所有者だと分かった
□ GameLogがゲーム記録だと分かった
□ GameResultが結果表示用だと分かった
□ EventInfoがイベント情報だと分かった
□ TileTypeがマスの種類だと分かった
□ GamePhaseがゲームの状態だと分かった
□ EventCategoryがイベントの分類だと分かった
□ プレイヤー名を変更してみた

まとめ

この章では、ゲームで使うデータを整理しました。

人生ゲームは、画面だけで動いているのではありません。

BoardTilePlayerStateEventInfo などのデータを使って、マス、プレイヤー、イベントを管理しています。

class は情報をまとめる箱、enum は選択肢をまとめるものです。

次の章では、_createTiles() を見ながら、人生のマスを作っていきます。

教材トップへ戻る