今回はUnityで物理演算+TPS型のカメラの動きを作っていこうと思います。
プレイヤーキャラの移動はAddForceだけで大体なんとかなりましたが、カメラの移動と回転には物理演算のオブジェクトの移動と回転を理解する必要があったのでかなり苦戦しました…!
カメラに関する記事は「回転」と「壁めり込み防止」の2つに分けて紹介していこうと思います。
前回の記事はこちらです!↓
もくじ
カメラの動かし方
TPS視点のカメラの回転を作る方法はいろいろあると思いますが、とりあえず自分が試してきちんと動かすことの出来た方法を2つ紹介します。RigidbodyのものとTransformのものです。
まずどちらの場合でも、カメラはy軸(左右)回転とx軸(上下)回転のオブジェクトを入れ子の構造にするのが良いかなと思います。(入れ子といってもやり方はそれぞれ違います。※後述)
y軸(左右)回転オブジェクトは中心がプレイヤーキャラの位置と同じになるようにします。
x軸(上下)回転オブジェクトは動かしたい方法によって中心の位置が変わってきます。

①プレイヤーキャラを中心に回転

②カメラを中心に回転(チルト)
①はx軸回転用オブジェクトをy軸オブジェクトと同じ位置にすればOKです。
②はx軸回転用オブジェクトをカメラの位置に置きますが、実はRigidbodyだとちょっと難しいです…。Transformで動かすのがオススメです。
どちらにしても、まずオブジェクトの配置を調整しておきます。
Rigidbodyで回転
RigidbodyのHinge Jointを使って回転の軸を作り、AddTorqueで滑らかに回す方法です。
Rigidbodyを使う場合、オブジェクトはヒエラルキーで親子関係にできないのでHinge JointのConnected Bodyに親側のオブジェクトを指定します。
Jointについては別記事にまとめているのでそちらも見てみてください!
RigidbodyならばMassやAnguler Dragを好きな値に調整しておいてプレイヤーの入力方向をAddTorqueで加えるだけなので、使うのはとても簡単です。
ただし向いていないことがいくつかあります。
2軸の視点回転が難しい
ジョイントは子オブジェクトのように完全に親からの相対的座標にあるわけではなく、ちょっとした力のかかり具合でズレることがあります。
左右回転と上下回転で二重構造にした場合、1つ目のジョイントはほぼ結合先と連動しますが2つ目からは少しずつズレが発生します。
わずかなズレですが、カメラだと着地などの際にかなり気になります。
キャラクターがジャンプや壁への衝突をしない仕様でゆるやかな動きなら気にならないのかなと思います。
なおジョイントにはConfigurable Jointという1つのジョイントで2軸の回転を作れるものもありますが、2軸の範囲内で自由に回転してしまうのでカメラの回転に使うのは難しそうです。
視点リセットが難しい
Hinge Jointで角度を指定して素早く正確にカメラを回すのはちょっと難しいです。
Hinge Jointの機能でSpringというものがあり、外から力が加わっていない時に指定の角度まで向きを戻すことはできます。
ただしこれを使ってカメラのリセットをするのはちょっと難しいかな…という印象です。
Springの戻す力とブレーキの力を設定してコントロールするのですが、強すぎれば指定の角度を越えてしまい弱すぎれば手前で止まるという感じで、どんなに頑張っても誤差が生じます。
エイムの必要なゲームでカメラに必要な「素早く正確にリセット」は厳しい気がします。
こちらも、ゲームの仕様として精密な動きが必要なければSpringでもいいのかなと思います。
Transformで回転
カメラの回転オブジェクトをプレイヤーキャラの子にする方法です。制御は簡単で正確に動かせますが、フワッと滑らかに加減速して回転させるには工夫が必要です。
僕は最終的にこちらの作り方でカメラの回転を作りましたが、作りたいゲームの仕様次第かなと思います。
回転には、y軸の角度を使って回転用オブジェクトのTransform.rotationに毎フレーム向きを指定します。
Quaternionの向きが必要なので、スクリプトの方で角度を-180.0f~180.0fのfloatの値で算出しておきQuaternion.Eulerで変換します。
transform.rotation = Quaternion.Euler(0.0f, y軸の角度, 0.0f);
みたいな感じです。
なお角度の値は-180.0f~180.0fの値に制限しなくても問題なく回転できますが、他のスクリプトでカメラの角度が必要になった時のために制限しておくと色々と使えるかなと思います。
僕はこんな感じの関数を作っています。
float RepeatAngle(float angle)
{
return Mathf.Repeat(angle + 180.0f, 360.0f) - 180.0f;
}
Transformの回転の注意点
最初何も知らずに毎フレーム「transform.rotation.eulerAnglesを使ってオブジェクトのQuaternionから前フレームの角度を取得 → 新しい角度の値を算出 → Quaternion.EulerでQuaternionに変換してカメラのTransform.rotationに指定」としてしまったのですが、これはNGな処理だったようです。
↓回転スクリプトの誤り #2
(参考) https://docs.unity3d.com/ja/2023.2/Manual/class-Quaternion.html
妙に重い感じがして変だなと気付きました。要注意です…。
角度用のfloatについてはオブジェクトのtransformから取得せず、別途スクリプトの方で変数を保持して更新する必要があります。
加減速させる
Transformでの回転の唯一の難しさがこれです。
プレイヤーの入力をAddTorqueみたいな滑らかな動きに変換したい…。でも物理演算みたいに自動ではできません。
アセット等で探せば物理っぽい数値計算をしてくれるものもありそうですが、とりあえず自分でいろいろ考えてみました。
(※僕は文系で高校時代は特に物理と数学が苦手だったので流し見してください…)
まず、回転力みたいなオブジェクトに溜まる力があると考えます。回転力は無限に溜まるわけではなく、加える力の最大値と軸の抵抗がつり合う一定の値まで溜まるような感じで、上限があります。
オブジェクトの回転は現在の回転力の強さに従って速度(というか1フレームで回る角度)が決まります。
回転力は、回転する際の空気抵抗で徐々に減ります。減る量はその時の速度によって決まります。
というわけで、こんな感じかな…と。
[SerializeField] float speed = 2.0f; //回転の速さ調整用
[SerializeField] float damping = 0.955f; //減衰率(回転力が抵抗を受けて減る率)
[SerializeField] float maxPower = 10.0f; //蓄積する回転力の最大値
float currentPower; //回転力
float currentAngle; //現在の角度(-180~180度)
void FixedUpdate()
{
currentPower = Mathf.Clamp(currentPower + playerInput.x, -maxPower, maxPower);
currentAngle = RepeatAngle(currentAngle + currentPower * speed);
transform.rotation = Quaternion.Euler(0.0f, currentAngle, 0.0f);
currentPower *= damping;
}
まあまあAddTorqueっぽい雰囲気は出せたかなと思います。
もっと良い計算もあるかもしれないけど、とりあえずこれでいきます…!
その他の注意事項
それ以外にも気になった点についてまとめておきます。
カメラの回転方向を変える
ゲームの設定でよくある「カメラの回転をリバース」は、プレイヤーの入力の値に+1か-1を乗算するのを切り替えるようにすればOKです。
遊びが必要…
上下回転ができるようになってからテストしていて気付いたのですが、スティックで完全に水平な入力をするのはちょっと難しいです。
水平にしているつもりでも癖があり、視点が下か上に寄っていきます…。
スティックの上下の入力については遊びの範囲を持たせ、範囲を超えた時だけ動くようにした方がよさそうです。
カメラが勝手に回転?
カメラを作った当初、床などに接触した時にカメラが勝手に回転することが時々ありました。
いろいろ調べた結果、見た目のせいで分かりづらいけれど実は着地時にプレイヤーキャラが回転しているということが分かりました。
Transformで親子関係にすれば当然のように子も回転しますし、Hinge Jointの場合でも親の回転は子に伝わります。
3Dモデルとか攻撃の当たり判定は回転させるとしても、このプレイヤーキャラの床・壁用の当たり判定を回転させる必要はないのかなと思います。そもそもプレイヤーキャラとカメラを別回転させたいので一緒に動いてしまっては困ります。
そのため、プレイヤーキャラはRigidBodyのConstraintsで全方向の回転をFreezeに指定してしまいました。
ここまでの仕上がり
動き方としては大体OKですが、このカメラは壁にめり込んで世界の裏側が見えてしまいます。
できればめり込みは発生させたくないので、対策を考えてみようと思います。
記事が長くなってしまったので今回はここまでにして、次の記事で壁めり込み対策について作っていこうと思います。
次回の記事はこちら↓