自動脱衣スクリプト2020

自動脱衣スクリプトの2020年版です。旧サイトで公開していたスクリプトを最適化し、これまでの知識と経験をもとに1から書き直しました。自作MODに自動脱衣機能を付けたり、プレイヤー用の脱衣かごを用意したりする際、ご参考になる部分があれば嬉しいです。

※2022/03/12 スクリプトの2行目に著作権表記を入れるようにしました。今まで通り表記なしで使っていただいても大丈夫です。


  1. 特定ゾーンに自動脱衣機能を付ける方法
  2. 脱衣かごを配置する方法
  3. スクリプト解説
  4. 著作権表記について

1. 特定ゾーンに自動脱衣機能を付ける方法

1-1. トリガーボックスで囲む

特定ゾーンに自動脱衣機能を付けるには、その範囲をトリガーボックスという見えないボックスで囲み、スクリプトを設定することになります。

まずは Creation Kit (CK) で対象のセルを表示し、何でも良いのでオブジェクトを選択してください。今回は、キナレス聖堂で中央の床を選択してみます。

この状態でツールボタンの「T」を押し、出てくるダイアログで「TriggerBox」を選択します。

薄い色のボックスが出現したら、下図のように適切な大きさに変更します。このボックスの中に入ると脱衣し、出ると服を着ることになります。

微調整したいときは、トリガーボックスをダブルクリックして Reference を開き、「Position」で位置を、「Bounds」で大きさを指定します。

1-2. ボックスに新規スクリプトを設定する

Reference の Scripts タブで「Add」ボタンを押します。
出てきたダイアログで [New Script] を選択して、新規スクリプトを追加します。

「Name」がスクリプト名になりますので、他のMODと被らないような名称を付けてください。

スクリプトが追加されたら、右クリックして「Edit Source」を選択し、編集画面を開きます。外部エディタを設定済みの場合は「Open in External Editor」です。

1-3. 自動脱衣スクリプト

編集画面を開くと、1行目はすでに入力されています。2行目以降を以下のようにしてください。このスクリプトで、プレイヤーもNPCも自動脱衣・着衣します。
このスクリプトにはSKSEが必要です。解説は記事の最後に載せています。

