Renpyでちょっと遊んでみた

## はじめに
この記事はNE Advent Calendar 2022のカレンダー1枚目の記事です。
いざ、Pythonでノベルゲームを作ろう！という機運があり、[Renpy](https://ja.renpy.org/)というビジュアルノベルエンジンを触ってみました。
その時に学んだことを少し、こちらで紹介させていただこいうと思います。

## RenpyはPythonが書ける
Renpyでは、ゲーム上で表示させるキャラクターやテキスト、背景などの様々なゲーム要素を、`.rpy`という拡張子のファイルに独自の記法でスクリプトを記述していきます。
また、`.rpy`ファイルの中でにpythonを記述するためのブロックを用意することで、Pythonコードを書くことができます。

```script.rpy
init python:
    # pythonコード

label start:
    # スクリプトを書くところ
```

Pythonがかけるので、Pythonでできることなら大体なんでもできそうです。
例えば、オブジェクト指向な書き方とか......。

```script.rpy
init python:
    class Person:
        def __init__(self, character, name):
            self.character = character
            self.name = name

label start:
    scene bg room
    $ character_a = Person(Character('とてもかわいいヒロイン'), 'ヒロインさん')
    character_a.character '[character_a.name]'

    $ renpy.pause(2)
    return
```

![スクリーンショット 2022-12-05 19.59.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2397566/c15f61f6-4b93-2af8-370d-eee465cb3505.png)

親愛度などもクラスに定義しておくといいかもですね〜。

```script.rpy
init python:
    class Person:
        def __init__(self, character, name, happiness=0):
            self.character = character
            self.name = name
            self.happiness = happiness
        def increment_happiness(self):
            self.happiness += 1

label start:
    scene bg room
    $ character_a = Person(Character('とてもかわいいヒロイン'), 'ヒロインさん')
    character_a.character '[character_a.name]'

    menu: # 選択肢
        '猫の写真を見せる':
            "わーいねこだあ"
            $ character_a.increment_happiness() # 親愛度の上昇
        "猫の写真を見せない":
            "......。"

    $ renpy.notify('親愛度が上昇しました。現在の親愛度は' + str(character_a.happiness))
    $ renpy.pause(2)
    return
```

※親愛度は親愛している対象があるわけで、たくさんのキャラクターが登場する場合はどのキャラに対する親愛度なのか、そもそも親愛度をどう定義するのかみたいな問題はありますが、ここでは割愛します。

他にもいろいろな機能が実装できると思いますが、
そうなるとどんどんクラスが増えてくる、といったことも考えられます。
Renpyでは複数ファイルにスクリプトを分割できるので、ファイルをいい感じに分けてそれぞれにクラスを書いていくということも可能です。


```person.py
init python:
    class Person():
        def __init__(self, character, name, happiness=0):
            self.character = character
            self.name = name
            self.happiness = happiness
        def increment_happiness(self):
            self.happiness += 1
```

```script.py
label start:
    scene bg room
    $ character_a = Person(Character('とてもかわいいヒロイン'), 'ヒロインさん')
    character_a.character '[character_a.name]'

    menu:
        '猫の写真を見せる':
            "わーいねこだあ"
            $ character_a.increment_happiness()
        "猫の写真を見せない":
            "......。"

    $ renpy.notify('親愛度が上昇しました。現在の親愛度は' + str(character_a.happiness))
    $ renpy.pause(2)
    return
```

また、Renpyはgameディレクトリ配下にある`.rpy`ファイル全てを一つのスクリプトとして捉えているようです。
https://ja.renpy.org/doc/html/language_basics.html
> 基本的に複数ファイルに分割したスクリプトと単一の大きなファイルでできたスクリプトに違いはありません。他のファイルにあるラベルに対して jump や呼び出しをして制御をファイル間で移動できます。これにより、自分のやり方に合わせてスクリプトをファイルに分けられます。(イベントごとや、一日ごとの)小さなファイルを好む人もいれば、大きなスクリプトを好む人もいるでしょう。

そのため、変数名やクラス名が被っちゃわないように気をつけたほうがよさそうです。

### ロールバックされるのは`.rpy`ファイルに書かれたもののみ
`.rpy`ファイルに書かれたスクリプトのデータは、Renpyがrevertableなデータとしてみてくれるので、ロールバックが可能です。
https://ja.renpy.org/doc/html/save_load_rollback.html
データの状態を保持して欲しいものは、`.rpy`ファイルに書いた方が良さそうです。

## さいごに
学生のころからRenpyには興味を持っていたのですが、なんだかんだで触らずにいました。
いい機会だと思い、少しですがやってみて、とても楽しかったです。
Renpyに興味があるけど触ったことない〜みたいな人の後押しができればいいなと思います。

## 参考資料
https://www.renpy.org/doc/html/
https://www.reddit.com/r/RenPy/comments/mp77lf/tutorial_object_oriented_programming_and_renpy/

本記事では参考にしていませんが、
youtubeで上がっている動画とかも参考になりました！