搜索
您的当前位置:首页正文

Unity—在应用中显示性能和Debug信息

来源:步旅网

工作中遇到的需求 记录一下实现方法:

UI页面:

unity设置:

一共设置了三个脚本:

FPS和性能管理类:

using System;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;

public class DebugUIManager : MonoBehaviour
{
    [SerializeField] private Text fpsText;//fps
    [SerializeField] private Text heapSizeText;//堆内存
    [SerializeField] private Text usedSizeText;//使用大小
    [SerializeField] private Text allocatedMemoryText;//Unity分配
    [SerializeField] private Text reservedMemoryText;//总内存
    [SerializeField] private Text unusedReservedMemoryText;//未使用内存

    private int _index = 1;
    private int _indexCount = 100;//更新间隔

    private const long Kb = 1024;
    private const long Mb = 1024 * 1024;

    private float updateInterval = 1f;//更新间隔
    private int frames;
    private float fps;
    private float lastInterval;
    private float timeNow;

    private void Start()
    {
        Init();
    }

    private void Init()
    {
        lastInterval = Time.realtimeSinceStartup;
        frames = 0;
        fps = 0.0f;
    }

    private void Update()
    {
        if (!gameObject.activeSelf) return; 

        _index++;
        if (_index == _indexCount)
        {
            ShowProfilerMsg();
        }

        frames++;
        timeNow = Time.realtimeSinceStartup;
        if (timeNow > lastInterval + updateInterval)
        {
            ShowFPSMsg();
        }

    }

    private void ShowProfilerMsg()
    {
        _index = 0;
        //堆内存
        if (heapSizeText)
        {
            heapSizeText.text = "堆内存 : " + Profiler.GetMonoHeapSizeLong() / Mb + " Mb";
        }

        //使用的
        if (usedSizeText)
        {
            usedSizeText.text = "使用大小 : " + Profiler.GetMonoUsedSizeLong() / Mb + " Mb";
        }

        // unity分配
        if (allocatedMemoryText)
        {
            allocatedMemoryText.text = "Unity分配 : " + Profiler.GetTotalAllocatedMemoryLong() / Mb + " Mb";
        }

        // 总内存
        if (reservedMemoryText)
        {
            reservedMemoryText.text = "总内存 : " + Profiler.GetTotalReservedMemoryLong() / Mb + " Mb";
        }

        // 未使用内存
        if (unusedReservedMemoryText)
        {
            unusedReservedMemoryText.text = "未使用内存 : " + Profiler.GetTotalUnusedReservedMemoryLong() / Mb + " Mb";
        }
    }

    /// <summary>
    /// 显示FPS信息
    /// </summary>
    private void ShowFPSMsg()
    {
        if (!fpsText) return;
        fps = frames / (timeNow - lastInterval);
        fpsText.text = fps.ToString("F2");
        frames = 0;
        lastInterval = timeNow;
    }

    /// <summary>
    /// 页面开关
    /// </summary>
    public void ShowAndHide()
    {
        gameObject.SetActive(!gameObject.activeSelf);
    }


}

