unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转
注意,这是在Game视图下的操作
先上代码
using UnityEngine;
using UnityEngine.EventSystems;
///
/// 管理着相机的移动,旋转,缩放视图,注视焦点等操作,该管理器是建立在UGUI上的
///
public class MyCameraManager : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{///
/// 鼠标选中的目标点,或者是其他给的目标点
///
public Transform Target;
public float PanSensitivity = 5f;
///
/// 是否在视图内
///
private bool _isPointerOverSceneView;
private LocalRotationAndScale _mouseOrbit;
///
/// 主要的相机
///
public Camera MainCamera { get;
private set;
}///
/// 是否进入旋转
///
private bool m_rotate;
///
/// 是否进入拖拽移动
///
private bool m_pan;
///
/// 相机参照点
///
private Transform _point;
///
/// 相机到_point点的距离
///
private float _distance;
///
/// 是否开启注视
///
private bool _isOpenLookAt;
///
/// 鼠标上一帧的位置
///
private Vector3 m_lastMousePosition;
private bool _lockInput;
///
/// 相机旋转参数
///
private float x = 0.0f;
///
/// 相机旋转参数
///
private float y = 0.0f;
void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
{_isPointerOverSceneView = false;
}
void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
{
_isPointerOverSceneView = true;
}protected virtual void Start()
{
CreatCamera();
GameObject go = new GameObject("Point");
_point = go.transform;
_mouseOrbit = MainCamera.gameObject.GetComponent();
if (_mouseOrbit == null)
{
_mouseOrbit = MainCamera.gameObject.AddComponent();
}UnlockInput();
_mouseOrbit.enabled = false;
}private void CreatCamera()
{
GameObject go = new GameObject("_mainCamera");
MainCamera = go.AddComponent();
}public void LockInput()
{
_lockInput = true;
}private void Enable()
{
UnlockInput();
}private void DisEnable()
{
LockInput();
MainCamera.enabled = false;
}private void Update()
{
HandleInput();
UpdateLerpHandle();
}private void HandleInput()
{if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2))
{_mouseOrbit.enabled = false;
m_rotate = false;
// _isOpenLookAt = false;
return;
}if (_lockInput)
{
return;
}if (Input.GetKeyDown(KeyCode.F))
{Focus();
}bool pan = Input.GetMouseButton(2);
bool rotate = Input.GetKey(KeyCode.AltGr) || Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
if (pan != m_pan)
{
m_pan = pan;
if (m_pan)
{
m_rotate = false;
}}
else
{
if (rotate != m_rotate)
{
m_rotate = rotate;
}
}bool isLocked = m_rotate || pan;
if (!_isPointerOverSceneView)
{
return;
}if (Input.GetMouseButtonDown(1))
{y = MainCamera.transform.localEulerAngles.x;
x = MainCamera.transform.localEulerAngles.y;
_isOpenLookAt = false;
}
if (Input.GetMouseButton(1) && !rotate)//自身旋转
{
x += Input.GetAxis("Mouse X") * 250f * 0.02f;
y -= Input.GetAxis("Mouse Y") * 120f * 0.02f;
y = LocalRotationAndScale.ClampAngle(y, -360f, 360f);
var rotation = Quaternion.Euler(y, x, 0);
MainCamera.transform.rotation = rotation;
_point.position = MainCamera.transform.position + MainCamera.transform.forward * _mouseOrbit.Distance;
}#region 处理相机的靠近拉远float mouseWheel = Input.GetAxis("Mouse ScrollWheel");
if (mouseWheel != 0 && !rotate)//按住滚轮键的靠近拉远
{
Vector3 distance = MainCamera.transform.forward * mouseWheel * 10f;
MainCamera.transform.position += distance;
if (mouseWheel < 0)
_mouseOrbit.Distance += distance.magnitude;
else _mouseOrbit.Distance += -distance.magnitude;
}if (Input.GetMouseButton(1) && rotate)//放大缩小模式//按住ALT加鼠标滑动的放大拉远
{
float x = Input.GetAxis("Mouse X") * 250f * 0.02f;
Vector3 distance = MainCamera.transform.forward * x * 0.05f;
MainCamera.transform.position += distance;
if (x < 0)
_mouseOrbit.Distance += distance.magnitude;
else _mouseOrbit.Distance += -distance.magnitude;
}#endregionif (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(2))
{
m_lastMousePosition = Input.mousePosition;
if (m_rotate)
{
_mouseOrbit.enabled = true;
}
}if (isLocked)
{
if (m_pan && !m_rotate)
{
Pan();
}
}}
public void UnlockInput()
{
_lockInput = false;
if (_mouseOrbit != null)
{
_point.position = MainCamera.transform.position + MainCamera.transform.forward * _mouseOrbit.Distance;
_mouseOrbit.Target = _point;
_mouseOrbit.SyncAngles();
}
}private void Pan()
{
Vector3 delta = m_lastMousePosition - Input.mousePosition;
delta = delta / Mathf.Sqrt(MainCamera.pixelHeight * MainCamera.pixelHeight + MainCamera.pixelWidth * MainCamera.pixelWidth);
delta *= PanSensitivity;
delta = MainCamera.cameraToWorldMatrix.MultiplyVector(delta);
MainCamera.transform.position += delta;
_point.position += delta;
m_lastMousePosition = Input.mousePosition;
}
///
/// 处理插值事务
///
private void UpdateLerpHandle()
{if (_isOpenLookAt)
{
Vector3 targetToCaemraVector3 = (MainCamera.transform.position - _point.position).normalized * _distance;
Vector3 cameraDir = -MainCamera.transform.forward * _distance;
Vector3 dir = Vector3.Lerp(targetToCaemraVector3, cameraDir, Time.deltaTime * 15f);
MainCamera.transform.position = _point.position + dir;
float value = https://www.it610.com/article/Mathf.Abs(Vector3.Dot(dir.normalized, targetToCaemraVector3.normalized));
if (Mathf.Abs(value - 1f) < 0.0000001f)
{
Debug.Log("关闭插值");
_isOpenLookAt = false;
}
}
}
private Bounds CalculateBounds(Transform t)
{
Renderer renderer = t.GetComponentInChildren();
if (renderer)
{
Bounds bounds = renderer.bounds;
if (bounds.size == Vector3.zero && bounds.center != renderer.transform.position)
{
bounds = TransformBounds(renderer.transform.localToWorldMatrix, bounds);
}
CalculateBounds(t, ref bounds);
if (bounds.extents == Vector3.zero)
{
bounds.extents = new Vector3(0.5f, 0.5f, 0.5f);
}
return bounds;
}return new Bounds(t.position, new Vector3(0.5f, 0.5f, 0.5f));
}
private void CalculateBounds(Transform t, ref Bounds totalBounds)
{
foreach (Transform child in t)
{
Renderer renderer = child.GetComponent();
if (renderer)
{
Bounds bounds = renderer.bounds;
if (bounds.size == Vector3.zero && bounds.center != renderer.transform.position)
{
bounds = TransformBounds(renderer.transform.localToWorldMatrix, bounds);
}
totalBounds.Encapsulate(bounds.min);
totalBounds.Encapsulate(bounds.max);
}CalculateBounds(child, ref totalBounds);
}
}
public static Bounds TransformBounds(Matrix4x4 matrix, Bounds bounds)
{
var center = matrix.MultiplyPoint(bounds.center);
// transform the local extents' axes
var extents = bounds.extents;
var axisX = matrix.MultiplyVector(new Vector3(extents.x, 0, 0));
var axisY = matrix.MultiplyVector(new Vector3(0, extents.y, 0));
var axisZ = matrix.MultiplyVector(new Vector3(0, 0, extents.z));
// sum their absolute value to get the world extents
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 = center, extents = extents };
}protected void Focus()
{
_isOpenLookAt = true;
Bounds bounds = CalculateBounds(Target);
float fov = MainCamera.fieldOfView * Mathf.Deg2Rad;
float objSize = Mathf.Max(bounds.extents.y, bounds.extents.x, bounds.extents.z) * 2.0f;
_distance = Mathf.Abs(objSize / Mathf.Sin(fov / 2.0f));
_mouseOrbit.Distance = _distance;
_point.position = bounds.center;
}}
using UnityEngine;
///
/// 绕目标旋转和向远处向近处看物体
///
public class LocalRotationAndScale : MonoBehaviour
{
private Camera m_camera;
public Transform Target;
public float Distance = 5.0f;
public float XSpeed = 5.0f;
public float YSpeed = 5.0f;
public float YMinLimit = -360f;
public float YMaxLimit = 360f;
public float DistanceMin = .5f;
public float DistanceMax = 5000f;
private float m_x = 0.0f;
private float m_y = 0.0f;
private void Awake()
{
m_camera = GetComponent();
}private void Start()
{
SyncAngles();
}public void SyncAngles()
{
Vector3 angles = transform.eulerAngles;
m_x = angles.y;
m_y = angles.x;
}private void LateUpdate()
{
float deltaX = Input.GetAxis("Mouse X");
float deltaY = Input.GetAxis("Mouse Y");
deltaX = deltaX * XSpeed;
deltaY = deltaY * YSpeed;
m_x += deltaX;
m_y -= deltaY;
m_y = ClampAngle(m_y, YMinLimit, YMaxLimit);
Zoom();
}private void OnEnable()
{
RestRotationInfo();
}public void RestRotationInfo()
{
m_y = m_camera.transform.localEulerAngles.x;
m_x = m_camera.transform.localEulerAngles.y;
}
public void Zoom()
{
Quaternion rotation = Quaternion.Euler(m_y, m_x, 0);
transform.rotation = rotation;
Vector3 negDistance = new Vector3(0.0f, 0.0f, -Distance);
Vector3 position = rotation * negDistance + Target.position;
transform.position = 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);
}
}
新建一个场景,按照我给的图片一步一步操作,运行,就可以看到效果了
第一步,首先用ugui建立一个Canvas,再建一个image,把这个image透明度设置为0,并且设置为全屏,就是把图片填满整个canvas。因为这个功能是建立在UGUI上的,只要是在imge上,就可以进行鼠标操作,其实也可以不用那么麻烦,看懂代码后,改几下就可以脱离ugui了。图片所示,就是这里限制在UGUI上,了解代码后,大家可以试试去掉。
文章图片
场景布置如图所示场景挂载一个MyCameraManager脚本就可以了
文章图片
文章图片
【unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转】场景的两个示例cube
文章图片
大概就是这样子了。其实别看就两个类而已,牵扯到的知识还是蛮多的,也需要一定的功力。其中最大难度的就是Focus()方法了。
///
/// 相机参照点
///
private Transform _point;
相机的运动主要是参照了这个点,好好的理解这个点的运动,那就会很好理解整个流程了。
代码还是有点乱,有心人可以整理下,让其更加简单优雅
其他就是对向量的理解了,还有对3D空间的理解。相信如果你能理解这些知识后,对你的游戏开发的帮助是很大的。
推荐阅读
- Unity和Android通信系列文章2——扩展UnityPlayerActivity
- 三门问题(蒙提霍尔悖论)分析与Golang模拟
- unity探究UGUI的Image中sprite和overrideSprite的区别
- 投石机可连续抛射石头【Algodoo|投石机可连续抛射石头【Algodoo | 物理模拟】
- unity|unity 在iOS平台跳转appstore
- OpenCV|OpenCV for Unity 通过WebCamTextureToMatHelper帮助类来获取摄像头的画面
- spring5源码系列--循环依赖|spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖
- Cesium入门8|Cesium入门8 - Configuring the Scene - 配置视窗
- Unity中使用反射机制调用函数
- Unity|Unity 对象池