Python ステートメント link

Ren'Py は、Python プログラミング言語で書かれており、Ren'Py スクリプトの中に Python のコードを埋め込められます。Python への対応は、フラグの設定から displayable の新規作成まで、様々な場面で利用できます。この章では、Ren'Py のスクリプトから Python コードのステートメントを直接呼び出す方法を説明します。

Ren'Py 7 は Python 2.7 を、 Ren'Py 8 は Python 3.9 をサポートします。

注釈

Python を知っているなら、それを活用できるでしょう。しかし Python について知っていることがそのまま適用出来るわけではありません。たとえば Ren'Py には含まれていない Python パッケージは Ren'Py 内で動作しません。

また、いくつかのPythonのコンストラクトは動作しますが、セーブ時に問題が発生する可能性があります。詳しくは セーブ、ロード、ロールバック のページ、特に 何がセーブされないか にある保存できないものについてのセクションを読んでください (ファイル、ソケット、イテレータ、タスク、フューチャー、ジェネレータには注意が必要です)。

最後に、多くのステートメントには Python で相当するものがありますが、それらは劣っている可能性があります。例えば、Ren'Pyは show ステートメントを予測し、画像を早期にロードできますが、 renpy.show() 関数は予測できません。

Python link

python ステートメントは python コードのブロックを受け取り、制御がそのステートメントまで到達するとそのコードを実行します。基本的な python ステートメントはとても単純です。

python:
    flag = True

python ステートメントは必要ならさらに複雑になります。

python:
    player_health = max(player_health - damage, 0)
    if enemy_vampire:
        enemy_health = min(enemy_health + damage, enemy_max_health)

python ステートメントにはその動作を変更するものが二つあります。 :

hide

hide が与えられると python ステートメントは無名のスコープで python のブロックを実行します。そのスコープは python ブロックが終了すると消失します。

これで python コードに保存されない一時的な変数を使用可能になります。しかしそれはつまり store オブジェクトに直接ではなくフィールドとしてアクセスする必要があるということでもあります。

in

in は名前を受け取ります。デフォルトの store で実行する代わりに python コードはその名前の store で実行されます。

一行 python link

デフォルトの store で実行される一行だけの python が欲しいというのはよくあることです。例えばフラグの初期化や更新には一行だけの python が使用されます。 一行だけの python をより便利に記述するために、一行 python のステートメントがあります。

一行 python ステートメントはドル記号 $ で始まり、その行にコードのすべてを含みます。ここでは 一行 python の例をいくつか示します。

# Set a flag.
$ flag = True

# Initialize a variable.
$ romance_points = 0

# Increment a variable.
$ romance_points += 1

# Call a function that exposes Ren'Py functionality.
$ renpy.movie_cutscene("opening.ogv")

一行 python は常にデフォルトの store で実行されます。

init python ステートメント link

init python ステートメントはゲームロード前の初期化時に python コードを実行します。特にこのコードはクラス、関数の定義やスタイル、設定変数、永続データの初期化に使用されます。

init python:

    def auto_voice_function(ident):
        return "voice/" + ident + ".ogg"

    config.auto_voice = auto_voice_function

    if persistent.endings is None:
        persistent.endings = set()

init 1 python:

    # The bad ending is always unlocked.
    persistent.endings.add("bad_ending")

優先度の値は initpython の間に配置出来ます。優先度が指定されないと、 0 が使用されます。 init ステートメントは優先度の小さいものから大きいものの順に実行されます。 優先度の同じ init ステートメントはファイルパスのユニコード順に、各ファイルの開始から終端まで実行されます。

Ren'Py 本体との衝突を防ぐため、開発者は -999 から 999 の範囲の優先度を使用するべきです。 0 以下の優先度は通常ライブラリーやテーマの設定に使用されます。通常の init コードには優先度 0 かそれ以上を使用するべきです。

init python ステートメントは hidein 節も受け取ります。

init python ブロックで値が設定された変数はその変数が参照するオブジェクトが変更されない限りセーブ、ロードされず、ロールバックにも参加しません。それゆえこれらの変数は初期化後は変更されるべきではありません。

警告

Ren'Py 内で作成され、何も継承しないか明示的に object を継承するクラス、およびこれらのクラスのサブクラスは、 __slots__ をサポートしません。これを実行しようとすると、古いバージョンのrenpyではロールバックの動作がおかしくなり、新しいバージョンではエラーが発生します。

スロットを持つクラスを作成するには、ロールバックをサポートしない python_object を明示的にサブクラスとして作成する必要があります。

define ステートメント link

define ステートメントは、初期化時に1つの変数を設定します。その変数は定数として扱われるので設定後は変更しないべきです。例えば

define e = Character("Eileen")

これは (後述のいくつかの利点を除いて) 次と等しいです。

init python:
    e = Character("Eileen")

