構成方法
空のオブジェクトを作成して、カメラを子オブジェクトとして、カメラ制御スクリプトをマウントします(オブジェクトをコード内の文字の位置と一致させます)。
親に対するカメラの距離を理想的なカメラと人の距離に設定します
サラウンドセンターとカメラを指定する
原理
サラウンドを実現するには、最初にサラウンドポイントとカメラのTransformプロパティを取得する必要があります。
周囲のポイントに対するカメラの向きを表現したい場合は、相対位置の説明を使用する方が便利な場合がありますが、そうではありません。位置を使用して説明する回転プロセスの補間は、明らかに補間に不便です。
このようにして、周囲のポイントに対するカメラの角度を取得し、距離を追加すると、カメラの位置を正確に表すことができます。
線形補間
変数を補間する場合は、最初に補間の実装を明確にする必要があります。線形補間とは何ですか?
2つの数値a、bが与えられた場合、aとbに対して線形補間を実行してから、補間の位置を表すパラメーターtを指定します。
たとえば、0と1の補間の場合、パラメータは0.1であり、1回の補間の結果は0.1です。
線形補間の式は+(ba)*tです。
では、補間の実際のアプリケーションは何ですか?
-カメラと動いているキャラクターの間の距離を滑らかにします
-カメラとキャラクターの回転角度をなめらかにし、一定時間慣性回転効果を実現します。
補間によって遅延が発生するのはなぜですか?
補間には2つの数値があるため、1つは現在の値、もう1つは目標値、もう1つはパーセンテージです。
目標値が変化しなくなると、現在の値が目標値に近づくまでに一定の時間がかかります。
2つの数値の差は、下向きの放物線と同様に、時間とともに変化します。
周囲の障害物の検出
キャラクターの正面、背面、左、右の4方向で光線検出を実行し、戻り距離を取得して、最小値を取得します。
より正確な結果が必要な場合は、直交方向ベクトルを2つずつ追加して4つの斜め方向ベクトルを取得できるため、8方向の距離検出を行うことができます。
カメラがブロックされている
カメラがブロックされている場合は、カメラの距離を、キャラクターの水平方向から最も近い障害物の距離よりも短くなるように調整します。
手動で距離を調整する場合は、希望の距離を記録してください。
遮られなくなった場合は、優先距離が復元されます。
コード
using UnityEngine;
using System.Collections;
using UnityEngine.Animations;
public class OrbitCamera : MonoBehaviour
{
public Transform pivot;
public Transform camera;
private float distance;
public bool distanceAdjustable = true;
public bool rotationAdjustable = true;
void Start()
{
targetSideRotation = transform.eulerAngles.y;
currentSideRotation = transform.eulerAngles.y;
targetUpRotation = transform.eulerAngles.x;
currentUpRotation = transform.eulerAngles.x;
distance = -camera.localPosition.z; //相机局部坐标z值为-1.8,那么相机与距离人物为1.8
}
void LateUpdate()
{
if (!pivot) return;
Follow();
DragRotate();
ScrollScale();
OcclusionJudge();
}
void Follow()
{
if (!pivot.gameObject.GetComponentInParent<ParentConstraint>())
{
transform.position = Vector3.Lerp(transform.position, pivot.position, Time.deltaTime * 5); //相机跟随角色的插值,相机当前帧的实际位置为它们中间10%的位置
camera.localPosition = -Vector3.forward * distance; //仅z有值,并且方向为负
}
else //高速运动下不再插值
{
transform.position = pivot.position;
camera.localPosition = -Vector3.forward * distance;
}
}
public float MinimumDegree = 0;
public float MaximumDegree = 60;
private float targetSideRotation;
private float targetUpRotation;
private float currentSideRotation;
private float currentUpRotation;
void DragRotate()
{
if (!rotationAdjustable) return;
if (Input.GetMouseButton(0))
{
targetSideRotation += Input.GetAxis("Mouse X") * 5;
targetUpRotation -= Input.GetAxis("Mouse Y") * 5;
}
targetUpRotation = Mathf.Clamp(targetUpRotation, MinimumDegree, MaximumDegree);
currentSideRotation = Mathf.LerpAngle(currentSideRotation, targetSideRotation, Time.deltaTime * 5);
currentUpRotation = Mathf.Lerp(currentUpRotation, targetUpRotation, Time.deltaTime * 5);
transform.rotation = Quaternion.Euler(currentUpRotation, currentSideRotation, 0);
}
float MinimumDistance = 1;
float MaximumDistance = 4;
void ScrollScale()
{
if (!distanceAdjustable) return;
distance *= (1 - Input.GetAxis("Mouse ScrollWheel") * 0.2f); //在原值的基础上调整为原值的百分比
distance = Mathf.Clamp(distance, MinimumDistance, MaximumDistance);
if(Input.GetAxis("Mouse ScrollWheel")!=0)
preferdDistance = distance;
}
float preferdDistance = 1;
bool resumable = false;
void OcclusionJudge()
{
if (Physics.Raycast(pivot.position, -camera.forward, distance))
{
resumable = true;
distance = NearestObstacleDistance(pivot);
while (Physics.Raycast(pivot.position, -camera.forward, distance) && distance > MinimumDistance)
{
distance *= 0.99f;
distance = Mathf.Clamp(distance, MinimumDistance, MaximumDistance);
float dist = Mathf.Lerp(-camera.transform.localPosition.z, distance, 1f);
camera.localPosition = -Vector3.forward * dist;
}
}
if (!resumable) return;
if (resumable && !Physics.Raycast(pivot.position, -camera.forward, preferdDistance))
{
distance = preferdDistance;
float dist = Mathf.Lerp(-camera.transform.localPosition.z, distance, 1f);
camera.localPosition = -Vector3.forward * distance;
resumable = false;
}
}
float NearestObstacleDistance(Transform start)
{
float dis = float.MaxValue;
RaycastHit hit;
Physics.Raycast(start.position, start.forward, out hit);
if(hit.distance!=0) dis = Mathf.Min(dis, hit.distance);
Physics.Raycast(start.position, -start.forward, out hit);
if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
Physics.Raycast(start.position, start.right, out hit);
if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
Physics.Raycast(start.position, -start.right, out hit);
if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
return dis;
}
}
効果は発揮されません、それは前に書かれたものよりはるかに優れています