CPU显示类:

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class CPUMessage : MonoBehaviour
{
    [Header("Components")]

    [SerializeField] private Text cpuCounterText;

    [Header("Settings")]

    [Tooltip("In which interval should the CPU usage be updated?")]
    [SerializeField] private float updateInterval = 1;

    [Tooltip("The amount of physical CPU cores")]
    [SerializeField] private int processorCount;


    [Header("Output")]
    public float CpuUsage;

    private Thread _cpuThread;
    private float _lasCpuUsage;

    private void Start()
    {
        Application.runInBackground = true;

        cpuCounterText.text = "0% CPU";

        // setup the thread
        _cpuThread = new Thread(UpdateCPUUsage)
        {
            IsBackground = true,
            // we don't want that our measurement thread
            // steals performance
            Priority = System.Threading.ThreadPriority.BelowNormal
        };

        // start the cpu usage thread
        _cpuThread.Start();
    }

    private void OnValidate()
    {
        // We want only the physical cores but usually
        // this returns the twice as many virtual core count
        //
        // if this returns a wrong value for you comment this method out
        // and set the value manually
        processorCount = SystemInfo.processorCount / 2;
    }

    private void OnDestroy()
    {
        // Just to be sure kill the thread if this object is destroyed
        _cpuThread?.Abort();
    }

    private void Update()
    {
        // for more efficiency skip if nothing has changed
        if (Mathf.Approximately(_lasCpuUsage, CpuUsage)) return;

        // the first two values will always be "wrong"
        // until _lastCpuTime is initialized correctly
        // so simply ignore values that are out of the possible range
        if (CpuUsage < 0 || CpuUsage > 100) return;

        // I used a float instead of int for the % so use the ToString you like for displaying it
        cpuCounterText.text = CpuUsage.ToString("F1") + "% CPU";

        // Update the value of _lasCpuUsage
        _lasCpuUsage = CpuUsage;
    }

    /// <summary>
    /// Runs in Thread
    /// </summary>
    private void UpdateCPUUsage()
    {
        var lastCpuTime = new TimeSpan(0);

        // This is ok since this is executed in a background thread
        while (true)
        {
            var cpuTime = new TimeSpan(0);

            // Get a list of all running processes in this PC
            var AllProcesses = Process.GetProcesses();

            // Sum up the total processor time of all running processes
            cpuTime = AllProcesses.Aggregate(cpuTime, (current, process) => current + process.TotalProcessorTime);

            // get the difference between the total sum of processor times
            // and the last time we called this
            var newCPUTime = cpuTime - lastCpuTime;

            // update the value of _lastCpuTime
            lastCpuTime = cpuTime;

            // The value we look for is the difference, so the processor time all processes together used
            // since the last time we called this divided by the time we waited
            // Then since the performance was optionally spread equally over all physical CPUs
            // we also divide by the physical CPU count
            CpuUsage = 100f * (float)newCPUTime.TotalSeconds / updateInterval / processorCount;

            // Wait for UpdateInterval
            Thread.Sleep(Mathf.RoundToInt(updateInterval * 1000));
        }
    }
}

最后是Debug的窗口:

Debug窗口整体是一个ScrollView,设置如图

给Content挂上Vertical Layout Group和Content Size Fitter组件,这样Contene会根据内容自动扩容。加一个Scrollbar Vertical是为了后面方便让debug信息自动滚动到底部显示(见代码)。

LogText也挂上Content Size Fitter,让LogText也自动扩容和换行。

 

 Debug管理类:

using UnityEngine;
using UnityEngine.UI;
using System.Text;
using DG.Tweening;

public class DebugMsge : MonoBehaviour
{
    public Text logText;
    public RectTransform content;
    public Scrollbar bar;//竖向滚动条

    private int count = 0;
    StringBuilder MyStrBulder;
    private bool isUpdate = false;


    public void AddText(string str)
    {
        isUpdate = true;
        MyStrBulder.AppendFormat("{0}:{1}\n", count, str);
        count++;
        isUpdate = false;
    }

    void Awake()
    {
        MyStrBulder = new StringBuilder();

        //核心方法就是Application.logMessageReceived这个事件
        Application.logMessageReceived += HandleLog;
    }

    void HandleLog(string message, string stackTrace, LogType type)
    {
        switch (type)
        {
            case LogType.Error:
                message = "<color=#FF0000>" + message + "</color>";
                break;
            case LogType.Assert:
                message = "<color=#0000ff>" + message + "</color>";
                break;
            case LogType.Warning:
                message = "<color=#EEEE00>" + message + "</color>";
                break;
            case LogType.Log:
                message = "<color=#000000>" + message + "</color>";
                break;
            case LogType.Exception:
                break;
            default:
                break;
        }

        AddText(message);
        onSkipToBottomShow();
    }

    // Update is called once per frame
    void Update()
    {
        logText.text = MyStrBulder.ToString();
    }

    /// <summary>
    /// 跳转至底部显示(使竖向滚动条的value保持为0即可)
    /// </summary>
    private void onSkipToBottomShow()
    {
        DOTween.To(() => bar.value = 0, v => bar.value = v, 0, 0.1f).SetEase(Ease.InElastic);
    }

}

因篇幅问题不能全部显示,请点此查看更多更全内容

Top