UnityでFPSをつくる その3-3 [ プレイヤーの移動 ]

2020年5月27日

49~67行目、これは一定時間ごとに呼ばれる関数です。

No.104

Update が毎フレーム実行されるのに対して、FixedUpdate は0.02秒ごと(初期設定の場合。パソコンの性能により多少の遅延あり)に実行されます。
Update でキー入力を受け付けて、FixedUpdate で移動をさせるようにしています。

No.105

上図のように、一定間隔で実行される FixedUpdate でキー入力を受け付けてしまうと、不規則の間隔なフレームに対してズレが生じ、一度も実行されないフレームが出てきてしまいます。
逆に Updateで物理演算を使って Rigidbody を移動させてしまうと、FixedUpdate と同期して行われる物理演算とズレが生じてカクカクした動きになる場合があります。
以上の理由から Update で入力受付、FixedUpdate で移動をするように処理を分けています。

52行目は if文を使って「どちらかが」の条件判断をしています。

No.106

「||」の左辺と右辺どちらか一方が true の場合、{}内の処理が実行され、どちらも false の場合、{}内の処理は実行されません。

No.103(2)

55と56行目でbool型変数をどちらも false に戻しています。

No.107

moveZpermission と moveXpermission はキー入力があった場合に true にするよう36~46行目で書いているので、どこかのタイミングで false にしないとキー入力の有無にかかわらず延々と {} 内の処理が実行され続けます。
キー入力と連動させる為に、if文の条件判定が終わったら false にすることで、正しく判定することができます。

No.108

60行目では Rigidbody に速度を与えて、キーが入力された方向にPlayerを動かしています。

No.109

Rigidbody に速度を与えるには Rigidbody の Vector3型 velocity 変数に値を渡します。
transform.rotation で現在のPlayerの向きを基準とした、X方向とZ方向のベクトルに deltaTime で補正をかけます。

No.110

➀の Vector3型は文字通り3つのベクトルを設定できます。(1.0f , 0 , 1.0f)と値を渡せば、X方向に1.0fY方向に0Z方向に1.0fの大きさを持つことになります。
playerRigidbody.velocity にこの値が渡された場合、playerオブジェクトは斜めに移動します。

No.111

矢印キーの上と右を同時に押すと斜めに移動したのは、この処理が行われていた為です。

No.112

②のtransform.rotationはPlayerの角度(回転情報)を取得できます。
まだPlayerには回転の動作を実装していませんが、Playerが向きを変えたときにはPlayerを基準にして前後左右に動いてほしいわけです。
今、Playerがどの方向を向いているのかそれをtransform.rotationで取得しています。

No.113

transform.rotationを掛け合わせないと、どこを向いていてもRotation(0,0,0)を基準とした移動をしてしまいます。
例として、Rotation Y を -45°(左に45度回転)にして transform.rotation の記述をコメントアウト(「//」以外に、「/**/」で括ることもできます)して実行してみます。

No.114
No.115
No.116

transform.rotation 有り

No.117
No.118

③はさきほども触れた Vector3 です、注目する箇所は new と書いてある部分です。

No.119

いままで変数を扱うには型を宣言して、その型に合った値を渡してきました。
float型には小数値bool型には論理値ですね。

No.120

Vector3型変数に値を渡す際に書いているこの new が何を意味するのかというと、オブジェクトを実体化しています。
今書いているPlayerMoveスクリプトはPlayerオブジェクトにアタッチする事で、設計図から実体化されてPlayerを動かします。

No.70(2)

この部分ですね、これが new と同じ役割を果たしています。
PlayerMoveスクリプト(PlayerMoveクラス=設計図)をPlayerにアタッチして実体化 → ゲームを実行するとPlayerMoveスクリプトに書かれた処理が始まる。

No.121

new Vector3(moveX * moveSpeed, 0, moveZ * moveSpeed) で実体化と同時にコンストラクタ(インスタンス作成時に実行される関数)で x, y, z の値を指定しています。
こうして作られたインスタンスが playerRigidbody.velocity で取得されて、X方向とZ方向の速度の値として利用されます。

ここで一つ疑問が・・・、「float型bool型では値を渡す時に new を付けなかったけど?」
これはリテラルと呼ばれるもので、値を直接書けば new を書かずとも実体を入手できるようになっている為です。

さらにもう一つ疑問が・・・、「変数宣言だけでもメモリ領域が確保されるけど、これは実体化してるの? new が書いてないけど?」
これは明示されていないだけで、内部的には宣言された時点で実体化された既定値を持ちます。
実際に確認してみましょう、PlayerMoveスクリプトで扱っている Rigidbody, float, bool, Vector3型の変数をそれぞれ宣言します。

No.122

これをStartの中でDebug.Logを使って中身の値を見てみます。

No.123

Consoleウィンドウでの結果は

No.124

宣言した変数はいずれも既定値を持っていました。
では、宣言の時点で次のように書いてみます。

No.125

それぞれの型で変数宣言時に引数なしのコンストラクタを実行して値を渡しています。
これをさきほど同様、Startの中でDebug.Logを使って中身の値を見てみます。

No.126

全く同じ結果になりました。
以上のことから宣言された時点で変数は実体化された既定値を持つよう内部で処理されていることがわかります。

No.127

次の説明に移ります、④の Time.deltaTime ですね。

No.128

FixedUpdateが初期設定で0.02秒ごとに実行されるというのは冒頭で説明しましたが、非常に重たい処理をした時にこの間隔にズレが生じる場合があります。
0.02秒ごとというのはフレームレートに換算すると50fpsですが、遅延が発生した場合0.025秒(40fps)だったり、0.04秒(25fps)になったりしてしまいます。

No.129

この差異をなくす為にTime.deldtaTimeを使います。
Time.deltaTimeは直前のフレームと今のフレーム間で経過した時間を返してくれます。
間隔が0.02秒ごとなら0.02の値が返ってきます。

No.130

これを乗算すると、

No.131

結果が一致しました。ただし、FixedUpdate 1回あたり1m進む処理のはずが、Time.deltaTimeの小さい値を乗算することによって1秒あたり1m進む処理になりました。
これを調整する為に適当な大きい値(今回の場合は100)を最後に乗算しています。

あと数行で最後ですが、今回も長くなってきたので一旦終わります。
続きは次回に、おつかれさまでした!

FPS

Posted by kenji