UnityでホラーゲームSIRENの視界ジャック機能を作成する

記事内に広告が含まれています。

今回はUnityでソニーから発売されたホラーゲームSIRENの視界ジャック機能を作成してみたいと思います。

SIRENの視界ジャック機能は屍人の視界(屍人の目線)で周りを見ることが出来、屍人の行動や喋っている言葉等を体感出来る機能です。

わたくしがSIRENを好きなので作ってみました。(^^)/

今回の機能を作成すると、

上のように移動キーを押した方向に一番近い敵の視界になり、敵から見たゲーム世界を見ることが出来ます。

最初はキーボードの方向キーで確認し、途中からPS4コントローラーを接続し確認しました。

PS4コントローラーを接続すると音声がコントローラーヘッドホンの方に流れるようなので音声が消えたようになりました(動画では音声が流れます)。

スポンサーリンク

視界ジャック機能の概要

視界ジャック機能を作る前にまずはどうやって作るか?を考えていきたいと思います。

操作キャラクターは通常通り移動や攻撃等(今回は移動機能だけ作成)が出来るようにして、視界ジャックを行うボタンを押した時に視界ジャックモードに入るとします。

今回はPS4コントローラーの△ボタンを視界ジャックモードに入るボタンに割り当てる事にします(キーボードでも出来ます)。

視界ジャックモードになったらキャラクターの移動や攻撃は出来ないようにし、コントローラーのアナログスティックで周りにいる敵を検索します。

視界ジャック中はテレビの砂嵐動画と音声を再生します。

アナログスティックの入力は横軸と縦軸の入力を3D空間のX軸とZ軸に割り当ててワールド軸の前方からの角度を計算し、敵のいる角度はワールド軸の前方からの角度で計算します。

「ワールド軸の前方から見たアナログスティックの入力角度」と「ワールド軸の前方から見た敵の角度」を比較しより近い敵を視界ジャックする敵とします。

主人公から見たアナログスティックの入力角度

ただしアナログスティックの入力をInput.GetAxisを使って、

上のようにXZ軸に割り当てた場合は必ずVector3.forward(ワールド軸の前方)が縦軸の入力であるInput.GetAxis(“Vertical”)になってしまいます。

例えばキャラクターを90度回転させてアナログスティックの↑を押したとしてもVector3.forwardの方向の入力になってしまい、キャラクターの前方の入力になりません。

アナログスティックの入力の前方とキャラクターの前方が合わない

本来であればアナログスティックの↑を押したらキャラクターの前方方向を押したと判定したいところです。

ここでややこしくなるのが、アナログスティックの↑を押した時にキャラクターの前方方向を押した事に出来た場合でも

アナログスティックの↑を押した時はワールド軸の前方からの角度を求める

ということです。

敵の角度もワールド軸の前方からの角度で、アナログスティックの角度もワールド軸の前方からの角度を求めます。

なのでキャラクターを90度回転させた時にアナログスティックの↑を押した時に90度が得られるようにします。

入力はキャラクターの前方から見た方向を得る必要はあるが、比較する敵の角度とアナログスティックの入力方向はワールド軸の前方からの角度で計算するということですね。

わたくしはここら辺でかなりこんがらがりました。(^_^;)

一番近い敵がわかったらアナログスティックの入力角度の差によって砂嵐の濃さと音量を変化させます。

またメインカメラのCameraとAudio Listenerコンポーネントを無効にし、一番近い敵に設定されているCameraとAudio Listenerを有効にします。

これが視界ジャック機能の概要となります。

視界ジャック機能の作成

視界ジャック機能をどうやって作るか?がわかったので、実際に作っていきましょう。

操作キャラクターの作成

まずは操作キャラクターを作成します。

今回の操作キャラクターは↑のキーを押したらキャラクターの前方に進み、↓を押したら後方に進み、←→を押したらその方向に回転するようなキャラクターになります。

