摄影图片网站/百度收录查询代码
前言
现在很多游戏都会在主页中显示一个场景,然后玩家的人物会在场景当中。这个时候往往会需要一些旋转功能,来方便玩家查看人物的各个角度。
一般的做法就是人物自身转动,摄像机不动,这样用户无法用其他视角观察到整个场景。另一种是人物不动,相机围绕人物,或者某个点转动,这种做法的缺点在于要么人物在屏幕中央(看向人物),要么会在屏幕左右乱跑(看向人物边上某个点),会局限UI的设计。
因此最好的方法便是可以让人物在屏幕中的任意位置,同时旋转起来的时候,转动相机,同时人物相对屏幕的位置也保持不变,如图:
github:https://github.com/luckyWjr/Demo
思路
因为相机看向的点(这里我们叫他LookedAt点),永远在屏幕的中央。假设我们的人物放在UI的左侧,那么我们要保证旋转相机的时候,人物的点(这里我们叫他Target点)在相机Camera看来始终在LookedAt点的左边。
我们可以把LookedAt点,Target点,和Camera,三个点想象成一个三角形,然后这个三角形围绕着Target点旋转,也就是LookedAt点和Camera同时以相同速度(角度/每秒)围绕Target点公转。即可实现我们要的效果
视角俯视或仰视的话,只需修改Camera的高度,其他两个点不动。
视角拉近或拉远,则需要同比例修改LookedAt点到Target点的距离和Camera到Target点的距离,Target点不动,即可保持三角形的三个角的角度不变。
代码实现
首先我们先搭建一个简单的场景,如图,Target点在LookAt点的左侧
我们可以先通过一些参数来初始化Camera的位置,然后此时Camera,LookedAt和Target三个点形成的三角形即我们到时候要旋转的样式。
然后获取变成和角度等,来计算旋转后的偏移值等等。具体注释见下面代码:
using UnityEngine;[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class CameraAndLookAtRotateWithTarget : MonoBehaviour
{[SerializeField] Transform m_target;//需要被观察的点[SerializeField] Transform m_lookedAt;//摄像机lookat的点[SerializeField] bool m_isTargetLeft;//相机看到的,m_target是否在m_lookedAt的左边[SerializeField] float m_defaultCameraAngleX = 180;//相机初始位置相对m_lookedAt.forward的水平角度偏移[SerializeField] float m_defaultCameraAngleY = 5;//相机初始位置相对法线为m_lookedAt.up的平面的角度偏移,锐角[SerializeField] float m_defaultCameraDistance = 8;//相机初始位置相对m_lookedAt的距离
#if UNITY_EDITOR[SerializeField] bool m_isChangeDefaultValue = false;//修改默认值之后设为true来重新设置摄像机初始位置,方便调试
#endif//相机上下的最大最小偏移角度[SerializeField] float m_cameraMaxAngleY = 60;[SerializeField] float m_cameraMinAngleY = 0;//相机距离m_target的最大最小距离[SerializeField] float m_cameraMaxDistance = 20;[SerializeField] float m_cameraMinDistance = 5;[SerializeField] float m_cameraCurrentDistance = 0;//显示当前相机距离m_target的距离,方便调试Transform m_transform;const float ANGLE_CONVERTER = Mathf.PI / 180;//弧度,用于Mathf.Sin,Mathf.Cos的计算//m_target到m_lookedAt的距离,m_target到相机距离float m_targetToLookedAtDistance, m_targetToCameraDistance;//m_lookedAt和相机到m_target的连线,x为相对m_target.forward的角度,y为相对法线为m_target.up的平面的角度float m_lookedAtAngleX, m_lookedAtAngleY, m_cameraAngleX, m_cameraAngleY;///m_lookedAt和相机的位置相对m_target位置的偏移Vector3 m_lookedAtOffset, m_cameraOffset;//是否旋转或拉近拉远了bool m_isChange = false;//距离每次变化的比例float m_changeDistanceRatio = 0.05f;void Awake(){m_transform = transform;}void Start(){SetCameraDefaultPosition();GetInitialData();}//设置摄像机的初始位置void SetCameraDefaultPosition(){Vector3 offset = CalculateOffset(m_defaultCameraDistance, m_defaultCameraAngleX, m_defaultCameraAngleY);m_transform.position = m_lookedAt.position + offset;m_transform.LookAt(m_lookedAt);}void GetInitialData(){//获取距离m_targetToLookedAtDistance = Vector3.Distance(m_lookedAt.position, m_target.position);m_targetToCameraDistance = Vector3.Distance(m_transform.position, m_target.position);//获取角度Vector3 targetToLookedAt = m_lookedAt.position - m_target.position;m_lookedAtAngleX = Vector3.Angle(Vector3.ProjectOnPlane(targetToLookedAt, m_target.up), m_target.forward);m_lookedAtAngleY = Vector3.Angle(Vector3.ProjectOnPlane(targetToLookedAt, m_target.up), targetToLookedAt);//取锐角if ((m_isTargetLeft && targetToLookedAt.x < 0) || (!m_isTargetLeft && targetToLookedAt.x > 0)){m_lookedAtAngleY = 180 - m_lookedAtAngleY;}Vector3 targetToCamera = m_transform.position - m_target.position;m_cameraAngleX = Vector3.Angle(Vector3.ProjectOnPlane(targetToCamera, m_target.up), m_target.forward);m_cameraAngleY = Vector3.Angle(Vector3.ProjectOnPlane(targetToCamera, m_target.up), targetToCamera);if ((m_isTargetLeft && targetToCamera.x < 0) || (!m_isTargetLeft && targetToCamera.x > 0)){m_cameraAngleY = 180 - m_cameraAngleY;}}#if UNITY_EDITORvoid Update(){if (m_isChangeDefaultValue){SetCameraDefaultPosition();m_isChangeDefaultValue = false;}}
#endifvoid LateUpdate(){if (m_isChange){//更新位置CalculateLookedAtAndCameraOffset();m_isChange = false;m_lookedAt.position = m_target.position + m_lookedAtOffset;m_transform.position = m_target.position + m_cameraOffset;m_transform.LookAt(m_lookedAt);}}//根据角度计算偏移相机和m_lookedAt相对m_target的偏移void CalculateLookedAtAndCameraOffset(){m_lookedAtOffset = CalculateOffset(m_targetToLookedAtDistance, m_lookedAtAngleX, m_lookedAtAngleY);m_cameraOffset = CalculateOffset(m_targetToCameraDistance, m_cameraAngleX, m_cameraAngleY);if (!m_isTargetLeft){m_lookedAtOffset.x = -m_lookedAtOffset.x;m_cameraOffset.x = -m_cameraOffset.x;}}//可以想象成在一个球面上运动,根据xy的角度和球的半径,计算球面上的一个点相对球心的偏移//先计算出高度y,然后就是在一个圆面上去计算xzVector3 CalculateOffset(float distance, float x, float y){Vector3 offset;offset.y = distance * Mathf.Sin(y * ANGLE_CONVERTER);float newRadius = distance * Mathf.Cos(y * ANGLE_CONVERTER);offset.x = newRadius * Mathf.Sin(x * ANGLE_CONVERTER);offset.z = newRadius * Mathf.Cos(x * ANGLE_CONVERTER);return offset;}//水平方向同时修改m_lookedAtAngleX和m_cameraAngleX,即m_lookAt和相机一同绕着m_target公转//垂直方向,只修改m_cameraAngleY,即只有相机会有高低变化public void Rotate(Vector2 v){if (v.x != 0){if (m_isTargetLeft){m_lookedAtAngleX -= v.x;m_cameraAngleX -= v.x;}else{m_lookedAtAngleX += v.x;m_cameraAngleX += v.x;}}if (v.y != 0){m_cameraAngleY += v.y;m_cameraAngleY = m_cameraAngleY > m_cameraMaxAngleY ? m_cameraMaxAngleY : m_cameraAngleY;m_cameraAngleY = m_cameraAngleY < m_cameraMinAngleY ? m_cameraMinAngleY : m_cameraAngleY;}m_isChange = true;}//同时缩放m_target到相机和m_target到m_lookedAt的距离public void ChangeDistance(float value){if (value > 0){if (m_targetToCameraDistance < m_cameraMaxDistance){m_targetToCameraDistance += m_targetToCameraDistance * m_changeDistanceRatio;m_targetToLookedAtDistance += m_targetToLookedAtDistance * m_changeDistanceRatio;}}else{if (m_targetToCameraDistance > m_cameraMinDistance){m_targetToCameraDistance -= m_targetToCameraDistance * m_changeDistanceRatio;m_targetToLookedAtDistance -= m_targetToLookedAtDistance * m_changeDistanceRatio;}}m_isChange = true;}
}
然后我们将这个组件挂载在场景的Camera上,同时写个组件来控制UI来调用Camera的旋转和距离缩放
using UnityEngine;
using UnityEngine.EventSystems;public class UIRotate : MonoBehaviour, IBeginDragHandler, IDragHandler
{[SerializeField]CameraAndLookAtRotateWithTarget m_controller;Vector2 m_lastPosition;public void OnBeginDrag(PointerEventData eventData){m_lastPosition = eventData.position;}public void OnDrag(PointerEventData eventData){//屏幕拖动m_controller.Rotate(m_lastPosition - eventData.position);m_lastPosition = eventData.position;}void Update(){//鼠标滚轮if (Input.GetAxis("Mouse ScrollWheel") < 0){m_controller.ChangeDistance(1);}if (Input.GetAxis("Mouse ScrollWheel") > 0){m_controller.ChangeDistance(-1);}}
}
挂载在UIPanel上即可。