FPSを作ってみよう10-キャラの視点の方向に銃口の先を向ける

前回

UnityのFPSゲームで上半身のアニメーションだけを変更し、銃を構えた状態で歩く、走る、ジャンプする事が出来るようにしていきます。 上半身だけのアニメーションを変更するので下半身は通常の歩く、走る、ジャンプのアニメーションを使用する事が出来ます。

で上半身のアニメーションだけを変更させて、上半身が銃を持ったり構えたりした状態で歩いたり走ったりする事が出来るようになりました。

以前の記事で、

FPSゲームでキャラクターの向きの変更や視点の方向を変える機能を作成していきます。視点の向きはパラメータで限界値を指定出来るようにして向きが変わりすぎないようにします。

視点の上下をする機能を作りましたが、体の向きは変えていなかったのでカメラの視点とのズレが発生していました。

視点と体の向きのズレ

↑のような感じで視点(カメラの向き)は上下しても体の向きを変えていない為に銃口は前を向いたままになっています。

今回は銃を構えた状態で歩いたりする事が出来るようになったので、視点を上下した時に
体の向きを変えて銃口を視点の先に変更していきたいと思います。

スポンサーリンク

上半身をカメラの向いている先に曲げる方法

やり方としては上半身の向きを変更し、カメラが向いている方向に向くようにしたいと思います。

publicでspineを宣言し、インスペクタで背のボーンを設定出来るようにしておきます。

このspineの角度を変更する事で上半身の向きをカメラの向いている方向に向かせます。

ここでカメラの向いている方向と背のボーンの方向が違うので、そこはキャラクターのボーンの設定によって変更してください。

新しい関数RotateBoneを作成します。

MyCharaのUpdate関数に以下の処理を追加します。

myCamera.localEulerAngles.xでカメラのX軸の角度を取得します。

腰のZ軸の角度にこのカメラの角度を足します。

カメラを上下させるとX軸の角度が変更する為、この変更度合いを背のボーンに加えています。

背のボーンではZ軸が上下に対応しているので、Z軸に足しています。

MyCharaのspineにEthanのSpine1のボーンをドラッグ&ドロップしてください。

EthanSpine1をspineに設定

↑のMyCharaはC#になっています(後で追加した画像の為)。

これで試しにUnityを実行して上半身が視点の上下で曲がるかどうか確認してみましょう。

背のボーンを曲げるスクリプトを設定したが曲がらない

スクリプトを記述しましたが上半身の角度が変わらずカメラの向きと合いません・・・(T_T)

Update関数内でカメラの角度を変更していて、同じフレームでカメラの角度を使って上半身の角度を変更するのが良くないようです。

そこでUpdate関数内でカメラの角度を変更した後、LateUpdate関数内で上半身の角度を変更させるようにします。

上半身のボーンをLateUpdate関数内で変更する

LateUpdate関数はUpdate関数の処理が実行された後に実行される関数です。

↑のようにLateUpdate関数内で処理をします。
(MyCharaスクリプト内に記述してください)

それでは再度実行して試してみましょう。

上半身を曲げた事でキャラクターの中身が見えてしまう

上半身がカメラの角度分曲がるようになりましたが、そのせいでキャラクターの中身が見えてしまうようになりました。

キャラクターの中身が見える原因はカメラの位置が背骨と連動してない為

Scene画面で確認するとMain CameraをEthanの直下に配置している為、上半身の角度が変わってもカメラの位置が変わりません。

カメラがキャラクターの中身を表示しないようにする

そこでMain CameraはEthan直下に残しつつ、カメラの位置を上半身の角度にそって移動するようにしましょう。

EthanHead1の直下にダミーのカメラ位置を設ける

カメラは目の代わりなので、Ethanの頭(EthanHead1)の直下にダミーのカメラ位置を作成します。

EthanHead1を選択した状態で右クリック→Create Emptyを選択し、空オブジェクトを作成して名前をCameraPositionとします。

目のあたりにCameraPositionを移動する

CameraPositionを移動し目の辺りに移動させます。

CameraPositionのTransform

↑のような位置に移動させました(適宜変更してください)。

これでダミーのカメラ位置が出来上がりました。
上半身を曲げた時にこのダミーカメラ位置も相対的に動くようになります。

カメラの角度処理はMain Cameraに別のスクリプトを使ってCameraPositionの位置と角度を参照するようにしていましたが、カスタマイズする時に大変な為、処理を変更しました。(2017/08/17)