キャラクターが前に進むには必ず↑のキーを押す必要があり、←→のキーで方向を定めます。

キャラクターのモデルにはスタンダードアセットのEthanを使用し、CharacterControllerの取り付けをしてコライダの調整をしておきます。

キャラクターのAnimatorControllerは立っている状態のIdle状態、歩いているWalk状態、後ろ向きに歩くWalkBack状態を作成します。

視界ジャック機能用の操作キャラクターのアニメーターコントローラー

それぞれの状態のアニメーションは以下のように設定します。

IdleにはスタンダードアセットのHumanoidIdle
WalkにはスタンダードアセットのHumanoidWalk
WalkBackにはスタンダードアセットのHumanoidWalk

を設定します。

またWalkBack状態を選択し、Speedに-1を設定してWalkBackでは歩くアニメーションを逆再生するようにします。

WalkBack状態のアニメーションは逆再生させる

アニメーションパラメータ―にFloat型のSpeedを作成します。

Idle→Walkの条件はSpeedがGreaterで0.1
Walk→Idleの条件はSpeedがLessで0.1
Idle→WalkBackの条件はSpeedがLessで-0.1
WalkBack→Idleの条件はSpeedがGreaterで-0.1

でそれぞれHas Exit Timeのチェックを外して直ぐに次の状態に遷移するようにします。

新しいスクリプトViewJackCharaスクリプトを作成し、キャラクターであるEthanに取り付けます。

スクリプトで行っていることは概要で説明したキャラクターの動きをさせる事とキャラクターの状態の取得と設定を出来るメソッドを用意していることです。

横軸の入力があればtransform.Rotateを使ってキャラクターを回転させます。

横に回転する時はアニメーションパラメータ―のSpeedを1にしてWalk状態に遷移させます。

縦軸の入力の場合はアニメーションパラメータ―のSpeedに縦軸の入力値をそのまま入れて↑だったらWalk状態、↓だったらWalkBack状態へと遷移するようにします。

また進む方向はキャラクターの向いている方向の前後になります。

キャラクターを移動出来るのはキャラクターの状態がHeroState.Normalの時だけで、HeroState.ViewJackの時は移動出来ません。

これで操作キャラクターが出来ました。

視界ジャック機能のスクリプトの作成

視界ジャック機能のスクリプトを作成していきます。

新しくViewJackScriptという名前のスクリプトを作成しEthanに取り付けます。

スクリプトが長いので少しづつ解説します。

フィールド宣言部

まずはフィールド宣言部を見ていきます。

enemyListには視界ジャックが出来る敵をインスペクタで設定します。

isViewJackは視界ジャックをしているかどうかのフラグです。

fieldOfViewは視界ジャックをする敵のキャラクターの視野の角度です。この範囲外の敵は視界ジャック適用外とします。

limitAngleOfViewJackはアナログスティックを押した角度と敵の角度の差がこの範囲内の時に一番近い敵かどうかの判定をします。

snowNoiseMaxValueAngleLimitは砂嵐の強度の最大値を設定します。

snowNoiseIntensityOffsetはアナログスティックの角度と敵の角度が完全に一致した時の砂嵐の強度の補正値を設定します。

viewJackCharaはキャラクターの操作スクリプトであるViewJackCharaです。

snowNoiseImageObjは砂嵐を表示するRawImageゲームオブジェクトを設定します。

mainCameraはメインカメラを設定します。

mainAudioListenerはメインカメラのAudio Listenerを設定します。

currentCameraは視界ジャックをしている敵のカメラを入れます。

currentAudioListenerは視界ジャックをしている敵のカメラに設定されているAudioListenerを入れます。

Updateメソッド

次にUpdateメソッドを見ていきます。

Input Managerで設定したViewJackボタンを押した時に視界ジャック機能のオン・オフをします。

Input Managerに関しては下記の記事の「UnityのInputManagerでキーとボタンの設定をする」の項目を参照して設定してみてください。

