主頁 >  其他 > 【游戲開發進階】新發帶你玩轉Unity日志列印技巧(彩色日志 | 日志存盤與上傳 | 日志開關 | 日志雙擊溯源)

【游戲開發進階】新發帶你玩轉Unity日志列印技巧(彩色日志 | 日志存盤與上傳 | 日志開關 | 日志雙擊溯源)

2021-08-03 08:32:38 其他

請添加圖片描述

文章目錄

      • 一、前言
      • 二、常規日志列印
        • 1、列印Hello World
        • 2、列印任意型別的資料
        • 3、context引數干嘛的
        • 4、格式化輸出
      • 三、彩色日志列印
      • 四、日志存盤與上傳
        • 1、列印日志事件
        • 2、寫日志到本地檔案
        • 3、日志上傳到服務器
      • 五、日志開關
      • 六、日志雙擊溯源問題
        • 方法一、GameLogger編譯為dll
        • 方法二、反射攔截,自主跳轉
      • 七、專案實戰
        • 1、界面制作
        • 2、C#代碼
          • 2.1、GameLogger.cs代碼
          • 2.2、LogUploader.cs代碼
          • 2.3 Main.cs代碼
        • 3、掛Main腳本
        • 4、Web服務器
          • 4.1、小皮,Web服務器
          • 4.2、PHP腳本:upload_log.php
        • 5、運行測驗
        • 6、工程原始碼
      • 八、完畢

一、前言

嗨,大家好,我是新發,
有鐵粉私信我問我能否寫一篇Unity日志列印相關的文章,
在這里插入圖片描述
今天,我就來好好講講~

二、常規日志列印

1、列印Hello World

相信很多剛學Unity的同學最早寫的一句代碼就是

Debug.Log("Hello World");

我們可以在Console視窗中看到輸出的日志,視窗下方是對應的呼叫堆疊,

注:呼叫堆疊可以很好的幫助我們定位問題,特別是報錯的Error日志,

在這里插入圖片描述

我們還可以輸出警告、錯誤日志,例:

Debug.Log("This is a log message.");
Debug.LogWarning("This is a warning message!");
Debug.LogError("This is an error message!");

如下:
在這里插入圖片描述

注:這里特別說一下,Console視窗有個Error Pause按鈕,意思是如果輸出了Error日志,則暫停運行,有時候策劃會跑過來說他的Unity運行游戲的時候突然卡死了,感覺就像發現了什么驚天大BUG,其實是他點了Error Pause,然后游戲中輸出了一句Error日志,
在這里插入圖片描述

2、列印任意型別的資料

事實上,Debug.Log的引數是object(即System.Object),

// Debug.cs

public static void Log(object message);

我們知道,在C#里面,所有型別都是繼承System.Object的,也就是說,我們可以傳任意型別的引數給Debug.Log

現在,我來考考你,下面這行代碼會不會報錯?

Debug.Log(null);

答案是不會報錯,它會輸出Null

現在,我們自定義一個類,比如:

public class TestClass
{
    public int a;
    public string b;
    public bool c;

    public TestClass(int a, string b, bool c)
    {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}

執行下面的代碼,它會輸出什么呢?

Debug.Log(new TestClass(1, "HaHa", true));

答案是:

TestClass

事實上,它是先執行了物件的ToString()方法,然后再輸出日志的,我們override(重寫)類的ToString()方法,就可以自定義輸出啦,比如:

public class TestClass
{
    public int a;
    public string b;
    public bool c;

    public TestClass(int a, string b, bool c)
    {
        this.a = a;
        this.b = b;
        this.c = c;
    }
    
	// 重寫ToString方法
    public override string ToString()
    {
        return string.Format("a:{0}\nb:{1}\nc:{2}", a, b, c);
    }
}

再執行下面這行代碼,

Debug.Log(new TestClass(1, "HaHa", true));

它輸出的就是

a:1
b:HaHa
c:True

3、context引數干嘛的

如果你看Debug類的原始碼,就會發現,它有一個接收兩個引數的Debug.Log方法,

// Debug.cs

public static void Log(object message, Object context);

這第二個引數context是干嘛用的呢?
我們來做下實驗,

GameObject go = new GameObject("go");
Debug.Log("Test", go);

效果如下,發現沒有,它可以幫我們定位到物體的實體,
請添加圖片描述
如果你的物體是一個還沒實體化的預設的參考,則它會直接定位到Project視圖中的資源,我們來做下實驗,
NewBehaviourScript.cs腳本代碼如下:

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public GameObject cubePrefab;

    void Start()
    {
        Debug.Log("Test", cubePrefab);
    }
}

掛到Main Camera上,把Cube.prefab預設拖給腳本的cubePrefab成員,如下:
在這里插入圖片描述
運行,測驗效果如下:
請添加圖片描述

4、格式化輸出

