2018年2月27日

【Papyrus.psc】NPCが室内ドアを閉めるようにする方法

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

ドアを開けたら閉めて欲しい!

NPCは室内ドアが閉まっていると開けて通りますが、その後閉めてくれるようにする方法です。


目次

  1. 対象のドアにトリガーを設定する
  2. トリガーにスクリプトを付ける
  3. プレイヤーには反応しないようにする

1. 対象のドアにトリガーを設定する

1-1. ドアにトリガーを付ける

室内ドアを通り抜けた後、ドアから離れるタイミングでスクリプトを発動します。その「ドアから離れるタイミング」を取得するため、対象のドアにトリガーを被せます。
例としてブリーズホームのリディアさんの部屋のドアに設定してみましょう。Cell View から「WhiterunBreezehome」を選択します。

Render Window に表示されたら、対象のドアを探します。

ドアを選択状態にして、ツールバーの「T」ボタンを押します。

Select Form ダイアログが出ますので、「TriggerBox」を選びます。

するとドアが薄い色のトリガーボックスで囲まれます。見えにくいかもしれませんが、このボックスの Reference を表示して設定していきます。

1-2. トリガーの調整

このドアは部屋の内側に開きます。そこで下図のように、開く側にトリガーボックスを大きくして寄せると自然な動きになります。

参考までにこのトリガーボックスのデータを示します。3D Data タブの Position X を「-659」にし、Primitive タブの Bounds X を「60」に変更しています。

今回はバニラの「TriggerBox」を流用しましたが、新規に3Dモデルの無いアクティベーターを作成してもOKです。アクティベーターの作成についてはCKでオブジェクトを固定する方法をご参照ください。

目次に戻る


2. トリガーにスクリプトを付ける

トリガーボックスには、新規で以下のようなスクリプトを付けます。スクリプト名は「CloseTheDoor」にしました。

Scriptname CloseTheDoor extends ObjectReference

ObjectReference Property objDoor Auto ; 対象のドアを指定

Event OnTriggerLeave(ObjectReference akTriggerRef)
    Actor actPasser ; 通った人
    actPasser = akTriggerRef as Actor

    If actPasser != None
        objDoor.Activate(actPasser) ; ドアをアクティベートして閉める
        actPasser = None
    EndIf
EndEvent

objDoor プロパティには対象のドアを指定してください。
Event OnTriggerLeave でトリガーから離れる瞬間に発動します。開いているドアというのは単純にアクティベートすれば閉まりますので、「objDoor.Activate(actPasser)」で通った人がドアをアクティベートして閉めています。

このスクリプトでは、プレイヤーおよびNPCが通るたびにドアが自動で閉まります。

目次に戻る


3. プレイヤーには反応しないようにする

上記のスクリプトでは、プレイヤーにとって自動ドアのような動きになりますので、プレイヤーには反応しないように改良してみます。
加えて、プレイヤーがドアの近くにいる場合はNPCが通っても閉まらないようにしましょう。

Scriptname CloseTheDoor extends ObjectReference

ObjectReference Property objDoor Auto ; 対象のドアを指定

Event OnTriggerLeave(ObjectReference akTriggerRef)
    Actor actPasser ; 通った人
    Actor actPlayer ; プレイヤー
    Float fltDistanceXY ; ドアとプレイヤーのXY距離の2乗
    Float fltDistanceZ ; ドアとプレイヤーのZ距離

    actPasser = akTriggerRef as Actor
    actPlayer = Game.GetPlayer()

    If actPasser != None && actPasser != actPlayer ; プレイヤーが通った時は反応しない
        fltDistanceXY = Math.Pow(objDoor.GetPositionX() - actPlayer.GetPositionX(), 2) + Math.Pow(objDoor.GetPositionY() - actPlayer.GetPositionY(), 2)
        fltDistanceZ = Math.Abs(objDoor.GetPositionZ() - actPlayer.GetPositionZ())

        If fltDistanceXY > 40000 || fltDistanceZ > 120 ; ドアとプレイヤーの距離が離れている場合のみ閉める
            objDoor.Activate(actPasser)
        EndIf

        actPasser = None
    EndIf

    actPlayer = None
EndEvent

actPlayer という変数を追加して、通った人とプレイヤーを区別するようにしました。
actPasser != actPlayer」という条件を付ければ、通った人がプレイヤーでない時だけ反応するようになります。

ドアとプレイヤーの距離については、GetPosition を使ってそれぞれの位置を取得し、計算しています。
XY方向(平面)は距離が200単位以上離れている時に反応します。スクリプト内では「fltDistanceXY > 40000」の部分で、2乗の値で判定しています。
Z方向(高さ)は120単位以上離れている場合にしました。1階と2階の高さは約200離れていますが、200に近い数値設定だと条件式に当てはまらずドアが閉まらないことがあります。

以上により、プレイヤーが近くにいない時だけNPCはドアを閉めるようになります。屋内でドアの開閉音がするのは、人がいることが感じられて意外と良いものですよ。

目次に戻る


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

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


0 件のコメント:

コメントを投稿

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