摄像机之间的管理、排序、3D坐标与平面坐标之间的转换等
3D坐标转换屏幕坐标 Unity的屏幕坐标系规定左下角是原点、X轴向右为正,Y轴向上为正。
Unity IMGUI坐标系规定左上角是原点,X轴向右为正,Y轴向下为正,如果我们想把转换的屏幕坐标用IMGUI在相同的位置显示出来,就需要用屏幕高度减去鼠标点击屏幕的Y轴坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using UnityEngine;public class D3CoordinateToScreenCoordinate : MonoBehaviour { private Vector2 m_ScreenPotin = Vector3.zero; void Update () { if (Input.GetMouseButtonDown(0 )) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray,out hit)) { m_ScreenPotin = Camera.main.WorldToScreenPoint(hit.point); } } } private void OnGUI () { GUI.color = Color.red; GUI.Label(new Rect(m_ScreenPotin.x, Screen.height - m_ScreenPotin.y, 200 , 40 ), string .Format("鼠标{0}" , m_ScreenPotin)); } }
首先从鼠标点击的位置发射一条射线,使用Physics.Raycast()
得到射线碰到的模型表面的位置hit,然后使用Camera.main.WorldToScreenPoint()
将hit碰撞点的3D世界坐标转换为相对当前摄像机的屏幕坐标。
3D坐标转换UI坐标 UI也有一个3D正交摄像机(详见UIGI,Canvas那一节),3D坐标转UI坐标其实就是先转成屏幕坐标,再由屏幕坐标转成UI相机的3D坐标。
在3D世界中任意移动圆柱体的位置,UI血条会同步跟随移动。
这里UI屏幕坐标转换和Camera屏幕坐标转换是一样的,因为都是用的Camera.main(返回的是有main camera这个tag的摄像机)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using UnityEngine;public class D3CoordinateToUICoordinate : MonoBehaviour { public Transform characterTransform; public RectTransform hpTransform; public Camera UICamera; public RectTransform canvasTransform; private Vector2 rectScreenPoint, cameraScreenPoint; private void Update () { rectScreenPoint = RectTransformUtility.WorldToScreenPoint(Camera.main, characterTransform.position); cameraScreenPoint = Camera.main.WorldToScreenPoint(characterTransform.position); Vector2 localPoint; if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasTransform,rectScreenPoint,UICamera,out localPoint)) { hpTransform.anchoredPosition = localPoint; } } private void OnGUI () { if (GUILayout.Button("显示UI屏幕坐标" )) Debug.Log(rectScreenPoint.ToString()); if (GUILayout.Button("显示Camera屏幕坐标" )) Debug.Log(cameraScreenPoint.ToString()); } }
主摄像机 主摄像机即MainCamera,是带有MainCamera这个Tag的摄像机。使用Camera.main()
直接访问,不过这个tag如果被很多摄像机所使用,那么就并不安全。
在UI上显示模型 UI上是不能直接显示模型的,我们可以添加一个模型层,再创建一个新的摄像机来单独看这个模型层。将模型摄像机的深度设置到最高,那么它将显示在场景和UI的最上面。
Camera_UI的Culling Mask是UI图层,Camera_Model的Culling Mask是Model图层,Main Camera的Culling Mask是除去UI和Model层的Mixed
由于Canvas的ScreenMatchMode是Expand,那么模型需要在不同分辨率下进行缩放。
我们先将模型放在Camera_Model的子级,然后在Camera_Model挂载下面的脚本,在Canvas中准备一个背景Image
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 using UnityEngine;using UnityEngine.UI;[RequireComponent(typeof(Camera)) ] public class ModelCamera : MonoBehaviour { public RectTransform uiArea; public Transform model; [Tooltip("开发分辨率宽度" ) ] public float designWidth = 1366f ; [Tooltip("开发分辨率高度" ) ] public float designHeight = 768f ; private float designScale; void Start () { designScale = designWidth / designHeight; } void Update () { model.transform.position = uiArea.transform.position; float scaleRate = (float )Screen.width / (float )Screen.height; float scaleFactor = scaleRate / designScale; if (scaleRate < designScale) model.localScale = Vector3.one * scaleFactor; else model.localScale = Vector3.one; } }
Render Texture 添加新摄像机来显示模型是有缺陷的。因为摄像机的深度(depth)决定了模型的渲染顺序,它要么显示在UI下面,要么显示在UI上面,如果想叠层显示在两个UI之间,就很麻烦了。我们可以使用Render Texture。它可以将某个摄像机看到的内容渲染到纹理上,最后再将这个纹理显示在UI的RawImage上。
开发中尽可能不要在Project中创建Render Texture文件,因为我们并不知道游戏中到底会用到多少个,Render Texture是可以在内存中创建的,这样本地就不需要保存这个文件了
在Render Texture上显示带特效的模型 粒子特效大都在使用Alpha Blending,它需要和后面的物体来做融合。在Render Texture上,如果粒子特效后面没有东西和他混合,显示就不正确。
我们可以把用在UI上的Sprite图片用在Sprite Render上,作为场景中的对象,就可以在粒子特效的后面当做背景了。特效与背景图混合后,再渲染到Render Texture上,最终在RawImage中也能正常显示
背景图用Sprite Renderer的好处是,它可以和UI混合使用。在代码中,我们可以根据Camera来自动计算背景图的区域,保证它可以挡住特效。
给背景的Sprite Renderer挂这个脚本来自适应模型摄像机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class SpriteFull : MonoBehaviour { private float distance = 3f ; public Camera modleCamera; private SpriteRenderer spriteRenderer; float worldScreenHeight, worldScreenWidth; void Start () { spriteRenderer = GetComponent<SpriteRenderer>(); spriteRenderer.material.renderQueue = 2980 ; } void Update () { Camera camera = modleCamera; camera.transform.rotation = Quaternion.Euler(Vector3.zero); float width = spriteRenderer.sprite.bounds.size.x; float height = spriteRenderer.sprite.bounds.size.y; if (camera.orthographic) { worldScreenHeight = camera.orthographicSize * 2.0f ; worldScreenWidth = worldScreenHeight / Screen.height * Screen.width; } else { worldScreenHeight = 2.0f * distance * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad); worldScreenWidth = worldScreenHeight * camera.aspect; } transform.localPosition = new Vector3(camera.transform.position.x, camera.transform.position.y, distance); transform.localScale = new Vector3(worldScreenWidth / width, worldScreenHeight / height, 0f ); } }
接着,将下列脚本挂载到模型摄像机中来自动创建Render Texture,并将其设置到RawImage上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using UnityEngine;using UnityEngine.UI;[RequireComponent(typeof(Camera)) ] public class ModelCameraCreateRendTex : MonoBehaviour { public RawImage rawImage; void Start () { float rate = 1.5f ; int width =(int )(rawImage.rectTransform.sizeDelta.x * rate); int height =(int )(rawImage.rectTransform.sizeDelta.y * rate); RenderTexture renderTexture = RenderTexture.GetTemporary(width, height, 16 , RenderTextureFormat.ARGB32); GetComponent<Camera>().targetTexture = renderTexture; rawImage.texture = renderTexture; } }
LOD Group Unity - Manual: LOD Group (unity3d.com)
Unity提供了LOD Group,同样的模型可以制作两份,一个是高精度的,一个是低精度的。最后,根据摄像机的远近来动态更换模型,或者剔除部分渲染。
在Edit——Project Settings——Quality中设置
LOD Bias:表示LOD的偏移值。LOD 级别是根据对象在屏幕上的尺寸选择的。当大小介于两个 LOD 级别之间时,选择可能会偏向于两个可用模型中较不详细或更详细的模型。这被设置为从 0 到 +infinity 的分数。当它设置在 0 和 1 之间时,它倾向于较少的细节。大于 1 的设置有利于更多细节。例如,将 LOD Bias 设置为 2 并使其在 50% 距离处发生变化,LOD 实际上仅在 25% 处发生变化。
Maximum LOD Level:表示LOD的最大等级,运行时修改它,可以优化低端机器。
LOD的运行原理是根据物体在摄像机内的百分比来调节显示的级别,首先,会给物体添加平面的包围盒,摄像机发生移动后,即可计算与这个包围盒的百分比。
可以添加任意数量的LOD并且设置每个等级显示或不显示什么。
总体来说,LOD Group技术就是用内存来换时间,预先加载了模型的好几个状态,然后根据摄像机与它的距离来切换不同的状态以保证渲染效果。