有時候我們要進行格式化輸出,可以使用string.Format進行格式化后再呼叫Debug.Log,例:

int a = 100;
float b = 0.6f;
Debug.Log(string.Format("a is: {0}, b is {1}", a, b));

你也可以直接使用Debug.LogFormat,例:

int a = 100;
float b = 0.6f;
Debug.LogFormat("a is: {0}, b is {1}", a, b);

三、彩色日志列印

我們上面列印出來的日志都是默認的顏色(白色),事實上,我們可以列印出彩色的日志,
格式<color=#rbg顏色值>xxx</color>,例:

Debug.LogFormat("This is <color=#ff0000>{0}</color>", "red");
Debug.LogFormat("This is <color=#00ff00>{0}</color>", "green");
Debug.LogFormat("This is <color=#0000ff>{0}</color>", "blue");
Debug.LogFormat("This is <color=yellow>{0}</color>", "yellow");

效果如下:
在這里插入圖片描述

注:建議約定好少數幾個顏色即可,不然五顏六色的看的眼花@_@

四、日志存盤與上傳

實際專案中,我們一般是需要把日志寫成檔案,方便出錯時通過日志來定位問題,Unity提供了一個事件:Application.logMessageReceived,方便我們來監聽日志列印,這樣我們就可以把日志的文本內容寫到檔案里存起來啦~

1、列印日志事件

我們一般在游戲啟動的入口腳本的Awake函式中去監聽Application.logMessageReceived事件,如下:

// 游戲啟動的入口腳本

void Awake()
{
	// 監聽日志回呼
    Application.logMessageReceived += OnLogCallBack;
}

/// <summary>
/// 列印日志回呼
/// </summary>
/// <param name="condition">日志文本</param>
/// <param name="stackTrace">呼叫堆疊</param>
/// <param name="type">日志型別</param>
private void OnLogCallBack(string condition, string stackTrace, LogType type)
{ 
    // TODO 寫日志到本地檔案
}

2、寫日志到本地檔案

Unity提供了一個可讀寫的路徑給我們訪問:Application.persistentDataPath,我們可以把日志檔案存到這個路徑下,

注:Application.persistentDataPath在不同平臺下的路徑:
WindowsC:/Users/用戶名/AppData/LocalLow/CompanyName/ProductName
AndroidAndroid/data/包名/files
Mac/Users/用戶名/Library/Caches/CompanyName/ProductName
iOS/var/mobile/Containers/Data/Application/APP名稱/Documents
需要注意,iOS的需要越獄并且使用Filza軟體才能查看檔案路徑哦

在這里插入圖片描述

例:

using System.IO;
using System.Text;
using UnityEngine;

public class Main: MonoBehaviour
{
	// 使用StringBuilder來優化字串的重復構造
    StringBuilder m_logStr = new StringBuilder();
    // 日志檔案存盤位置
    string m_logFileSavePath;

    void Awake()
    {
    	// 當前時間
        var t = System.DateTime.Now.ToString("yyyyMMddhhmmss");
        m_logFileSavePath = string.Format("{0}/output_{1}.log", Application.persistentDataPath, t);
        Debug.Log(m_logFileSavePath);
        Application.logMessageReceived += OnLogCallBack;
        Debug.Log("日志存盤測驗");
    }

    /// <summary>
    /// 列印日志回呼
    /// </summary>
    /// <param name="condition">日志文本</param>
    /// <param name="stackTrace">呼叫堆疊</param>
    /// <param name="type">日志型別</param>
    private void OnLogCallBack(string condition, string stackTrace, LogType type)
    {
        m_logStr.Append(condition);
        m_logStr.Append("\n");
        m_logStr.Append(stackTrace);
        m_logStr.Append("\n");

        if (m_logStr.Length <= 0) return;
        if (!File.Exists(m_logFileSavePath))
        {
            var fs = File.Create(m_logFileSavePath);
            fs.Close();
        }
        using (var sw = File.AppendText(m_logFileSavePath))
        {
            sw.WriteLine(m_logStr.ToString());
        }
        m_logStr.Remove(0, m_logStr.Length);
    }
}

我們可以在Application.persistentDataPath路徑下看到日志檔案,
在這里插入圖片描述

3、日志上傳到服務器

實際專案中,我們可能需要把日志上傳到服務端,方便進行查詢定位,
上傳檔案我們可以使用UnityWebRequest來處理,這里需要注意,我們的日志檔案可能很小也可能很大,正常情況下都比較小,但是有時候報錯了是會回圈列印日志的,導致日志檔案特別大,所以我們要考慮到大檔案讀取的情況,否則讀取日志檔案時會很卡,建議使用位元組流讀取,
在這里插入圖片描述

例:

