2017年12月3日

【Papyrus.psc】目の前にオブジェクトを作り出す

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

魔法でキャベツを作り出したいと思ったことはありませんか?
インベントリからお皿を捨てた瞬間、スイートロールにすり替えたいと思ったことは?

大丈夫。スクリプトで解決できますよ。今回は PlaceAtMe のお話です。


目次

  1. 新しく魔法を作成する
  2. 魔法でアイテムを作り出す
  3. アイテムを捨てたときに別の物にすり替える
  4. アイテムの出現位置を変更する

1. 新しく魔法を作成する

1-1. 魔法の構造

キャベツ魔法を作りたいなら、スクリプトを書く前に新しく魔法を作らないといけません。細かいことは省いて、最低限必要なことだけやります。

魔法の内部構造は結構複雑で、Spell(覚える魔法)> Magic Effect(魔法効果)> Projectile や ImpactData など(Effect に使う各種設定)というようにいくつもの設定があります。
今回はキャベツを作り出したいだけなので、自己にかける回復魔法をコピーし、回復効果だけを取り除いて使います。

1-2. Magic Effect(魔法効果)の複製

まずは Object Window の Magic > Magic Effect から、RestoreHealthConcSelf を探して右クリックし、Duplicate(複製)します。

複製品を開き、ID と Name を変更します。ここでは「CreateCabbageEffect」にしました。
回復効果を無効にするため、Assoc. Item 1 を「None」にします。
Magic item description が魔法の説明になるので、とりあえず「Create a cabbage.」にしておきました。

1-3. Spell(魔法)の複製

Object Window の Magic > Spell から HealingLeftHand を Duplicate します。

複製品を開き、ID と Name を変更します。Name が魔法の名前になります。
右枠内に設定されている既存の項目を右クリックして Delete します。
そのままもう一度右クリックして New を選択し、先ほど作成した CreateCabbageEffect を指定します。

CreateCabbageEffect の Magnitude や Duration は 0 のままでOKです。

これで魔法は作成できました。見た目は回復魔法そのままですがご容赦を。

目次に戻る


2. 魔法でアイテムを作り出す

2-1. 魔法効果に設定するスクリプト

魔法を唱えると手から光が出て、いざ発動!というときにキャベツを出します。
このスクリプトは Magic Effect ダイアログの Papyrus Scripts: に付けます。今回の場合は CreateCabbageEffect ですね。その記述が以下です。

Scriptname CreateCabbageScript extends ActiveMagicEffect

Potion Property potCabbage Auto ; キャベツ

Event OnEffectFinish(Actor akTarget, Actor akCaster)
    Game.GetPlayer().PlaceAtMe(potCabbage, 1)
EndEvent

Event OnEffectFinish が、魔法を唱え終わったときに発動するイベントです。ほとんどの魔法ではこのイベントでいけるでしょう。

スクリプトを保存したら、potCabbage プロパティに「FoodCabbage」を指定します。プロパティの指定については オブジェクトを表示したり消したりする を参照してください。キャベツなどの食べ物は、回復効果があるので Potion タイプに分類されています。

2-2. 物を作り出す「PlaceAtMe

単に物を作り出すだけなら Game.GetPlayer().PlaceAtMe(~, 1) だけのお手軽コードです。
「1」の部分が出てくる個数ですので、変更すれば2個でも3個でも出せます。

出てくる場所はプレイヤーの足元、というか足の裏です。プレイヤーが立っている座標です。
出現した瞬間に Havok が働きますので、出てくる物によってはポーンと飛んでいってしまったり、プレイヤーがカクカクしたりします。
出現位置の調整は最後にしっかりまとめてありますので、今はこのまま進めましょう。

2-3. 魔法の習得

作成した魔法を使えるようにしましょう。どこかにアクティベーターを設置して、以下のようなコードを追記します。

Spell Property speCabbage Auto ; キャベツ魔法

Event OnActivate(ObjectReference akActionRef)
    If akActionRef != Game.GetPlayer()
        Return
    EndIf

    akActionRef.AddSpell(speCabbage)