PS4コントローラーの設定ではMotion JoyやBetter PS3はいらないっぽい?ので他のソフトのダウンロードやインストールはせず「UnityのInputManagerでキーとボタンの設定をする」の項目だけまずは設定してみてください。

(今確認したらPS3コントローラーもMotion JoyやBetter PS3がいらなくなってるかも?)

UnityでPS3コントローラー(ゲームパッド)に対応して動かしてみる
UnityのゲームでPS3コントローラー等のゲームパッドを使用してみます。パソコンのゲーム用コントローラーを持っていない人は便利かもしれません

わたくしの環境ではPS4コントローラーの△ボタンはjoystick button 3に割り当てました。

PS4コントローラーの△ボタンの割り当て

キャラクターの状態をGetStateメソッドで取得し、ノーマル状態なら視界ジャック状態へ、視界ジャック状態ならノーマル状態へと切り替えます。

その時に砂嵐を表示しているRawImageの有効・無効、砂嵐の音声を設定しているAudioSourceの再生・停止を切り替えます。

視界ジャック状態からノーマル状態へと移行する時はInitializeViewJackメソッドを呼び出して視界ジャック設定の初期化を行います。

isViewJackがtrueの時は視界ジャック中なのでViewJackメソッドを呼び出します。

ViewJackメソッド

ViewJackメソッドを見ていきます。

方向キーの上下左右を押している時はInitializeViewJackメソッドを呼んで初期化します。

アナログスティックの入力をXZ軸に割り当てます。

closestEnemyはアナログスティックの入力方向と一番近い敵を入れます。

closestEnemyRotは一番近い敵とアナログスティックの角度の差を入れます。

enemyDirectionはY軸を0に固定したキャラクターから見た敵の方向ベクトルを計算しています。

enemyAngleFromFrontOfCharaは敵の方向とキャラクターの方向の角度を求めてキャラクターの視野内にいるかどうかの判定に使います。

実際の角度とは別に二つの正規化されたベクトルの内積を求めると0はキャラクターの横、1は前方、-1は後方になるのでそれを使って視野内にいるかどうかも求められますが、今回はわかりやすく角度を使いました。

enemyRotはワールド軸の前方からの敵のY軸の角度を求めています。

inputDirectionRotはアナログスティックの入力方向のY軸の角度にキャラクター自身の向いているY軸の角度を足してワールド軸の前方からのアナログスティックの角度を計算しています。

キャラクター自身の角度を足さないとアナログスティックの入力方向はキャラクターの向きにかかわらず全て同じになってしまいます。

Mathf.Repeatを使って0~360度に値を補正しています(角度を0~360度未満にする為)。

heroAndEnemyAngleではワールド軸前方からの敵の角度とワールド軸前方からのアナログスティックの角度を引いてMathf.Absで絶対値を計算し、Mathf.Roundで指定した値に最も近い整数を返します。

敵がキャラクターの視野角度内にいる敵だけを判定します。

敵と入力の角度が視界ジャック範囲より大きい時はheroAndEnemyAngleを360から引いて再度代入します。

これは角度が0から反時計回りに回転した時に359→358と0から数字が増えてしまうので、そういった時の為に360から引いた値にします。

例えば、敵の角度が355で入力の角度が0の時は実際はheroAndEnemyAngleが5だが、計算上355になってしまうのでこれの対応です。

その後、敵と入力の角度の差がlimitAngleOfViewJack(視界ジャックをする範囲)内の時でclosestEnemy(一番近い敵)が設定されていない時はその敵を登録し、敵と入力の角度の差をclosestEnemyRotに入れます。

既にclosestEnemyが登録されている時は今調べている敵と入力の角度差とclosestEnemyRotの大きさを調べで角度差が小さい方をclosestEnemyとclosestEnemyRotに設定します。

一番近い敵が存在した場合は砂嵐動画を再生するRawImageの透明度とAudioSourceの音量を調整します。