次にMyCharaスクリプトにcameraPositionというダミーカメラのTransform情報を保持するフィールドを宣言し、RotateCamera、RotateBone関数内でmyCameraの角度を参照、直接変更していた箇所をcameraPositionの角度に変更し、LateUpdate関数内でRotateBone関数を実行した後にカメラ位置を更新します。

それではMain Cameraの位置が移動するようになったかUnityの実行ボタンを押して確認してみましょう。

カメラの向きと上半身が連動するか確認

↑のように視点を上下するとMain Cameraの位置も頭の位置と連動して動くようになりキャラクターの中身が表示されるという事がなくなりました。

IKのSetLookAtWeightを使って上半身をカメラの向きに変える方法

今回のようにキャラクターのボーンを操作しなくても、IKのSetLookAtWeight、SetLookAtPositionを使ってキャラクターの向きをカメラの方向を向かせるという方法もあります。

ただ、左手のIKを使って手の位置を調整している場合この方法は取れません。

左手だけ元の位置から動かなくなってしまいます。

左手のIKを使っているとSetLookAtPositionがうまく動作しない

↑のように左手が元の位置から動いていません。

左手のIKを使わずアニメーションの作成段階で銃をうまく持たせる事が出来ているならば左手のIKの機能であるHandIKを使うのをやめて
MyCharaスクリプト等に以下の処理を取りつけます。

↑のようにウエイトを設定し、カメラの位置からカメラの前方向の100m先の位置を見るように設定しています。

SetLookAtWeightに関しては

Unityで主人公キャラに付いてくるサブキャラを作成します。主人公キャラがある程度離れたら主人公と一定の距離になるまで付いてきます。 また、RPGのパーティの隊列のような機能も作成してみます。

を参照してください。

またOnAnimatorIK関数のイベントを発生させるにはAnimatorのレイヤーでIK Passにチェックを入れている必要があります。
(前の記事をご覧いただいている方はチェックが入っているはずです)

今後はボーンの角度の変更で話は進んでいくのでIKで体の向きを変えている場合は記事の内容と変わってくる点にご注意ください。

終わりに

今回でキャラクターの動きが完成しました。
ようやくですね・・・・(^^)v

前回のFPSの記事から少し期間が開きましたね、この記事自体はずいぶん前に出来上がっていたんですが、FPS記事ばかりでもブログの記事内容が隔たってしまう為に
公開はしていませんでした。

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます

コメント

  1. みどり より:

    ありがとうございます

  2. みどり より:

    出来ました ありがとうございます

    GUN CLASS

    var spreadofbulletUp : float = 1f;
    public static var SpreadUpper : float = 0f;

    SpreadUpper = spreadofbulletUp;

    リコイルカメラクラス

    recoil = Gun.SpreadUpper;

    かめさん リコイルカメラクラスのリコイルにSpreadUpperの値を入れているのですが、
    public static var SpreadUpper : float = 0f;
    SpreadUpper = spreadofbulletUp;
    この形式だと0f が 下で spreadofbulletUp の値に変更していますが
    recoil = Gun.SpreadUpper; このやり方だと 変更前の0f になります、
     変更後の値を適応させたいです 打開策があればよろしくお願いします

    • 反動のパラメータをGunClassで用意してそのパラメータをRecoilCameraClassで使うんでしょうか?

      GunClass

      RecoilCameraClass

      GunClassのインスタンスを生成して使うのでなければ何らかのゲームオブジェクトに取り付けてStartメソッドを実行させSpreadUpperの値を更新しておきます。
      RecoilCameraClassも何らかのゲームオブジェクトに取り付け、StartメソッドでGunClass.SpreadUpperの値をrecoilに入れてコンソールに表示したら1が表示されました。

      どちらのスクリプトのStartメソッドが早く実行されるかもわからないので場合によっては0が入るかも?

      GunClassを設定しているゲームオブジェクトが先にゲーム上に登場しているのであれば問題はなさそうですが。

      GunClassをゲームオブジェクトに取り付けずただのデータクラスとして作成する場合は

      とScriptableObjectを継承してGunClassを作成しておくとゲームオブジェクトに取り付けなくてもRecoilCameraClassからGun.SpreadUpperの値を参照出来ます。

      SpreadUpperはクラスフィールドなのでインスタンスフィールドのspreadofbulletUpを入れる事は出来ません。

      この場合はJavaScriptでもクラスを作成する形で記述しなければいけませんが、今後を考えるとJavaScriptでスクリプトを組むのもクラス形式で書いていった方がいいかもしれません。

  3. 匿名 より:

    左右への視点移動は可能なのですが高さが変更できない状態で、、
    がんばってみます

    • FPSの記事で作成しているMyCharaのスクリプトでカメラの角度(MyCamera)を変更しているので、この制御と反動の為のダミーのCameraPositionの角度変更の2つの部分でカメラの角度を変更しようとしてる為に出来ないみたいです。

      となるとMyCharaスクリプトのカメラの角度変更と反動の為のダミーのCameraPositionの角度変更をうまく組み合わせないとだめですね。

      そこら辺をうまく改良してみてください・・・。

      • とりあえず作ってみました。

        MyCharaでのカメラの角度変更とCameraPositionに新しく取り付けた反動のスクリプトで処理がかぶってしまうのでMyCharaスクリプトに処理を統合します。

        CameraPositionを

        public var cPos : Transform;

        のようにフィールドに入れられるようにしておきます。

        また銃を撃った反動が起きている状態かどうかのフラグ

        private var recoilFlag : boolean = false;

        も宣言しておきます。

        MyCharaスクリプトのUpdateメソッドの銃を撃つ処理の部分で

        銃を撃った時の反動でカメラ角度を変更していない時はrecoilFlagをオンにし、その時のカメラのローカル角度を初期角度として保持しておきます。

        次に角度計算をしているLateUpdateメソッドです(元はC#で作ったので変更する箇所があるかも?)。

        recoilFlagがオンの時はカメラに反動の処理を加えます。
        maxRecoilは現在の角度位置+反動の最大角度となります。

        反動が終了したかどうかはQuaternion値の比較条件に変更しました。Dotで計算するとうまく判定出来ない事があった為ですが、うまくいかない場合は条件を変えてください。

        recoilFlagがオフの時にキャラクターが横に向けたり、キー押しでカメラの角度を変更出来るようにします。
        else以下に記述しているのは銃の反動中はキャラクターの横向きや体の向きを変更出来ないようにする為です。

        反動中でも動かせるようにするにはelse以下ではなくrecoilFlagのif文の外に書く事で動かせるようになります。

        また反動中は銃の構えを解除するのを防止したい場合はUpdateの銃を構える処理のところで

        銃を構えるボタンを押していない時に解除していたところをelse ifにしてrecoilFlagがオフの時という条件を加えます。

        再びLateUpdateに戻ります。

        最後にボーンの角度変更をした後にメインカメラの位置と角度をCameraPositionの位置と角度に変更します。

        今まではメインカメラの位置と角度をCameraPositionの位置に合わせるスクリプトCameraTransformのUpdateで合わせている為タイミングが遅れました。

        この遅れを無くすためにMyCharaスクリプトのLateUpdateでCameraPositionの位置と角度を修正した後に変更するようにします。これでタイミングのズレがなくなります。

        CameraTransformスクリプトは使わないという事になりますね。

        以上です。

        不具合があっても修正の義務は放棄させて頂きます。(-.-)

  4. みどり より:

    助かりました。 お忙しいところありがとうございます。
    かめさんの下のスクリプトなのですがそのままだとカメラを動かせないので銃をうった処理に追加させていただきましたが、うまく動作しません。改善方法などまた時間があった時よろしければお願いいたしますm(。≧Д≦。)m

    • わたくしの記事と同じように作っていた場合は動かせるはずなんですが、違う作りにしている場合はわたくしの方では対処出来ません。(._.)

      キャラクターの階層構造、メインカメラをダミーのカメラ位置に合わせて移動させているかどうかなど確認してみてください。

      SetRookAtWeight等を使ってカメラ位置に体を向けさせている場合はおかしくなる可能性もあります(わたくしの場合これでカメラ位置に体を向けさせていない)。

      カメラの角度の変更をUpdateからLateUpdateにすると角度が変更出来る事もあります。

  5. みどり より:

    分かりました。ありがとうございます。
    JavaScriptで作成しているのですが、キャラクターコントロールとは別のスクリプトからカメラの向きを左クリックするごとに上にあげているのですが(反動)やはりうまくいきません。やっぱり同じクラスにコードを書いたほうがいいのですかね、、時間があれば サンプルコードをいただけるとうれしいです。

    • 元のスクリプトの処理を残しつつJavaScriptでスクリプトを書いてみました。

      ダミーのカメラ位置であるCameraPositionに以下のスクリプトを取り付けます。

      銃を撃つボタンを押したら銃の反動が終わるまで指定した角度まで回転し、その後元の角度に戻しています。

      メインカメラはメインカメラに設定しているスクリプトでダミーのカメラ位置と角度に合わせているので、ここではダミーの角度だけを変更します。

      MainCameraに取り付けてあるダミーのカメラ位置へと逐一変更しているスクリプトに角度の処理も追加します。

      記事のスクリプトだとカメラ位置をUpdateで修正してましたがLateUpdateの方がいいかもしれません。

      打つたびにカメラ位置を元の位置から始めるようにする場合にUpdateだとキャラクターの中身が見えてしまう事がありました。

      反動テストgif

      ↑のようになりました。

      設定値を変更すると大きな反動にする事も出来ます。

  6. みどり より:

    前回の武器のスイッチ機能はゲームオブジェクトの有無でできるようになりました。
    ありがとうございます

    • 銃の反動をカメラを動かして実際の手元を動かさなくていいならわたくしの記事でいうとCameraPositionというダミーのカメラの位置に

      ↑のようなスクリプトを取り付ければ銃を撃っている間はカメラがブレます。がカメラの位置をブレさせているだけなので実際のキャラクターの手元はブレません。

      実際に手元をブレさせたい場合はキャラクターのボーンを少し動かします。

      ↑のスクリプトをブレさせたいキャラクターのボーンに設定します。

      Updateで角度を変えても反応しない為、LateUpdateメソッドで実行します。

      Ethanで言うと手だけならEthanRightHandとEthanLeftHand、腕から動かすならEthanRightForeArmとEthanLeftForeArmに取り付けます。

      打っているアニメーションの間ブレが発生すると長い時用にwaveTimeを設け指定の時間内の時だけブレさせます。

      これらのスクリプトは単純な反動を表現出来ますが、特定の位置へ行って戻るという動きは出来ません。

      そちらにする場合はそれ用に書き換えてください。

      銃自体の動きなら銃を撃つアニメーション自体で手を動かしておいてそれに手をブレさせるスクリプトを追加するといいかもしれませんね。

      https://www.youtube.com/watch?v=-wKLkRGYC1c

      ↑が実行例です。

      わたくし自身の作業を進めなくてはいけないのでこれ以降の質問には積極的には返信出来ませんのでご了承ください。(._.)

  7. みどり より:

    (´༎ຶོρ༎ຶོ`)
    銃を撃つとリコイル(反動を追加したいのですが)銃の位置がおかしくなりますカメラの向きは大丈夫です。

    var recoilMod : Transform;
    var weapon : GameObject;
    var maxRecoil_x : float = -20;
    var recoilSpeed : float = 10;
    var recoil : float = 0.0;

    if(recoil > 0)
    {
    var maxRecoil = Quaternion.Euler (maxRecoil_x, 0, 0);
    recoilMod.rotation = Quaternion.Slerp(recoilMod.rotation, maxRecoil, Time.deltaTime * recoilSpeed);
    weapon.transform.localEulerAngles.x = recoilMod.localEulerAngles.x;
    recoil -= Time.deltaTime;
    }
    else
    {
    recoil = 0;
    var minRecoil = Quaternion.Euler (0, 0, 0);
    recoilMod.rotation = Quaternion.Slerp(recoilMod.rotation, minRecoil,Time.deltaTime * recoilSpeed / 2);
    weapon.transform.localEulerAngles.x = recoilMod.localEulerAngles.x;
    }
    }

    RecoilMod が ダミーのカメラ
    Weapon が メインカメラです

  8. みどり より:

    ありがとうございました!
    うまく動作するようになりました、原因でしたがカメラをメインカメラに設定していなかったためでした。長文ありがとうございます。勉強になります。
     また 武器の切り替えができる=>切り替えた武器によっての手の位置などを変更したいので(武器ごとにアニメーターを変更)よろしければ、そちらのほうも追加していただけたらうれしいです

    • FPSカテゴリの記事はしばらく更新はないと思います。(._.)

      一応ヒントになるかどうかわかりませんが、作り方をざっくり紹介します。

      武器の切り替えのやり方はハンドガン、ショットガン、マシンガンなどで個別の銃を構えるアニメーション、撃つアニメーション(アニメーションを使わずともUnity側で手元を動かす事でも出来そう)を用意しますが、

      銃を構えた時の銃口の高さを合わせた方がいいかもしれません(今はハンドガンの銃口をカメラの中心に合わせている為)。

      必ずしも銃口をカメラの中心に合わせなくてもいい場合は銃を撃った時の辺り判定を銃口の先ではなくカメラの中心からレイを飛ばして判定するようにすればカメラの中心部分の敵との辺り判定が出来ます。

      アニメーションを新しく作成せず、両手のIKを使うのであれば拳銃のアニメーションをそのまま使い右手を引き金、左手をそれぞれの銃を支える位置に持っていきます。

      ゲームパッドでゲームを操作しているとして、武器の切り替えを十字キーで切り替えるとしたら↑のキーを押したらマシンガン、→のキーを押したらハンドガン等へと切り替える場合はAnimatorでbool値のアニメーションパラメータを武器の種類毎に作っておきそのアニメーションへと遷移させます。

      武器の種類毎に銃を構える状態、撃つ状態と作成するのでサブステートマシンを使って整理した方がいいかもしれませんね。

      武器切り替えボタンを押したら手の子要素にある武器のゲームオブジェクトを削除し、切り替えた武器をプレハブで生成し手の子要素に配置します。

      武器毎に予め位置と角度を設定(武器情報として攻撃力等と一緒に保持しておくといいかも)しておき、その武器に切り替えたらその位置と角度になるようにしておきます。

      両手IKバージョンで武器に手を合わせる場合は武器の銃口がカメラの中心になるような位置に配置し右手、左手を置く位置・角度を武器毎に設定した値に変更します。

      キャラクターには装備している武器情報を保持出来るようにし(記事内でしてたかな?)、武器自体にも種類、攻撃力、射程距離等を保持しておきます。

      キャラクターが装備している武器がわかればその武器が持っている情報もわかるので攻撃した時に与えるダメージの計算も出来ます。

      ざっとこんな感じで作れると思いますが、以前ちょこっと作った時に銃口の先を攻撃対象にするのかカメラの中心を攻撃対象にするかでわたくしの場合は銃口の先を選んだんですが、

      アニメーションを作成する段階で銃口の先を合わせるのがうまくできず(今はどうかな・・・)Unity側に取り込んでもうまく出来なかった為、武器切り替え機能に関してはFPSカテゴリの記事でも後の方でやろうかと思っていました。

      現時点ではFPSカテゴリの記事自体を作っていないので作り始めたとしても武器切り替え機能は後の方になるんじゃないかと思っております。

      (-.-)

  9. みどり より:

    オブジェクトエラーが生じます
    NullReferenceException: Object reference not set to an instance of an object
    cCon.OnAnimatorIK () (at Assets/Script/cCon.js:132)

    • 詳しい事が書いていないので推定してお応えしますね。

      英語文を読むとNull参照例外:オブジェクト参照がオブジェクトのインスタンスにセットされていない
      というようなエラーが出ています。

      なのでcConに何も入っていないのかもしれません。

      おそらくcConはCharacterControllerの参照を入れておく為のフィールドとして使っていると思うので

      等と宣言していると思います。

      その為、cConを使うのであればStart関数内等で

      としてこのスクリプトを設定しているのと同じゲームオブジェクトに設定しているCharacterControllerを取得しておく必要があります。

      この辺りは前の記事

      https://gametukurikata.com/fps/avataranimation

      にも載っているので同じように取得します。

      それは置いておきまして、この記事のIKの処理をしたいのだと思うのでIKを使用する為にAnimatorのBaseのレイヤーでIK Passにチェックを入れます。

      IK Passにチェックを入れるとOnAnimatorIKという関数が実行されるようになります。

      OnAnimatorIK関数はMonoBehaviourクラスで定義されている関数でStartやUpdate等もMonoBehaviourで定義されています。

      エラー表示にjsと出てるのでJavaScriptでスクリプトを組んでおられると思うので明示的にはMonoBehaviourクラスを継承して新しいクラスを作成していないと思います。

      C#の場合は明示的にMonoBehaviourクラスを継承して新しいスクリプトを作りますが、JavaScriptの場合は明示しなくてもMonoBehaviourクラスを継承するように作られます。

      その為、MonoBehaviourクラスで定義されている他のStartやUpdate等も自分で定義していなくてもそれぞれのタイミングで実行されています。

      ちょっと違うところに説明が飛びましたが・・・、

      つまりOnAnimatorIK関数はStartやUpdateと同じように使用します。

      JavaScriptの場合は

      ↑のようにfunctionの後にOnAnimatorIKを記述します。

      cConはCharacterControllerの参照を入れているだけでCharacterControllerの中ではOnAnimatorIK関数は定義されていませんので、

      cCon.OnAnimatorIK()はエラーになります。

      なのでOnAnimatorIK関数を書くとすれば

      ↑のような感じでStart、Update等と同じように記述します。