ビジュアルノベルの面白さは「選択」にある。プレイヤーの選んだ道でストーリーが変わるからこそ、繰り返し遊びたくなる。
この記事では、Ren'Py 8.5.2 で選択肢と分岐を実装する方法を、基本の menu 文からマルチエンディングまで解説する。
menu文の基本
menu 文はプレイヤーに選択肢を提示し、選んだ結果に応じて処理を分岐させる。
label start:
"道が二手に分かれている。"
menu:
"どちらに進む?"
"左の道へ":
"左の道を選んだ。森が広がっている。"
"右の道へ":
"右の道を選んだ。川が見えてきた。"
"旅は続く。"
menu: の直下に書いた文字列("どちらに進む?")はキャプションとして表示される。その下の各文字列が選択肢になる。選択肢のブロック内に、選ばれたときの処理を書く。
選択肢のブロックが終わると、menu の後の行("旅は続く。")に合流する。どの選択肢を選んでも、ここに戻ってくる。
キャラクターに質問させる
キャプションの代わりに、キャラクターのセリフとして質問を出すこともできる。
define e = Character("エイリーン")
label start:
menu:
e "どっちがいい?"
"お茶":
e "お茶だね。"
"コーヒー":
e "コーヒーだね。"
e "どっちがいい?" と書くと、エイリーンのセリフとして表示された後に選択肢が出る。
jumpとcallの使い分け
選択肢の中身が長くなってきたら、jump や call で別の label に処理を移す。この2つは似ているが、戻ってくるかどうか が決定的に違う。
jump — 一方通行
label start:
menu:
"どちらに進む?"
"森へ":
jump forest_route
"街へ":
jump town_route
label forest_route:
"森の奥深くへ進んだ。"
jump chapter_2
label town_route:
"賑やかな街に着いた。"
jump chapter_2
label chapter_2:
"第2章が始まる。"
jump は指定した label に飛び、元の場所には戻らない。ルート確定後の遷移やエンディングへのジャンプに使う。
call — 行って帰ってくる
label start:
"冒険の準備をしよう。"
call shop
"買い物を終えて旅に出発した。"
label shop:
"何を買う?"
menu:
"剣を買う":
$ has_sword = True
"盾を買う":
$ has_shield = True
return
call は指定した label に飛び、return で呼び出し元に戻る。共通シーン(ショップ、ミニゲーム、回想)など、複数の場所から呼び出したい処理に使う。
比較表
jump | call | |
|---|---|---|
| 戻る | 戻らない | return で戻る |
| 用途 | ルート分岐、エンディング | 共通シーン、サブルーチン |
| スタック | 積まない | 積む(return で消化) |
フラグで分岐を管理する
選択肢の結果を変数(フラグ)に保存しておくと、後のシーンで参照できる。
フラグの宣言
# default で宣言(セーブファイルに保存される)
default met_fairy = False
default affection = 0
label start:
menu:
"妖精に話しかける?"
"話しかける":
$ met_fairy = True
$ affection += 10
"妖精と友達になった。"
"無視する":
"そのまま通り過ぎた。"
フラグは必ず default で宣言する。default で宣言した変数はセーブファイルに保存され、ロード時に正しく復元される。
フラグを使った分岐
label chapter_2:
if met_fairy:
"あの時の妖精が現れた。"
$ affection += 5
else:
"見知らぬ妖精が現れた。"
if affection >= 10:
"妖精はあなたに微笑んだ。"
else:
"妖精は警戒している。"
if/elif/else で変数の値に応じて表示するテキストやイベントを切り替える。
条件付き選択肢
選択肢自体に if 条件を付けると、条件を満たしたときだけその選択肢が表示される。
default has_key = False
label locked_door:
"鍵のかかった扉がある。"
menu:
"鍵を使う" if has_key:
"扉が開いた。"
jump secret_room
"引き返す":
"別の道を探すことにした。"
has_key が False のとき、「鍵を使う」は選択肢に表示されない。プレイヤーには「引き返す」しか見えない。
選べない選択肢をグレーアウトで見せる
条件未達成の選択肢を非表示にするのではなく、グレーアウトして「存在するが選べない」と示したい場合がある。
init python:
config.menu_include_disabled = True
label locked_door:
menu:
"鍵を使う" if has_key:
"扉が開いた。"
"引き返す":
"別の道を探すことにした。"
config.menu_include_disabled = True を設定すると、条件を満たさない選択肢がグレーアウト状態で表示される。プレイヤーに「別の条件を満たせば選べる」というヒントを与えられる。
一度選んだ選択肢を除外する
set を使うと、すでに選んだ選択肢をメニューから自動的に除外できる。
default explored = set()
label explore:
"どこを調べる?"
menu:
set explored
"机の上":
"手紙が見つかった。"
"本棚":
"古い日記が見つかった。"
"窓の外":
"誰かの影が見えた。"
if len(explored) < 3:
jump explore
else:
"すべて調べ終えた。"
マルチエンディングを実装する
ここまでの知識を組み合わせて、マルチエンディングを実装してみよう。
ポイント型:好感度で分岐
プレイヤーの選択に応じてポイントを加減し、最終的な値でエンディングを決める。
default affection = 0
label start:
"エイリーンと出会った。"
menu:
"挨拶する":
$ affection += 10
"エイリーンは嬉しそうだ。"
"素通りする":
"エイリーンは寂しそうだ。"
"しばらく旅を続けた。"
menu:
"エイリーンを助ける":
$ affection += 20
"エイリーンの目が輝いた。"
"見て見ぬふりをする":
$ affection -= 10
"エイリーンは失望した。"
jump ending
label ending:
if affection >= 20:
jump good_ending
elif affection >= 0:
jump normal_ending
else:
jump bad_ending
label good_ending:
"エイリーンと固い絆で結ばれた。"
"GOOD END"
return
label normal_ending:
"エイリーンとは友人のままだった。"
"NORMAL END"
return
label bad_ending:
"エイリーンは去っていった。"
"BAD END"
return
フラグ型:特定の選択の組み合わせで分岐
ポイントではなく、特定のイベントを経験したかどうかでエンディングを決める。
default saved_cat = False
default found_letter = False
label start:
menu:
"猫を助ける":
$ saved_cat = True
"通り過ぎる":
pass
menu:
"手紙を読む":
$ found_letter = True
"手紙を無視する":
pass
jump ending
label ending:
if saved_cat and found_letter:
"すべての真実にたどり着いた。"
"TRUE END"
elif saved_cat:
"猫が恩返しに来た。"
"CAT END"
elif found_letter:
"手紙の謎を解いた。"
"LETTER END"
else:
"何も見つけられなかった。"
"NORMAL END"
return
実際のゲームでは、ポイント型とフラグ型を組み合わせて使うことが多い。好感度でメインルートを決め、フラグで細かいバリエーションを出すのが定番のパターンだ。
まとめ
この記事で解説したこと:
- menu文: 選択肢の基本構文。キャプションやキャラクターのセリフで質問を出せる
- jump vs call:
jumpは一方通行、callはreturnで戻れる。共通シーンにはcallを使う - フラグ管理:
defaultで変数を宣言し、選択結果を保存する - 条件付き選択肢:
if条件で表示/非表示を制御。config.menu_include_disabledでグレーアウト表示 - マルチエンディング: ポイント型(好感度)とフラグ型(イベント経験)の2パターン
Ren'Pyの基本は 入門ガイド を、変数の管理については 変数管理ガイド を、UIの作り方は screen言語入門 を参照してほしい。
公式リソース:
- In-Game Menus — menu文の公式リファレンス
- Labels & Control Flow — label/jump/callの詳細
- Conditional Statements — if/elif/elseの詳細
- r/RenPy — コミュニティ