【效率开发】游戏开发Debug效率方法总结

"程序员的一半生命都浪费在了调试上。"

        ——Brian Kernighan(计算机科学家,曾参与开发C语言)

图片

(图片来源:forbesindia)

Debug无疑是程序员最头疼,也是耗费时间最多的一个环节,因此如何提高Debug效率至关重要。本文将会根据小棋自身多年的开发经验,总结和分享五种常用的调试方式,帮助你提高开发效率。

图片

注:案例中会使用到C#和python语言,其他语言基本同理。


一、Log

> 看起来平平无奇,却是最基础最简洁的debug方式,在没有Vscode等编辑器之前,是最常用的调试方式啦。O(∩_∩)O~

1. python

>>> print("hello world")hello world

2. C#

// C# Console.WriteLine("hello world")
// Unity对此进行了封装Debug.Log("hello world");print("hello world");

其实Unity中print底层调用的也是Debug.Log

图片

3. Log如何帮助我们得到有用信息?

学会如何打Log也是一个重要编程能力,举个简单的例子:Unity开发游戏过程中经常会提示空指针异常。

NullReferenceException: Object reference not set to an instance of an object

这时候我们可以把Log打在报错附近,查看报错附近的代码语义:

ResManager.LoadPrefab(path);

可以看到这里尝试加载某个路径的物体,尝试打印一些关键信息,比如:​​​​​​​

print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");print(path);

其中,第一行是一个显眼的标识,方便快速定位log,第二行是关键信息。最终结果:​​​​​​​

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Panel/MenuPanel1

然后到加载路径一看,原来路径下只有Panel/MenuPanel,没有Panel/MenuPanel1,修改为正确路径,这样问题就得到解决了。


二、traceback

traceback是我入职当年,我认为导师教我的最简单又好用的功能。有时候游戏在运行过程中,莫名其妙执行了一个操作:比如玩家在某个时间执行了两次攻击逻辑,又或者在错误的时间某个UI界面弹出了。

总结下,适合traceback调试有两个基本要求:

  1. 攻击和UI打开的执行逻辑你知道在哪里 (*^▽^*)

  2. 为什么会执行,谁执行了这个逻辑,不知道 o(╥﹏╥)o

这时候怎么做呢?

图片

1、Python

在python中非常简洁:

在被调用的方法中执行,traceback.print_stack。

当这个方法被执行的时候,我们就清楚的知道是谁执行了这个代码。

图片

2. Unity

Unity游戏引擎深知traceback对于开发效率的提升,因此在编辑器中我们甚至无需做任何处理,就能得到调用栈的信息。

图片

下方有完整的traceback调用栈,双击log信息还能跳转到指定的代码行数,非常方便。

但是这有个前提,需要再Project Settings -> Player -> Other 对Stack Trace进行设置,将其统一设置为ScriptOnly就ok了。(这个设置是默认的)

图片


三、把Log输出到文件中

当你把游戏打开输出之后,或者把python编译成exe运行时,没有了编辑器,也没有log窗口了。这时候我们该如何调试项目呢?

把Log输出到文本文件中就是一个不错的方法,市面上主流的软件也都会执行这一操作。

这里有两种办法供大家选择:

1. 方法一,写一个新的Log.Print方法,调用这个方法的log会输出到文件中。

图片

2. 方法二,用户可以继续使用系统内置的打印方法,不改变用户习惯,我们只监听或者劫持系统的打印方法,并将其输出到log文件中。

显然,第二种方法是更好的。其中Unity用于监听默认打印事件的方法是:

Application.logMessageReceived

而python对应的事件是:

sys.stdout

其他语言也有类似的输出流。

这里我以Unity为例,提供一个参考案例(抄了代码记得点点在看和收藏呀,求求您啦~):​​​​​​​