// 讀取日志檔案的位元組流
byte[] ReadLogFile()
{
	byte[] data = null;
	
	using(FileStream fs = File.OpenRead("你的日志檔案路徑")) 
	{
		int index = 0;
		long len = fs.Length;
		data = new byte[len];
		// 根據你的需求進行限流讀取
		int offset = data.Length > 1024 ? 1024 : data.Length;
		while (index < len) 
		{
			int readByteCnt = fs.Read(data, index, offset);
			index += readByteCnt;
			long leftByteCnt = len - index;
			offset = leftByteCnt > offset ? offset : (int)leftByteCnt;
		}
		Debug.Log ("讀取完畢");
	}
	return data;
}

// 將日志位元組流上傳到web服務器
IEnumerator HttpPost(string url, byte[] data)
{
	WWWForm form = new WWWForm();
	// 塞入描述欄位,欄位名與服務端約定好
	form.AddField("desc", "test upload log file");
	// 塞入日志位元組流欄位,欄位名與服務端約定好
	form.AddBinaryData("logfile", data, "test_log.txt", "application/x-gzip");
	// 使用UnityWebRequest
	UnityWebRequest request = UnityWebRequest.Post(url, form);
	var result = request.SendWebRequest();
    while (!result.isDone)
    {
        yield return null;
        //Debug.Log ("上傳進度: " + request.uploadProgress);
    }
    if (!string.IsNullOrEmpty(request.error))
    {
        GameLogger.LogError(request.error);
    }
    else
    {
        GameLogger.Log("日志上傳完畢, 服務器回傳資訊: " + request.downloadHandler.text);
    }
    request.Dispose();
}

呼叫:

byte[] data = ReadLogFile();
StartCoroutine(HttpPost("http://你的web服務器", data));

五、日志開關

實際專案中,我們可能需要做日志開關,比如開發階段日志開啟,正式發布后則關閉日志,
Unity并沒有給我們提供一個日志開關的功能,那我們就自己封裝一下吧~
例:

using UnityEngine;

public class GameLogger 
{
    // 普通除錯日志開關
    public static bool s_debugLogEnable = true;
    // 警告日志開關
    public static bool s_warningLogEnable = true;
    // 錯誤日志開關
    public static bool s_errorLogEnable = true;

    public static void Log(object message, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(message, context);
    }

    public static void LogWarning(object message, Object context = null)
    {
        if (!s_warningLogEnable) return;
        Debug.LogWarning(message, context);
    }

    public static void LogError(object message, Object context)
    {
        if (!s_errorLogEnable) return;
        Debug.LogError(message, context);
    }
}

我們所有使用Debug列印日志的地方,都是改用GameLogger來列印,這樣就可以統一通過GameLogger來開關日志的列印了~

不過,這里會有一個問題,就是我們在Console日志視窗雙擊日志的時候,它只會跳轉到GameLogger里,而不是跳轉到我們呼叫GameLogger的地方,
比如我們在Test腳本中呼叫GameLogger.Log,如下:

// Test.cs 

using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        GameLogger.Log("哈哈哈哈哈");
    }
}

看,它是跳到GameLogger里,而不是跳到我們的Test腳本了,這個是顯然的,但我們能不能讓它跳到Test腳本里呢?
請添加圖片描述
下面我就來給大家表演一下!請往下看~

六、日志雙擊溯源問題

要解決上面的問題,有兩種方法:
方法一:把GameLogger編譯成dll放在工程中,把原始碼刪掉;
方法二:通過反射分析日志視窗的堆疊,攔截Unity日志視窗的打開檔案事件,跳轉到我們指定的代碼行處,
下面我來說下具體操作,

方法一、GameLogger編譯為dll

事實上,我們寫的C#代碼都會被Unity編譯成dll放在工程目錄的Library/ScriptAssemblies目錄中,默認是Assembly-CSharp.dll
在這里插入圖片描述
我們可以使用ILSpy.exe反編譯一下它,

注:ILSpy反編譯工具可以從GitHub下載:https://github.com/icsharpcode/ILSpy

在這里插入圖片描述
不過我們看到,這個dll包含了其他的C#代碼,我們能不能專門只為GameLogger生成一個dll呢?可以滴,只需要把GameLogger.cs單獨放在一個子目錄中,并創建一個Assembly Definition,如下,
在這里插入圖片描述
Assembly Definition重命名為GameLogger,如下,
在這里插入圖片描述
我們再回到Library/ScriptAssemblies目錄中,就可以看到生成了一個GameLogger.dll啦,
在這里插入圖片描述

把它剪切到Unity工程的Plugins目錄中,把我們的GameLogger.cs腳本刪掉或是移動到工程外備份(Assembly Definition檔案也刪掉),如下:
在這里插入圖片描述
這樣,就大功告成了,我們測驗一下日志雙擊溯源,如下,可以看到,現在可以正常跳轉到我們想要的地方了,
請添加圖片描述

方法二、反射攔截,自主跳轉

上面我們是把GameLogger.cs編譯成dll然后放回工程中,這種方法比較簡單,下面這個方法稍微比較有難度,看不懂的同學不要緊,就當做漲知識吧~