colorやvolumeは0~1の間の数値を指定する必要があるので砂嵐の透明度と音量は最大値を1、最低値を0に変換する必要があります。

今回の場合は最大値はsnowNoiseMaxValueAngleLimitで指定した値にしていて、初期値を40にしているので40の時に1にして0の時の0にする必要があります。

なので、Mathf.InverseLerpを使って比率を求めます。

Mathf.InverseLerpは第1引数と第2引数の間の第3引数の割合を求めます。

例えば

上の場合は結果として0.6が得られます。

closestEnemyの子要素のCameraコンポーネントやAudioListenerコンポーネントを取得し有効にします。

逆にMainCameraのCameraとAudioListenerは無効にします。

方向キーを押していない時でメインカメラが無効の状態の時はInitializeViewJackメソッドを呼び出し設定の初期化をします。

InitializeViewJackメソッド

InitializeViewJackメソッドを見ていきます。

InitializeViewJackメソッドではメインカメラのCamera、AudioListenerコンポーネントを有効にし、currentCameraとcurrentAudioListenerが設定されていれば無効にしnullを設定します。

また砂嵐の設定も初期値に戻します。

これで視界ジャックのスクリプトが出来ました。

インスペクタでいくつか設定する必要がありますが、その前に砂嵐やメインカメラの設定、敵キャラクターの作成をしていきます。

砂嵐の設定

視界ジャック時に表示する砂嵐を作成していきます。

表示する砂嵐動画は

Blenderでアナログテレビの砂嵐動画を作成する
Blenderでアナログテレビ放送の番組終了後に流れていたザーーーという砂嵐(スノーノイズ)の動画で作成していきます。

の記事で作成したものを使います。

上の記事では動画内に音声も含ませていますが、Unity内で連続再生させると音声がループする時に途切れてしまう為、音声は別途Audacityで3秒程のザーッという音声をWAV形式で出力しUnityに取り込んでおきます。

取り込んだ動画のインスペクタは

Unityに取り込んだ視界ジャック用の動画

取り込んだ音声のインスペクタは

Unityに取り込んだ視界ジャック用の音声

のように設定しました。

次にヒエラルキー上で右クリック→UI→RawImageを選択します。

RawImageの横と縦の幅はstretchにして画面全体に流れるようにします。

作成したRawImageには先ほど取り込んだ砂嵐の動画を流しますが、動画を流すにはVideo Playerコンポーネントを使用する必要があります。

Video PlayerではRender Modeの設定で動画の出力先を設定出来ます。

今回は一旦Render Textureに動画を出力し、このRender TextureをRawImageのテクスチャに設定することで動画を流すようにします。

Assetsフォルダ内で右クリック→Create→Render Textureを選択し名前をViewJackとします。

ViewJackを選択しインスペクタでSizeを512に変更します。

視界ジャック用のRenderTextureの設定

レンダーテクスチャが出来たので設定をしていきます。

RawImageゲームオブジェクトを選択し、インスペクタのAdd Componentボタンを押してVideo→Video Playerを選択します。

RawImageゲームオブジェクトのRaw Imageコンポーネントの横のチェックを外します。

これで砂嵐動画は最初は表示されません。

Raw ImageのTextureには先ほど作成したViewJackレンダーテクスチャを設定します。

こうすることでVideo PlayerでViewJackレンダーテクスチャに出力し、それをRaw ImageのTextureに設定する事で動画を流します。

Video PlayerのVideo Clipには砂嵐の動画を設定し、Play On Awakeにチェックを入れて自動で再生され、Loopにチェックを入れて繰り返し再生されるようにします。

Render ModeではRender Textureにしレンダーテクスチャに出力されるようにし、Target Textureでは実際のレンダーテクスチャファイルを指定します。

Audio OutputはNoneにして音声は出力しないようにします。

視界ジャック用の動画を流すRawImageのインスペクタ

次にRawImageのインスペクタのAdd ComponentからAudio→Audio Sourceを選択します。

