プロデルでアドベンチャーゲームを日本語らしく作る

#プロデルでゲーム作り

この記事は「[日本語プログラミング言語「プロデル」 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/produire)」23日目の記事です。

今回もゲームを作ってみようと思います。以前から「プロデル」で検索した時の関連キーワードをよく見るとプロデルというキーワードに並んで「ゲーム」という言葉が目に付いていました。ブログでもゲームプログラミング関連の記事の閲覧数が他と比べて高い事から、今年はもっぱらレトロゲームを作ってみました。

## アドベンチャーゲームのシナリオを日本語で書く

今回は、プロデルでアドベンチャーゲームを作ってみようと思います。アドベンチャーゲームというと専用な開発環境やゲームを得意とするプログラミング言語もありますし、無謀かもしれませんが、作ってみようと思います。

アドベンチャーゲームは、ゲームのロジックやシステムよりもゲームの進行であるシナリオを作る方に重点が置かれると思います。ゲーム開発環境の中には、シナリオそのものを日本語風に記述する独自の言語が使われることもあるようです。実はシナリオを定義する言語として、日本語プログラミング言語は適役ではないのかと思っています。記号の羅列のシナリオデータを書く代わりにプロデルの日本語で書かれたプログラムでシナリオを書くことで、よりストーリーを練ることに集中できるかもしれません。

ここではキャラクター2人が会話をするシナリオを作ってみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101225/c643fc40-f334-12b3-bcb7-d4911f834dec.png)



## 素材

まず下準備として、アドベンチャーゲームに必要な画像を用意します。

###A.png
![A.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101225/2b043041-e04a-c304-c347-4741a91bccaf.png)
###B.png
![B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101225/4cf729ae-7b0f-3677-9ec0-c562bf08c02b.png)
###背景.png
![背景.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101225/0c663ddb-279d-a1a7-3fe1-0a6e8909ba87.png)
###枠.png
![枠.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101225/2eddd72e-aa99-1934-c32c-6750ac07bfe0.png)

## システムを作る

次に、アドベンチャーゲームの土台となるゲームシステムを作ります。とはいってもウィンドウにキャンバス部品を貼り付けただけのシンプルなものです。マウスもしくはキーボードの操作でゲームが進行するようにしておきます。
キャンバス部品には、背景やキャラクタ、メッセージを表示する枠をそれぞれ画像図形として貼り付けてゲーム画面を作ります。
この後定義する「台本」種類や「アクション」種類では、シナリオに沿ってこれらの図形を操作して、ゲームを進行させていきます。

###役者
登場人物を「役者」種類として定義します。「役者」種類には、対応する画像図形のオブジェクトを保持しておき、登場したり退場したりできるようにしておきます。

###台本
ゲームのシナリオは、「台本」種類を定義して記述することにします。
この種類を使ってアドベンチャーゲームをどのように展開していくかを定義していきます。「進める」手順では、シナリオとしてゲームの進行内容をアクションとして順番に書いていきます。

「台本」種類では、アクションの数に応じて、次のような手順を定義します。これらのアクションを組み合わせてシナリオを組み立てていきます。

- [役者]が[台詞]と、言う手順
- [役者]が登場する
- [役者]が退場する
- 待機する
- [質問]を[選択肢]から選択する

一つ一つのアクションについては、この後説明しますが、これらの手順を使ってゲームシナリオを記述していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101225/5b4f9016-f975-3c1b-fbfd-470c4b23b8d4.png)



## シナリオを構成するアクション

シナリオ中の一つ一つの動作をどのように描画するかは、アクションとして定義しておきます。ここでは「アクション」種類を継承したそれぞれのアクション種類を定義します。最低限のアクションとして次のような表の種類を定義しました。さらにこれら以外にもアクションを定義すれば、より複雑なシナリオを作ることもできるかと思います。

| 種類 | 説明 |
|:-:|:-:|
| アクション  | アクションのベースとなるアクション  |
| 登場アクション  | キャラクタを登場させるアクション  |
| 退場アクション  | キャラクタを退場させるアクション  |
| 会話アクション  | 会話をメッセージ枠に一文字ずつ表示するアクション  |
| 待機アクション  | プレーヤーがメッセージを読み終わるまで待機するアクション  |
| 選択アクション  | プレーヤーに選択肢から選択してもらうアクション  |