Scriptname AutoUnequip_scrTrigger extends ObjectReference
{Copyright (C) 2020,2022 eka. (https://ekagames.lifecycle.jp/creation-kit/unequip-all/)}
 
Armor Property armRing Auto ; NPC用リング
FormList Property folArmor Auto ; プレイヤー防具
Form Property fomL Auto ; プレイヤー左手
Weapon Property wepR Auto ; プレイヤー右手
 
;------------------------------ ボックスに入るとき
Event OnTriggerEnter(ObjectReference akActionRef)
    Actor actRef = akActionRef as Actor
 
    If actRef == None
        Return
    ElseIf actRef == Game.GetPlayer() ; プレイヤー
        wepR = actRef.GetEquippedWeapon() ; 右手武器取得
        fomL = actRef.GetEquippedObject(0) ; 左手アイテム取得
 
        folArmor.Revert()
        Armor armWorn
        Int i = 0
 
        While i < 31 ; 装備スロット順に防具取得
            armWorn    \
                = actRef.GetWornForm(Math.LeftShift(1, i)) as Armor
            If armWorn
                folArmor.AddForm(armWorn)
            EndIf
            i += 1
        EndWhile
 
        armWorn = None
    Else ; NPC
        actRef.AddItem(armRing, 1, true)
        actRef.EquipItem(armRing, false, true)
        actRef.RemoveItem(armRing, 9, true) ; Outfit 装備
        Utility.Wait(0.05)
    EndIf
 
    actRef.UnequipAll() ; 脱衣
    actRef = None
EndEvent
 
;------------------------------ ボックスから出るとき
Event OnTriggerLeave(ObjectReference akActionRef)
    Actor actRef = akActionRef as Actor
 
    If actRef == None
        Return
    ElseIf actRef == Game.GetPlayer() ; プレイヤー
        If wepR && actRef.GetItemCount(wepR) > 0
            actRef.EquipItemEx(wepR, 1) ; 右手装備
        EndIf
        wepR = None
 
        If fomL && actRef.GetItemCount(fomL) > 0
            actRef.EquipItemEx(fomL, 2) ; 左手装備
        EndIf
        fomL = None
 
        Armor armWorn
        Int i = folArmor.GetSize()
 
        While i > 0
            i -= 1
            armWorn = folArmor.GetAt(i) as Armor
            If armWorn && actRef.GetItemCount(armWorn) > 0
                actRef.EquipItemEx(armWorn) ; 防具装備
            EndIf
        EndWhile
 
        armWorn = None
        folArmor.Revert()
    Else ; NPC
        actRef.AddItem(armRing, 1, true)
        actRef.EquipItem(armRing, false, true)
        actRef.RemoveItem(armRing, 9, true) ; Outfit 装備
    EndIf
 
    actRef = None
EndEvent

ツールバーの File > Save を選択し、スクリプトをセーブ(コンパイル)します。

コンパイルに成功すると「Compilation succeeded.」の文字が出ます。

1-4. プロパティに用いるオブジェクトを作成する

プロパティとは、スクリプトに付けられた変数の一種です。ゲームデータとスクリプトとの橋渡しをするのが主な目的で、プロパティを指定することによって、スクリプトからゲームデータにアクセスしたり操作したりすることができます。

今回必要なオブジェクトは次の2つで、これらを作成してプロパティに指定します。

  • Armor armRing … NPCの装備をコントロールするための指輪。
  • FormList folArmor … プレイヤーの防具情報を保持するためのリスト。

指輪は何でも良いのですが、今回は金の指輪をコピーし、MOD専用として使いたいと思います。
Object Window で Items > Armor から「JewelryRingGold」を開きます。

ID と Name を任意の名称に変更します。ゲーム内では表示しないので、分かりやすいもので構いません。
「Playable」のチェックを外すと、プレイヤーが扱うことのできない、非表示アイテムになります。

編集してOKボタンを押すと、新しいオブジェクトとして作成するかどうかのダイアログが出ますので、「はい」を選択します。これで金の指輪のコピーが作成されます。

次に、防具情報を保持するための FormList を用意します。
Miscellaneous > FormList を開き、どこでも良いので右クリックして「New」を選択します。

ここでは ID に任意の名称を付けるだけでOKです。

1-5. プロパティに指定する

作成したオブジェクトをプロパティに指定していきます。
トリガーボックスの Reference を開き、スクリプトを選択状態にして「Properties」ボタンを押します。

ダイアログが開いたら、「armRing」を選択して右側の「Edit Value」ボタンを押してください。

Pick Object 欄で、作成した「armRing」を選択します。同様にして「folArmor」も指定してください。他の2つは未指定のままで大丈夫です。

以上で完成です。トリガーボックスの範囲が自動脱衣仕様になりました。
esp を保存してロードオーダーに加えてから、ゲーム内で確認してみましょう。


2. 脱衣かごを配置する方法

2-1. かごを作成する

脱衣かごは、セルのどこかに配置してアクティベートするものとします。見た目だけバニラのかごを流用し、家具のようにアクティベートできるようにしましょう。

Object Window で Items > MiscItem から Basket06 を開きます。Model 欄の「Edit」ボタンを押して、見た目を確認します。

見た目が確認できたら、nif ファイルのパスをドラッグ選択してコピーします。

WorldObjects > Activator を開き、どこでも良いので右クリックして「New」を選択します。

ID と Name に任意の名称を入力します。Name はゲーム内でクロスヘアを合わせたときに表示されるテキストです。
そして、Model の右側の「Edit」ボタンを押します。

モデルが入っていない空の画面が開くので、「Edit」を押します。

ファイル名のところに、先ほどコピーした nif ファイルのパスをそのまま貼り付けます。

元の画面に戻ってモデルが表示されればOKです。このとき、うまく表示されず赤い三角形になってしまうことがあります。そのときは、nif ファイルのパスをもう一度貼り付け直してみてください。それでうまくいくと思います。

Model 欄にパスが入ったことを確認したら、「OK」ボタンを押して確定させます。新規のオブジェクトは、一度確定させないとスクリプトを設定することができません。

2-2. かごに新規スクリプトを設定する

作成した Activator を再度開いて、Scripts の「Add」ボタンを押します。スクリプトの追加方法はトリガーボックスの場合と同様です。

トリガーボックスのときは配置したボックスの方にスクリプトを設定しましたが、今度は配置する前のベースアイテムに設定することになります。こうすることで、この脱衣かごを複数配置しても、各個別にスクリプトを設定する必要がなくなります。

2-3. 脱衣かごスクリプト

脱衣かごはプレイヤー専用としました。体 (Body) の部位に何か装備していれば脱衣し、そうでなければ脱衣時の装備を着衣するというシンプルな仕様です。

これをフラグ管理仕様にすることもできますが、プレイヤーが自分で着衣を済ませた場合などの処理が複雑になるため、あまりお勧めしません。

Scriptname AutoUnequip_scrBasket extends ObjectReference
{Copyright (C) 2020,2022 eka. (https://ekagames.lifecycle.jp/creation-kit/unequip-all/)}
 
FormList Property folArmor Auto ; プレイヤー防具
Form Property fomL Auto ; プレイヤー左手
Weapon Property wepR Auto ; プレイヤー右手
 
;------------------------------ アクティベート
Event OnActivate(ObjectReference akActionRef)
    Actor actRef = akActionRef as Actor
 
    If actRef == None || actRef != Game.GetPlayer()
        actRef = None
        Return
    EndIf
 
    Armor armWorn
    Int i
 
    If actRef.GetWornForm(4) ; 脱衣
        wepR = actRef.GetEquippedWeapon() ; 右手武器取得
        fomL = actRef.GetEquippedObject(0) ; 左手アイテム取得
 
        folArmor.Revert()
        i = 0
 
        While i < 31 ; 装備スロット順に防具取得
            armWorn    \
                = actRef.GetWornForm(Math.LeftShift(1, i)) as Armor
            If armWorn
                folArmor.AddForm(armWorn)
            EndIf
            i += 1
        EndWhile
 
        actRef.UnequipAll() ; 脱衣
    Else ; 着衣
        If wepR && actRef.GetItemCount(wepR) > 0
            actRef.EquipItemEx(wepR, 1) ; 右手装備
        EndIf
        wepR = None
 
        If fomL && actRef.GetItemCount(fomL) > 0
            actRef.EquipItemEx(fomL, 2) ; 左手装備
        EndIf
        fomL = None
 
        i = folArmor.GetSize()
 
        While i > 0
            i -= 1
            armWorn = folArmor.GetAt(i) as Armor
            If armWorn && actRef.GetItemCount(armWorn) > 0
                actRef.EquipItemEx(armWorn) ; 防具装備
            EndIf
        EndWhile
 
        folArmor.Revert()
    EndIf
 
    actRef = None
    armWorn = None
EndEvent

自動脱衣の場合と処理は似ています。プロパティは folArmor のみ作成して設定すればOKです。

2-4. 脱衣かごを配置する

配置したい場所を Render Window に表示し、Object Window から脱衣かごをドラッグ&ドロップします。スクリプトをベースアイテムに設定したため、これだけで脱衣かごとして機能します。

そのままでは触れたときに動いてしまうので、Reference を開いて次の2か所の設定で固定します。

  • 「Don’t Havok Settle」にチェックを入れる。
  • 固定用スクリプト「defaultDisableHavokOnLoad」を追加する。

固定用スクリプトはバニラで使われているものです。プロパティを4つ持っていますので、「havokOnHit」を選択して「Edit Value」を押し、下図の状態にしてください。

2-5. トリガーボックスと併用する

もし、トリガーボックスの自動脱衣と脱衣かごとを併用する場合は、トリガーボックス側のプレイヤー脱衣を無効にする必要があります。脱衣かごでプレイヤーを管理し、トリガーボックスでNPCを管理することになります。

トリガーボックスのスクリプトは、以下のように変更します。

Scriptname AutoUnequip_scrTrigger extends ObjectReference
{Copyright (C) 2020,2022 eka. (https://ekagames.lifecycle.jp/creation-kit/unequip-all/)}
 
Armor Property armRing Auto ; NPC用リング
 
;------------------------------ ボックスに入るとき
Event OnTriggerEnter(ObjectReference akActionRef)
    Actor actRef = akActionRef as Actor
 
    If actRef == None || actRef == Game.GetPlayer()
        actRef = None
        Return
    EndIf
 
    actRef.AddItem(armRing, 1, true)
    actRef.EquipItem(armRing, false, true)
    actRef.RemoveItem(armRing, 9, true) ; Outfit 装備
    Utility.Wait(0.05)
    actRef.UnequipAll() ; 脱衣
    actRef = None
EndEvent
 
;------------------------------ ボックスから出るとき
Event OnTriggerLeave(ObjectReference akActionRef)
    Actor actRef = akActionRef as Actor
 
    If actRef == None || actRef == Game.GetPlayer()
        actRef = None
        Return
    EndIf
 
    actRef.AddItem(armRing, 1, true)
    actRef.EquipItem(armRing, false, true)
    actRef.RemoveItem(armRing, 9, true) ; Outfit 装備
    actRef = None
EndEvent


3. スクリプト解説

3-1. NPCを脱衣させる

NPCを脱衣させているのは、以下の1行です。これだけで、すべての装備品を外すことができます。

actRef.UnequipAll()

ただし、この UnequipAll() という関数は使い方に注意する必要があります。というのも、特定の場面で高確率のCTDを引き起こすからです。

それは、「セーブデータをロードした直後で、一度もセル移動をせず、NPCに対して関数を発動させた場合」です。

この原因は未だに解明できていません。体型MODを入れていない環境でも起こり、それが起こる状況のセーブデータでは再現率100%のように感じます。

対策として、脱衣の前に Outfit を着用させ直すことで、CTDをほぼ防ぐことができます。それが以下の処理です。

actRef.AddItem(armRing, 1, true)
actRef.EquipItem(armRing, false, true)
actRef.RemoveItem(armRing, 9, true)
Utility.Wait(0.05)

NPCに対して AddItem() や RemoveItem() 関数でアイテムの出し入れをすると、自動的に Outfit を着用し直します。これはデフォルトの仕様ですが、動作がやや不確実なため、間に EquipItem() を挟むと安定します。

この処理を UnequipAll() の前に入れることで、CTDを防いでいます。

3-2. NPCを着衣させる

NPCを着衣させるには、たった今出てきた Outfit の着用し直しを行うだけで良いです。

フォロワーでないNPCなら Outfit に設定された防具のみを装備しますし、フォロワーとして雇用中であれば、さらに最適装備に切り替わります。

3-3. プレイヤーを脱衣させる

プレイヤーの脱衣も UnequipAll() で行います。プレイヤーの場合、この関数でCTDを引き起こすことはありません。

ただ、脱衣前の装備情報を保持しておかないといけませんので、以下の3つのプロパティを使っています。

FormList Property folArmor Auto ; プレイヤー防具
Form Property fomL Auto ; プレイヤー左手
Weapon Property wepR Auto ; プレイヤー右手

右手武器は以下のように取得できます。

wepR = actRef.GetEquippedWeapon() ; 右手武器取得

左手はたいまつの可能性があるので、Weapon でなく Form で取得します。

fomL = actRef.GetEquippedObject(0) ; 左手アイテム取得

防具は複数ありますので、FormList にリストとして保持します。それが以下の部分です。

i = 0

While i < 31 ; 装備スロット順に防具取得
    armWorn	\
        = actRef.GetWornForm(Math.LeftShift(1, i)) as Armor
    If armWorn
        folArmor.AddForm(armWorn)
    EndIf
    i += 1
EndWhile

GetWornForm() を使い、30番から60番までの装備スロットをチェックしています。装備スロットは他にもあるのですが、一般にはこれでほぼ取得できると思います。

この関数は Form を返してきますので、最後に as Armor を付けて変換し、None でなければリストに加えるようにしています。

3-4. プレイヤーを着衣させる

プレイヤーの着衣は、保持していた情報をチェックし、そのアイテムを所持していれば装備するという方法で行います。

If wepR && actRef.GetItemCount(wepR) > 0
    actRef.EquipItemEx(wepR, 1) ; 右手装備
EndIf
wepR = None

If fomL && actRef.GetItemCount(fomL) > 0
    actRef.EquipItemEx(fomL, 2) ; 左手装備
EndIf
fomL = None

i = folArmor.GetSize()

While i > 0
    i -= 1
    armWorn = folArmor.GetAt(i) as Armor
    If armWorn && actRef.GetItemCount(armWorn) > 0
        actRef.EquipItemEx(armWorn) ; 防具装備
    EndIf
EndWhile

武器も防具もSKSEの EquipItemEx() という関数で装備させます。特に防具については、バニラの EquipItem() 関数を使ってはいけません。プレイヤーが独自に付呪した装備品の場合、EquipItem() 関数では付呪効果が適用されないからです。そこだけ気をつければ、問題なく元の姿に戻れると思います。

常に EquipItemEx() を使えば良さそうに思えるかもしれませんが、バニラの EquipItem() にも使い道はあります。例えば上述したNPCの Outfit を着用させるスクリプトでは、EquipItem() の方でないと逆に安定しません。

プレイヤーの着衣で一つ問題点を挙げるとすれば、「矢」を指定できない点です。Papyrus には、装備中の矢の情報を取得する関数はありません。UnequipAll() で外すことはできても、複数の矢を所持している場合に、元の矢を装備させるのが難しいのです。

矢を管理するには、インベントリに入った時点でその矢の情報を保持し、かつ装備したかどうかを専用クエストで監視することになると思います。脱衣機能のためだけにそのようなシステムを組むのは少しやりすぎな感じがしますので、これは仕様ということにして良いでしょう。


4. 著作権表記について

当ページで公開している Papyrus のスクリプトは、配布MODにおける利用、および個人利用問わず自由にご利用ください。スクリプトの2行目に著作権表記をしていますので、スクリプトをコピー&ペーストして使っていただいて構いません。必要であればご自身の著作権も併記してください。著作権は放棄しませんが、著作権表記そのものを省略することも許可します。

あまり難しいことは考えず、スクリプトについては自由に一部または全部をコピー・改変して使ってくださいということです。あらためて書かなくてもいいようスクリプト内に著作権表記をしていますが、消してしまっても構いません。ただし、スクリプトの内容によって生じた不具合・損害等については、一切の責任を負いかねますのでご了承ください。

ご厚意により配布場所等で著作権表記、あるいはリンクをしていただける場合には、以下の文字列をお使いください。

Copyright © 2020,2022 eka.
https://ekagames.lifecycle.jp/creation-kit/unequip-all/

※2022/03/12 改稿


Leave a comment

メールアドレスが公開されることはありません。

18 + 13 =