我們上面看到,在Console日志視窗雙擊日志可以跳到對應的代碼行處,這個邏輯是Unity編輯器幫我們做的,我們能不能找到Console日志視窗本身的代碼,看看它究竟是怎么做的呢?

事實上,我們的Unity編輯器的各個視圖視窗,其實也是使用C#寫出來的,我們可以在Unity引擎的安裝路徑的Editor/Data/Managed目錄中找到UnityEditor.dll
在這里插入圖片描述
里面就包含了我們Unity編輯器的各個視圖的代碼,我們可以使用ILSpy.exe反編譯它,找到ConsoleWindow類,如下,
在這里插入圖片描述
很快,我就找到了當前激活狀態下的日志的代碼,
在這里插入圖片描述
也就是說,我只要拿到這個m_ActiveText的值就可以知道你雙擊的日志是什么了,
封裝一下介面:

#if UNITY_EDITOR
/// <summary>
/// 獲取當前日志視窗選中的日志的堆疊資訊
/// </summary>
/// <returns></returns>
static string GetStackTrace()
{
    // 通過反射獲取ConsoleWindow類
    var ConsoleWindowType = typeof(UnityEditor.EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
    // 獲取視窗實體
    var fieldInfo = ConsoleWindowType.GetField("ms_ConsoleWindow", 
        System.Reflection.BindingFlags.Static | 
        System.Reflection.BindingFlags.NonPublic);
    var consoleInstance = fieldInfo.GetValue(null);
    if (consoleInstance != null)
    {
        if ((object)UnityEditor.EditorWindow.focusedWindow == consoleInstance)
        {
            // 獲取m_ActiveText成員
            fieldInfo = ConsoleWindowType.GetField("m_ActiveText", 
                System.Reflection.BindingFlags.Instance | 
                System.Reflection.BindingFlags.NonPublic);
            // 獲取m_ActiveText的值
            string activeText = fieldInfo.GetValue(consoleInstance).ToString();
            return activeText;
        }
    }
    return null;
}
#endif

那么接下來就是我怎么攔截打開Unity打開代碼的事件并重新制定跳轉的位置呢?
Unity提供了一個OnOpenAssetAttribute回呼,
詳細可以參見Unity官方手冊:https://docs.unity3d.com/cn/2020.2/ScriptReference/Callbacks.OnOpenAssetAttribute.html
在這里插入圖片描述
這樣我們就可以通過它來攔截打開代碼的事件了,(return true表示攔截,return false則不攔截)

#if UNITY_EDITOR
[UnityEditor.Callbacks.OnOpenAssetAttribute(0)]
static bool OnOpenAsset(int instanceID, int line)
{
	// TODO 根據日志堆疊分析要打開的代碼,return true;
	
	return false;
}
#endif

現在關鍵是如何跳轉到具體代碼的某一行,我找到了這個介面,
在這里插入圖片描述
萬事俱備,封裝一下介面:

#if UNITY_EDITOR
[UnityEditor.Callbacks.OnOpenAssetAttribute(0)]
static bool OnOpenAsset(int instanceID, int line)
{
    string stackTrace = GetStackTrace();
    if (!string.IsNullOrEmpty(stackTrace) && stackTrace.Contains("GameLogger:Log"))
    {
    	// 使用正則運算式匹配at的哪個腳本的哪一行
        var matches = System.Text.RegularExpressions.Regex.Match(stackTrace, @"\(at (.+)\)", 
            System.Text.RegularExpressions.RegexOptions.IgnoreCase);
        string pathLine = "";
        while (matches.Success)
        {
            pathLine = matches.Groups[1].Value;

            if (!pathLine.Contains("GameLogger.cs"))
            {
                int splitIndex = pathLine.LastIndexOf(":");
                // 腳本路徑
                string path = pathLine.Substring(0, splitIndex);
                // 行號
                line = System.Convert.ToInt32(pathLine.Substring(splitIndex + 1));
                string fullPath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets"));
                fullPath = fullPath + path;
                // 跳轉到目標代碼的特定行
                UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(fullPath.Replace('/', '\\'), line);
                break;
            }
            matches = matches.NextMatch();
        }
        return true;
    }
    return false;
}
#endif

整合一下,最終GameLogger.cs代碼如下:

using UnityEngine;

public class GameLogger
{
    // 普通除錯日志開關
    public static bool s_debugLogEnable = true;
    // 警告日志開關
    public static bool s_warningLogEnable = true;
    // 錯誤日志開關
    public static bool s_errorLogEnable = true;

    public static void Log(object message, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(message, context);
    }

    public static void LogWarning(object message, Object context = null)
    {
        if (!s_warningLogEnable) return;
        Debug.LogWarning(message, context);
    }

    public static void LogError(object message, Object context)
    {
        if (!s_warningLogEnable) return;
        Debug.LogError(message, context);
    }



#if UNITY_EDITOR
    [UnityEditor.Callbacks.OnOpenAssetAttribute(0)]
    static bool OnOpenAsset(int instanceID, int line)
    {
        string stackTrace = GetStackTrace();
        if (!string.IsNullOrEmpty(stackTrace) && stackTrace.Contains("GameLogger:Log"))
        {
            // 使用正則運算式匹配at的哪個腳本的哪一行
            var matches = System.Text.RegularExpressions.Regex.Match(stackTrace, @"\(at (.+)\)",
                System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            string pathLine = "";
            while (matches.Success)
            {
                pathLine = matches.Groups[1].Value;

                if (!pathLine.Contains("GameLogger.cs"))
                {
                    int splitIndex = pathLine.LastIndexOf(":");
                    // 腳本路徑
                    string path = pathLine.Substring(0, splitIndex);
                    // 行號
                    line = System.Convert.ToInt32(pathLine.Substring(splitIndex + 1));
                    string fullPath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets"));
                    fullPath = fullPath + path;
                    // 跳轉到目標代碼的特定行
                    UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(fullPath.Replace('/', '\\'), line);
                    break;
                }
                matches = matches.NextMatch();
            }
            return true;
        }
        return false;
    }

    /// <summary>
    /// 獲取當前日志視窗選中的日志的堆疊資訊
    /// </summary>
    /// <returns></returns>
    static string GetStackTrace()
    {
        // 通過反射獲取ConsoleWindow類
        var ConsoleWindowType = typeof(UnityEditor.EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
        // 獲取視窗實體
        var fieldInfo = ConsoleWindowType.GetField("ms_ConsoleWindow",
            System.Reflection.BindingFlags.Static |
            System.Reflection.BindingFlags.NonPublic);
        var consoleInstance = fieldInfo.GetValue(null);
        if (consoleInstance != null)
        {
            if ((object)UnityEditor.EditorWindow.focusedWindow == consoleInstance)
            {
                // 獲取m_ActiveText成員
                fieldInfo = ConsoleWindowType.GetField("m_ActiveText",
                    System.Reflection.BindingFlags.Instance |
                    System.Reflection.BindingFlags.NonPublic);
                // 獲取m_ActiveText的值
                string activeText = fieldInfo.GetValue(consoleInstance).ToString();
                return activeText;
            }
        }
        return null;
    }
#endif
}

我們測驗一下日志雙擊溯源,如下,可以看到,現在可以正常跳轉到我們想要的地方了,
請添加圖片描述

七、專案實戰

寫到這里了,那就順手做個實戰專案吧~

1、界面制作

使用UGUI簡單做下界面,
在這里插入圖片描述
保存為MainPanel.prefab預設,
在這里插入圖片描述

2、C#代碼

三個腳本,如下
在這里插入圖片描述
GameLogger.cs:封裝Debug日志列印,實作日志開關控制、彩色日志列印、日志檔案存盤等功能;
LogUploader.cs:實作日志上傳到服務器的功能;
Main.cs:程式入口腳本,同時實作UI界面互動,
原理我上文都有講,這里就不贅述代碼細節了,可以看注釋,我都寫得比較詳細,

2.1、GameLogger.cs代碼
/// <summary>
/// 封裝Debug日志列印,實作日志開關控制、彩色日志列印、日志檔案存盤等功能
/// 作者:林新發 博客:https://blog.csdn.net/linxinfa
/// </summary>

using UnityEngine;
using System.Text;
using System.IO;

public class GameLogger
{
    // 普通除錯日志開關
    public static bool s_debugLogEnable = true;
    // 警告日志開關
    public static bool s_warningLogEnable = true;
    // 錯誤日志開關
    public static bool s_errorLogEnable = true;

    // 使用StringBuilder來優化字串的重復構造
    private static StringBuilder s_logStr = new StringBuilder();
    // 日志檔案存盤位置
    private static string s_logFileSavePath;

    /// <summary>
    /// 初始化,在游戲啟動的入口腳本的Awake函式中呼叫GameLogger.Init
    /// </summary>
    public static void Init()
    {
        // 日期
        var t = System.DateTime.Now.ToString("yyyyMMddhhmmss");
        s_logFileSavePath = string.Format("{0}/output_{1}.log", Application.persistentDataPath, t);
        Application.logMessageReceived += OnLogCallBack;
    }

    /// <summary>
    /// 列印日志回呼
    /// </summary>
    /// <param name="condition">日志文本</param>
    /// <param name="stackTrace">呼叫堆疊</param>
    /// <param name="type">日志型別</param>
    private static void OnLogCallBack(string condition, string stackTrace, LogType type)
    {
        s_logStr.Append(condition);
        s_logStr.Append("\n");
        s_logStr.Append(stackTrace);
        s_logStr.Append("\n");

        if (s_logStr.Length <= 0) return;
        if (!File.Exists(s_logFileSavePath))
        {
            var fs = File.Create(s_logFileSavePath);
            fs.Close();
        }
        using (var sw = File.AppendText(s_logFileSavePath))
        {
            sw.WriteLine(s_logStr.ToString());
        }
        s_logStr.Remove(0, s_logStr.Length);
    }

    public static void UploadLog(string desc)
    {
        LogUploader.StartUploadLog(s_logFileSavePath, desc);
    }

    /// <summary>
    /// 普通除錯日志
    /// </summary>
    public static void Log(object message, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(message, context);
    }

    /// <summary>
    /// 格式化列印日志
    /// </summary>
    /// <param name="format">例:"a is {0}, b is {1}"</param>
    /// <param name="args">可變引數,根據format的格式傳入匹配的引數,例:a, b</param>
    public static void LogFormat(string format, params object[] args)
    {
        if (!s_debugLogEnable) return;
        Debug.LogFormat(format, args);
    }

    /// <summary>
    /// 帶顏色的日志
    /// </summary>
    /// <param name="message"></param>
    /// <param name="color">顏色值,例:green, yellow,#ff0000</param>
    /// <param name="context">背景關系物件</param>
    public static void LogWithColor(object message, string color, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(FmtColor(color, message), context);
    }

    /// <summary>
    /// 紅色日志
    /// </summary>
    public static void LogRed(object message, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(FmtColor("red", message), context);
    }

    /// <summary>
    /// 綠色日志
    /// </summary>
    public static void LogGreen(object message, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(FmtColor("green", message), context);
    }

    /// <summary>
    /// 黃色日志
    /// </summary>
    public static void LogYellow(object message, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(FmtColor("yellow", message), context);
    }

    /// <summary>
    /// 青藍色日志
    /// </summary>
    public static void LogCyan(object message, Object context = null)
    {
        if (!s_debugLogEnable) return;
        Debug.Log(FmtColor("#00ffff", message), context);
    }

    /// <summary>
    /// 帶顏色的格式化日志列印
    /// </summary>
    public static void LogFormatWithColor(string format, string color, params object[] args)
    {
        if (!s_debugLogEnable) return;
        Debug.LogFormat((string)FmtColor(color, format), args);
    }

    /// <summary>
    /// 警告日志
    /// </summary>
    public static void LogWarning(object message, Object context = null)
    {
        if (!s_warningLogEnable) return;
        Debug.LogWarning(message, context);
    }

    /// <summary>
    /// 錯誤日志
    /// </summary>
    public static void LogError(object message, Object context = null)
    {
        if (!s_errorLogEnable) return;
        Debug.LogError(message, context);
    }

    /// <summary>
    /// 格式化顏色日志
    /// </summary>
    private static object FmtColor(string color, object obj)
    {
        if (obj is string)
        {
#if !UNITY_EDITOR
            return obj;
#else
            return FmtColor(color, (string)obj);
#endif
        }
        else
        {
#if !UNITY_EDITOR
            return obj;
#else
            return string.Format("<color={0}>{1}</color>", color, obj);
#endif
        }
    }

    /// <summary>
    /// 格式化顏色日志
    /// </summary>
    private static object FmtColor(string color, string msg)
    {
#if !UNITY_EDITOR
        return msg;
#else
        int p = msg.IndexOf('\n');
        if (p >= 0) p = msg.IndexOf('\n', p + 1);// 可以同時顯示兩行
        if (p < 0 || p >= msg.Length - 1) return string.Format("<color={0}>{1}</color>", color, msg);
        if (p > 2 && msg[p - 1] == '\r') p--;
        return string.Format("<color={0}>{1}</color>{2}", color, msg.Substring(0, p), msg.Substring(p));
#endif
    }

    #region 解決日志雙擊溯源問題
#if UNITY_EDITOR
    [UnityEditor.Callbacks.OnOpenAssetAttribute(0)]
    static bool OnOpenAsset(int instanceID, int line)
    {
        string stackTrace = GetStackTrace();
        if (!string.IsNullOrEmpty(stackTrace) && stackTrace.Contains("GameLogger:Log"))
        {
            // 使用正則運算式匹配at的哪個腳本的哪一行
            var matches = System.Text.RegularExpressions.Regex.Match(stackTrace, @"\(at (.+)\)",
                System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            string pathLine = "";
            while (matches.Success)
            {
                pathLine = matches.Groups[1].Value;

                if (!pathLine.Contains("GameLogger.cs"))
                {
                    int splitIndex = pathLine.LastIndexOf(":");
                    // 腳本路徑
                    string path = pathLine.Substring(0, splitIndex);
                    // 行號
                    line = System.Convert.ToInt32(pathLine.Substring(splitIndex + 1));
                    string fullPath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets"));
                    fullPath = fullPath + path;
                    // 跳轉到目標代碼的特定行
                    UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(fullPath.Replace('/', '\\'), line);
                    break;
                }
                matches = matches.NextMatch();
            }
            return true;
        }
        return false;
    }

    /// <summary>
    /// 獲取當前日志視窗選中的日志的堆疊資訊
    /// </summary>
    /// <returns></returns>
    static string GetStackTrace()
    {
        // 通過反射獲取ConsoleWindow類
        var ConsoleWindowType = typeof(UnityEditor.EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
        // 獲取視窗實體
        var fieldInfo = ConsoleWindowType.GetField("ms_ConsoleWindow",
            System.Reflection.BindingFlags.Static |
            System.Reflection.BindingFlags.NonPublic);
        var consoleInstance = fieldInfo.GetValue(null);
        if (consoleInstance != null)
        {
            if ((object)UnityEditor.EditorWindow.focusedWindow == consoleInstance)
            {
                // 獲取m_ActiveText成員
                fieldInfo = ConsoleWindowType.GetField("m_ActiveText",
                    System.Reflection.BindingFlags.Instance |
                    System.Reflection.BindingFlags.NonPublic);
                // 獲取m_ActiveText的值
                string activeText = fieldInfo.GetValue(consoleInstance).ToString();
                return activeText;
            }
        }
        return null;
    }
#endif
    #endregion 解決日志雙擊溯源問題
}
2.2、LogUploader.cs代碼
/// <summary>
/// 實作日志上傳到服務器的功能
/// 作者:林新發 博客:https://blog.csdn.net/linxinfa
/// </summary>

using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

public class LogUploader : MonoBehaviour
{
    private static string LOG_UPLOAD_URL = "http://127.0.0.1:7890/upload_log.php";

    public static void StartUploadLog(string logFilePath, string desc)
    {
        var go = new GameObject("LogUploader");
        var bhv = go.AddComponent<LogUploader>();
        bhv.StartCoroutine(bhv.UploadLog(logFilePath, LOG_UPLOAD_URL, desc));
    }

    /// <summary>
    /// 上報日志到服務端
    /// </summary>
    /// <param name="url">http介面</param>
    /// <param name="desc">描述</param>
    private IEnumerator UploadLog(string logFilePath, string url, string desc)
    {
        var fileName = Path.GetFileName(logFilePath);
        var data = ReadLogFile(logFilePath);
        WWWForm form = new WWWForm();
        // 塞入描述欄位,欄位名與服務端約定好
        form.AddField("desc", desc);
        // 塞入日志位元組流欄位,欄位名與服務端約定好
        form.AddBinaryData("logfile", data, fileName, "application/x-gzip");
        // 使用UnityWebRequest
        UnityWebRequest request = UnityWebRequest.Post(url, form);
        var result = request.SendWebRequest();

        while (!result.isDone)
        {
            yield return null;
            //Debug.Log ("上傳進度: " + request.uploadProgress);
        }
        if (!string.IsNullOrEmpty(request.error))
        {
            GameLogger.LogError(request.error);
        }
        else
        {
            GameLogger.Log("日志上傳完畢, 服務器回傳資訊: " + request.downloadHandler.text);
        }
        request.Dispose();
    }

    private byte[] ReadLogFile(string logFilePath)
    {
        byte[] data = null;

        using (FileStream fs = File.OpenRead(logFilePath))
        {
            int index = 0;
            long len = fs.Length;
            data = new byte[len];
            // 根據你的需求進行限流讀取
            int offset = data.Length > 1024 ? 1024 : data.Length;
            while (index < len)
            {
                int readByteCnt = fs.Read(data, index, offset);
                index += readByteCnt;
                long leftByteCnt = len - index;
                offset = leftByteCnt > offset ? offset : (int)leftByteCnt;
            }
        }
        return data;
    }
}
2.3 Main.cs代碼
using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour
{
    /// <summary>
    /// 日志開關
    /// </summary>
    public Toggle logEnableTgl;
    /// <summary>
    /// 列印日志按鈕
    /// </summary>
    public Button logBtn;
    /// <summary>
    /// 上傳日志按鈕
    /// </summary>
    public Button uploadBtn;
    /// <summary>
    /// 日志文本Text
    /// </summary>
    public Text logText;

    void Awake()
    {
        GameLogger.Init();

        // 監聽日志,輸出到logText中
        Application.logMessageReceived += (string condition, string stackTrace, LogType type) => 
        {
            switch(type)
            {
                case LogType.Log:
                    {
                        if (!GameLogger.s_debugLogEnable) return;
                    }
                    break;
                case LogType.Warning:
                    {
                        if (!GameLogger.s_warningLogEnable) return;
                    }
                    break;
                case LogType.Error:
                    {
                        if (!GameLogger.s_errorLogEnable) return;
                    }
                    break;
            }
            logText.text += condition + "\n";
        };
    }

    private void Start()
    {
        logText.text = "";
        uploadBtn.onClick.AddListener(() =>
        {
            GameLogger.UploadLog("上傳日志測驗");
        });
        logBtn.onClick.AddListener(() =>
        {
            GameLogger.Log("列印一行日志");
        });

        logEnableTgl.onValueChanged.AddListener((v) => 
        {
            GameLogger.s_debugLogEnable = v;
        });

        GameLogger.Log("大家好,我是林新發");
        GameLogger.LogCyan("我的CSDN博客:https://blog.csdn.net/linxinfa");
        GameLogger.LogYellow("歡迎關注、點贊,感謝支持~");
        GameLogger.LogRed("???????????");
    }
}

3、掛Main腳本

MainPanel.prefab預設掛上Main.cs腳本,并賦值UI成員,如下:
在這里插入圖片描述

4、Web服務器

我們的日志要上傳到Web服務器,所以我們需要搭建一個Web服務器,簡單的做法是使用PHP小皮~

4.1、小皮,Web服務器

關于小皮,可以看我之前寫的這篇文章:https://blog.csdn.net/linxinfa/article/details/103033142
我們設定一下埠號,比如7890
在這里插入圖片描述
啟動Apache
在這里插入圖片描述
這樣,我們就已經啟動了一個Web服務器了,通過http://127.0.0.1:7890即可訪問

4.2、PHP腳本:upload_log.php

我們打開Web服務器的根目錄,
在這里插入圖片描述
在根目錄中創建一個upload_log.php
在這里插入圖片描述
php代碼如下:

<?php
// 獲取描述
$desc = $_POST["desc"];
// 獲取臨時檔案路徑
$tmp = $_FILES["logfile"]["tmp_name"];
// 檔案保存位置
$savePath = "upload/" . $_FILES["logfile"]["name"];
// 判斷檔案是否已存在
if (file_exists($savePath))
{
	// 檔案已存在,洗掉它
	unlink($savePath);
}
// 保存檔案到savePath的路徑下
move_uploaded_file($tmp, $savePath);
echo "檔案上傳成功";
?>

我們再創建一個upload檔案夾,用于存放上傳上來的日志,
在這里插入圖片描述

5、運行測驗

運行Unity,測驗效果如下:
請添加圖片描述
日志成功上傳到了Web服務器的upload目錄中,
在這里插入圖片描述

日志檔案內容如下:

大家好,我是林新發
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:Log (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:72)
Main:Start () (at Assets/Scripts/Main.cs:70)


<color=#00ffff>我的CSDN博客:https://blog.csdn.net/linxinfa</color>
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:LogCyan (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:131)
Main:Start () (at Assets/Scripts/Main.cs:71)


<color=yellow>歡迎關注、點贊,感謝支持~</color>
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:LogYellow (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:122)
Main:Start () (at Assets/Scripts/Main.cs:72)


<color=red>???????????</color>
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:LogRed (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:104)
Main:Start () (at Assets/Scripts/Main.cs:73)


列印一行日志
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:Log (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:72)
Main/<>c:<Start>b__5_1 () (at Assets/Scripts/Main.cs:62)
UnityEngine.EventSystems.EventSystem:Update () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)


列印一行日志
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:Log (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:72)
Main/<>c:<Start>b__5_1 () (at Assets/Scripts/Main.cs:62)
UnityEngine.EventSystems.EventSystem:Update () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)


列印一行日志
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:Log (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:72)
Main/<>c:<Start>b__5_1 () (at Assets/Scripts/Main.cs:62)
UnityEngine.EventSystems.EventSystem:Update () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)


列印一行日志
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:Log (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:72)
Main/<>c:<Start>b__5_1 () (at Assets/Scripts/Main.cs:62)
UnityEngine.EventSystems.EventSystem:Update () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)


列印一行日志
UnityEngine.Debug:Log (object,UnityEngine.Object)
GameLogger:Log (object,UnityEngine.Object) (at Assets/Scripts/GameLogger/GameLogger.cs:72)
Main/<>c:<Start>b__5_1 () (at Assets/Scripts/Main.cs:62)
UnityEngine.EventSystems.EventSystem:Update () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)

6、工程原始碼

本文工程原始碼我一上傳到CODE CHINA,感興趣的同學可自行下載下來學習,
地址:https://codechina.csdn.net/linxinfa/UnityLoggerDemo
注:我使用的Unity版本為:2021.1.7f1c1
在這里插入圖片描述

八、完畢

好了,沒想到又寫到這么晚,就到這里吧~
我是林新發:https://blog.csdn.net/linxinfa
原創不易,若轉載請注明出處,感謝大家~
喜歡我的可以點贊、關注、收藏,如果有什么技術上的疑問,歡迎留言或私信~

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/291582.html

標籤:其他

上一篇:三子棋游戲【C語言版】

下一篇:目標檢測也就是這么簡單

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more