EndEvent

AddSpell で簡単に魔法を覚えることができます。speCabbage プロパティに、先ほど作成した魔法「CreateCabbage」を指定すればOKです。

お時間のある方はここでレッツテストプレイ! 魔法を唱えるたびにキャベツが出てくるだけですが、それなりに楽しめましたよ。ゲルダさんのお掃除の邪魔にはなります。

目次に戻る


3. アイテムを捨てたときに別の物にすり替える

3-1. インベントリから捨てた瞬間に発動するイベント

今度は、お皿をインベントリから捨てた瞬間にスイートロールとすり替えるスクリプトを書いてみます。以下のコードを Items > MiscItem > SilverPlate01(皿)に付けます。

Potion Property potSweet Auto ; スイートロール

Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
    If akNewContainer != None
        Return
    EndIf

    Game.GetPlayer().PlaceAtMe(potSweet, 1)

    Self.Disable()
    Self.Delete()
EndEvent

potSweet プロパティには「FoodSweetroll(スイートロール)」を指定します。

Event OnContainerChanged は、そのアイテムの収納場所が変更されたときのイベントです。
収納場所とは、樽や宝箱、インベントリなどです。

このイベントでは変更する前後の収納場所を取得することができ、akNewContainer は変更後の新しい収納場所を指します。アイテムを捨てたときは akNewContainer == None として判定できます。

上記のスクリプトでは、None でない場合に Return ですぐ終了するようになっています。例えばインベントリから宝箱に仕舞うときには実行されません。捨てたときにだけ PlaceAtMe でスイートロールが出現します。

3-2.「Self」について

上記スクリプトで Self が出てきました。これは、その名の通り自己を指します。スクリプトを付けた対象であり、この場合は皿です。

Self.Disable() とすることで、皿が非表示になります。そのままでは存在はしていますから、Self.Delete() として消去しています。

Self はプロパティや変数のように宣言しておく必要がありません。いつでも自己を指すコードとして使えます。

3-3. アイテムすり替えの用途

すり替えの使い道としては、インベントリに入っているアイテムを捨てた瞬間に、見た目が同様のアクティベーターにすり替える例があります。
アクティベーターというのはインベントリに取得させることができませんので、インベントリ専用のアイテムと設置用のアクティベーターを分けて管理するんですね。アクティベーター側に取得用のスクリプトを付ければ、取得したり設置したりできる1つのアイテムに見えるわけです。

今はわかりやすいように皿をスイートロールに替える方法としてご説明しました。
このスクリプトを皿に付けたいという方はいないと思いますが、実際にやると同じ皿すべてに影響しますのでご注意ください。盗まれても安心なスカイリムにしたいなら別ですが。

目次に戻る


4. アイテムの出現位置を変更する

4-1. 変数の作成と格納

Game.GetPlayer().PlaceAtMe でアイテムを出現させると、そのままではプレイヤーの足元に出現します。この出現位置を任意の場所に変更しましょう。

実は PlaceAtMe には引数が4つあります。その4番目に「true」を指定すると、Disable(非表示)の状態で出現させることができます。
「非表示で出現?」という方は、CKで既存オブジェクトを非表示にする方法 が参考になると思います。

この Disable 出現を使って、皿を捨てた位置(目の前)にスイートロールを出しましょう。皿のスクリプトを次のように修正します。

Potion Property potSweet Auto ; スイートロール

Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
    If akNewContainer != None
        Return
    EndIf

    ObjectReference objSweet ; 配置するスイートロール
    objSweet = Game.GetPlayer().PlaceAtMe(potSweet, 1, false, true)
    objSweet.SetPosition(Self.GetPositionX(), Self.GetPositionY(), Self.GetPositionZ())
    objSweet.SetAngle(Self.GetAngleX(), Self.GetAngleY(), Self.GetAngleZ())

    Self.Disable()
    objSweet.Enable()
    Self.Delete()

    objSweet = None
EndEvent

