前回で,地図の上を主人公( プレーヤ )が移動できるようになりました.
さらに移動できない場所( 岩 )を設定し,RPGのフィールド移動の基本ができあがりました.
今回は「鍵」を設定しましょう.
「ある場所で鍵を拾う」「鍵を持ってゴール地点に行けば,その面はクリア」
というプロット( 物語上の設定 )を考えます.
プレーヤがゴール地点に到着した際に,次の判定条件をつけます.
- 鍵を持っていない:何も起こらない
- 鍵を持っている:「ゲーム画面クリア」イベントが起こる
判定には「鍵を持っているか否か」という「状態の情報」が必要です.
状態の情報には「フラグ( 旗 )」というブール値( Boolean: 0/1の2状態のみを持つ )
を用いて管理します.
これには,スポーツの審判( 線審など )が持っている旗をイメージして下さい.
普段は旗を降ろしていますが,条件を満たした時( ボールがアウトした時 )に旗を揚げますね.
それと同じです.実際には変数の値に"0"か"1"を与え,それを判定に使います.
例えば "flag_key"という変数を使うとすると,こうなります.
- 初期状態( =鍵は持ってない )では旗は降りてる( False ): flag_key = 0
- 条件を満たしたら( =鍵を持った )旗を揚げる( True ):flag_key = 1
※追補1※ なぜ旗の上げ下げをイメージするかと言うと,フラグのブール値"0/1"と,
扱う論理の真偽が逆になる事があるからです.
◎例えばCPU内部のステータスレジスタには「ゼロフラグ( ZF )」と呼ばれるフラグがあります.
これは演算結果がゼロになったら立つので,これを"0/1"で説明すると混乱します.
◎また,ゲームで敵を全部やっつけたかどうかを判定する場合に,「敵がいない」
というフラグを設定すると,「敵がいる時=False/いなくなった時=True」になります.
これを"0/1"で考えるとややこしいですね.
※追補2※ なお,今回は判定対象が1つだけであり,"flag_key" という変数で扱っていますが,
複雑なプログラムでは多くのフラグを使い分けます.( 後述「フラグ変数の命名法」参照 )
フラグを一括して扱うほうがよい場合は配列変数( リスト )を使い,
「何番のフラグ」という形で扱います.
※また,フラグは"0/1"のブール値ですが,状態判定に"0/1/2/3"等の多値論理を使うこともあります.
この場合はフラグではなく「ステータス( status )」と呼びます.
※閑話休題※ 最近の小説やドラマ・マンガでは「恋愛フラグ」「死亡フラグ」など,よく使われますね.
「この戦いが終わったら彼女にプロポーズするんだ」「フラグ立てるなよ!」
「あの子の目,キレイだな~」「フラグ立ったなこれは」
これはプログラミング用語,特にゲームプログラムから来ているのですね.
課題 11-1
教科書§6-3,pp.186-188「鍵を拾ってフラグを立てる」を読み,chap6.pyを更新しましょう.
鍵を拾えるようになりましたか?
【フラグ変数の命名法】
今回のソース中で使うフラグ変数は"flag_key"ですが,フラグの数が複数になった場合,
状態を示すのにわかりやすい変数名をつけるべきでしょう.
変数命名の「流儀」は色々ありますが,例を挙げておきます.
- (保持)has を使う:"has_key":鍵を持っているか.持っていれば"True(=1)"
- (完了)過去分詞形を使う:"is_completed":ある事が完了してるか
- (存在)isやexistsを使う:"emeny_exists"/"is_enemy":敵がまだ残っているか
- (状態)isを使う:"is_visible":見える状態か
上記はいずれも一種の疑問/確認の意味を持つため,「YesならTrue」と,
変数名から論理を理解することができ,使い方を間違えにくくなります.
一方で,"flg_A""flag_B"……といった抽象的な命名だとどうでしょうか.
これだけでは状態の意味( 鍵を持ってるかどうか,敵がいるかどうか等 )が不明瞭ですね.
プログラム中でその変数を使う際にいちいち,フラグ状態の意味を確認する必要があります.
これではフラグの値の間違った使い方,いわゆる「論理エラー」を誘発しやすくなります.
また,ソースコードの可読性も低下します.
Boolean値を扱うフラグ変数の命名には注意しましょう.
鍵のフラグの扱いができたので,次はそれを使ったゴールのイベントを考えます.
- 条件を満たしてゴールした時は,エンディングの画面を表示する
- 画面に文字を表示するには,create_text 関数を用いる
- その後ゲームを続ける必要がないので,ボタンを無効化( ["state"] = "disabled" )する
課題 11-2
教科書pp.189-192「ゴールに到着したときにフラグを判定する」を読み,
chap6.pyを更新しましょう.エンディングに到達できましたか?
課題 11-3
教科書では鍵は1つでしたが,鍵を2つにします.
「2つとも取らないとゴールに到達できない」という条件での判定を考えましょう.
★chap6.py に対して変更すべき項目,追加すべき項目を具体的に挙げ,それぞれを説明しなさい.
※説明に必要ならその部分のソースコードを提示してもよい
※注意※ ゴール判定を座標値でやらないこと( 過去にそういう例があったので )
課題 11-4
教科書では,ゴールに到達したら矢印ボタンを無効化しています.
もしこれを無効化せず,それをユーザが押下したら何が起るでしょうか.
★起こる現象を具体的に挙げて,ゲームにとっての意味や問題を説明しなさい.
※注意※現プログラムでは( 致命的な )問題は起こりません.
だからといって「まあセーフにしとこう」とはなりません.
今後,ゲームの中身を追加・拡張していく場合を考慮し,
「〇〇すると□□が生じる.それはゲームにとっては△△な問題である」というふうに,
実例を交えて挙げて下さい.
【豆知識:潜在的バグ】
プログラムが複雑になってくると,明らかな文法的エラー以外に,
潜在的なバグの可能性が高くなります.例えば以下のようなものです.
- a.比較的単純なバグ
- 計算ミス:演算式,演算子や演算順位の間違い
- 変数ミス:使うべき変数の間違い
- 論理ミス:フラグを逆に使う,分岐条件のAND/ORの間違い
- ループミス:for/whileの数や条件式,終了条件の間違い
- b.設計上のバグ
- イベント設計ミス:ボタンの有効/無効,画像や文字列提示の間違い
- 遷移設計ミス:場面/状態/状況を切り替える条件・判定の間違い
- バックドア:設計者がメンテナンス等のために設置した「裏道」
:ミスではないが第三者に解析・悪用される事を考えると,一種のバグと言える
これらはインタプリタ/コンパイラのエラーチェックはパスするものの,
プログラム全体として思った通りに正しく動作しない「バグ」です.
複雑なプログラムになると,これを見つけるのはとてもやっかいなものです.
単純な入力条件であれば「総当りテスト」をすることでバグ出しできます.
しかしRPGのように複数の段階・場面をもち,様々なフラグやステータスによって動作が異なるような
複雑なプログラムになると「総当り」は現実的ではありません.
また,ユーザビリティ――ゲームであれば「ユーザが最初から最後まで楽しめるか」
という難易度や快適さ( いわゆるゲームバランス )の設定は,設計時には決めづらいものです.
これらはソフトウェア科学の中で「ソフトウェアテスト」という一つの研究ジャンルにもなっています.
プログラミングする側としては,潜在的バグを可能な限りなくす工夫をする必要があります.
※上述したように,変数の命名法を工夫するのもその一助になりますね.
最近はテストケースを生成するのにAIを活用し,テストの自動化・効率化を図る研究が活発です.
提出が必要なのは,課題11-3と11-4 です.
課題11-1,11-2はソースコードの入力と実行だけで構いません.が,
必ず正しく動作するまでしっかりプログラミングして下さい.でないと11-3,11-4ができません.
- 課題をmanabaのレポート欄「第11回課題」に提出して下さい.
- 直接書き込む「オンライン入力方式」です.
- 冒頭には「誰へ」「学籍・氏名」「挨拶一言」「要約:どの課題に答えたか」を文章として書きます.
- 実習時間内に終わらなかった場合,締切は「次回の実習開始( 金曜 9:15 )まで」です.
※注意※
◎しっかり課題文を読み,「何をどう答えるべきか」を把握して下さい.
◎manaba入力画面には時間制限があります.いったんPCのエディタでテキストファイルを作成してから,それをコピーするようにしてください.その際のファイル名は自由ですがわかりやすいものを.特にプログラムのソースファイルと混同しないように注意しましょう.
◎実行画面,ソースコード,エラーメッセージ等は"----"や"====="等の境界線を入れて「ここから」「ここまで」を明確にして下さい.
◎ひとまとまりの回答の前後には空行を入れて読みやすくして下さい.びっしり書かれているとチェック/評価の際の間違いの原因になります.
◎提出前にもう一度,冷静に読み直して下さい.細かい部分でも訂正があれば手をかけて下さい.