define ステートメントはドットと変数名を続けて任意で名前付き store (以下参照) も受け取られます。その store がなければ作成されます。例

define character.e = Character("Eileen")

define ステートメントは任意にインデックスも受け取れ、辞書に要素を追加させられます。例

define config.tag_layer["eileen"] = "master"

= に加えて、define はあと2つのオペレーターを受け取れます。 += オペラーターは追加で、一般的にリストの連結に使用されます。 |= オペレーターは一般的にセットの連結に使用されます。例

define config.keymap["dismiss"] += [ "K_KP_PLUS" ]
define endings |= { "best_ending" }

define ステートメントを使用する利点の 1 つは代入が行なわれたファイル名と行番号を記録し、ランチャーのナビゲーション機能が利用可能になることです。もう一つの利点は Lint で例えば、同じ変数が2重定義されているかを検出して、異なる値を持つ定義された変数を確認できることです。

define ステートメントを使用して定義された変数は定数として扱われ、セーブロードされないため、変更しないべきです。この定数の性質はフィールドアクセスや添え字によってそれらの変数からアクセス出来るオブジェクトまで拡張されます(Ren'Py はこれを強制しませんが、守らない場合、未定義の動作をします)。

default ステートメント link

default ステートメントは、ゲーム開始時かロード後に変数が定義されていなければその変数の値を設定します。例えば

default points = 0

変数 points がゲーム開始時に定義されていなければ、このステートメントは次と等しいです。

label start:
    $ points = 0

変数 points がゲームロード時に定義されていなければ、次と等しいです。

label after_load:
    $ points = 0

default ステートメントはドットと変数名を続けて任意で名前付き store (以下参照) も受け取れます。その store がなければ作成されます。例

default schedule.day = 0

define ステートメントと同じで Lintdefault ステートメントに対するチェックと最適化を提供します。

注釈

ゲームの中で変更されやすい変数はすべて default にすることを強くお勧めします。もし init pythondefine を使って変数を宣言した場合、プレイヤーがゲームをプレイしてその変数を変更した後、メインメニューに戻って新しいゲームを始めると、その変数には init python で設定した値がないので、前のゲームが新しく始めたゲームに "leak" します。代わりにスタートラベルにこれらの変数を作成すると、以前から存在したセーブファイルをロードしたときに、これらの変数が欠落することになります。

Store の名前 link

Ren'Py が Python 変数を格納するデフォルトの場所は store と呼ばれます。 store で使用する名前が衝突しないかの確認は重要です。

define ステートメントは値を、それがキャラクターの定義に使用されていても変数に代入します。つまりキャラクターとフラグに同じ名前は使用出来ません

以下のコードには欠陥があります。

define e = Character("Eileen")

label start:

    $ e = 0

    e "Hello, world."

    $ e += 1
    e "You scored a point!"

これは変数 e がキャラクターとフラグの両方に使用されているため動作しません。通常 store に格納されるその他の物にはトランジションと transform があります。。

アンダースコア "_" で初まる名前は Ren'Py 内部で使用するものとして扱われます。詳しくは以下を参照してください。 Index of Reserved Names

その他の名前付き store link

名前付きの store はpython の関数と変数をモジュールに分ける方法を提供します。モジュールにコードを置くと名前が衝突する機会を減らせます。各 store は 1 つの Python モジュールに対応します。デフォルト store は store で、名前付き store は store.named としてアクセスできます。

名前付きストアは python in (または init pythonpython early のようなもの) や default, define, transform ステートメントを使って作成できます。変数は from store.named import variable で個別にインポートでき、名前付き store 自体は from store import named でインポートできます。

名前付き store は pythoninit python (または python early) に in 節を与えてアクセスでき、これらはすべて指定の名前付き store に含まれる Python を実行します。

init python in mystore:

    serial_number = 0

    def serial():

        global serial_number
        serial_number += 1
        return serial_number

default character_stats.chloe_substore.friends = {"Eileen",}

label start:
    $ serial = mystore.serial()

    if "Lucy" in character_stats.chloe_substore.friends:
        chloe "Lucy is my friend !"
    elif character_stats.chloe_substore.friends:
        chloe "I have friends, but Lucy is not one of them."

    python in character_stats.chloe_substore:
        friends.add("Jeremy")

python in ブロックからはデフォルト "outer" store は renpy.storeimport store によりアクセス可能です。

名前付きの store はデフォルトの store 同様にセーブロード、ロールバックに参加します。 persistent, config, renpy などのような特別な名前空間はそうではなく、その中への substore 作成もサポートしません。

定数 store link

名前付きの store はその _constant という名前の変数を True に設定して定数であると宣言できます。例

init python in mystore:
    _constant = True

ある store が定数であるとき、その store の変数は保存されず、それらの変数からのみアクセス出来るオブジェクトはロールバックに参加しません。

定数 store の変数は初期化時の間は変更できます。その store が定数として扱われなければいけないのは初期化 ( definetransfor ステートメントなどを含め ) 完了後のみです。

Ren'Py にはこれを強制する方法はないので、定数 store の変数が初期化後には変更されないと保証するのは制作者の責任です。

store が定数であると宣言する理由は、各 store と変数にはセーブ、ロード、ロールバックをサポートするためのオーバーヘッドが僅かながらあるためです。定数 store はこのオーバーヘッドを回避します。

以下の store はデフォルトで定数として宣言されています。

_errorhandling
_gamepad
_renpysteam
_warper
audio
achievement
build
director
iap
layeredimage
updater

JSONDB link

class JSONDB(filename, default=None) link

JSONDB は、 JSON を使用してデータを保存する2階層データベースです。ゲーム開発者が、ゲームスクリプトの一部としてバージョン管理可能なデータベースにデータを保存するのに使用することを目的としています。例えば、 say ステートメントの表示方法を変更できるような、各say ステートメントに関連する情報を保存できます。

JSONDB は、プレイヤーの行動によって変化するデータには向きません。そのようなデータには、 永続データ や通常のセーブファイルの方が適しています。

データベースには、 Python が JSON にシリアライズできるデータのみを格納する必要があります。これにはリスト、(文字列をキーとする)辞書、文字列、数値、 True 、 False 、 None が含まれます。相互運用性や2つのフォーマット間でのデータの変換方法、関連する様々な落とし穴については、 Python documentation を参照してください。

データベースの2つの階層は、どちらも文字列をキーとする辞書です。第1階層は読み取り専用で、第1階層辞書のキーにアクセスすると、第2階層辞書が作成され、オプションでデフォルトの内容を持ちます。第2階層の辞書は読み書き可能で、第2階層の辞書のキーの1つが変更されると、ゲームが終了するときにその変更がデータベースに保存されます。

他の永続データ同様、 JSONDB はロールバックに参加しません。

JSONDB は初期化中 ( init python ブロックや define ステートメント ) に作成されるべきで、少なくとも辞書のキーが1つ設定されれば自動的にディスクに保存されます。 例

define balloonData = JSONDB("balloon.json", default={ "enabled" : False })

これは balloon.json ファイルに JSONDB を作成し、デフォルトの値を持たせます。第二階層の値は通常の辞書として使用出来ます。

screen say(who, what):

    default bd = balloonData[renpy.get_translation_identifier()]

    if bd["enabled"]:
        use balloon_say(who, what)
    else:
        use adv_say(who, what)

    if config.developer:
        textbutton "Dialogue Balloon Mode":
            action ToggleDict(bd, "enabled")

JSONDB のコンストラクトは次の引数を取ります。 :

filename

そのデータベースが保存されるファイル名です。これはゲームディレクトリーに対して相対指定です。 ".json" でファイル名を終えることを推奨します。

default

None でなければ、辞書を指定します。新しい第二階層の辞書が作成されると、このオブジェクトはシャローコピーされ、その新しい辞書の初期化に使用されます。新しい辞書は少なくとも1つのキーが保存された場合のみ、データベースの一部として保存されます。

本体、サードパーティーの Python モジュールとパッケージ link

Ren'Py は純粋な python モジュールとパッケージをインポートできます。ゲームのために書かれた本体のモジュールとパッケージは game ディレクトリーに直接配置されます。サードパーティーのパッケージは game/python-packages ディレクトリーに配置できます。

例えば python-dateutil パッケージをインストールするにはゲームのベースディレクトリーを自環境に合わせて次のコマンドを実行します。

pip install --target game/python-packages python-dateutil

モジュールとパッケージどちらでも init python ブロックからインポート出来ます。

init python:
    import dateutil.parser

警告

.rpy ファイルで定義された Python コードは、ロールバックで動作するために変換されます。 .py ファイルからインポートされた Python コードは変換されません。そのため、 python コードで作成されたオブジェクトはロールバックで動作しないので、作成後は変更するべきではありません。

すべての Python パッケージが Ren'Py と互換性があるわけではありません。インストールするパッケージを監査し、そのパッケージが動作するかどうかを確認するのはあなたです。

Pythonのインジェクション link

ベースディレクトリに exec.py という名前のファイルを作成して、ゲーム実行時に python を挿入できます。このファイルは別の名前で作成、編集してから移動することを推奨します。

Ren'Pyは exec.py``という名前のファイルを見つけると、そのファイルの内容を読み込んでから削除し、 Python ``exec を使ってゲームストア内でその内容を実行します。これは常にインタラクション中に行われます。

これは、デバッグ ツールのサポートを意図しています。デフォルトでは、開発者モードが True の場合に有効になりますが、 RENPY_EXEC_PY 環境変数を設定することでも有効にできます。