using UnityEngine;using System.IO;using System;using System.Diagnostics;
public class LogToFile : MonoBehaviour{    private string logFilePath;    private StreamWriter streamWriter;
    void Start()    {        // 创建一个新的文件名,包含时间戳        string fileName = "log_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt";        // 获取日志文件夹的路径        string logFolder = Application.persistentDataPath + "/log";        // 确保日志文件夹存在        Directory.CreateDirectory(logFolder);        // 拼接出完整的日志文件路径        logFilePath = Path.Combine(logFolder, fileName);
        // 开始写入日志        streamWriter = File.AppendText(logFilePath);
        // 监听日志事件        Application.logMessageReceived += LogMessageHandler;    }
    void OnDestroy()    {        // 关闭文件流        if (streamWriter != null)        {            streamWriter.Close();        }    }
    void LogMessageHandler(string logString, string stackTrace, LogType type)    {        // 获取当前时间        string currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");                // 使用 System.Diagnostics.StackTrace 获取调用栈信息        StackTrace trace = new StackTrace(true);        // 获取最后一个帧的栈帧        StackFrame frame = trace.GetFrame(trace.FrameCount - 1);        // 获取调用的脚本路径        string scriptPath = Path.GetFileName(frame.GetFileName());        // 获取调用的行号        int lineNumber = frame.GetFileLineNumber();
        // 将日志信息写入文件        streamWriter.WriteLine(currentTime + " [" + type + "] " + scriptPath + "(" + lineNumber + "): " + logString);        // 强制刷新缓冲区,确保日志信息立即写入文件        streamWriter.Flush();    }}

其中,关键代码是:​​​​​​​

// 监听日志事件        Application.logMessageReceived += LogMessageHandler;

其他代码主要是对log信息进行整理,提供:

  • 事件信息

  • 调用栈信息

  • 调用行信息

把LogToFile文件放到场景中的某个物体上后,来看看执行后的效果:​​​​​​​

2024-03-31 16:55:37 [Log] LoginScene.cs(11): >>>>>>>>>>>>>>>>>>>>>> openUI: MenuPanel2024-03-31 16:55:37 [Exception] (0): ArgumentException: The Object you want to instantiate is null.2024-03-31 16:55:37 [Log] MenuPanel.cs(55): >>>> start2024-03-31 16:55:37 [Exception] (0): NullReferenceException: Object reference not set to an instance of an object

非常方便的可以定位到问题。

图片

图片

图片


四、点击调试

点击调试主要讲游戏开发,这里以Unity举例。

对于用户输入来说,鼠标和手指点击是非常重要的一个操作,但是开发过程中经常发现点不到我们想要的物体,这时候可以通过射线检测判断下当前点击事件被哪些物体拦截了。

下面提供代码:​​​​​​​

using UnityEngine;using UnityEngine.EventSystems;
public class ClickDetector : MonoBehaviour{    void Update()    {        // 检测鼠标左键点击        if (Input.GetMouseButtonDown(0))        {            // 检查是否点击在 UI 上            if (EventSystem.current.IsPointerOverGameObject())            {                // 获取点击的 UI 元素                GameObject clickedObject = EventSystem.current.currentSelectedGameObject;
                // 打印 UI 元素的名称                if (clickedObject != null)                {                    Debug.Log("Clicked on UI element: " + clickedObject.name);                }                else                {                    Debug.Log("Clicked on UI element, but no object found.");                }            }            else            {                // 如果没有点击在 UI 上,则检查物体                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);                RaycastHit hit;
                if (Physics.Raycast(ray, out hit))                {                    // 打印物体的名称                    Debug.Log("Clicked on object: " + hit.collider.gameObject.name);                }                else                {                    Debug.Log("Clicked, but no object found.");                }            }        }    }}

具体使用方法,就是把这个脚本放到场景中,然后鼠标点到某个物体,就会打印出指定的物体名称。如果是因为某个物体挡在前面导致被拦截,就能很快的找到问题所在了。

图片

图片

图片

五、断点调试

断点调试是一种在程序执行过程中暂停执行并允许程序员检查程序状态的调试技术。

在大多数集成开发环境(IDE)和调试器中,你可以通过点击编辑器的某一行代码旁边的区域(通常是左侧)来设置断点

当程序执行到这一行时,会自动停止执行,然后你可以逐步执行代码、观察变量值、检查堆栈跟踪等。

图片

因为之前在其他平台已经发过了,所以这里就简单贴下链接,感兴趣的同学可以跳转过去看看。

图片

图片

图片


 

欢迎大家后台私信补充更多效率开发的方法,目前公众号还不支持留言功能,会尽快想办法哒~

 想了解更多游戏开发知识,可以扫描下方二维码,免费领取游戏开发4天训练营课程


好啦,以上就是本期分享的内容。

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

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

相关文章

redis抖动问题导致延迟或者断开的处理方案

目录: 1、使用背景2、redis重试机制3、redis重连机制4、其他一些解决redis抖动问题方案 1、使用背景 客户反馈文件偶现打不开,报错现象是session not exist,最终定位是redis抖动导致的延迟/断开的现象,最终研发团方案是加入redis…

紫光计算机项目卓越中心负责人孙宇受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 紫光计算机科技有限公司信息技术中心项目总监&卓越中心负责人孙宇先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾,演讲议题为“PMO卓越中心核心能力拆解与落地-用创新绘制新蓝图”。大会将于6月29-30日在北京举办&#xff0c…

docker安装向量数据库milvus

Miluvs Milvus 向量数据库能够帮助用户轻松应对海量非结构化数据(图片 / 视频 / 语音 / 文本)检索。 单节点 Milvus 可以在秒内完成十亿级的向量搜索,分布式架构亦能满足用户的水平扩展需求。 Milvus 向量数据库的应用场景包括:互联网娱乐(图片搜索 / 视频搜索)、新零售…

二叉树的非递归遍历(c++)

前序 . - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/binary-tree-preorder-traversal/description/ 1---2---4---5--…

CentOS7 安装 Kamailio

https://www.kamailio.org/wiki/packages/rpms 官方文档说 yum -y install yum-utils yum-config-manager --add-repo https://rpm.kamailio.org/centos/kamailio.repo 但目前这样其实行不通 需要这样做: yum install --disablerepokamailio --enablerepokamai…

Web3Tools - 助记词生成

Web3Tools - 助记词生成工具 本文介绍了一个简单的助记词生成工具,使用 React 和 Material-UI 构建。用户可以选择助记词的语言和长度,然后生成随机的助记词并显示在页面上 功能介绍 选择语言和长度: 用户可以在下拉菜单中选择助记词的语言&…

收放卷伺服控制系统详细算法介绍(电子齿轮+张力PID卷绕轴控制功能块)

收放卷控制系统涉及的内容非常多,这里我们介绍全伺服系统利用电子齿轮指令实现主从轴的比例随动速度控制,收放卷控制算法介绍常用链接如下 1、收放卷+排线控制 收放卷+排线控制系统框图-CSDN博客文章浏览阅读24次。1、收放卷前馈量计算FC收放卷前馈量计算FC(CODESYS ST源代…

bevformer详解(1):论文介绍

3D 视觉感知任务,包括基于多摄像头的3D检测和地图分割对于自动驾驶系统至关重要。本文提出了一种名为BEVFormer的新框架,它通过使用空间和时间的Transformer 学习统一的BEV表示来支持多个自动驾驶感知任务。简而言之,BEVFormer通过预定义的网格形式的Bev Query与空间和时间空…

ssrf初步

一,简介 全称:Server-Side Request Forgery(中文:服务器端请求伪造) 攻击者从服务端发起请求,让服务器连接任意外部系统,从而泄露敏感数据。主要利用各种协议的请求伪造,例如php协…

TCP的四次挥手过程

TCP连接是双向传输的对等的模式(全双工模式),就是说双方都可以同时向对方发送或接收数据。 而断开的时候,也是双方都可以主动断开,此时需要经过四次挥手的过程,流程如下图所示: 主动方发送FIN包…

面向侧扫声纳目标检测的YOLOX-ViT知识精馏

面向侧扫声纳目标检测的YOLOX-ViT知识精馏 摘要IntroductionRelated WorkYOLOv-ViTKnowledge DistillationExperimental Evaluation Knowledge Distillation in YOLOX-ViT for Side-Scan Sonar Object Detection 摘要 在本文中,作者提出了YOLOX-ViT这一新型目标检测…

大数据Scala教程从入门到精通第七篇:Scala在IDEA中编写Hello World

一:Scala在IDEA中编写Hello World 想让我们的idea支持scala的编写,需要安装一个插件。

PHP极简网盘系统源码

源码说明:PHP极简网盘系统源码 轻量级文件管理与共享系统网站源码 极简网盘是一个轻量级的文件管理与共享系统,支持多用户,可以作为网盘程序使用,无需数据库。 下 载 地 址 : runruncode.com/php/19760.html 安装…

网络安全等级保护的发展历程

1994年国务院147号令第一次提出,计算机信息系统实行安全等级保护,这也预示着等保的起步。 2007年《信息安全等级保护管理办法》的发布之后。是等保在各行业深耕落地的时代。 2.0是等保版本的俗称,不是等级。等保共分为五级,二级…

MySql数据库基础知识

大家好,在当今软件世界中,软件测试人员肩负着至关重要的职责,确保软件的质量与稳定性。而对于软件测试工作来说,了解 MySQL 基础知识是一项极具价值的技能。MySQL 作为广泛应用的关系型数据库管理系统,在众多软件项目中…

D - Another Sigma Problem(ABC)

思路:我们可以处理一个后缀来记录当前数a[i]需要乘上多少(类似于1110这样的),然后对于当前位来说,对答案的贡献还要加上(i - 1) * a[i],因为a[i]还要做前(i - 1)个数的后缀。 代码: #include &…

ctfshow web274

web274 thinkphp框架序列化漏洞 EXP <?php namespace think; abstract class Model{protected $append[];private $data[];function __construct(){$this->append["lin">["ctf","show"]];$this->data["lin">new Req…

【C++】vector的底层原理讲解及其实现

目录 一、认识vector底层结构 二、初始化vector的函数 构造函数拷贝构造赋值构造initializer_list构造迭代器区间构造 三、迭代器 四、数据的访问 五、容量相关的函数 六、关于数据的增删查改操作 一、认识vector底层结构 STL库中实现vector其实是用三个指针来完成的&#x…

进入泛型的世界

泛型的理解和好处 泛型的好处 编译时&#xff0c;检查添加元素的类型&#xff0c;提高了安全性减少了类型转换的次数&#xff0c;提高效率 不使用泛型 Dog-加入->Object-取出->Dog&#xff08;向下转型&#xff09; Dog放入到ArrayList 会先转成Object&#xff0c;在转…

YOLOv5-7.0改进(二)BiFPN替换Neck网络

前言 针对红绿灯轻量化检测&#xff0c;上一节使用MobileNetv3替换了主干网络&#xff0c;本篇将在使用BiFPN替换Neck的方式优化算法~ 往期回顾 YOLOv5-7.0改进&#xff08;一&#xff09;MobileNetv3替换主干网络 目录 一、BiFPN简介二、改进方法一第一步&#xff1a;在com…