ブログ
これまでに経験してきたプロジェクトで気になる技術の情報を紹介していきます。
カメラの位置固定で上下左右移動・ズーム
サーバ側のCMSの開発を行っていたりしますが、今回はUnityでクライアントエンジニアの山中です。 カメラから映る画面をズームして上下左右に移動させたい。 正し、3Dモデルの為、カメラ自身が動くと奥行きのある背景などが見え方が変わる為、カメラ位置を固定にしたいというオーダーがありました。
移動に関してはカメラのRotationを変更させて映る範囲を変更させました。 オイラー角のxで縦移動、yで横移動と映る範囲が変わります。 なのでスワイプ移動とはxとyは逆になります。 また、xを動かすと下に動きますので符号を逆にします。 コレでスワイプ移動は出来ました。
移動範囲の制限で気を付けないといけない事は、その場で回転させているので真っ直ぐに動いているわけではないということです。
各変数は originalPosXとoriginalPosYは初期のオイラー角のxとyです。 初期の位置を中心に、最大FOVとの差をカメラの移動範囲係数とするupMoveValue、downMoveValue、leftMoveValue、rightMoveValueを掛けた範囲で移動できるようにします。 移動速度はswipSpeedで変化します。
if (Input.touchCount == 1)
{
// タッチしている点を取得
Touch t1 = Input.GetTouch(0);
// 初期位置保存
if (t1.phase == TouchPhase.Began)
{
previousPos = t1.position;
}
// 移動
if (t1.phase == TouchPhase.Moved)
{
// 前回との差を取得
currentPos = Input.mousePosition;
Vector3 diffDistance = previousPos - currentPos;
var x = diffDistance.x;
diffDistance.x = diffDistance.y * -1f;
diffDistance.y = x;
// カメラの現在値取得
Vector3 nowVector = camera.transform.localRotation.eulerAngles;
if (nowVector.x > 180)
{
nowVector.x -= 360f;
}
// 変更値を加える
nowVector += diffDistance * swipSpeed;
// FOVの差を取得
float fovDiff = fovMax - camera.fieldOfView;
// 移動範囲制限
nowVector = new Vector3(Mathf.Clamp(nowVector.x, originalPosX - upMoveValue * fovDiff, originalPosX + downMoveValue * fovDiff), Mathf.Clamp(nowVector.y, originalPosY - leftMoveValue * fovDiff, originalPosY + rightMoveValue * fovDiff), nowVector.z);
// 更新
if (nowVector != camera.transform.localRotation.eulerAngles)
{
camera.transform.localRotation = Quaternion.Euler(nowVector);
}
previousPos = t1.position;
}
}
ズームに関してはピンチイン・ピンチアウトに合わせて、FOVの調整で対応します。 Input.touchCountが2以上なら指を二本使用していると、判断してピンチイン・ピンチアウト処理を行います。 ピンチアウトするほど値(camera.fieldOfView)を小さくして拡大表示です。 そして開始時の2点の中心点になるように移動可能範囲で移動します。
各変数は 拡大の速度はpinchSpeed、中心点の移動速度はfovMoveCoで変化します。
else if (Input.touchCount >= 2)
{
// タッチしている2点を取得
Touch t1 = Input.GetTouch(0);
Touch t2 = Input.GetTouch(1);
// 2点タッチ開始時の距離と現在距離を記憶
if (t2.phase == TouchPhase.Began)
{
backDist = Vector2.Distance(t1.position, t2.position);
// 2点中央
Vector3 middlePos = (t1.position + t2.position) / 2f;
middlePos = camera.ScreenToWorldPoint(middlePos + new Vector3(0, 0, 1f));
// 角度計算
middleRot = Quaternion.LookRotation(middlePos - camera.transform.position).eulerAngles;
if (middleRot.x > 180)
{
middleRot.x -= 360f;
}
// 現在値保存
pinchStartFov = camera.fieldOfView;
// カメラ位置取得
pinchStartRot = camera.transform.rotation.eulerAngles;
if (pinchStartRot.x > 180)
{
pinchStartRot.x -= 360f;
}
// 変動予定量
pinchChangeValue = Vector3.Distance(middlePos, pinchStartRot);
}
else if (t1.phase == TouchPhase.Moved && t2.phase == TouchPhase.Moved)
{
// タッチ位置の移動後、長さを再測
float newDist = Vector2.Distance(t1.position, t2.position);
// 前回の距離からの相対値を取り、更新値を決める
float newValue = camera.fieldOfView - (newDist - backDist) * pinchSpeed;
// 最小最大と比べて更新
camera.fieldOfView = Mathf.Clamp(newValue, fovMin, fovMax);
backDist = newDist;
// 【(開始時の)中央点に合わせてカメラ位置更新】
// FOVの差を取得
float fovDiff = fovMax - camera.fieldOfView;
// 変化値に合わせて移動
var nowVector = LerpPos(pinchStartRot, middleRot, Mathf.Abs(pinchStartFov - camera.fieldOfView) / pinchChangeValue * fovMoveCo, originalPosX + downMoveValue * fovDiff, originalPosX - upMoveValue * fovDiff, originalPosY + rightMoveValue * fovDiff, originalPosY - leftMoveValue * fovDiff);
// 更新
if (nowVector != camera.transform.localRotation.eulerAngles)
{
camera.transform.localRotation = Quaternion.Euler(nowVector);
}
}
// 1点タッチに戻った時のスワイプ判定の為に更新
previousPos = t1.position;
if (t1.phase == TouchPhase.Ended)
{
previousPos = t2.position;
}
}
此処で出てくるLerpPosは以下です。 xとyを緩やかに動かして制限範囲を超えないようにします。
private Vector3 LerpPos(Vector3 target, Vector3 goal, float t, float xMax, float xMin, float yMax, float yMin)
{
t = Mathf.Clamp(t, 0, 1f);
var x = Mathf.Lerp(target.x,goal.x,t);
if (x > xMax)
{
x = xMax;
}
else if (x < xMin)
{
x = xMin;
}
var y = Mathf.Lerp(target.y, goal.y, t);
if (y > yMax)
{
y = yMax;
}
else if (y < yMin)
{
y = yMin;
}
return new Vector3(x, y, target.z);
}
}
以上 山中がお伝えしました。またお会いしましょう!
コメントはありません。