ObjectReference objSweet で、変数を定義しています。変数の型はプロパティのタイプと同様のものが使えます。上記の例では、ObjectReference という型で、objSweet という名前の変数を定義したことになります。
ObjectReference は配置されたオブジェクト全般を格納できますので、次の行で出現させたスイートロールを格納し、それ以降の行で扱えるようにしています。

変数の定義と同時に値を格納することも可能で、上の2行を次のようにまとめてしまうこともできます。

    ObjectReference objSweet = Game.GetPlayer().PlaceAtMe(potSweet, 1, false, true)

変数はこのスクリプトの中でだけ使うもので、プロパティのように esm/esp ファイルと関連付けたりしません。プロパティはゲームデータとスクリプトを事前に紐付ける役割が大きいのに対し、変数はスクリプト内で値を格納して使うものになります。

今回出現させたスイートロールのように、事前に指定しておくことができず、一時的に扱って値を保持しておく必要のないものは、変数に格納します。これをプロパティに格納することもできてしまいますが、プロパティの使い方としては好きではないです。

4-2. アイテムの位置と角度の指定

objSweet にスイートロールを格納したら、位置と角度を指定します。

  • objSweet.SetPosition(x, y, z) … xyzの座標を指定
  • objSweet.SetAngle(x, y, z) … xyz軸に対する角度を指定

Self.GetPositionX() で皿のx座標が取得できますので、それをスイートロールのx座標に指定しています。他の座標、角度も皿から取得してスイートロールに指定しています。皿とまったく同じ位置に、同じ角度でスイートロールを置いたことになります。

4-3. 皿とスイートロールとのすり替え

スイートロールはまだ非表示状態でしたので、皿とすり替えます。
Self.Disable()objSweet.Enable() で、皿を非表示にしてスイートロールを表示しています。このすり替えは一瞬で起こりますので、ゲーム内ではインベントリから捨てた皿がスイートロールにパッと切り替わるように見えます。

スクリプトの最後に objSweet = None として、使った変数をクリアして片付けておきます。
そのままでもプレイに支障はありませんが、「変数は必ず初期値を確認し、使ったらクリアする」というのが、エラーを起こさないための個人的な決め事です。

4-4. 位置と角度指定の応用

もし出現位置を少し前の足元に指定したい場合は、次のように変更すると上手くいきます。

Potion Property potSweet Auto ; スイートロール

Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
    If akNewContainer != None
        Return
    EndIf

    Actor actPlayer ; プレイヤー
    Float fltAngle ; プレイヤーの角度
    Float fltX ; 配置するx座標
    Float fltY ; 配置するy座標
    Float fltZ ; 配置するz座標
    ObjectReference objSweet ; 配置するスイートロール

    actPlayer = Game.GetPlayer()
    fltAngle = actPlayer.GetAngleZ()
    fltX = actPlayer.GetPositionX() + Math.Sin(fltAngle) * 50
    fltY = actPlayer.GetPositionY() + Math.Cos(fltAngle) * 50
    fltZ = actPlayer.GetPositionZ()

    objSweet = actPlayer.PlaceAtMe(potSweet, 1, false, true)
    objSweet.SetPosition(fltX, fltY, fltZ)
    objSweet.SetAngle(0, 0, fltAngle)

    Self.Disable()
    objSweet.Enable()
    Self.Delete()

    actPlayer = None
    objSweet = None
EndEvent

変数を追加で定義しています。Actor は人や動物の型で、プレイヤーを格納しました。Float は小数を含む数値を扱います。

プレイヤーの位置と角度からスイートロールのxyz座標を決めるために、Math.SinMath.Cos などの三角関数を用います。
三角関数の説明はしませんが、プレイヤーの位置と角度から目の前にオブジェクトを置くときは、いつでもこの方法の応用で書けます。「50」の数値を大きくすれば、より離れた所に置かれます。

それから、スイートロールの角度を指定している所は、xyを「0」と指定することで水平に設置しています。床に置く場合には非常に有効です。

以上の座標と角度の指定方法は、最初に作成したキャベツ魔法のときにも同様に使えます。

目次に戻る


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

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


0 件のコメント:

コメントを投稿

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