走っている時の呼吸音を追加します。Elzeeさんの作品です。
使い方
普通にインストールするだけです。
走ると呼吸音が再生されるようになります。音は結構小さいので、ヘッドフォンで聞かないとわかりにくいかもしれません。
改造してみた
呼吸音の再生条件が「走っている時」なので、走り出すとすぐに呼吸音が再生され、走るのをやめるとすぐに再生が止まります。これを走り出してしばらくしたら呼吸音が再生され、走るのをやめてもしばらくは再生を続けるようにしてみました。
スタミナの概念を追加する
SkyrimにはスタミナというActor Valueがありました。Falloutシリーズではスタミナがなく、かわりにAPがあります。
Fallout 4はActor Valueを自由に作成できるようになったので、スタミナを作りました。
Actor Valueのメリットとして、Actor単位でリソースを管理できる、各種Conditionに使える、Magic Effectで増減できるという点があげられます。何よりデータ構造的に理にかなっており美しいので、Modの開発がやりやすくなります。
SkyrimではActor Valueを増やせないので、かわりにグローバル変数を使ったり、Factionに所属させて代用したりする方法がありました。グローバル変数はゲーム内全体で共有するのでActor単位で作ると管理が大変になってしまう問題があります。FactionはRankをうまく使えばActorごとにリソースを管理できそうですが、元々派閥管理ために用意された仕組みを他のことに転用するため不具合の起こる可能性があがります。
スタミナが増減するアビリティを作る
走るとスタミナが減り、走るのをやめるとスタミナが回復するアビリティを用意します。これをプレイヤーに持たせると、自然にスタミナが増減するようになります。
スタミナというリソースを使うメリットは、走る以外にもスタミナを増減する機会を追加していきやすいという点があります。例えば地雷を踏んだらスタミナが大幅に減るとか、怪我をしている最中はスタミナが減り続けるとか、いろいろと応用がききます。
スタミナが減っている時に呼吸音を再生する
スタミナが一定値以下になったら効果が出るアビリティを用意します。
効果(Magic Effect)にはスクリプトを付け、そこで呼吸音を再生します。
ScriptName ElzeeBreathe:ElzeeBreatheEffectScript extends ActiveMagicEffect
;-- Properties --------------------------------------
Sound Property FemaleBreathInSound Auto Const
Sound Property FemaleBreathOutSound Auto Const
Sound Property MaleBreathInSound Auto Const
Sound Property MaleBreathOutSound Auto Const
ActorValue Property Stamina Auto
;-- Variables ---------------------------------------
Sound BreathInSound
Sound BreathOutSound
bool bIn = True
Actor MySelf
;-- Functions ---------------------------------------
Event OnEffectStart(Actor akTarget, Actor akCaster)
Debug.Trace("ElzeeBreathe:ElzeeBreatheEffectScript OnEffectStart")
MySelf = akTarget
if MySelf.GetActorBase().GetSex() != 0
BreathInSound = FemaleBreathInSound
BreathOutSound = FemaleBreathOutSound
else
BreathInSound = MaleBreathInSound
BreathOutSound = MaleBreathOutSound
endif
Self.starttimer(0.0, 0)
EndEvent
Event ontimer(int tid)
Debug.Trace("ElzeeBreathe:ElzeeBreatheEffectScript ontimer")
if bIn
BreathInSound.play(MySelf)
bIn = False
else
BreathOutSound.play(MySelf)
bIn = True
endif
if MySelf.GetValue(Stamina) < 7
Self.starttimer(1.0, 0)
else
Self.starttimer(2.0, 0)
endif
EndEvent
スタミナが7未満なら呼吸音を荒く、7以上15未満ならゆっくりとしました。
既存のコードを調整
オリジナルは常時稼働するクエストにてプレイヤーの左足の足音発生イベントを監視して、足音の発生に合わせて呼吸音を再生しています。このクエストの稼働を止め、かわりに上記のアビリティをプレイヤーに持たせるようにします。
サウンドはすべてに「走っている」という条件が設定されていますので、これを取り除きます。これで立ち止まっている時でも呼吸音が再生されます。
注意事項
Actor Valueを増やすとActor全員に設定される関係上、セーブデータが大きくなることになります。もしかすると、初期値のまま増減させなければ理屈の上ではセーブデータに含める必要がないので、気にする必要はないのかもしれませんが、ベセスダがそこまで考えて実装しているのかは不明です。
機能をプレイヤーだけに限定するのならグローバル変数を使う方法もあります。ただしスタミナの増減はグローバル変数の書き換えになるため、スクリプト化が必須となってしまいます。個人的にはこちらの方が嫌なのでActor Valueとしました。
Actor Valueの初期値はなぜか一律100になってしまいます。これを変更する方法がわからなかったので、クエストの初期化処理のところにプレイヤーのスタミナを20にする処理を追加しました。
スクリプトからActor Valueにダメージを与えたり回復させる際はDamageValue関数やRestoreValue関数を使います。ModValueやSetValueは恒久的な変化になってしまうため、以降の処理が狂います。
関数名 | 用途 |
---|---|
DamageValue | 一時的に減らす。 ダメージを与える。 |
RestoreValue | 一時的に減っている値を戻す。 ダメージを回復する。 |
ModValue | 指定した値だけ増減させて変更する。 ずっとそのまま。 |
SetValue | 指定した値に変更する。 ずっとそのまま。 |
スタミナが減る機会を増やす
おまけとして、いくつか例をまとめておきます。
爆発を受けた時
ScriptName SampleQuestScript extends Quest
Actor Property PlayerRef Auto Const
ActorValue Property Stamina Auto
Keyword Property WeaponTypeExplosive Auto
Event OnInit()
RegisterForHitEvent(PlayerRef, akSourceFilter = WeaponTypeExplosive)
EndEvent
Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, \
bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)
Float fStamina = PlayerRef.GetValue(Stamina)
PlayerRef.DamageValue(Stamina, fStamina)
Utility.Wait(5.0)
RegisterForHitEvent(PlayerRef, akSourceFilter = WeaponTypeExplosive)
EndEvent
WeaponTypeExplosiveというキーワードは手榴弾や地雷にあるので便利です。
怪我をした時
ScriptName SampleQuestScript extends Quest
Actor Property PlayerRef Auto Const
ActorValue Property Stamina Auto
Keyword Property WeaponTypeExplosive Auto
Event OnInit()
Self.RegisterForRemoteEvent(PlayerRef, "OnCripple")
EndEvent
Event Actor.OnCripple(Actor akSender, ActorValue akActorValue, Bool abCrippled)
if abCrippled
Float fStamina = PlayerRef.GetValue(Stamina)
PlayerRef.DamageValue(Stamina, fStamina)
endif
EndEvent