孙广东 2017.3.27
http://blog.csdn.NET/u010019717
翻译自:http://www.theappguruz.com/blog/create-and-optimize-on-head-health-bars-in-unity
如果你想要学习如何优化你的游戏,你可以查阅这篇博客︰
理想的例子使用Head Health Bars的地方就是帝国时代(如下图所示的屏幕)。
会出现的问题就是 FPS 很低!!!!!
很多人的做法可能是在 每个角色上添加 world space canvas 这个UGUI !
让我们理解这一些案例,然后最后讨论出一个理想的解决方案。
案例测试 1
让我们生成 100 个角色。(在我的例子中使用球体)
创建角色与world canvas作为角色的子对象(像下图一样设置Canvas 和 Image)
下面的图像显示相同︰
在这里有一个 PlayerGenerate.cs 脚本,生成的100个角色的代码
using UnityEngine; using System.Collections; public class PlayerGenerator : MonoBehaviour { public GameObject playerPrefab; public GameObject healthBarPrefab; public Transform playersParent; public RectTransform healthPanelRect; void Start() { GeneratePlayers(); } private void GeneratePlayers() { Vector3 position = new Vector3(3.75f,6.5f,5.0f); for (int i = 0; i < 10; i++) { position -= new Vector3(0,0.6f,0.90f); for (int j = 0; j < 10; j++) { GeneratePlayerAt(position); position -= new Vector3(0.85f,0,0); } position = new Vector3(3.75f,position.y,position.z); } } private void GeneratePlayerAt(Vector3 position) { GameObject player = Instantiate(playerPrefab, position, Quaternion.identity) as GameObject; player.transform.parent = playersParent; } }
此外让每个玩家运动起来, 把以下脚本附加到角色上。
PlayerMovement.cs
using UnityEngine; using System.Collections; public class PlayerMovement : MonoBehaviour { public float speed=5; public Vector3 direction=new Vector3(0,1,0); // Update is called once per frame void Update () { transform.Translate(direction * speed * Time.deltaTime); } }我们要看场景当前的FPS , 工具脚本如下:
同时为脚本赋值(在编辑器中)
HUDFPS.cs
using UnityEngine; using System.Collections; using UnityEngine.UI; public class HUDFPS : MonoBehaviour { // Attach this to a GUIText to make a frames/second indicator. // // It calculates frames/second over each updateInterval, // so the display doesz not keep changing wildly. // // It is also fairly accurate at very low FPS counts (<10). // We do this not by simply counting frames per interval, but // by accumulating FPS for each frame. This way we end up with // correct overall FPS even if the interval renders something like // 5.5 frames. public float updateInterval = 0.5F; public Text t; private float accum = 0; // FPS accumulated over the interval private int frames = 0; // Frames drawn over the interval private float timeleft; // Left time for current interval void Start() { if (!GetComponent<Text>()) { Debug.Log("UtilityFramesPerSecond needs a GUIText component!"); enabled = false; return; } timeleft = updateInterval; t = GetComponent<Text>(); } void Update() { timeleft -= Time.deltaTime; accum += Time.timeScale / Time.deltaTime; ++frames; // Interval ended - update GUI text and start new interval if (timeleft <= 0.0) { // display two fractional digits (f2 format) float fps = accum / frames; string format = System.String.Format("{0:F2} FPS", fps); t.text = format; if (fps < 30) t.color = Color.yellow; else if (fps < 10) t.color = Color.red; else t.color = Color.green; //DebugConsole.Log(format, level); timeleft = updateInterval; accum = 0.0F; frames = 0; } } }
我的手机显示 fps 约 30 FPS。
让我们看一看探查器
Canvas.SendWillRenderCanvases()占用 14 毫秒,
Canvas.builtBatch 需要 6.76ms,
Canas.Render 需要 5.88ms。
如何使用single overlay canvas?
计算出的位置,只需将health bar图像放在每个角色?
案例测试 2
1) 创建一个 health bar 预制体 内容就是一个简单的图像。
2) 现在创建 HealthBar.cs 脚本,如下所示,并将其附加到刚刚的预制体上:
using UnityEngine; using System.Collections; using UnityEngine.UI; public class HealthBar : MonoBehaviour { #region PRIVATE_VARIABLES private Vector2 positionCorrection = new Vector2(0, 100); #endregion #region PUBLIC_REFERENCES public RectTransform targetCanvas; public RectTransform healthBar; public Transform objectToFollow; #endregion #region PUBLIC_METHODS public void SetHealthBarData(Transform targetTransform,RectTransform healthBarPanel) { this.targetCanvas = healthBarPanel; healthBar = GetComponent<RectTransform>(); objectToFollow = targetTransform; RepositionHealthBar(); healthBar.gameObject.SetActive(true); } public void OnHealthChanged(float healthFill) { healthBar.GetComponent<Image>().fillAmount = healthFill; } #endregion #region UNITY_CALLBACKS void Update() { RepositionHealthBar(); } #endregion #region PRIVATE_METHODS private void RepositionHealthBar() { Vector2 ViewportPosition = Camera.main.WorldToViewportPoint(objectToFollow.position); Vector2 WorldObject_ScreenPosition = new Vector2( ((ViewportPosition.x * targetCanvas.sizeDelta.x) - (targetCanvas.sizeDelta.x * 0.5f)), ((ViewportPosition.y * targetCanvas.sizeDelta.y) - (targetCanvas.sizeDelta.y * 0.5f))); //now you can set the position of the ui element healthBar.anchoredPosition = WorldObject_ScreenPosition; } #endregion }
3) 修改以前的PlayerGenerator.cs脚本,如下所示。
using UnityEngine; using System.Collections; public class PlayerGenerator : MonoBehaviour { public GameObject playerPrefab; public GameObject healthBarPrefab; public Transform playersParent; public RectTransform healthPanelRect; void Start() { GeneratePlayers(); } private void GeneratePlayers() { Vector3 position = new Vector3(3.75f,6.5f,5.0f); for (int i = 0; i < 10; i++) { position -= new Vector3(0,0.6f,0.90f); for (int j = 0; j < 10; j++) { GeneratePlayerAt(position); position -= new Vector3(0.85f,0,0); } position = new Vector3(3.75f,position.y,position.z); } } private void GeneratePlayerAt(Vector3 position) { GameObject player = Instantiate(playerPrefab, position, Quaternion.identity) as GameObject; player.transform.parent = playersParent; } private void GeneratePlayerHealthBar(Transform player) { GameObject healthBar = Instantiate(healthBarPrefab) as GameObject; healthBar.GetComponent<HealthBar>().SetHealthBarData(player, healthPanelRect); healthBar.transform.SetParent(healthPanelRect, false); } }
每创建一个角色,同时也创建一个 health bar 然后位置放到头顶位置!
现在,让我们在移动设备上测试它。
提升了 20 fps !!!!!
让我们现在检查探查器。