模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转
·
注意,这是在Game视图下的操作
2026-4-30号:有了AI的加持,去除UGUI限制,增加
右键飞行模式:
- 按住右键拖动鼠标:控制相机朝向
- 按住右键 + WASD:前后左右移动
- 按住右键 + Q/E:下降/上升
- 按住右键 + Shift:加速移动
Alt + 右键 拉近拉远:
- 按住 Alt + 右键,鼠标左右或上下拖动都会沿相机 forward 方向拉近/拉远
- 可在 Inspector 调 AltRightDragZoomSensitivity

先上代码
using UnityEngine;
/// <summary>
///
///
///
/// </summary>
public class MyCameraManager : MonoBehaviour
{
[Header("Target")]
public Transform Target;
[Header("Camera")]
public Camera ControlledCamera;
public bool CreateCameraIfMissing = true;
[Header("Input")]
[Tooltip("When enabled, input only works while the mouse is inside the Game view screen rectangle.")]
public bool RequireMouseInsideScreen = true;
[Tooltip("Optional normalized input area. (0,0,1,1) means the full screen.")]
public Rect NormalizedInputArea = new Rect(0f, 0f, 1f, 1f);
public float PanSensitivity = 0.01f;
public float FreeLookSensitivity = 4f;
public float FlyMoveSpeed = 10f;
public float FlyFastMultiplier = 3f;
public float ZoomSensitivity = 10f;
public float AltRightDragZoomSensitivity = 0.01f;
private bool _lockInput;
private bool _orbiting;
private bool _panning;
private bool _altRightZooming;
private Transform _point;
private LocalRotationAndScale _mouseOrbit;
private Vector3 _lastMousePosition;
private float _distance = 5f;
private float _freeLookX;
private float _freeLookY;
public Camera MainCamera => ControlledCamera;
protected virtual void Start()
{
EnsureCamera();
if (ControlledCamera == null)
{
Debug.LogError("[MyCameraManager] No camera found. Assign ControlledCamera or enable CreateCameraIfMissing.");
enabled = false;
return;
}
GameObject pointObject = new GameObject("SceneViewCamera_Point");
_point = pointObject.transform;
pointObject.hideFlags = HideFlags.DontSave;
_point.position = Target != null
? CalculateBounds(Target).center
: ControlledCamera.transform.position + ControlledCamera.transform.forward * _distance;
_mouseOrbit = ControlledCamera.GetComponent<LocalRotationAndScale>();
if (_mouseOrbit == null)
_mouseOrbit = ControlledCamera.gameObject.AddComponent<LocalRotationAndScale>();
_mouseOrbit.Target = _point;
_mouseOrbit.Distance = _distance;
_mouseOrbit.enabled = false;
SyncFreeLookAngles();
UnlockInput();
}
private void EnsureCamera()
{
if (ControlledCamera != null)
return;
ControlledCamera = Camera.main;
if (ControlledCamera != null || !CreateCameraIfMissing)
return;
GameObject cameraObject = new GameObject("_mainCamera");
ControlledCamera = cameraObject.AddComponent<Camera>();
cameraObject.transform.position = new Vector3(0f, 2f, -5f);
cameraObject.transform.rotation = Quaternion.Euler(20f, 0f, 0f);
}
public void LockInput()
{
_lockInput = true;
}
public void UnlockInput()
{
_lockInput = false;
if (ControlledCamera != null)
ControlledCamera.enabled = true;
}
private void Update()
{
HandleInput();
}
private void HandleInput()
{
if (ControlledCamera == null || _lockInput)
return;
bool canUseInput = IsMouseInsideInputArea();
if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2))
{
StopOrbit();
_panning = false;
_altRightZooming = false;
return;
}
if (!canUseInput)
return;
if (Input.GetKeyDown(KeyCode.F))
Focus();
bool altPressed = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt) || Input.GetKey(KeyCode.AltGr);
if (altPressed && Input.GetMouseButtonDown(1))
{
_altRightZooming = true;
_lastMousePosition = Input.mousePosition;
}
if (_altRightZooming && altPressed && Input.GetMouseButton(1))
{
AltRightDragZoom();
return;
}
if (Input.GetMouseButtonDown(1) && !altPressed)
SyncFreeLookAngles();
if (Input.GetMouseButton(1) && !altPressed)
{
FreeLook();
FlyMove();
}
if (Input.GetMouseButtonDown(2))
{
_panning = true;
_lastMousePosition = Input.mousePosition;
}
if (_panning && Input.GetMouseButton(2))
Pan();
if (altPressed && Input.GetMouseButtonDown(0))
StartOrbit();
if (_orbiting && altPressed && Input.GetMouseButton(0))
UpdateOrbitDistanceFromCamera();
ZoomByMouseWheel();
}
private void FreeLook()
{
_freeLookX += Input.GetAxis("Mouse X") * FreeLookSensitivity;
_freeLookY -= Input.GetAxis("Mouse Y") * FreeLookSensitivity;
_freeLookY = LocalRotationAndScale.ClampAngle(_freeLookY, -89f, 89f);
ControlledCamera.transform.rotation = Quaternion.Euler(_freeLookY, _freeLookX, 0f);
_point.position = ControlledCamera.transform.position + ControlledCamera.transform.forward * _distance;
}
private void FlyMove()
{
Vector3 move = Vector3.zero;
if (Input.GetKey(KeyCode.W))
move += ControlledCamera.transform.forward;
if (Input.GetKey(KeyCode.S))
move -= ControlledCamera.transform.forward;
if (Input.GetKey(KeyCode.D))
move += ControlledCamera.transform.right;
if (Input.GetKey(KeyCode.A))
move -= ControlledCamera.transform.right;
if (Input.GetKey(KeyCode.E))
move += Vector3.up;
if (Input.GetKey(KeyCode.Q))
move -= Vector3.up;
if (move.sqrMagnitude <= 0.0001f)
return;
float speed = FlyMoveSpeed;
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
speed *= FlyFastMultiplier;
Vector3 offset = move.normalized * (speed * Time.deltaTime);
ControlledCamera.transform.position += offset;
_point.position += offset;
}
private void AltRightDragZoom()
{
Vector3 mouseDelta = Input.mousePosition - _lastMousePosition;
float dragAmount = mouseDelta.x + mouseDelta.y;
if (Mathf.Abs(dragAmount) > 0.001f)
{
float scaledDistance = Mathf.Max(_distance, 1f);
Vector3 offset = ControlledCamera.transform.forward * (dragAmount * AltRightDragZoomSensitivity * scaledDistance);
ControlledCamera.transform.position += offset;
_distance = Mathf.Max(Vector3.Distance(ControlledCamera.transform.position, _point.position), 0.01f);
if (_mouseOrbit != null)
_mouseOrbit.Distance = _distance;
}
_lastMousePosition = Input.mousePosition;
}
private void Pan()
{
Vector3 delta = Input.mousePosition - _lastMousePosition;
Vector3 move =
-ControlledCamera.transform.right * (delta.x * PanSensitivity * _distance) -
ControlledCamera.transform.up * (delta.y * PanSensitivity * _distance);
ControlledCamera.transform.position += move;
_point.position += move;
_lastMousePosition = Input.mousePosition;
}
private void StartOrbit()
{
_orbiting = true;
_mouseOrbit.Target = _point;
_mouseOrbit.Distance = _distance;
_mouseOrbit.RestRotationInfo();
_mouseOrbit.enabled = true;
}
private void StopOrbit()
{
_orbiting = false;
if (_mouseOrbit != null)
_mouseOrbit.enabled = false;
SyncFreeLookAngles();
}
private void UpdateOrbitDistanceFromCamera()
{
_distance = Mathf.Max(Vector3.Distance(ControlledCamera.transform.position, _point.position), 0.01f);
_mouseOrbit.Distance = _distance;
}
private void ZoomByMouseWheel()
{
float mouseWheel = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Approximately(mouseWheel, 0f))
return;
Vector3 offset = ControlledCamera.transform.forward * (mouseWheel * ZoomSensitivity * Mathf.Max(_distance, 1f));
ControlledCamera.transform.position += offset;
_distance = Mathf.Max(Vector3.Distance(ControlledCamera.transform.position, _point.position), 0.01f);
if (_mouseOrbit != null)
_mouseOrbit.Distance = _distance;
}
protected void Focus()
{
if (Target == null)
return;
Bounds bounds = CalculateBounds(Target);
_point.position = bounds.center;
float fov = ControlledCamera.fieldOfView * Mathf.Deg2Rad;
float objectSize = Mathf.Max(bounds.extents.x, bounds.extents.y, bounds.extents.z) * 2f;
_distance = Mathf.Max(objectSize / Mathf.Sin(fov * 0.5f), 0.5f);
ControlledCamera.transform.position = bounds.center - ControlledCamera.transform.forward * _distance;
ControlledCamera.transform.LookAt(bounds.center);
SyncFreeLookAngles();
if (_mouseOrbit != null)
{
_mouseOrbit.Target = _point;
_mouseOrbit.Distance = _distance;
_mouseOrbit.RestRotationInfo();
}
}
private bool IsMouseInsideInputArea()
{
if (!RequireMouseInsideScreen)
return true;
Vector3 mouse = Input.mousePosition;
Rect area = new Rect(
NormalizedInputArea.x * Screen.width,
NormalizedInputArea.y * Screen.height,
NormalizedInputArea.width * Screen.width,
NormalizedInputArea.height * Screen.height);
return area.Contains(mouse);
}
private Bounds CalculateBounds(Transform target)
{
Renderer renderer = target.GetComponent<Renderer>();
Bounds bounds = renderer != null
? renderer.bounds
: new Bounds(target.position, Vector3.one);
CalculateChildBounds(target, ref bounds);
if (bounds.extents == Vector3.zero)
bounds.extents = new Vector3(0.5f, 0.5f, 0.5f);
return bounds;
}
private void CalculateChildBounds(Transform target, ref Bounds totalBounds)
{
foreach (Transform child in target)
{
Renderer renderer = child.GetComponent<Renderer>();
if (renderer != null)
{
totalBounds.Encapsulate(renderer.bounds.min);
totalBounds.Encapsulate(renderer.bounds.max);
}
CalculateChildBounds(child, ref totalBounds);
}
}
public static Bounds TransformBounds(Matrix4x4 matrix, Bounds bounds)
{
Vector3 center = matrix.MultiplyPoint(bounds.center);
Vector3 extents = bounds.extents;
Vector3 axisX = matrix.MultiplyVector(new Vector3(extents.x, 0f, 0f));
Vector3 axisY = matrix.MultiplyVector(new Vector3(0f, extents.y, 0f));
Vector3 axisZ = matrix.MultiplyVector(new Vector3(0f, 0f, extents.z));
extents.x = Mathf.Abs(axisX.x) + Mathf.Abs(axisY.x) + Mathf.Abs(axisZ.x);
extents.y = Mathf.Abs(axisX.y) + Mathf.Abs(axisY.y) + Mathf.Abs(axisZ.y);
extents.z = Mathf.Abs(axisX.z) + Mathf.Abs(axisY.z) + Mathf.Abs(axisZ.z);
return new Bounds(center, extents * 2f);
}
private void SyncFreeLookAngles()
{
if (ControlledCamera == null)
return;
Vector3 angles = ControlledCamera.transform.eulerAngles;
_freeLookX = angles.y;
_freeLookY = angles.x;
}
private void OnDestroy()
{
if (_point != null)
Destroy(_point.gameObject);
}
}
using UnityEngine;
/// <summary>
///
/// </summary>
public class LocalRotationAndScale : MonoBehaviour
{
public Transform Target;
public float Distance = 5f;
public float XSpeed = 5f;
public float YSpeed = 5f;
public float YMinLimit = -360f;
public float YMaxLimit = 360f;
public float DistanceMin = 0.5f;
public float DistanceMax = 5000f;
private Camera _camera;
private float _x;
private float _y;
private void Awake()
{
_camera = GetComponent<Camera>();
}
private void Start()
{
SyncAngles();
}
public void SyncAngles()
{
Vector3 angles = transform.eulerAngles;
_x = angles.y;
_y = angles.x;
}
private void OnEnable()
{
RestRotationInfo();
}
private void LateUpdate()
{
if (Target == null)
return;
float deltaX = Input.GetAxis("Mouse X") * XSpeed;
float deltaY = Input.GetAxis("Mouse Y") * YSpeed;
_x += deltaX;
_y -= deltaY;
_y = ClampAngle(_y, YMinLimit, YMaxLimit);
Distance = Mathf.Clamp(Distance, DistanceMin, DistanceMax);
Zoom();
}
public void RestRotationInfo()
{
Transform cameraTransform = _camera != null ? _camera.transform : transform;
_y = cameraTransform.localEulerAngles.x;
_x = cameraTransform.localEulerAngles.y;
}
public void Zoom()
{
if (Target == null)
return;
Quaternion rotation = Quaternion.Euler(_y, _x, 0f);
transform.rotation = rotation;
Vector3 negativeDistance = new Vector3(0f, 0f, -Distance);
transform.position = rotation * negativeDistance + Target.position;
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360f)
angle += 360f;
if (angle > 360f)
angle -= 360f;
return Mathf.Clamp(angle, min, max);
}
}
新建一个场景,一个cube放在场景中间,把cube拖拽到Target,点击运行即可
场景布置如图所示 场景挂载一个MyCameraManager脚本就可以了

场景的两个示例cube

大概就是这样子了。其实别看就两个类而已,牵扯到的知识还是蛮多的,也需要一定的功力。其中最大难度的就是Focus()方法了。
/// <summary>
/// 相机参照点
/// </summary>
private Transform _point;
相机的运动主要是参照了这个点,好好的理解这个点的运动,那就会很好理解整个流程了。
代码还是有点乱,有心人可以整理下,让其更加简单优雅
其他就是对向量的理解了,还有对3D空间的理解。相信如果你能理解这些知识后,对你的游戏开发的帮助是很大的。
更多推荐
所有评论(0)