2018年1月26日

【Papyrus.psc】選択式メッセージボックスの作り方

- Papyrusスクリプトの記憶を記録に残すシリーズその7 -

画像は使いまわします。あくまでもエコのため。

選択式メッセージボックスで分岐し、異なる処理をさせてみましょう。複数のメッセージボックスを行き来する方法についても触れていきます。


目次

  1. 選択式メッセージを作成する
  2. 一方通行のメッセージを表示する
  3. Stateを使って複数のメッセージを行き来する

1. 選択式メッセージを作成する

1-1. 新規メッセージの作成

まずは表示するメッセージを新しく作成します。Object Window の Miscellaneous > Message を開き、右クリックして「New」を選択すると、新規メッセージ画面が開きます。

ID には自分がわかりやすい名前を付けます。
その右側の「Message Box」にチェックが入っていることを確認します。
Message Text は、ダイアログの説明として表示される文章です。不要なら空欄のままでも良いです。
その下の Menu Buttons が選択肢になります。右クリックして「New」を選択すると、新しい選択肢を追加することができます。

1-2. 選択肢の設定

Menu Buttons に登録できる選択肢の数は10個までです。Index に0~9までの番号が付きますので、この番号をスクリプト内で使っていくことになります。

Item Text が選択肢の文字列です。ゲーム中ではメッセージボックス内に横並びで表示されますので、あまり長いと画面からはみ出てしまうかもしれません。

選択肢自体を表示させるかどうかをその下の Item Conditions で指定できますので、必要であればこれを設定します。
例えば「鉄の片手斧を持っていないときだけ2番の The iron ax を出したい!」と思ったら、その選択肢の Item Conditions を下図のようにします。

Condition Function を「GetItemCount」にし、その後ろを「IronWarAxe == 0.0000」にします。Run on は「Player」です。これで、プレイヤーが鉄の片手斧を持っていないときだけ選択肢が出ます。

1-3. Condition Function の種類

Condition Function にはかなり多くの条件を指定できます。その中から個人的に良く使うものだけ挙げておきます。

Condition Function 用途
GetDisabled DisableかEnableか
GetEquipped 装備しているかどうか
GetGlobalValue Object Window の Miscellaneous > Global の値を参照する
GetIsID Form ID を参照する
GetIsSex 男性か女性か
GetItemCount 所持しているアイテム数
GetLevel レベル
GetLocked 施錠されているかどうか
HasPerk パークを習得しているかどうか
HasShout シャウトを習得しているかどうか
HasSpell 魔法を習得しているかどうか
IsInList Object Window の Miscellaneous > Form List を参照する
IsSneaking スニーク状態かどうか

目次に戻る


2. 一方通行のメッセージを表示する

それでは、スクリプトを使ってメッセージを表示してみましょう。
スクリプトの発動は、配置オブジェクトからでも魔法からでも良いです。あるいは特定のアイテムを装備したりドロップしたりしたときでも良いです。Event On~ を以下のように使います。

使うEvent スクリプトを付ける対象 表示タイミング
OnActivate 配置オブジェクト アクティベート
OnEffectFinish Maggic Effect 魔法を唱えた後
OnEquipped アイテム アイテムを装備
OnContainerChanged(None) アイテム アイテムをドロップ

ここでは配置オブジェクトの例を示します。
配置オブジェクトに以下のようなスクリプトを追加すると、アクティベートされた瞬間にメッセージを表示します。

Message Property mesMenu Auto ; 作成したメッセージを指定するプロパティ

;--------------------------------------------------アクティベート
Event OnActivate(ObjectReference akActionRef)
    Int intIndex ; 選択されたIndexを格納する変数
    intIndex = mesMenu.Show() ; メッセージを表示し、選択番号をintIndexに取得する

    If intIndex == 0
        Debug.MessageBox("大嘘をついて金の斧を選びました。")
    ElseIf intIndex == 1
        Debug.MessageBox("少し嘘をついて銀の斧を選びました。")
    ElseIf intIndex == 2
        Debug.MessageBox("正直に鉄の斧を選びました。")
    Else ; intIndexが3(キャンセル)のとき
        ; 何もしない
    EndIf
EndEvent

mesMenu プロパティには先ほど作成したメッセージを指定します。mesMenu.Show() でメッセージが表示されますので、そこで選択された番号を intIndex に取得しています。0~2番では確認メッセージが表示され、3番は何もせず終了します。

複数のメッセージを作成しておけば、ここからさらに細かく分岐させていくこともできます。メッセージの数だけプロパティを設定し、分岐の中で別のメッセージを表示するだけです。
元のメッセージに戻らない、一方通行のメッセージ選択ならこれでOKです。

目次に戻る


3. Stateを使って複数のメッセージを行き来する

3-1. Stateの設定

メッセージを行き来する場合の方法です。「戻る」選択肢で1つ前のメッセージに戻れるようにしたいときですね。

そのためには「State」というものを使います。直訳すると「状態」ですが、個人的にはこれを理解するのに大変苦労しました。上手く説明できるかどうか不安ですが、少しでもヒントになればということで書いてみます。今回のメッセージボックスに使うだけなら、ここは読み飛ばしても良いです。

それでは下の図を参考に進めていきます。

1つのスクリプトには、State ~ EndState を1区切りとして複数のStateを設定しておくことができ、スクリプトの実行中に特定のStateへ移行することが可能です。State ~ EndState で区切られていない部分は、自動的に「Empty State」という状態になります。

Stateの移行は GotoState というコマンドで行います。上図では「Empty State」と「State Blue」の2つを切り替えて処理していくことになります。