### アクションの動作
それぞれのアクションでの描画動作は、「アクション」種類を継承した各種類の「実行する」手順に書きます。
またアクション実行中に画面をクリックしてスキップした場合には「スキップする」手順を実行します。また画面上でキー入力した時は「[キー]を押す」手順を実行します。このようにすることで、例えば特定のアクション実行中にキー入力やマウス操作でアクションの動作を定義しやすくなります。

##完成したプログラム

```
シナリオという台本を作る
キャラAという役者(「キャラA」)を作る
キャラBという役者(「キャラB」)を作る

メイン画面を表示する

シナリオスレッドというスレッドを作る
シナリオスレッドで『
	シナリオを進める
』を実行する

待機する
メイン画面とは
	ウィンドウを継承する
はじめの手順
	初期化する
	ーー貼り付けた部品に対する操作をここに書きます
		キャンバス1へ画像を描いて背景とする
			その位置と大きさは、｛0,0,200,300｝
			その自動調整は、「自動調整」
			その画像を「背景.png」に変える
		キャンバス1へ画像を描いてキャラAの図形とする
			その位置と大きさは、｛50,100,200,300｝
			その自動調整は、「自動調整」
			その画像を「A.png」に変える
			その画像を透明化する
			その表示を×に変える
		キャンバス1へ画像を描いてキャラBの図形とする
			その位置と大きさは、｛400,100,200,300｝
			その自動調整は、「自動調整」
			その画像を「B.png」に変える
			その画像を透明化する
			その表示を×に変える
		キャンバス1へ画像を描いてメッセージ枠とする
			その位置と大きさは、｛80,350,200,300｝
			その自動調整は、「自動調整」
			その画像を「枠.png」に変える
			その画像を白で透明化する
		キャンバス1へ「」という文字を描いてシナリオのメッセージボックスとする
			その位置と大きさは、｛85,355,190,290｝
			そのフォントを「メイリオ」に変える
			その文字色を白色に変える
			その文字サイズを16に変える
終わり
	初期化する手順
	ーー自動生成された手順です。ここにプログラムを書き加えても消える場合があります
		この実質大きさを｛946,704｝に変える
		この内容を「アドベンチャーゲーム」に変える
		初期化開始する
		キャンバス1というキャンバスを作る
			その位置と大きさを｛0,0,946,704｝に変える
			そのドッキング方向を「全体」に変える
		初期化終了する
		この設計スケール比率を｛144,144｝に変える
	終わり
	キャンバス1がクリックされた時の手順
		シナリオをスキップする
	終わり
	キャンバス1のキーが押された時の手順
		シナリオでこの時のキー名を押す
	終わり
	自分を進行する手順
		シナリオを進める
	終わり
終わり

台本とは
	+メッセージボックス
	+現在動作
	
	自分を進める手順
		キャラAが登場する
		キャラAが「こんにちは」と、言う
		待機する
		キャラBが登場する
		キャラBが「やぁ、こんにちは」と、言う
		待機する
		「今日は何する」を{「遊ぶ」,「帰る」}から選択する
		もしその選択番号が1なら
			キャラAが「ワイワイ」と、言う
			待機する
			キャラBが「ガヤガヤ」と、言う
			待機する
		もし終わり
		キャラAが「じゃあね」と、言う
		待機する
		キャラAが退場する
		キャラBが「バイバイ」と、言う
		待機する
		キャラBが退場する
		「」と、言う
	終わり
	
	自分をスキップする手順
		もし現在動作が無なら返す
		現在動作をスキップする
	終わり
	自分で[キー]を押す手順
		もし現在動作が無なら返す
		現在動作でキーを押す
	終わり
	自分で〈[役者]が〉[台詞]と、言う手順
		会話アクション(役者,台詞)を作って現在動作とする
		自分で現在動作を実行する
		現在動作を返す
	終わり
	自分で[役者]が登場する手順
		登場アクション(役者)を作って現在動作とする
		自分で現在動作を実行する
		現在動作を返す
	終わり
	自分で[役者]が退場する手順
		退場アクション(役者)を作って現在動作とする
		自分で現在動作を実行する
		現在動作を返す
	終わり
	自分で待機する手順
		待機アクションを作って現在動作とする
		自分で現在動作を実行する
		現在動作を返す
	終わり
	自分で[質問]を[選択肢]から選択する手順
		選択アクション(質問,選択肢)を作って現在動作とする
		自分で現在動作を実行する
		現在動作を返す
	終わり
終わり

役者とは
	+図形
	+名前
	はじめ(名前)の手順
		自分の名前は、名前
	終わり
	自分が、登場する手順
		図形の表示を○に変える
	終わり
	自分が、退場する手順
		図形の表示を×に変える
	終わり
終わり


アクションとは
終わり

登場アクションとは
	アクションを継承する
	+役者
	はじめ(役者)の手順
		自分の役者は、役者
	終わり
	自分を[台本]で実行する手順
		役者が登場する
	終わり
	自分をスキップする手順
	終わり
	自分で[キー]を押す手順
	終わり
終わり
退場アクションとは
	アクションを継承する
	+役者
	はじめ(役者)の手順
		自分の役者は、役者
	終わり
	自分を[台本]で実行する手順
		役者が退場する
	終わり
	自分をスキップする手順
	終わり
	自分で[キー]を押す手順
	終わり
終わり

会話アクションとは
	アクションを継承する
	+台詞
	+話者
	+文字番号
	はじめ(話者,台詞)の手順
		自分の話者は、話者
		自分の台詞は、台詞
	終わり
	自分を[台本]で実行する手順
		話者名は、「」
		もし話者が無でなければ話者名は、「[話者の名前]: 」
		文字番号を0から台詞の文字数まで増やしながら繰り返す
			台詞の先頭から(文字番号)文字取り出してメッセージとする
			台本のメッセージボックスの内容を「[話者名][メッセージ]」に変える
			0.1秒待つ
		繰り返し終わり
		「[話者名][メッセージ]」に台本のメッセージボックスの内容を変える
	終わり
	自分をスキップする手順
		文字番号は、台詞の文字数-1
	終わり
	自分で[キー]を押す手順
		キーについて分岐
		「Enter」の場合
			スキップする
		分岐終わり
	終わり
終わり

待機アクションとは
	アクションを継承する
	-実行中	
	はじめ()の手順
	終わり
	自分を[台本]で実行する手順
		初期メッセージは、台本のメッセージボックスの内容
		フラグは、○
		実行中は、○
		繰り返す
			もしフラグなら
				台本のメッセージボックスの内容を「[初期メッセージ]▼」に変える
				フラグは、×
			そうでなければ
				台本のメッセージボックスの内容を初期メッセージに変える
				フラグは、○
			もし終わり
			0.5秒待つ
			もし実行中でないなら繰り返しを抜け出す
		繰り返し終わり
	終わり
	自分をスキップする手順
		実行中は、×
	終わり
	自分で[キー]を押す手順
		キーについて分岐
		「Enter」の場合
			スキップする
		分岐終わり
	終わり
終わり
選択アクションとは
	アクションを継承する
	-実行中
	+質問
	+選択肢
	+選択番号=1
	はじめ(質問,選択肢)の手順
		自分の質問は、質問
		自分の選択肢は、選択肢
	終わり
	自分を[台本]で実行する手順
		初期メッセージは、台本のメッセージボックスの内容
		実行中は、○
		繰り返す
			メッセージは、質問&改行
			数を1から[選択肢の個数]まで増やしながら繰り返す
				もし数が選択番号なら
					メッセージは、「[メッセージ]■[選択肢(数)][改行]」
				そうでなければ
					メッセージは、「[メッセージ]□[選択肢(数)][改行]」
				もし終わり
			繰り返し終わり
			台本のメッセージボックスの内容をメッセージに変える
			0.1秒待つ
			もし実行中でないなら繰り返しを抜け出す
		繰り返し終わり
	終わり
	自分で[キー]を押す手順
		キーについて分岐
		「↑」の場合
			もし選択番号が2以上なら選択番号を減らす
			そうでなければ選択番号は、選択肢の個数
		「↓」の場合
			もし選択番号が[選択肢の個数]未満なら選択番号を増やす
			そうでなければ選択番号は、1
		「Enter」の場合
			スキップする
		分岐終わり
	終わり
	自分をスキップする手順
		実行中は、×
	終わり
終わり
```

##まとめ

今回は、アドベンチャーゲームを作ってみました。「台本」種類の手順定義を工夫して、プロデルの文法を活かし、本当の台本のような表記でシナリオを記述できるようにしました。少し変わったプロデルの活用方法として、このような定義記述言語として日本語プログラミングの文法を利用するアイディアはアドベンチャーゲーム以外にも応用できる可能性があると思います。

一方で完成したアドベンチャーゲームは、システムとシナリオ記述を含めて300行足らずのプログラムで実現しました。さらに凝ったアニメーションやエフェクトを入れることとで、それらしくなるかと思っています。次の機会に取り組んでみたいと思います。