通行料の仕組みを理解しよう
忙しい方はここだけ見て
この章で見る大事な場所は、ここです。
if (owner.ownerPlayerId == _currentPlayer.id) {
await _showLifeEventModal(
title: tile.label,
categoryLabel: 'OWN PROPERTY',
description: 'ここは自分が所有している物件です。通行料は発生しません。',
icon: Icons.home_work,
accentColor: AppColors.green,
);
_addLog('${_currentPlayer.name} は自分の物件 ${tile.label} に止まりました。');
_moveToNextPlayer();
return;
}
await _payRent(tile, owner);
意味はこうです。
物件マスに止まる
↓
所有者を確認する
↓
自分の物件なら通行料なし
↓
他の人の物件なら通行料を支払う
通行料を支払う処理は、ここです。
Future<void> _payRent(BoardTile tile, PropertyOwner owner) async {
final PlayerState payer = _currentPlayer;
final int ownerIndex = _players.indexWhere(
(PlayerState player) => player.id == owner.ownerPlayerId,
);
final PlayerState receiver = _players[ownerIndex];
_players[_currentPlayerIndex] = payer.copyWith(
cash: payer.cash - tile.rent,
);
_players[ownerIndex] = receiver.copyWith(
cash: receiver.cash + tile.rent,
);
setState(() {});
}
つまり、こうです。
止まった人のお金を減らす
↓
所有者のお金を増やす
この章でやること
この章では、他のプレイヤーが所有している物件に止まったときの「通行料」の仕組みを見ていきます。
人生ゲームでは、物件を買うだけではなく、他の人がその物件に止まることで収入が入ります。
物件を買う
↓
他の人が止まる
↓
通行料を受け取る
この仕組みが入ると、ゲームに「資産を持つ意味」が出てきます。
今日のゴール
この章のゴールは、次の3つです。
1. 自分の物件なら通行料が発生しないと分かる
2. 他人の物件なら通行料を支払うと分かる
3. 支払う人のcashが減り、所有者のcashが増えると分かる
難しい計算はありません。
基本は、お金を移動しているだけです。
Step 1:物件には通行料がある
物件マスには、rent があります。
BoardTile(
index: 2,
age: 23,
stage: '社会人スタート期',
label: '小さなカフェ',
description: '小さなカフェに投資できます。所有すると、他のプレイヤーが止まった時に通行料を得られます。',
type: TileType.property,
price: 300,
rent: 80,
),
大事なのはここです。
rent: 80,
これは、
他のプレイヤーが止まったら、80を支払う
という意味です。
Step 2:物件マスに止まると所有者を確認する
物件マスに止まると、まず所有者を確認します。
final PropertyOwner? owner = _findOwner(tile.index);
これは、
この物件は誰かが買っているか?
を調べています。
まだ誰も買っていない場合は、購入できるかどうかの処理に進みます。
すでに誰かが買っている場合は、通行料の処理に進みます。
Step 3:自分の物件なら通行料なし
所有者がいる場合でも、それが自分の物件なら通行料は発生しません。
if (owner.ownerPlayerId == _currentPlayer.id) {
await _showLifeEventModal(
title: tile.label,
categoryLabel: 'OWN PROPERTY',
description: 'ここは自分が所有している物件です。通行料は発生しません。',
icon: Icons.home_work,
accentColor: AppColors.green,
);
_addLog('${_currentPlayer.name} は自分の物件 ${tile.label} に止まりました。');
_moveToNextPlayer();
return;
}
意味はこうです。
物件の所有者ID
=
今のプレイヤーID
↓
自分の物件
↓
通行料なし
Step 4:他人の物件なら通行料を払う
自分の物件ではない場合は、この処理に進みます。
await _payRent(tile, owner);
これは、
通行料を支払う処理に進む
という意味です。
_payRent() の中で、お金の移動が行われます。
Step 5:_payRent()を探す
lib/main.dart を開いて、次の文字を検索します。
_payRent
このような関数があります。
Future<void> _payRent(BoardTile tile, PropertyOwner owner) async {
final PlayerState payer = _currentPlayer;
final int ownerIndex = _players.indexWhere(
(PlayerState player) => player.id == owner.ownerPlayerId,
);
if (ownerIndex == -1) {
_moveToNextPlayer();
return;
}
final PlayerState receiver = _players[ownerIndex];
await _showLifeEventModal(
title: tile.label,
categoryLabel: 'RENT PAYMENT',
subtitle: '${tile.age}歳|${tile.stage}',
description:
'${receiver.name} が所有している物件です。${payer.name} は通行料 ${tile.rent} を支払います。',
icon: Icons.currency_yen,
accentColor: AppColors.orange,
cashChange: -tile.rent,
);
_players[_currentPlayerIndex] = payer.copyWith(
cash: payer.cash - tile.rent,
);
_players[ownerIndex] = receiver.copyWith(
cash: receiver.cash + tile.rent,
);
_addLog('${payer.name} は ${receiver.name} に通行料 ${tile.rent} を支払いました。');
setState(() {});
_moveToNextPlayer();
}
少し長いですが、中心はとてもシンプルです。
支払う人を決める
↓
受け取る人を決める
↓
支払う人のお金を減らす
↓
受け取る人のお金を増やす
Step 6:支払う人を決める
最初に、支払う人を決めています。
final PlayerState payer = _currentPlayer;
payer は、通行料を支払う人です。
今のターンのプレイヤーが、他の人の物件に止まったので、その人が支払います。
payer = 今止まったプレイヤー
Step 7:受け取る人を探す
次に、所有者を探しています。
final int ownerIndex = _players.indexWhere(
(PlayerState player) => player.id == owner.ownerPlayerId,
);
これは、
プレイヤー一覧の中から
物件の所有者IDと同じ人を探す
という意味です。
見つかった人が、通行料を受け取る人です。
Step 8:receiverとは?
所有者が見つかったら、次のコードで受け取る人を取り出します。
final PlayerState receiver = _players[ownerIndex];
receiver は、通行料を受け取る人です。
payer = 支払う人
receiver = 受け取る人
この2つが分かれば、通行料処理はかなり理解しやすくなります。
Step 9:通行料の画面を表示する
お金を動かす前に、通行料の画面を表示します。
await _showLifeEventModal(
title: tile.label,
categoryLabel: 'RENT PAYMENT',
subtitle: '${tile.age}歳|${tile.stage}',
description:
'${receiver.name} が所有している物件です。${payer.name} は通行料 ${tile.rent} を支払います。',
icon: Icons.currency_yen,
accentColor: AppColors.orange,
cashChange: -tile.rent,
);
ここでは、画面にこう表示しています。
誰の物件か
誰が支払うか
いくら支払うか
cashChange: -tile.rent は、支払う人目線で「お金が減る」という意味です。
Step 10:支払う人のお金を減らす
実際にお金を減らしているのは、この部分です。
_players[_currentPlayerIndex] = payer.copyWith(
cash: payer.cash - tile.rent,
);
例えば、支払う人の所持金が1500で、通行料が80なら、
1500 - 80 = 1420
になります。
つまり、支払う人の現金が80減ります。
Step 11:受け取る人のお金を増やす
次に、所有者のお金を増やします。
_players[ownerIndex] = receiver.copyWith(
cash: receiver.cash + tile.rent,
);
例えば、受け取る人の所持金が1200で、通行料が80なら、
1200 + 80 = 1280
になります。
つまり、所有者の現金が80増えます。
Step 12:通行料は「お金の移動」
通行料は、ただお金が消えるのではありません。
支払う人から、所有者へ移動しています。
支払う人:-80
所有者:+80
全体で見ると、
プレイヤー間でお金が移動している
ということです。
この仕組みがあると、物件を買う意味が出てきます。
Step 13:ログを追加する
通行料を支払ったら、ログを追加します。
_addLog('${payer.name} は ${receiver.name} に通行料 ${tile.rent} を支払いました。');
例えば、こう表示されます。
Player 2 は Player 1 に通行料 80 を支払いました。
ログがあると、あとから何が起きたか確認しやすくなります。
Step 14:画面を更新して次のプレイヤーへ
お金を移動したあと、画面を更新します。
setState(() {});
そして、次のプレイヤーへ進みます。
_moveToNextPlayer();
流れはこうです。
通行料を支払う
↓
画面を更新する
↓
次のプレイヤーへ
Step 15:ownerIndex == -1 とは?
途中に、この処理があります。
if (ownerIndex == -1) {
_moveToNextPlayer();
return;
}
これは、念のための安全確認です。
indexWhere() は、見つからなかった場合に -1 を返します。
ownerIndex == -1
↓
所有者が見つからなかった
↓
安全のため次のプレイヤーへ
通常はあまり起きませんが、アプリが止まらないようにするための保険です。
Step 16:通行料を変えるには?
通行料は、物件マスの rent を変えます。
変更前です。
rent: 80,
高くしたい場合です。
rent: 150,
安くしたい場合です。
rent: 50,
_payRent() の中を直接変えるより、まずは _createTiles() の rent を変えるのがおすすめです。
触ってみよう
今回は、小さなカフェの通行料を変えてみましょう。
_createTiles() の中から、この物件を探します。
BoardTile(
index: 2,
age: 23,
stage: '社会人スタート期',
label: '小さなカフェ',
description: '小さなカフェに投資できます。所有すると、他のプレイヤーが止まった時に通行料を得られます。',
type: TileType.property,
price: 300,
rent: 80,
),
通行料を 80 から 120 にします。
rent: 120,
保存して、他のプレイヤーがその物件に止まったときに、120支払われれば成功です。
もう1つ触ってみよう
通行料を高くすると、ゲームの逆転が起きやすくなります。
例えば、こうします。
price: 300,
rent: 200,
この場合、安く買えて高い通行料が入るので、かなり強い物件になります。
バランスを取りたい場合は、価格も上げると自然です。
price: 600,
rent: 200,
よくあるエラーと直し方
1. rentを文字にしてしまった
悪い例です。
rent: '120',
正しくはこちらです。
rent: 120,
数字は ' で囲みません。
2. 自分の物件でも通行料が発生してしまう
自分の物件では通行料を払わないように、この処理があります。
if (owner.ownerPlayerId == _currentPlayer.id) {
// 自分の物件
}
これを消すと、自分の物件に止まっても通行料処理に進む可能性があります。
初心者のうちは消さないようにしましょう。
3. payerとreceiverを逆にしてしまう
支払う人と受け取る人を逆にすると、お金の流れが逆になります。
正しい考え方です。
payer = 止まった人
receiver = 物件の所有者
お金の動きはこうです。
cash: payer.cash - tile.rent
cash: receiver.cash + tile.rent
4. setStateを消してしまった
通行料を支払ったあとには、画面更新が必要です。
setState(() {});
これを消すと、データは変わっていても画面の表示がすぐ変わらないことがあります。
5. _moveToNextPlayer()を消してしまった
最後には、次のプレイヤーへ進みます。
_moveToNextPlayer();
これを消すと、ターンが進まなくなることがあります。
この章で覚えること
この章で覚えることは、3つだけです。
1. rent は通行料
2. 他人の物件に止まると通行料を支払う
3. 支払う人のcashが減り、所有者のcashが増える
まずはこれだけで大丈夫です。
やる気を維持するコツ
通行料が入ると、ゲームに「戦略」が生まれます。
ただサイコロを振るだけではなく、
どの物件を買うか
どの物件の通行料を高くするか
安い物件を増やすか
高い物件を少なくするか
を考えられるようになります。
これは、ゲーム制作でとても大事な「バランス調整」です。
最初は難しく考えず、rent の数字を少し変えて遊んでみましょう。
チェックリスト
□ rent が通行料だと分かった
□ _handlePropertyTile() で所有者を確認していると分かった
□ 自分の物件なら通行料なしだと分かった
□ 他人の物件なら _payRent() に進むと分かった
□ payer が支払う人だと分かった
□ receiver が受け取る人だと分かった
□ payer.cash - tile.rent の意味が分かった
□ receiver.cash + tile.rent の意味が分かった
□ rent を変更して動作確認した
まとめ
この章では、通行料の仕組みを確認しました。
物件の通行料は rent で決まります。
自分の物件に止まった場合は、通行料は発生しません。
他のプレイヤーの物件に止まった場合は、止まった人の cash が減り、所有者の cash が増えます。
次の章では、イベントモーダルを作って、給料・税金・イベント・通行料などを画面に分かりやすく表示する仕組みを見ていきます。