初期状態は通常「Empty State」になりますので、「State Blue」の内容は隠れていて実行されることはありません。「Empty State」の中で GotoState("Blue") という行にくるとその時点で「State Blue」に移行します。

このとき、表向きは「State Blue」が前面に出て、「Empty State」の内容は隠れてしまいます。
そのスクリプトが実行されている間はStateが維持されていて、他から同じスクリプトが発動されたときは始めから「State Blue」の内容が実行されることになります。

ただし、Stateを移行したときの注意点として、「移行前のStateも同時進行で完遂される」というものがあります。上図で「Empty State」から「State Blue」に移行しても、元の「Empty State」の内容は最後まで実行されます。逆も同様です。

それぞれのStateには同じイベントを登録しておくことができます。例えば Event OnActivate を各Stateに書いておけば、アクティベートしたときの処理をStateによって変えることができます。

Papyrusでは1つのスクリプトを同時に複数実行することができるため、もしプレイヤーがアクティベートボタンを連打した場合、その回数分スクリプトが走ります。同じ処理を繰り返さないためには、以下のように Empty State の最初で別のStateに移行し、移行先のStateではアクティベートしても何も起こらないようにします。処理が終わったら Empty State に戻します。

; ここは Empty State(宣言する必要はない)

Event OnActivate(ObjectReference akActionRef)
    GotoState("Blue") ; State Blue に移行する。移行後もこの Empty State の内容は実行される

    ;ここに実行したい内容を書く
    Debug.MessageBox("今から10秒間は、いくらアクティベートしてもこのメッセージが出ません。")

    Utility.Wait(10)
    GotoState("") ; 10秒後に Empty State に戻す
EndEvent

;--------------------------------------------------ステート Blue
State Blue
    ; Empty State で GotoState("") の行にくるまではこのStateになる
    Event OnActivate(ObjectReference akActionRef)
        ; Stateが Blue の間は、アクティベートされても何もしない
    EndEvent
EndState

…伝わりましたでしょうか?別に難しいことが行われているわけではありませんが、説明がしにくいんですよね。あとこれに Function という機能が絡んでくると、さらに説明がややこしくなります。まぁ読み飛ばして次に行ってもらっても大丈夫です。

3-2. Stateを変更した瞬間にメッセージを表示する

メッセージの行き来にStateを使う場合は、単純に「メッセージの数だけStateを用意する」ことを考えればOKです。
Stateのイベントとして Event OnBeginState() というのがありまして、そのStateに移行した瞬間に発動します。

2つのメッセージを行き来する例を挙げてみましょう。

Message Property mes1 Auto ; メッセージその1。「その2へ」「キャンセル」の2択。
Message Property mes2 Auto ; メッセージその2。「実行」「戻る」の2択。

;--------------------------------------------------アクティベート
Event OnActivate(ObjectReference akActionRef)
    GotoState("Show1")
EndEvent

;--------------------------------------------------ステート Show1
State Show1 ; ステートの宣言
    Event OnBeginState() ; このステートに移行した瞬間に実行されるイベント
        Int intIndex = mes1.Show() ; メッセージその1を表示し、選択番号を取得
        If intIndex == 0 ; 「その2へ」
            Utility.Wait(0.001)
            GotoState("Show2")
            Return
        Else ; 「キャンセル」
            ;何もしないで終了
            GotoState("")
        EndIf
    EndEvent
EndState

;--------------------------------------------------ステート Show2
State Show2
    Event OnBeginState()
        Int intIndex = mes2.Show() ; メッセージその2を表示
        If intIndex == 0 ; 「実行」
            Debug.MessageBox("実行しました。")
            GotoState("")
        Else ; 「戻る」
            Utility.Wait(0.001)
            GotoState("Show1")
            Return
        EndIf
    EndEvent
EndState

メッセージを表示するタイミングとしてStateの移行を使っているだけですので、大体のイメージは湧くと思います。選んだ intIndex の番号によってStateを切り替えるかどうかが決まり、選択の仕方によってはメッセージその1とその2を交互に繰り返し表示することができます。

ここでのポイントは Utility.Wait(0.001)Return です。

Stateの説明でも触れましたが、Stateを移行しても元のStateは同時に実行されています。元のStateへ再び移行する前に Wait で一瞬の区切りを入れ、Return でそのStateを終了させておくことで、Stateを移行した順番通りに処理させるようにしています。
これらの記述がないと、処理内容によっては正しい順番で処理されないことがあります。

実は上記の例では処理が簡潔なため、WaitReturn が無くても正しく動作します。ただし、処理の中で Debug.MessageBox() を使用する場合には順番が乱れることが多いようです。
もしメッセージの行き来に失敗してしまう場合は、「GotoState()Utility.Wait()Return で挟む」ようにすると良いでしょう。

また、すべての処理が終わった段階で GotoState("") で Empty State に戻しています。私はこのように明示する方が好きですが、スクリプトの処理が完了すればStateは自動的に初期状態に戻りますので、上記の例では書かなくても大丈夫です。

最後に余談となりますが、今回お示ししたメッセージの行き来はFunctionという機能を用いても実装できます。ただし個人的にはFunctionの好きな使い方ではないので、Stateで実装することをお勧めしておきます。

目次に戻る


【Papyrus.psc】シリーズリスト

補足:私はスクリプトを書くのは好きですが、専門家ではありません。内容は creationkit.com の情報と個人的な経験を基にして書いています。どうかご参考程度にご覧ください。


0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。