QFramework实现原理 一 :日志篇

        作为一款轻量级开源的Unity程序框架,QFramework结合了作者凉鞋多年的开发经验,是比较值得想要学习框架的初学者窥探一二的对象,我就尝试结合凉鞋大大给出的文档和ai,解析一下其背后的代码逻辑,以作提升自己的一次试炼

        声明:本系列不做任何商业用途,权当学习交流,而且我是个菜鸡算法能力和代码能力都不如常人,如有不足还请斧正

03. CodeGenKit 脚本生成 — QFramework 1.0.60 文档

讲的是这个东西 

 0.总览与疑问

        我有一个疑问就是,这部分大概有三百多行了

        因此我不是很清楚凉鞋大大写的该框架大约一千多行代码是什么意思

        然后官网文档也没看到这部分(可能是我没找到?)因此十分不解,是不是因为这个并不算做框架主体内容,就是边角料部分呢??

/****************************************************************************
 * Copyright (c) 2016 - 2023 liangxiegame UNDER MIT License
 *
 * https://qframework.cn
 * https://github.com/liangxiegame/QFramework
 * https://gitee.com/liangxiegame/QFramework
 *
 ****************************************************************************/

using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;

namespace QFramework
{
    using System;
    using UnityEngine;

#if UNITY_EDITOR
    [ClassAPI("02.LogKit", "LogKit", 4)]
    [APIDescriptionCN("简单的日志工具")]
    [APIDescriptionEN("Simple Log ToolKit")]
#endif
    public class LogKit
    {
#if UNITY_EDITOR
        // v1 No.157
        [MethodAPI]
        [APIDescriptionCN("Debug.Log & Debug.LogFormat")]
        [APIDescriptionEN("Debug.Log & Debug.LogFormat")]
        [APIExampleCode(@"
LogKit.I(""Hello LogKit"");
// Hello LogKit
LogKit.I(""Hello LogKit {0}{1}"",1,2);
// Hello LogKit 12
""Hello LogKit FluentAPI"".LogInfo();
// Hello LogKit FluentAPI
")]
#endif
        public static void I(object msg, params object[] args)
        {
            if (mLogLevel < LogLevel.Normal)
            {
                return;
            }

            if (args == null || args.Length == 0)
            {
                Debug.Log(msg);
            }
            else
            {
                Debug.LogFormat(msg.ToString(), args);
            }
        }

#if UNITY_EDITOR
        // v1 No.158
        [MethodAPI]
        [APIDescriptionCN("Debug.LogWarning & Debug.LogWaringFormat")]
        [APIDescriptionEN("Debug.LogWarning & Debug.LogWarningFormat")]
        [APIExampleCode(@"
LogKit.E(""Hello LogKit"");
// Hello LogKit
LogKit.E(""Hello LogKit {0}{1}"",1,2);
// Hello LogKit 12
""Hello LogKit FluentAPI"".LogError();
// Hello LogKit FluentAPI
")]
#endif

        public static void W(object msg, params object[] args)
        {
            if (mLogLevel < LogLevel.Warning)
            {
                return;
            }

            if (args == null || args.Length == 0)
            {
                Debug.LogWarning(msg);
            }
            else
            {
                Debug.LogWarningFormat(msg.ToString(), args);
            }
        }


#if UNITY_EDITOR
        // v1 No.159
        [MethodAPI]
        [APIDescriptionCN("Debug.LogError & Debug.LogErrorFormat")]
        [APIDescriptionEN("Debug.LogError & Debug.LogErrorFormat")]
        [APIExampleCode(@"
LogKit.E(""Hello LogKit"");
// Hello LogKit
LogKit.E(""Hello LogKit {0}{1}"",1,2);
// Hello LogKit 12
""Hello LogKit FluentAPI"".LogError();
// Hello LogKit FluentAPI
")]
#endif
        public static void E(object msg, params object[] args)
        {
            if (mLogLevel < LogLevel.Error)
            {
                return;
            }

            if (args == null || args.Length == 0)
            {
                Debug.LogError(msg);
            }
            else
            {
                Debug.LogError(string.Format(msg.ToString(), args));
            }
        }


#if UNITY_EDITOR
        // v1 No.160
        [MethodAPI]
        [APIDescriptionCN("Debug.LogException")]
        [APIDescriptionEN("Debug.LogException")]
        [APIExampleCode(@"
LogKit.E(""Hello LogKit"");
// Hello LogKit
LogKit.E(""Hello LogKit {0}{1}"",1,2);
// Hello LogKit 12
""Hello LogKit FluentAPI"".LogError();
// Hello LogKit FluentAPI
")]
#endif
        public static void E(Exception e)
        {
            if (mLogLevel < LogLevel.Exception)
            {
                return;
            }

            Debug.LogException(e);
        }


#if UNITY_EDITOR
        // v1 No.161
        [MethodAPI]
        [APIDescriptionCN("获得 StringBuilder 用来拼接日志")]
        [APIDescriptionEN("get stringBuilder for generate log string")]
        [APIExampleCode(@"
LogKit.Builder()
    .Append(""Hello"")
    .Append("" LogKit"")
    .ToString()
    .LogInfo();
// Hello LogKit
")]
#endif
        public static StringBuilder Builder()
        {
            return new StringBuilder();
        }


        public enum LogLevel
        {
            None = 0,
            Exception = 1,
            Error = 2,
            Warning = 3,
            Normal = 4,
            Max = 5,
        }

        private static LogLevel mLogLevel = LogLevel.Normal;

#if UNITY_EDITOR
        // v1 No.162
        [PropertyAPI]
        [APIDescriptionCN("日志等级设置")]
        [APIDescriptionEN("log level")]
        [APIExampleCode(@"
LogKit.Level = LogKit.LogLevel.None;
LogKit.I(""LogKit""); // no output
LogKit.Level = LogKit.LogLevel.Exception;
LogKit.Level = LogKit.LogLevel.Error;
LogKit.Level = LogKit.LogLevel.Warning;
LogKit.Level = LogKit.LogLevel.Normal;
LogKit.I(""LogKit""); 
// LogKit
LogKit.Level = LogKit.LogLevel.Max;
")]
#endif
        public static LogLevel Level
        {
            get => mLogLevel;
            set => mLogLevel = value;
        }
    }

    public static class LogKitExtension
    {
        public static StringBuilder GreenColor(this StringBuilder self, Action<StringBuilder> childContent)
        {
            self.Append("<color=green>");
            childContent?.Invoke(self);
            self.Append("</color>");
            return self;
        }

        public static void LogInfo<T>(this T selfMsg)
        {
            LogKit.I(selfMsg);
        }

        public static void LogWarning<T>(this T selfMsg)
        {
            LogKit.W(selfMsg);
        }

        public static void LogError<T>(this T selfMsg)
        {
            LogKit.E(selfMsg);
        }

        public static void LogException(this Exception selfExp)
        {
            LogKit.E(selfExp);
        }
    }

#if UNITY_EDITOR
    // * 参考: https://zhuanlan.zhihu.com/p/92291084
    // 感谢 https://github.com/clksaaa 提供的 issue 反馈 和 解决方案
    public static class OpenAssetLogLine
    {
        private static bool m_hasForceMono = false;

        // 处理asset打开的callback函数
        [UnityEditor.Callbacks.OnOpenAssetAttribute(-1)]
        static bool OnOpenAsset(int instance, int line)
        {
            if (m_hasForceMono) return false;

            // 自定义函数,用来获取log中的stacktrace,定义在后面。
            string stack_trace = GetStackTrace();
            // 通过stacktrace来定位是否是自定义的log,log中有LogKit/LogKit.cs,很好识别
            if (!string.IsNullOrEmpty(stack_trace) && stack_trace.Contains("LogKit/LogKit.cs"))
            {
                // 正则匹配at xxx,在第几行
                Match matches = Regex.Match(stack_trace, @"\(at (.+)\)", RegexOptions.IgnoreCase);
                string pathline = "";
                while (matches.Success)
                {
                    pathline = matches.Groups[1].Value;

                    // 找到不是我们自定义log文件的那行,重新整理文件路径,手动打开
                    if (!pathline.Contains("LogKit/LogKit.cs") && !string.IsNullOrEmpty(pathline))
                    {
                        int split_index = pathline.LastIndexOf(":");
                        string path = pathline.Substring(0, split_index);
                        line = Convert.ToInt32(pathline.Substring(split_index + 1));
                        m_hasForceMono = true;
                        //方式一
                        AssetDatabase.OpenAsset(AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path), line);
                        m_hasForceMono = false;
                        //方式二
                        //string fullpath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets"));
                        // fullpath = fullpath + path;
                        //  UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(fullpath.Replace('/', '\\'), line);
                        return true;
                    }

                    matches = matches.NextMatch();
                }

                return true;
            }

            return false;
        }

        static string GetStackTrace()
        {
// 找到类UnityEditor.ConsoleWindow
            var type_console_window = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
// 找到UnityEditor.ConsoleWindow中的成员ms_ConsoleWindow
            var filedInfo =
                type_console_window.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
// 获取ms_ConsoleWindow的值
            var ConsoleWindowInstance = filedInfo.GetValue(null);
            if (ConsoleWindowInstance != null)
            {
                if ((object)EditorWindow.focusedWindow == ConsoleWindowInstance)
                {
// 找到类UnityEditor.ConsoleWindow中的成员m_ActiveText
                    filedInfo = type_console_window.GetField("m_ActiveText",
                        BindingFlags.Instance | BindingFlags.NonPublic);
                    string activeText = filedInfo.GetValue(ConsoleWindowInstance).ToString();
                    return activeText;
                }
            }

            return null;
        }
    }
#endif
}

1.LogLevel 枚举 前提

        在这之前应该认识一个枚举用来控制日志输出等级,不知道为什么凉鞋大大把这个枚举写到脚本中间去了



        public enum LogLevel
        {
            None = 0,
            Exception = 1,
            Error = 2,
            Warning = 3,
            Normal = 4,
            Max = 5,
        }

        private static LogLevel mLogLevel = LogLevel.Normal;

其属性 

  public static LogLevel Level
  {
      get => mLogLevel;
      set => mLogLevel = value;
  }

 2.LogKit 类  日志核心

LogKit 类是日志工具的核心类,提供了多种日志记录方法和日志等级设置

给出了几个方法,I=Log,E=Error,W=Warning

以I举例,形参msg:信息本体,args:可变长参数信息

   public static void I(object msg, params object[] args)
   {
       //判断枚举类型
       if (mLogLevel < LogLevel.Normal)
       {
           return;
       }

       if (args == null || args.Length == 0)
       {
           Debug.Log(msg);
       }
       else
       {
           Debug.LogFormat(msg.ToString(), args);
       }
   }

经过日志等级判断后通过UnityEngine下的LogFromat输出

     LogKit.I("Hello LogKit{0},{1}",123456,789);

另外Error还需要这个东西,用来捕获异常的

try
{
    // 可能会引发异常的代码
    int[] arr = null;
    int value = arr[0];
}
catch (Exception e)
{
    LogKit.E(e);
}

        System下的Exception 是 C# 中表示异常的基类,常见的异常有 NullReferenceException(当尝试访问空引用的成员时)、ArgumentException(当传递给方法的参数不合法时)、FileNotFoundException(当尝试访问不存在的文件时)等

3.LogKitExtension类 封装拓展

        这里对StringBuilder 类进行了拓展

        就是把传进来的字符串变绿或者其他什么的

   public static StringBuilder Builder()
   {
       return new StringBuilder();
   }

  public static StringBuilder GreenColor(this StringBuilder self, Action<StringBuilder> childContent)
  {
      self.Append("<color=green>");
      childContent?.Invoke(self);
      self.Append("</color>");
      return self;
  }

        然后就是对LogKit里面的方法的再封装,

  public static void LogInfo<T>(this T selfMsg)
  {
      LogKit.I(selfMsg);
  }

  public static void LogWarning<T>(this T selfMsg)
  {
      LogKit.W(selfMsg);
  }

  public static void LogError<T>(this T selfMsg)
  {
      LogKit.E(selfMsg);
  }

  public static void LogException(this Exception selfExp)
  {
      LogKit.E(selfExp);
  }

这里是比较巧妙的:

1.是因为编译器自动将string传了进去 也就是泛型的隐式转换

2.this在这里是扩展方法关键词,允许你向现有类型添加新的方法,而无需修改该类型的原始代码,也就说给string添加了拓展方法,因此一个字符串可以直接点出该方法

      "Hellow Qf".LogInfo( );

4.OpenAssetLogLine类 定位错误

        起到一个定位错误行的作用,是一种对原来unity定位错误行方法的再封装,比如

        但是这部分信息量太大了,System和UnityEngine的方法没有细读过,因此这部分代码读起来十分费劲,让ai办了

using UnityEngine;
using UnityEditor;
using System.Text.RegularExpressions;
using System.Reflection;

public static class OpenAssetLogLine
{
    // 标志,用于防止多次执行打开文件的操作
    private static bool m_hasForceMono = false;

    // 处理 asset 打开的 callback 函数
    [UnityEditor.Callbacks.OnOpenAssetAttribute(-1)]
    static bool OnOpenAsset(int instance, int line)
    {
        // 检查是否已经在处理打开文件操作,如果是则返回 false,避免重复操作
        if (m_hasForceMono) return false;

        // 自定义函数,用来获取 log 中的 stacktrace,定义在后面
        string stack_trace = GetStackTrace();
        // 通过 stacktrace 来定位是否是自定义的 log,log 中有 LogKit/LogKit.cs,很好识别
        if (!string.IsNullOrEmpty(stack_trace) && stack_trace.Contains("LogKit/LogKit.cs"))
        {
            // 正则匹配 at xxx,在第几行
            Match matches = Regex.Match(stack_trace, @"\(at (.+)\)", RegexOptions.IgnoreCase);
            string pathline = "";
            while (matches.Success)
            {
                // 获取匹配的组的值
                pathline = matches.Groups[1].Value;

                // 找到不是我们自定义 log 文件的那行,重新整理文件路径,手动打开
                if (!pathline.Contains("LogKit/LogKit.cs") &&!string.IsNullOrEmpty(pathline))
                {
                    // 找到路径和行号的分隔符的位置
                    int split_index = pathline.LastIndexOf(":");
                    // 获取文件路径
                    string path = pathline.Substring(0, split_index);
                    // 获取行号并转换为整数
                    line = Convert.ToInt32(pathline.Substring(split_index + 1));
                    m_hasForceMono = true;
                    // 方式一:使用 AssetDatabase 打开指定路径和行号的资产文件
                    AssetDatabase.OpenAsset(AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path), line);
                    m_hasForceMono = false;
                    // 方式二:
                    // string fullpath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets"));
                    // fullpath = fullpath + path;
                    // UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(fullpath.Replace('/', '\\'), line);
                    return true;
                }
                // 继续匹配下一个结果
                matches = matches.NextMatch();
            }
            return true;
        }
        return false;
    }

    // 获取堆栈跟踪信息
    static string GetStackTrace()
    {
        // 找到类 UnityEditor.ConsoleWindow
        var type_console_window = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
        // 找到 UnityEditor.ConsoleWindow 中的成员 ms_ConsoleWindow
        var filedInfo =
            type_console_window.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
        // 获取 ms_ConsoleWindow 的值
        var ConsoleWindowInstance = filedInfo.GetValue(null);
        if (ConsoleWindowInstance!= null)
        {
            // 检查当前聚焦的窗口是否是控制台窗口
            if ((object)EditorWindow.focusedWindow == ConsoleWindowInstance)
            {
                // 找到类 UnityEditor.ConsoleWindow 中的成员 m_ActiveText
                filedInfo = type_console_window.GetField("m_ActiveText",
                    BindingFlags.Instance | BindingFlags.NonPublic);
                // 获取 m_ActiveText 的值,该值包含堆栈跟踪信息
                string activeText = filedInfo.GetValue(ConsoleWindowInstance).ToString();
                return activeText;
            }
        }
        return null;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/959912.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图论汇总1

1.图论理论基础 图的基本概念 二维坐标中&#xff0c;两点可以连成线&#xff0c;多个点连成的线就构成了图。 当然图也可以就一个节点&#xff0c;甚至没有节点&#xff08;空图&#xff09; 图的种类 整体上一般分为 有向图 和 无向图。 有向图是指 图中边是有方向的&a…

_CLASSDEF在C++中的用法详解及示例

_CLASSDEF在C++中的用法详解及示例 _CLASSDEF的定义与使用示例说明代码解析总结在C++编程中,宏(Macro)是一种预处理指令,它允许程序员在编译之前对代码进行文本替换。_CLASSDEF是一个自定义的宏,它提供了一种便捷的方式来定义类及其相关类型。本文将详细介绍_CLASSDEF在C+…

华为数据之道-读书笔记

内容简介 关键字 数字化生产 已经成为普遍的商业模式&#xff0c;其本质是以数据为处理对象&#xff0c;以ICT平台为生产工具&#xff0c;以软件为载体&#xff0c;以服务为目的的生产过程。 信息与通信技术平台&#xff08;Information and Communication Technology Platf…

从CRUD到高级功能:EF Core在.NET Core中全面应用(四)

初识表达式树 表达式树&#xff1a;是一种可以描述代码结构的数据结构&#xff0c;它由一个节点组成&#xff0c;节点表示代码中的操作、方法调用或条件表达式等&#xff0c;它将代码中的表达式转换成一个树形结构&#xff0c;每个节点代表了代码中的操作例如&#xff0c;如果…

系统思考—问题分析

很多中小企业都在面对转型的难题&#xff1a;市场变化快&#xff0c;资源有限&#xff0c;团队协作不畅……这些问题似乎总是困扰着我们。就像最近和一位企业主交流时&#xff0c;他提到&#xff1a;“我们团队每天都很忙&#xff0c;但效率始终没见提升&#xff0c;感觉像是在…

MySQL 的索引类型【图文并茂】

基本分类 文本生成MindMap:https://app.pollyoyo.com/planttext <style> mindmapDiagram {node {BackgroundColor yellow}:depth(0) {BackGroundColor SkyBlue}:depth(1) {BackGroundColor lightGreen} } </style> * MySQL 索引** 数据结构角度 *** B树索引*** 哈…

华硕笔记本装win10哪个版本好用分析_华硕笔记本装win10专业版图文教程

华硕笔记本装win10哪个版本好用&#xff1f;华硕笔记本还是建议安装win10专业版。Win分为多个版本&#xff0c;其中家庭版&#xff08;Home&#xff09;和专业版&#xff08;Pro&#xff09;是用户选择最多的两个版本。win10专业版在功能以及安全性方面有着明显的优势&#xff…

【深度学习】 自动微分

自动微分 正如上节所说&#xff0c;求导是几乎所有深度学习优化算法的关键步骤。 虽然求导的计算很简单&#xff0c;只需要一些基本的微积分。 但对于复杂的模型&#xff0c;手工进行更新是一件很痛苦的事情&#xff08;而且经常容易出错&#xff09;。 深度学习框架通过自动…

虚幻浏览器插件 UE与JS通信

温馨提示&#xff1a;本节内容需要结合插件Content下的2_Communication和Resources下的sample.html 一起阅读。 1. UE调用JS 1.1 JS脚本实现 该部分共两步: 导入jstote.js脚本实现响应函数并保存到 ue.interface 中 jsfunc 通过json对象传递参数&#xff0c;仅支持函数名小…

CDN、源站与边缘网络

什么是“源站” 源服务器 源服务器的目的是处理和响应来自互联网客户端的传入请求。源服务器的概念通常与边缘服务器或缓存服务器的概念结合使用。源服务器的核心是一台运行一个或多个程序的计算机&#xff0c;这些程序旨在侦听和处理传入的客户端请求。源服务器可以承担为网…

数据库、数据仓库、数据湖有什么不同

数据库、数据仓库和数据湖是三种不同的数据存储和管理技术&#xff0c;它们在用途、设计目标、数据处理方式以及适用场景上存在显著差异。以下将从多个角度详细说明它们之间的区别&#xff1a; 1. 数据结构与存储方式 数据库&#xff1a; 数据库主要用于存储结构化的数据&…

Java Swing 基础组件详解 [论文投稿-第四届智能系统、通信与计算机网络]

大会官网&#xff1a;www.icisccn.net Java Swing 是一个功能强大的 GUI 工具包&#xff0c;提供了丰富的组件库用于构建跨平台的桌面应用程序。本文将详细讲解 Swing 的基础组件&#xff0c;包括其作用、使用方法以及示例代码&#xff0c;帮助你快速掌握 Swing 的核心知识。 一…

Mac m1,m2,m3芯片使用nvm安装node14报错

使用nvm安装了node 12/16/18都没有问题&#xff0c;到14就报错了。第一次看到这个报错有点懵&#xff0c;查询资料发现是Mac芯片的问题。 Issue上提供了两个方案&#xff1a; 1、为了在arm64的Mac上安装node 14&#xff0c;需要使用Rosseta&#xff0c;可以通过以下命令安装 …

多模态论文笔记——ViViT

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细解读多模态论文《ViViT: A Video Vision Transformer》&#xff0c;2021由google 提出用于视频处理的视觉 Transformer 模型&#xff0c;在视频多模态领域有…

网络安全 | F5-Attack Signatures-Set详解

关注&#xff1a;CodingTechWork 创建和分配攻击签名集 可以通过两种方式创建攻击签名集&#xff1a;使用过滤器或手动选择要包含的签名。  基于过滤器的签名集仅基于在签名过滤器中定义的标准。基于过滤器的签名集的优点在于&#xff0c;可以专注于定义用户感兴趣的攻击签名…

【C语言系列】深入理解指针(4)

深入理解指针&#xff08;4&#xff09; 一、回调函数是什么&#xff1f;二、qsort使用举例2.1使用qsort函数排序整型数据2.2使用qsort排序结构数据 三、qsort函数的模拟实现四、总结 一、回调函数是什么&#xff1f; 回调函数就是一个通过函数指针调用的函数。 如果你把函数的…

零售业革命:改变行业的顶级物联网用例

mpro5 产品负责人Ruby Whipp表示&#xff0c;技术进步持续重塑零售业&#xff0c;其中物联网&#xff08;IoT&#xff09;正引领这一变革潮流。 研究表明&#xff0c;零售商们正在采用物联网解决方案&#xff0c;以提升运营效率并改善顾客体验。这些技术能够监控运营的各个方面…

macos的图标过大,这是因为有自己的设计规范

苹果官方链接&#xff1a;App 图标 | Apple Developer Documentation 这个在官方文档里有说明&#xff0c;并且提供了sketch 和 ps 的模板。 figma还提供了模板&#xff1a; Figma

【从零到一,C++项目实战】CineShare++(基于C++的视频点播系统)

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…

【leetcode100】从前序与中序遍历序列构造二叉树

1、题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,nul…