AudioClipに砂嵐用の音声クリップを設定し、Loopにチェックを入れて繰り返し再生されるようにします。

AudioClipのLoopにチェックした場合は繰り返し再生しても終わりと初めの部分で途切れるという事はありませんでした。

視界ジャック用の動画用の音声の設定

これで砂嵐の設定が終わりました。

メインカメラの設定

メインカメラの設定をしていきます。

メインカメラはシーン作成時にあるMainCameraをそのまま使用します。

インスペクタのAdd ComponentからScripts→UnityStandardAssets.Utility→SmoothFollowを選択します。

視界ジャック機能のメインカメラの設定

Targetには操作キャラクターの頭付近に空のゲームオブジェクトを作成し、名前をCameraFocusPositionとし、それをTargetに設定しました。

この辺りは

Unityでカメラの設定とキャラクターの追従をさせる
Unityで使うカメラの設定値を変更したり、カメラがキャラクターを追従するようにするしていきます。

を参照してください。

敵キャラクターの設定

敵キャラクターの配置と設定をします。

今回はアセットストアで無料でダウンロード出来るゾンビを敵キャラクターとして使用します。

アセットストアで検索窓に「zombi」と入れ、Sort byでPrice(Low to High)を選択すると上位で出てきます。

インポートしたらZombie→Prefabs→Zombiをヒエラルキー上に配置します。

ヒエラルキー上のZombiを選択した状態で右クリック→Cameraを選択します。

CameraをZombiの目の辺りに移動します(ゾンビのボーンのBip01 Headの子要素に移動させ目の辺りに移動)。

Cameraを選択しインスペクタのCameraコンポーネントとAudio Listenerコンポーネントのチェックを外し無効化しておきます。

視界ジャックされる敵のカメラの設定

今回は敵キャラクターは動かさないのでカメラは単にZombiの子要素に置きました。

またあらかじめAnimatorに設定されているアニメーターコントローラーは選択してDeleteキーを押し未設定の状態にしておきます。

これで敵キャラクターの設定は終了です。

視界ジャック機能スクリプトViewJackScriptのインスペクタの設定

これで機能が全て出来たのであとはViewJackScriptのインスペクタの設定をするだけです。

ViewJackScriptのインスペクタの設定

EnemyListには全ての敵キャラクターを登録します。

ViewJackCharaには自身
SnowNoiseImageObjにはRawImage
MainCameraにはMainCamera
MainAudioListenerにはMainCamera

をドラッグ&ドロップして設定します。

これで視界ジャック機能の完成です。(^^)/

終わりに

今回の機能で方向キーと敵の方向を比較する時の角度の計算で結構混乱しました。

角度計算するにも色々なメソッドがあって、Vector3.Angle、Vector3.SignedAngle、Quaternion.Angle、Quaternion.LookRotation、Quaternion.FromToRotation等々。

Unityの角度計算メソッドをわかりやすくするサンプルの作成
Unityの角度計算メソッドの使い方をわかりやすくする為にシーンビューでゲームオブジェクトを動かして計算された角度を視覚的に確認出来るサンプルを作成していきます。

どれが一番使えるかなぁと色々試しているうちに何が何やらわからなくなりはまってしまいました。

もっと賢いやり方もあるかもしれませんが、とりあえず出来た方法で作りました。

SIRENの視界ジャックがどのように作られているかはわかりませんが、似たようなものは出来たのではないかと思います。

さらに機能を近づけるならば、一番近い敵の視界ジャックをしている時に〇ボタンを押したらその敵の視界ジャックに固定するという風にすると良さそうですね。

視界ジャック機能を作っているうちにまたSIRENやりたくなってきたなぁ・・・・(´Д`)

新作をPS4で出してくれないかなぁ・・・・(´Д`)

最近市販のホラーゲーム少なくないですか?(´Д`)

(´-ε-`)ボソボソ

タイトルとURLをコピーしました