我正在为2名玩家制作一个小街机射击游戏,并且需要让屏幕聚焦于2个玩家,我让相机在X轴的玩家中心移动,但是我认为当2玩家靠得更近,相机也越来越近了。
这是透视pov:
移动相机比改变fov更好。计算相机距离的公式是
cameraDistance = (distanceBetweenPlayers / 2 / aspectRatio) / Tan(fieldOfView / 2);
请注意,玩家出现在视口的边缘,因此可以添加一些小的边距。这是我的脚本:
public Transform player1;
public Transform player2;
private const float DISTANCE_MARGIN = 1.0f;
private Vector3 middlePoint;
private float distanceFromMiddlePoint;
private float distanceBetweenPlayers;
private float cameraDistance;
private float aspectRatio;
private float fov;
private float tanFov;
void Start() {
aspectRatio = Screen.width / Screen.height;
tanFov = Mathf.Tan(Mathf.Deg2Rad * Camera.main.fieldOfView / 2.0f);
}
void Update () {
// Position the camera in the center.
Vector3 newCameraPos = Camera.main.transform.position;
newCameraPos.x = middlePoint.x;
Camera.main.transform.position = newCameraPos;
// Find the middle point between players.
Vector3 vectorBetweenPlayers = player2.position - player1.position;
middlePoint = player1.position + 0.5f * vectorBetweenPlayers;
// Calculate the new distance.
distanceBetweenPlayers = vectorBetweenPlayers.magnitude;
cameraDistance = (distanceBetweenPlayers / 2.0f / aspectRatio) / tanFov;
// Set camera to new position.
Vector3 dir = (Camera.main.transform.position - middlePoint).normalized;
Camera.main.transform.position = middlePoint + dir * (cameraDistance + DISTANCE_MARGIN);
}
视野可以这样计算:
FOV = 2 * arctan((0.5 * distanceBetweenPlayers) / (distanceFromMiddlePoint * aspectRatio));
请注意,这给出了FOV,其中玩家位于视口的边缘。可以增加小幅度。我想自己尝试一下,这是我的脚本:
public Transform player1;
public Transform player2;
private const float FOV_MARGIN = 15.0f;
private Vector3 middlePoint;
private float distanceFromMiddlePoint;
private float distanceBetweenPlayers;
private float aspectRatio;
void Start () {
aspectRatio = Screen.width / Screen.height;
}
void Update () {
// Find the middle point between players.
middlePoint = player1.position + 0.5f * (player2.position - player1.position);
// Position the camera in the center.
Vector3 newCameraPos = Camera.main.transform.position;
newCameraPos.x = middlePoint.x;
Camera.main.transform.position = newCameraPos;
// Calculate the new FOV.
distanceBetweenPlayers = (player2.position - player1.position).magnitude;
distanceFromMiddlePoint = (Camera.main.transform.position - middlePoint).magnitude;
Camera.main.fieldOfView = 2.0f * Mathf.Rad2Deg * Mathf.Atan((0.5f * distanceBetweenPlayers) / (distanceFromMiddlePoint * aspectRatio));
// Add small margin so the players are not on the viewport border.
Camera.main.fieldOfView += FOV_MARGIN;
}
如果视野变大,我建议移动相机,因为视角会因较大的视野而扭曲。
jparimaa的回答对我有很大的帮助,但是对于我来说,它对于狭窄的屏幕(例如手机肖像模式)并不起作用。这是因为它只考虑了高度计算,导致两个玩家在垂直靠近时远离屏幕水平。
我更新它以正确计算高度和宽度情况下的相机距离。计算来自Unity docs(与jparimaa相同):
The Size of the Frustum at a Given Distance from the Camera
另请注意:Camera.fieldOfView是垂直视野
我最终得到了以下适用于我的情况的代码:多个太空船带有3D透视摄像头,可在2D(x-z)平面上控制:
using UnityEngine;
public class MeleeCamera : MonoBehaviour
{
public Transform[] targets;
public float padding = 15f; // amount to pad in world units from screen edge
Camera _camera;
void Awake()
{
_camera = GetComponent<Camera>();
}
private void LateUpdate() // using LateUpdate() to ensure camera moves after everything else has
{
Bounds bounds = FindBounds();
// Calculate distance to keep bounds visible. Calculations from:
// "The Size of the Frustum at a Given Distance from the Camera": https://docs.unity3d.com/Manual/FrustumSizeAtDistance.html
// note: Camera.fieldOfView is the *vertical* field of view: https://docs.unity3d.com/ScriptReference/Camera-fieldOfView.html
float desiredFrustumWidth = bounds.size.x + 2 * padding;
float desiredFrustumHeight = bounds.size.z + 2 * padding;
float distanceToFitHeight = desiredFrustumHeight * 0.5f / Mathf.Tan(_camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
float distanceToFitWidth = desiredFrustumWidth * 0.5f / Mathf.Tan(_camera.fieldOfView * _camera.aspect * 0.5f * Mathf.Deg2Rad);
float resultDistance = Mathf.Max(distanceToFitWidth, distanceToFitHeight);
// Set camera to center of bounds at exact distance to ensure targets are visible and padded from edge of screen
_camera.transform.position = bounds.center + Vector3.up * resultDistance;
}
private Bounds FindBounds()
{
if (targets.Length == 0)
{
return new Bounds();
}
Bounds bounds = new Bounds(targets[0].position, Vector3.zero);
foreach (Transform target in targets)
{
if (target.gameObject.activeSelf) // if target not active
{
bounds.Encapsulate(target.position);
}
}
return bounds;
}
}
为您的相机提供一系列缩放级别。使用两个字符之间的距离作为比率,以确定沿缩放范围的距离。
只是为了补充@nokola的答案,(我还不能发表评论......)看起来他的相机只能超过物体,所以不要这样做:
_camera.transform.position = bounds.center + Vector3.up * resultDistance;
你可能会这样做:
_camera.transform.position = bounds.center + -_camera.transform.forward * resultDistance;
现在它应该适用于任何旋转相机。 (对我来说它至少做了)