首页 > 代码库 > Unity3d学习 相机的跟随
Unity3d学习 相机的跟随
最近在写关于相机跟随的逻辑,其实最早接触相机跟随是在Unity官网的一个叫Roll-a-ball tutorial上,其中简单的涉及了关于相机如何跟随物体的移动而移动,如下代码:
1 using UnityEngine; 2 using System.Collections; 3 4 public class CameraController : MonoBehaviour { 5 6 public GameObject player; 7 8 private Vector3 offset; 9 10 void Start () 11 { 12 offset = transform.position - player.transform.position; 13 } 14 15 void LateUpdate () 16 { 17 transform.position = player.transform.position + offset; 18 } 19 }
可以很容易的理解上述的代码: 在初始化时计算与对应物体的向量差值,然后在LateUpdate中对相机位置进行及时更新,至于为什么要放在LateUpdate,因为LateUpdate是等所有脚本的Update跑完之后
在更新自己的逻辑,这样相机得到物体的位置往往是最新的 。具体可以看 Unity关于脚本生命周期 中有提到。
上述的代码 , 相机是实时跟踪的, 其实相机的跟踪可以变的跟平滑一点,可以利用Unity中的Mathf.Lerp,在每一帧做一个线性的差值,这样的话可以使相机跟随变的更平滑一点,如下优化的代码:
1 using UnityEngine; 2 using System.Collections; 3 4 public class FollowBehavior : MonoBehaviour { 5 6 public Transform trackingTarget; 7 public float offsetX = 5.0f; 8 public float offsetY = 4.0f; 9 public float followSpeed = 1.0f; 10 11 public bool isXLocked = false; 12 public bool isYLocked = false; 13 // Use this for initialization 14 void Start () { 15 //m_offset = transform.position - trackingTarget.position; 16 } 17 18 // Update is called once per frame 19 void LateUpdate () { 20 //transform.position = trackingTarget.position + m_offset; 21 float newX = transform.position.x; 22 float targetX = trackingTarget.position.x + offsetX; 23 if (!isXLocked) 24 { 25 newX = Mathf.Lerp(newX, targetX, Time.deltaTime * followSpeed); 26 } 27 float newY = transform.position.y; 28 float targetY = trackingTarget.position.y + offsetY; 29 if (!isYLocked) 30 { 31 newY = Mathf.Lerp(newY, targetY, Time.deltaTime * followSpeed); 32 } 33 transform.position = new Vector3(newX , newY , transform.position.z); 34 } 35 }
上述代码能满足大多数情况,但是如果一个场景里有多个焦点呢? 比如现在要满足的业务条件是:
- 当鼠标对屏幕进行拖拽时,需要移动相机
- 当有多个焦点时,如何更好的切换
我们先来实现第一个需求,先讲讲现在具备哪些条件:
- Input.GetMouseButtonDown(0) : 这个表示在某一帧按下鼠标左键,会返回true,如果你一直按着不放(返回的是false),直到你松开再按下(才会再次返回true) 可以参考文档
- Input.GetMouseButton(0): 这个表示当前是鼠标左键按下,会返回true 可以参考文档
通过上述接口,我们可以实现拖拽了,思路的话就不细说,看代码就行:
1 void DragCamera() 2 { 3 Vector3 nowMousePos = Input.mousePosition; 4 Vector3 move = nowMousePos - m_originDragPos; 5 move = Camera.main.ScreenToViewportPoint(move) * DragSpeed * -1; 6 //平移没有差值运算 7 transform.Translate(move); 8 float x = Mathf.Clamp(transform.position.x, minXAndY.x, maxXAndY.x); 9 float y = Mathf.Clamp(transform.position.y, minXAndY.y, maxXAndY.y); 10 Vector3 pos = new Vector3(x , y , transform.position.z); 11 transform.position = pos; 12 m_originDragPos = nowMousePos; 13 } 14 15 // Update is called once per frame 16 void Update() 17 { 18 int mouse = (int)MouseType.LEFT; 19 //记录某一帧时按下的状态(之后的持续按下都返回false,知道下次释放在按下返回true) 20 if (Input.GetMouseButtonDown(mouse)) 21 { 22 m_bIsDrag = true; 23 //屏幕坐标系 24 m_originDragPos = Input.mousePosition; 25 return; 26 } 27 //表示当前的释放 28 if (!Input.GetMouseButton(mouse)) 29 { 30 m_bIsDrag = false; 31 return; 32 } 33 } 34 35 void LateUpdate() 36 { 37 if (m_bIsDrag) 38 { 39 DragCamera(); 40 } 41 }
这边提一下在DragCamera函数中如果OriginDragPos不及时更新,屏幕在鼠标移动时会一直移动,因为在计算是产生的move向量一直有值,所以会不断偏移,这边看需求吧。
上述的代码已经可以实现相机的拖拽了,但是如果你的屏幕上有UI结构,按下UI结构时,点击UI结构 ,其实也会调用 Input.GetMouseButtonDown(0),就会调用拖拽函数,但是
往往这种情况下,是不需要将m_bIsDrag设为true,所以如何优化屏蔽呢? 看如下代码:
1 // Update is called once per frame 2 void Update() 3 { 4 int mouse = (int)MouseType.LEFT; 5 //记录某一帧时按下的状态(之后的持续按下都返回false,知道下次释放在按下返回true) 6 if (Input.GetMouseButtonDown(mouse)) 7 { 8 //不能是UI层 9 PointerEventData pointerData = http://www.mamicode.com/new PointerEventData(EventSystem.current); 10 pointerData.position = Input.mousePosition; 11 List<RaycastResult> results = new List<RaycastResult>(); 12 EventSystem.current.RaycastAll(pointerData, results); 13 14 if (results.Count > 0) 15 { 16 if (results[0].gameObject.layer == LayerMask.NameToLayer("UI")) 17 { 18 return; 19 } 20 } 21 m_bIsDrag = true; 22 //屏幕坐标系 23 m_originDragPos = Input.mousePosition; 24 return; 25 } 26 //表示当前的释放 27 if (!Input.GetMouseButton(mouse)) 28 { 29 m_bIsDrag = false; 30 return; 31 } 32 }
这边要提一下关于Unity5.X中GUI的事件系统 确定事件产生到接收 流程是 输入模块产生事件数据 PointerEventData ,通过投影模块(射线)确定具体UI , 最终到具体UI来接收数据,
由于这不是本篇的重点,可以看一下 关于事件系统的博文 我们这里模仿了前两步骤,确定当前鼠标输入的点是否UI有就直接return.
关于焦点确定,其实算是优化项吧 ,我这边采样的是委托/事件方式来发送对应的Tranform,当然也可以直接接口。
Unity3d学习 相机的跟随