
マイケル
みなさんこんばんは!
マイケルです!
マイケルです!

エレキベア
クマ〜〜〜〜〜

マイケル
今回は複数のオブジェクト全体を写すカメラワークの方法について紹介するよ!

エレキベア
複数のオブジェクトクマ??

マイケル
そう。具体的には今タワーゲームを作っているところなんだけど、
ゲーム終了後に積み上げたタワー全体を写す処理を実装したんだ!
ゲーム終了後に積み上げたタワー全体を写す処理を実装したんだ!


マイケル
完成系としてはこんな感じだね

エレキベア
タワーゲームでよくある動きのやつクマね

マイケル
その通り!
さっそく実装してみよう!
さっそく実装してみよう!
参考元サイト

マイケル
本題に入る前に、今回の実装にあたって
下記の記事を参考にさせていただきました!
下記の記事を参考にさせていただきました!
Unityで複数のオブジェクトがカメラの描画範囲内に収まるように自動調整できるカメラワークを模索してみた。

エレキベア
分かりやすい記事クマ〜〜〜

マイケル
こちらの記事ではタワーに関わらず、複数オブジェクトがある場合を考えて実装しています!
すごく勉強になったのでこちらも見てみてくださいね
すごく勉強になったのでこちらも見てみてくださいね
カメラワークの考え方

マイケル
基本的には、下記のように
三角関数を利用してカメラの距離を計算するといった考えで実装したよ!
三角関数を利用してカメラの距離を計算するといった考えで実装したよ!


マイケル
三角関数は下記の通り、tanθ = r / xで求められます!


マイケル
これを利用して、オブジェクトの中心部にポイントを置いて、
カメラの視野角(FOV角度)と中心から最も遠いオブジェクトの距離から
距離を算出するといった考え方です!
カメラの視野角(FOV角度)と中心から最も遠いオブジェクトの距離から
距離を算出するといった考え方です!

エレキベア
三角関数なつかしいクマ〜〜〜
高校数学で出てくるやつクマね
高校数学で出てくるやつクマね

マイケル
ぶっちゃけうろ覚えだったぜ!
距離を算出するスクリプト

マイケル
それでは実装に入ります!

マイケル
まずは中心部からカメラまでの距離を算出するスクリプトを作りましょう!
CenterPointとするオブジェクトを作成して、下記スクリプトをアタッチします!
CenterPointとするオブジェクトを作成して、下記スクリプトをアタッチします!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraPointController : MonoBehaviour
{
public Camera usingCamera;
private Vector3 pos = new Vector3(0, 0, 0);
private Vector3 center = new Vector3(0, 0, 0);
private float radius; // タワー全体の半径
private float margin = 10.0f; // 半径を少し余分にとるための値
private float distance; // タワー全体が写る距離
void Start()
{
// カメラオブジェクトを取得
CameraController cameraController = FindObjectOfType<CameraController>();
usingCamera = cameraController.GetComponent<Camera>();
}
// マグネタワー全体が映るカメラ位置を返却する
public Vector3 GetAllTowerLookPosition()
{
// マグネタワーのオブジェクトリストを取得
List<Transform> magneTowerTransformList = new List<Transform>();
MagnetController[] magneList = FindObjectsOfType<MagnetController>();
foreach (MagnetController magne in magneList)
{
// リストに追加
magneTowerTransformList.Add(magne.transform);
}
//オブジェクトのポジションの平均値を算出
foreach (Transform trans in magneTowerTransformList)
{
pos += trans.position;
}
center = pos / magneTowerTransformList.Count;
// CenterPointのポジションをタワーの中心に配置
transform.position = center;
// 中心から最も遠いオブジェクトとの距離を算出
foreach (Transform trans in magneTowerTransformList)
{
radius = Mathf.Max(radius, Vector3.Distance(center, trans.position));
}
// カメラをCenterPointの高さの位置に移動する
usingCamera.transform.position = new Vector3(usingCamera.transform.position.x, transform.position.y, 0);
// タワー全体が写る距離を算出し、位置情報として返却する
// 式: 半径 / tan(カメラの描画角度 / 2)
distance = (radius + margin) / Mathf.Tan(usingCamera.fieldOfView * 0.5f * Mathf.Deg2Rad);
return new Vector3(distance, transform.position.y, 0);
}
}

マイケル
細かく見ていきましょう!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraPointController : MonoBehaviour
{
・・・略・・・
// マグネタワー全体が映るカメラ位置を返却する
public Vector3 GetAllTowerLookPosition()
{
// マグネタワーのオブジェクトリストを取得
List<Transform> magneTowerTransformList = new List<Transform>();
MagnetController[] magneList = FindObjectsOfType<MagnetController>();
foreach (MagnetController magne in magneList)
{
// リストに追加
magneTowerTransformList.Add(magne.transform);
}
//オブジェクトのポジションの平均値を算出
foreach (Transform trans in magneTowerTransformList)
{
pos += trans.position;
}
center = pos / magneTowerTransformList.Count;
// CenterPointのポジションをタワーの中心に配置
transform.position = center;
・・・略・・・
}
}

