Unity - 透过枪的范围看
Unity - Looking through the scope of a gun
现在我有 2 个摄像头:主摄像头显示枪的正常状态,第二个摄像头连接到枪上(枪是主摄像头的子摄像头),切换时它会透过瞄准镜增加枪的视野。
为了更好地理解,这里有一张图片:
现在,如果我只是打开第二个摄像头并关闭主摄像头,效果会很好,但不是很理想。每个场景应该只有 1 个摄像头。
所以我想 Lerp 相机的位置以通过范围查看并手动减小视野。所以我写了下面的脚本:
[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {
private Transform CameraTransform = null;
public Transform ZoomedTransform;
private bool zoomed = false;
void Start () {
CameraTransform = Camera.main.transform;
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.LeftShift))
{
CameraTransform.position = Vector3.Lerp (
CameraTransform.position,
CameraTransform.position + ZoomedTransform.position,
5f * Time.deltaTime
);
CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
}
}
}
问题是它不起作用:当我按下变焦按钮时,相机以光速快速穿过场景,很难准确判断发生了什么。
谁能告诉我我做错了什么?我认为这与父子关系有关,但即使我尝试使用静态值,我似乎也无法复制正确的解决方案。
层次结构:
我已经有一段时间没有在 Unity 中做任何事情了,但我认为它是在帧时间而不是实际时间处理 Lerp 函数。您将需要在帧时间未处理的另一个函数中调用它。
你的 lerp 目标是相对于相机的当前位置的,所以它一直在移动。这是您的目标:
CameraTransform.position + ZoomedTransform.position
这意味着随着您的相机移动以靠近该位置,相机的新位置会导致目的地发生变化。所以你的相机一直在移动。
您的目的地应该是ZoomedTransform.position
。不需要添加,因为 position
is in world coordinates. (And when you actually need to convert between spaces, check out TransformPoint
和类似的方法。)
(此答案假设 ZoomedTransform
是相对变换,而不是 31eee384 的答案所怀疑的相机的绝对位置。)
我认为您的代码存在一些问题。我将分别处理它们,以便它们更容易理解,但它们都与以下行有关:
CameraTransform.position = Vector3.Lerp (CameraTransform.position, CameraTransform.position + ZoomedTransform.position, 5f * Time.deltaTime);
首先,让我们看看您是如何使用 Vector3.Lerp()
的。对于 Vector3.Lerp()
的第三个参数,您提供 5f * Time.deltaTime
。这个值到底是用来做什么的?嗯,标准帧率大约是 60 FPS,所以 Time.deltaTime
= ~1/60。因此,5f * Time.deltaTime
= 5/60 = ~0.0833.
不过,Vector3.Lerp()
对第三个参数的期望是什么?根据 to the documentation,第三个参数应介于 0 和 1 之间,并确定返回的 Vector3
是否应该更接近给定的第一个或第二个 Vector3
。所以是的,5f * Time.deltaTime
落在这个范围内,但不会发生插值 - 因为它总是在 ~0.0833 左右,而不是从 0 到 1(或 1 到 0)。每一帧,你基本上总是回来 cameraPos + zoomTransform * 0.0833
。
另一个值得注意的问题是您如何在每一帧更新 CameraTransform.position
的值,然后使用该新(增加的)值作为下一帧 Vector3.Lerp()
的参数。 (这有点像在循环中执行 int i = i + 1;
。)这就是为什么你的相机在地图上飞得如此之快的原因。这是每一帧发生的事情,使用我之前计算的 Vector3.Lerp()
的假设结果(伪代码):
// Frame 1
cameraPosFrame_1 = cameraPosFrame_0 + zoomTransform * 0.0833;
// Frame 2
cameraPosFrame_2 = cameraPosFrame_1 + zoomTransform * 0.0833;
// Frame 3
cameraPosFrame_3 = cameraPosFrame_2 + zoomTransform * 0.0833;
// etc...
每一帧,zoomTransform * 0.0833
都会添加到相机的位置。这最终会成为真正、非常快速、不间断的价值增长——所以你的相机会在地图上飞来飞去。
解决这些问题的一种方法是使用变量来存储相机的初始本地位置、缩放进度和缩放速度。这样,我们就不会丢失相机的原始位置,并且我们都可以跟踪缩放进度以及何时停止。
[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {
private Transform CameraTransform = null;
public Transform ZoomedTransform;
private Vector3 startLocalPos;
private float zoomProgress = 0;
private float zoomLength = 2; // Number of seconds zoom will take
private bool zoomed = false;
void Start () {
CameraTransform = Camera.main.transform;
startLocalPos = CameraTransform.localPosition;
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.LeftShift))
{
zoomProgress += Time.deltaTime;
CameraTransform.localPosition = Vector3.Lerp (startLocalPos, startLocalPos + ZoomedTransform.position, zoomProgress / zoomLength);
CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
}
}
}
希望这对您有所帮助!如果您有任何问题,请告诉我。这个答案确实有点啰嗦,所以我希望你能从中得到重点。
现在我有 2 个摄像头:主摄像头显示枪的正常状态,第二个摄像头连接到枪上(枪是主摄像头的子摄像头),切换时它会透过瞄准镜增加枪的视野。
为了更好地理解,这里有一张图片:
现在,如果我只是打开第二个摄像头并关闭主摄像头,效果会很好,但不是很理想。每个场景应该只有 1 个摄像头。
所以我想 Lerp 相机的位置以通过范围查看并手动减小视野。所以我写了下面的脚本:
[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {
private Transform CameraTransform = null;
public Transform ZoomedTransform;
private bool zoomed = false;
void Start () {
CameraTransform = Camera.main.transform;
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.LeftShift))
{
CameraTransform.position = Vector3.Lerp (
CameraTransform.position,
CameraTransform.position + ZoomedTransform.position,
5f * Time.deltaTime
);
CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
}
}
}
问题是它不起作用:当我按下变焦按钮时,相机以光速快速穿过场景,很难准确判断发生了什么。
谁能告诉我我做错了什么?我认为这与父子关系有关,但即使我尝试使用静态值,我似乎也无法复制正确的解决方案。
层次结构:
我已经有一段时间没有在 Unity 中做任何事情了,但我认为它是在帧时间而不是实际时间处理 Lerp 函数。您将需要在帧时间未处理的另一个函数中调用它。
你的 lerp 目标是相对于相机的当前位置的,所以它一直在移动。这是您的目标:
CameraTransform.position + ZoomedTransform.position
这意味着随着您的相机移动以靠近该位置,相机的新位置会导致目的地发生变化。所以你的相机一直在移动。
您的目的地应该是ZoomedTransform.position
。不需要添加,因为 position
is in world coordinates. (And when you actually need to convert between spaces, check out TransformPoint
和类似的方法。)
(此答案假设 ZoomedTransform
是相对变换,而不是 31eee384 的答案所怀疑的相机的绝对位置。)
我认为您的代码存在一些问题。我将分别处理它们,以便它们更容易理解,但它们都与以下行有关:
CameraTransform.position = Vector3.Lerp (CameraTransform.position, CameraTransform.position + ZoomedTransform.position, 5f * Time.deltaTime);
首先,让我们看看您是如何使用 Vector3.Lerp()
的。对于 Vector3.Lerp()
的第三个参数,您提供 5f * Time.deltaTime
。这个值到底是用来做什么的?嗯,标准帧率大约是 60 FPS,所以 Time.deltaTime
= ~1/60。因此,5f * Time.deltaTime
= 5/60 = ~0.0833.
不过,Vector3.Lerp()
对第三个参数的期望是什么?根据 to the documentation,第三个参数应介于 0 和 1 之间,并确定返回的 Vector3
是否应该更接近给定的第一个或第二个 Vector3
。所以是的,5f * Time.deltaTime
落在这个范围内,但不会发生插值 - 因为它总是在 ~0.0833 左右,而不是从 0 到 1(或 1 到 0)。每一帧,你基本上总是回来 cameraPos + zoomTransform * 0.0833
。
另一个值得注意的问题是您如何在每一帧更新 CameraTransform.position
的值,然后使用该新(增加的)值作为下一帧 Vector3.Lerp()
的参数。 (这有点像在循环中执行 int i = i + 1;
。)这就是为什么你的相机在地图上飞得如此之快的原因。这是每一帧发生的事情,使用我之前计算的 Vector3.Lerp()
的假设结果(伪代码):
// Frame 1
cameraPosFrame_1 = cameraPosFrame_0 + zoomTransform * 0.0833;
// Frame 2
cameraPosFrame_2 = cameraPosFrame_1 + zoomTransform * 0.0833;
// Frame 3
cameraPosFrame_3 = cameraPosFrame_2 + zoomTransform * 0.0833;
// etc...
每一帧,zoomTransform * 0.0833
都会添加到相机的位置。这最终会成为真正、非常快速、不间断的价值增长——所以你的相机会在地图上飞来飞去。
解决这些问题的一种方法是使用变量来存储相机的初始本地位置、缩放进度和缩放速度。这样,我们就不会丢失相机的原始位置,并且我们都可以跟踪缩放进度以及何时停止。
[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {
private Transform CameraTransform = null;
public Transform ZoomedTransform;
private Vector3 startLocalPos;
private float zoomProgress = 0;
private float zoomLength = 2; // Number of seconds zoom will take
private bool zoomed = false;
void Start () {
CameraTransform = Camera.main.transform;
startLocalPos = CameraTransform.localPosition;
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.LeftShift))
{
zoomProgress += Time.deltaTime;
CameraTransform.localPosition = Vector3.Lerp (startLocalPos, startLocalPos + ZoomedTransform.position, zoomProgress / zoomLength);
CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
}
}
}
希望这对您有所帮助!如果您有任何问题,请告诉我。这个答案确实有点啰嗦,所以我希望你能从中得到重点。