使 UI 元素跟随相机
Make UI element to follow camera
我想让菜单跟随相机,这样当用户注视菜单时,就可以触发菜单。
我在我的Update
函数中参考FirstPersonCamera
主摄像头设置菜单的位置,
menuCanvas.transform.position = FirstPersonCamera.transform.position
+(FirstPersonCamera.transform.forward * 4);
不幸的是,菜单总是粘在相机中心,因此它总是被触发。如何正确定位上下文菜单?
你可以,例如使用Vector3.Lerp
以使运动更平滑(远距离物体移动速度更快,近距离物体移动更慢)。
然后您可以将目标位置限制在显示器的中心,但允许在每个方向上进行偏移。一个非常简单的例子可能看起来像
// how far to stay away fromt he center
public float offsetRadius = 0.3f;
public float distanceToHead = 4;
public Camera FirstPersonCamera;
// This is a value between 0 and 1 where
// 0 object never moves
// 1 object jumps to targetPosition immediately
// 0.5 e.g. object is placed in the middle between current and targetPosition every frame
// you can play around with this in the Inspector
[Range(0, 1)]
public float smoothFactor = 0.5f;
private void Update()
{
// make the UI always face towards the camera
transform.rotation = FirstPersonCamera.transform.rotation;
var cameraCenter = FirstPersonCamera.transform.position + FirstPersonCamera.transform.forward * distanceToHead;
var currentPos = transform.position;
// in which direction from the center?
var direction = currentPos - cameraCenter;
// target is in the same direction but offsetRadius
// from the center
var targetPosition = cameraCenter + direction.normalized * offsetRadius;
// finally interpolate towards this position
transform.position = Vector3.Lerp(currentPos, targetPosition, smoothFactor);
}
当然这远非完美,但我希望这是一个好的起点。
一个萌 complex/complete 解决方案可以,例如在 HoloToolkit: SphereBasedTagalong
中找到(它被称为 MixedRealityToolkit 2.x.x
之前的任何版本)
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
using UnityEngine;
namespace HoloToolkit.Unity
{
/// <summary>
/// A Tagalong that stays at a fixed distance from the camera and always
/// seeks to stay on the edge or inside a sphere that is straight in front of the camera.
/// </summary>
public class SphereBasedTagalong : MonoBehaviour
{
[Tooltip("Sphere radius.")]
public float SphereRadius = 1.0f;
[Tooltip("How fast the object will move to the target position.")]
public float MoveSpeed = 2.0f;
[Tooltip("When moving, use unscaled time. This is useful for games that have a pause mechanism or otherwise adjust the game timescale.")]
public bool UseUnscaledTime = true;
[Tooltip("Display the sphere in red wireframe for debugging purposes.")]
public bool DebugDisplaySphere = false;
[Tooltip("Display a small green cube where the target position is.")]
public bool DebugDisplayTargetPosition = false;
private Vector3 targetPosition;
private Vector3 optimalPosition;
private float initialDistanceToCamera;
void Start()
{
initialDistanceToCamera = Vector3.Distance(this.transform.position, Camera.main.transform.position);
}
void Update()
{
optimalPosition = Camera.main.transform.position + Camera.main.transform.forward * initialDistanceToCamera;
Vector3 offsetDir = this.transform.position - optimalPosition;
if (offsetDir.magnitude > SphereRadius)
{
targetPosition = optimalPosition + offsetDir.normalized * SphereRadius;
float deltaTime = UseUnscaledTime
? Time.unscaledDeltaTime
: Time.deltaTime;
this.transform.position = Vector3.Lerp(this.transform.position, targetPosition, MoveSpeed * deltaTime);
}
}
public void OnDrawGizmos()
{
if (Application.isPlaying == false) return;
Color oldColor = Gizmos.color;
if (DebugDisplaySphere)
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(optimalPosition, SphereRadius);
}
if (DebugDisplayTargetPosition)
{
Gizmos.color = Color.green;
Gizmos.DrawCube(targetPosition, new Vector3(0.1f, 0.1f, 0.1f));
}
Gizmos.color = oldColor;
}
}
}
我想让菜单跟随相机,这样当用户注视菜单时,就可以触发菜单。
我在我的Update
函数中参考FirstPersonCamera
主摄像头设置菜单的位置,
menuCanvas.transform.position = FirstPersonCamera.transform.position
+(FirstPersonCamera.transform.forward * 4);
不幸的是,菜单总是粘在相机中心,因此它总是被触发。如何正确定位上下文菜单?
你可以,例如使用Vector3.Lerp
以使运动更平滑(远距离物体移动速度更快,近距离物体移动更慢)。
然后您可以将目标位置限制在显示器的中心,但允许在每个方向上进行偏移。一个非常简单的例子可能看起来像
// how far to stay away fromt he center
public float offsetRadius = 0.3f;
public float distanceToHead = 4;
public Camera FirstPersonCamera;
// This is a value between 0 and 1 where
// 0 object never moves
// 1 object jumps to targetPosition immediately
// 0.5 e.g. object is placed in the middle between current and targetPosition every frame
// you can play around with this in the Inspector
[Range(0, 1)]
public float smoothFactor = 0.5f;
private void Update()
{
// make the UI always face towards the camera
transform.rotation = FirstPersonCamera.transform.rotation;
var cameraCenter = FirstPersonCamera.transform.position + FirstPersonCamera.transform.forward * distanceToHead;
var currentPos = transform.position;
// in which direction from the center?
var direction = currentPos - cameraCenter;
// target is in the same direction but offsetRadius
// from the center
var targetPosition = cameraCenter + direction.normalized * offsetRadius;
// finally interpolate towards this position
transform.position = Vector3.Lerp(currentPos, targetPosition, smoothFactor);
}
当然这远非完美,但我希望这是一个好的起点。
一个萌 complex/complete 解决方案可以,例如在 HoloToolkit: SphereBasedTagalong
中找到(它被称为 MixedRealityToolkit 2.x.x
之前的任何版本)
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using UnityEngine; namespace HoloToolkit.Unity { /// <summary> /// A Tagalong that stays at a fixed distance from the camera and always /// seeks to stay on the edge or inside a sphere that is straight in front of the camera. /// </summary> public class SphereBasedTagalong : MonoBehaviour { [Tooltip("Sphere radius.")] public float SphereRadius = 1.0f; [Tooltip("How fast the object will move to the target position.")] public float MoveSpeed = 2.0f; [Tooltip("When moving, use unscaled time. This is useful for games that have a pause mechanism or otherwise adjust the game timescale.")] public bool UseUnscaledTime = true; [Tooltip("Display the sphere in red wireframe for debugging purposes.")] public bool DebugDisplaySphere = false; [Tooltip("Display a small green cube where the target position is.")] public bool DebugDisplayTargetPosition = false; private Vector3 targetPosition; private Vector3 optimalPosition; private float initialDistanceToCamera; void Start() { initialDistanceToCamera = Vector3.Distance(this.transform.position, Camera.main.transform.position); } void Update() { optimalPosition = Camera.main.transform.position + Camera.main.transform.forward * initialDistanceToCamera; Vector3 offsetDir = this.transform.position - optimalPosition; if (offsetDir.magnitude > SphereRadius) { targetPosition = optimalPosition + offsetDir.normalized * SphereRadius; float deltaTime = UseUnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime; this.transform.position = Vector3.Lerp(this.transform.position, targetPosition, MoveSpeed * deltaTime); } } public void OnDrawGizmos() { if (Application.isPlaying == false) return; Color oldColor = Gizmos.color; if (DebugDisplaySphere) { Gizmos.color = Color.red; Gizmos.DrawWireSphere(optimalPosition, SphereRadius); } if (DebugDisplayTargetPosition) { Gizmos.color = Color.green; Gizmos.DrawCube(targetPosition, new Vector3(0.1f, 0.1f, 0.1f)); } Gizmos.color = oldColor; } } }