マイケル
上記部分では、カメラに写したいオブジェクトのリストを取得して、
中心の位置にCenterPointオブジェクトを配置しています!
中心の位置にCenterPointオブジェクトを配置しています!

エレキベア
positionの合計値をオブジェクトの数で割って位置を割り出すクマね

マイケル
取得するオブジェクトの部分は各自書き換えてくださいね!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraPointController : MonoBehaviour
{
・・・略・・・
private float margin = 10.0f; // 半径を少し余分にとるための値
・・・略・・・
// マグネタワー全体が映るカメラ位置を返却する
public Vector3 GetAllTowerLookPosition()
・・・略・・・
// 中心から最も遠いオブジェクトとの距離を算出
foreach (Transform trans in magneTowerTransformList)
{
radius = Mathf.Max(radius, Vector3.Distance(center, trans.position));
}
// カメラをCenterPointの高さの位置に移動する
usingCamera.transform.position = new Vector3(usingCamera.transform.position.x, transform.position.y, 0);
// タワー全体が写る距離を算出し、位置情報として返却する
// 式: 半径 / tan(カメラの描画角度 / 2)
distance = (radius + margin) / Mathf.Tan(usingCamera.fieldOfView * 0.5f * Mathf.Deg2Rad);
return new Vector3(distance, transform.position.y, 0);
}
}

マイケル
あとは最も遠いオブジェクトの距離を取得して半径rを取得、
「usingCamera.fieldOfView * 0.5f」でθを取得して公式に当てはめるだけです!
「usingCamera.fieldOfView * 0.5f」でθを取得して公式に当てはめるだけです!

エレキベア
marginの変数は何クマ?

マイケル
これは半径を余分にとるための値で、
例えば下記の図を見たら分かる通り、単純にオブジェクトとの距離で半径を算出した場合には見えない部分が発生してしまうんだ!
例えば下記の図を見たら分かる通り、単純にオブジェクトとの距離で半径を算出した場合には見えない部分が発生してしまうんだ!


マイケル
オブジェクトの位置は具体的にはオブジェクトの中心位置だから
オブジェクトの大きさまでは考慮されていない。
オブジェクトの大きさまでは考慮されていない。
そのため、
① オブジェクトの大きさを考慮して半径を算出する。
② 少し余分に半径を設定する。
どちらかの対処が必要なんだ!

エレキベア
位置だけで算出してるから起こる症状クマね・・・

マイケル
①の方法は少し計算が複雑になるから、
今回は②の方法で実装したんだ!
今回は②の方法で実装したんだ!


マイケル
marginの値を決める時はこのようなことを踏まえた上で設定するようにしてね!

エレキベア
勉強になったクマ〜〜〜
カメラを移動するスクリプト

マイケル
あとは算出した距離の位置にカメラを移動させるのみ!
移動スクリプトは下記になります!
移動スクリプトは下記になります!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
/** 設定値 */
private float lookSpeed = 0.5f; // カメラ移動速度
private Vector3 velocityZero = Vector3.zero;
/** コンポーネント */
private CameraPointController cameraPointController;
/** 変数 */
private GameObject lookTarget; // カメラ視点対象
private Vector3 lookPosition; // カメラ視点の位置
private bool changeLook = false; // カメラ視点変更フラグ
void Start()
{
cameraPointController = FindObjectOfType<CameraPointController>();
}
void Update()
{
// カメラ視点位置に到着した場合
if (transform.position == lookPosition)
changeLook = false;
// カメラ視点位置へ一定速度で移動する
if (changeLook)
transform.position = Vector3.SmoothDamp(transform.position, lookPosition, ref velocityZero, lookSpeed);
}
// タワー全体の視点対象設定処理
public void LookTowerTarget()
{
// CameraPointよりカメラ位置を算出して取得
lookPosition = cameraPointController.GetAllTowerLookPosition();
transform.LookAt(cameraPointController.transform);
changeLook = true;
}
}

マイケル
今回はVector3.SmoothDamp()メソッドを使用して
ゆっくりと移動するよう実装しました!
ゆっくりと移動するよう実装しました!

エレキベア
これはいろんな場面で使えそうクマね

マイケル
これで全体を写すカメラワークの実装は完了です!
おわりに

マイケル
というわけで今回はカメラワークの実装でした!
どうだったかな?
どうだったかな?

エレキベア
三角関数覚えてなくて焦ったクマ

マイケル
俺も勉強嫌いだったからうろ覚えだったよ・・・。
学生の頃もこういう活用法とか知ってたらモチベが上がってたかもしれないね
学生の頃もこういう活用法とか知ってたらモチベが上がってたかもしれないね

エレキベア
何にせよ楽しかったクマ〜〜〜

マイケル
楽しむことが一番だ!!
それでは今日はこの辺で!
アデュー!!
それでは今日はこの辺で!
アデュー!!

エレキベア
クマ〜〜〜〜〜〜
コメント