怎样在Unity里面进行代码的查错

本文详细介绍了在Unity中编写代码时可能出现的两种错误类型:编译错误和逻辑错误,并提供了排查这些错误的有效方法,包括使用断点和打印输出。

继续讲一下在Unity写代码的查错问题,面对新手,老鸟请自动跳过。
对于程序基础比较薄弱的部分Unity使用者,经常会遇到某个项目不能运行,或者运行的时候没有出现自己想要得到的结果。这时候,很有可能是项目里面的代码报错了,或者代码写错了。代码报错的最直接表现是有错误打印,一般在Unity窗口最下面会出现错误打印,不过只会出现一行,我们可以打开Unity的打印输出窗口来确定有没有报错。
这里写图片描述
从Window标签页下面打开Console,就可以打开输出窗口:
比如这样的:
这里写图片描述
Console窗口也有一些操作的选项,比如左边有几个选项:
Clear:点击一下,可以把现有的所有打印清除掉
Collapse:可以把同一个地方输出的打印折叠起来,同类型打印只显示一条
Clear on Play:在每次运行项目的时候清除上一次输出的打印
Error Pause:当出现error级别的错误打印时,项目会暂停运行。

右边也有三个小按钮,一个是白色的气泡图标,一个是黄色的三角形图标,一个是红色的气泡图标,后面还跟着数字。
这三个按钮分别对应Unity的三个打印级别:Log、Warning和Error。后面的数字是该类型的打印的条数。如果想隐藏某个级别的打印,可以在这里操作。

介绍完打印输出窗口之后,接下来就说一下代码发生错误的2种类型。第一种,是编译错误。第二种是运行错误
1、编译错误:
这是指项目内的代码存在语法错误,导致了在编译的时候编不过。
这里写图片描述
很多新手可能都会问过别人,上图出现的提示是什么意思?为什么自己的项目不能运行?
这里所表达的意思,就是你的代码里面有编译级别的错误,需要先把报错的地方解决掉了,才能继续运行。
这时候我们需要打开Console窗口去看看究竟是哪里的代码写错了:
这里写图片描述
这里很清楚的说明了错误出现在哪一个类的哪一行,比如上面这个例子就是出现在DebugTest类的第9行。
这里写图片描述
我这样写代码,没用声明变量param1,就直接使用了,那么代码在编译的时候找不到param1的声明,所以报错了。只需要改成int param1 = 1+2;就不会报错了。编译级别的报错还有可能是因为你少打了个分号、少输入了一个括号之类导致的,所以需要小心。

另外一种错误,是逻辑错误,也就是运行时才会出现的错误。
比如我这样写代码:
这里写图片描述
然后编译通过了,但在运行时报错了:
这里写图片描述
我这个报错是特意模拟了跨越了很多个方法调用的报错,所以报错的时候会出现堆栈,显示出报错的方法调用的每一个过程,报错堆栈如红色箭头所示,是从下到上的。

上面已经说出了错误出现的2种常见的情况,针对于编译错误,其实很好查了,还没运行的时候就出现,哪里错了就查哪里的代码来修改。不好查的一般是逻辑错误。我们需要在运行的时候,知道某个方法在调用的时候的变量变化情况、输入输出的参数有没有异常之类的,才能定位到问题出在哪里。

这时候一般就有2种方法去查错,第一种是程序断点,另外一种是加打印输出。
怎样在Unity里断点查问题,我在2013年的时候已经写过文章说明了,过程大同小异,也就不再重复,可以跳转来看看:
http://liweizhaolili.blog.163.com/blog/static/162307442013214485190/
需要说的是,很多朋友不知道什么是断点,这一点很重要。在可以断点的环境下,断点是查问题最快的手段,是程序员最基本的技能之一。断点具体的含义可以自行百度一下,我简单的概括就是,可以在你写代码的地方的某一行点一下,然后程序运行到那一行的时候,自动停下来,让你去看看当前程序运行时,每个参数的具体的值是什么。然后你可以逐行代码继续往下运行,继续观察变量改变的情况,最后定位到出问题的地方。

能断点是一件很幸福的时候,但某些时候是不能断点的,比如你发布了release版本的安装包之后,你就不能再连接编辑器去断点了,或者写lua脚本的时候虽然也能通过一些特别的方法去断点,但我还是觉得麻烦于是也习惯用打印来定位问题。
刚才说过,Unity有三种打印级别,在代码里面可以主动的进行打印:
这里写图片描述
比如我这样写,得出的结果是:
这里写图片描述
Unity就会根据我指定的打印级别,把内容打印出来了。

有些情况下,由于打印的数量很多,你会发现没有办法很容易的找到你想打印的内容。这里我推荐一个插件,叫做ConsolsE。
这里写图片描述
这个插件的界面和一般的Console界面类似,不过多了一个过滤框,然后右边三种打印类型的最大数量不再是999+,而是实际打印的条数。

当我想查看某个关键字的打印内容时,我就可以在过滤框里面输入:
这里写图片描述

这里写图片描述
这时候,显示的内容就只会是包含我输入的关键字的打印结果了。

说了这么长,其实内容很少,总结一下,出现代码错误有2种情况,第一种是编译错误,第二种是逻辑错误。编译错误哪里报错改哪里,逻辑报错有2种查错手段,断点和打印。

<think> 首先,用户的问题是:“我想查找在Unity中不同程序级调用方法的解决方案 请问Unity中不同程序级调用方法的实现方式”。基于引用内容,我需要提供一个清晰的解答。 回顾引用内容: - 引用 [1]:讨论协程(coroutines),强调它们不是线程,而是在主线程上运行。建议避免阻塞操作和使用C#作业系统用于多线程。 - 引用 [2]:展示使用InvokeRepeating进行延迟调用方法的示例。 - 引用 [3]:解释Unity中的程序集(Assembly),包括使用.asmdef文件管理代码模块。 - 引用 [4]:Unity中C#和JavaScript方法的相互调用,指定文件位置要求。 - 引用 [5]:讨论Unity如何通过HTTP调用后端接口,涉及URL和URI的区别。 用户的问题焦点是“不同程序级调用方法的实现方式”,意思是不同级别或组件之间的方法调用解决方案。结合引用,这包括: - 协程调用 - 延迟调用(如Invoke) - 程序集之间的调用(通过程序集管理) - C#和JS之间的调用 - 外部接口调用(HTTP) 系统指令: - 所有行内数学表达式用$...$格式(但本问题不涉及数学,可能不需要)。 - 独立公式用$$...$$格式(同样可能不适用)。 - LaTeX语法正确(本回答可能不需要)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,逐步解决问题。 结构化回答: 1. **引言**:简述Unity中不同程序级调用方法的重要性。 2. **主要实现方式**:基于引用,列出并解释各种调用方法: - 协程调用(参考[^1]) - 延迟调用(如InvokeRepeating,参考[^2]) - 程序集之间的调用(使用.asmdef文件,参考[^3]) - C#和JavaScript之间的调用(参考[^4]) - 外部接口调用(HTTP,参考[^5]) 3. **详细解释**:对每种方式提供简要说明、示例或关键点。 4. **结论**:总结推荐做法。 5. **相关问题**:生成相关问题作为结尾。 确保引用标识: - 在解释每种方法时,自然地添加引用标识。 生成相关问题: - 类似于示例,例如: - 如何优化协程性能? - Unity中Invoke方法的局限是什么? - 如何在程序集中共享代码? - 等。 完整回答框架: - 用中文写。 - 结构清晰:标题、列表、代码块(如果有)。 - 代码块:如果适用,使用类似示例的格式。 最终回答: - 开头:直接回答问题。 - 主体:分点解释。 - 结尾:相关问题。 </think> ### Unity中不同程序级调用方法的实现方式 在Unity开发中,不同程序级(例如脚本之间、模块之间或外部系统)的方法调用是常见需求,涉及性能优化、代码组织和系统集成。基于提供的引用内容,我将系统性地解释几种核心实现方式,包括协程调用、延迟调用、程序集间调用、C#与JS互调以及外部接口调用。每种方式都遵循Unity的主线程模型和模块化原则,确保代码高效可靠。 #### 1. **协程调用(Coroutines)** - **描述**:协程是Unity中实现异步操作的轻量级解决方案,允许方法在多个帧中暂停和恢复。协程运行在主线程上,适用于延时执行或分步任务,但不适合CPU密集型操作以避免阻塞主线程[^1]。 - **实现方式**: - 使用 `IEnumerator` 接口和 `yield` 关键字。 - 通过 `StartCoroutine()` 启动协程。 - 示例代码: ```csharp using UnityEngine; using System.Collections; public class CoroutineExample : MonoBehaviour { void Start() { StartCoroutine(DelayedMethod()); } IEnumerator DelayedMethod() { yield return new WaitForSeconds(2f); // 延迟2秒 Debug.Log("协程方法执行完毕"); // 实际逻辑:如更新UI或游戏状态 } } ``` - **关键点**:协程不是线程;若需多线程优化,应使用C# Job System[^1]。 #### 2. **延迟调用(如Invoke和InvokeRepeating)** - **描述**:用于在指定时间后执行方法,或定期重复执行。适用于定时任务(如游戏状态刷新),操作简单但缺乏协程的灵活性[^2]。 - **实现方式**: - `Invoke("MethodName", delay)`:延迟调用一次。 - `InvokeRepeating("MethodName", initialDelay, repeatInterval)`:定期重复调用。 - 示例(基于引用[^2]): ```csharp using UnityEngine; public class TimerExample : MonoBehaviour { void Start() { InvokeRepeating("UpdateGameState", 2f, 1f); // 2秒后开始,每秒重复 } void UpdateGameState() { Debug.Log("Game state updated at: " + Time.time); // 示例逻辑:NPC行为或资源刷新 } } ``` - **关键点**:方法名必须以字符串形式传入,易出错;推荐使用协程或事件系统替代复杂场景[^2]。 #### 3. **程序集间调用(Assembly调用)** - **描述**:Unity使用程序集(Assembly)管理代码模块,通过Assembly Definition Files(.asmdef)实现不同程序集的隔离和调用。这能缩短编译时间、解决依赖冲突[^3]。 - **实现方式**: - 创建.asmdef文件定义程序集边界。 - 使用 `public` 访问修饰符暴露方法。 - 在其他程序集中引用目标程序集。 - 示例步骤: 1. 在Unity编辑器中,右键文件夹 > Create > Assembly Definition。 2. 在脚本A的程序集定义中,声明公共方法: ```csharp // 程序集A的脚本 public class ClassA { public void SharedMethod() { Debug.Log("跨程序集方法调用成功"); } } ``` 3. 在程序集B的脚本中调用: ```csharp // 程序集B的脚本 public class ClassB : MonoBehaviour { void Start() { var obj = new ClassA(); obj.SharedMethod(); // 直接调用 } } ``` - **关键点**:默认所有脚本编译到Assembly-CSharp.dll;.asmdef提升模块化,但需手动管理依赖[^3]。 #### 4. **C#与JavaScript互调** - **描述**:在Unity旧版或WebGL项目中,C#和JS脚本可能需相互调用。JS文件必须位于特定文件夹(如Plugins)[^4]。 - **实现方式**: - **C#调用JS方法**:使用 `Application.ExternalCall()` 或 `SendMessage()`。 - **JS调用C#方法**:JS文件需导入Unity,并通过 `unityInstance.SendMessage()` 触发。 - 示例(基于引用[^4]): - C#调用JS: ```csharp // C#脚本(位于任意文件夹) public class CS_Caller : MonoBehaviour { void Start() { Application.ExternalCall("jsMethod"); // 调用JS方法 } } ``` - JS调用C#(JS文件放在Plugins文件夹): ```javascript // JavaScript文件(Plugins/example.jslib) mergeInto(LibraryManager.library, { jsMethod: function() { console.log("JS方法被调用"); unityInstance.SendMessage("GameObject", "CS_Method"); // 调用C#方法 } }); ``` - **关键点**:现代Unity推荐使用C#统一开发,JS互调主要用于兼容旧项目或Web集成[^4]。 #### 5. **外部接口调用(HTTP API)** - **描述**:Unity通过HTTP与后端服务交互,实现数据增删改查。这涉及URL/URI处理,适合模块化开发[^5]。 - **实现方式**: - 使用 `UnityWebRequest` 类发送请求。 - 解析JSON响应(如用 `JsonUtility`)。 - 示例代码: ```csharp using UnityEngine; using UnityEngine.Networking; using System.Collections; public class APICaller : MonoBehaviour { void Start() { StartCoroutine(CallAPI("https://api.example.com/data")); } IEnumerator CallAPI(string url) { using (UnityWebRequest request = UnityWebRequest.Get(url)) { yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { Debug.Log("响应数据: " + request.downloadHandler.text); // 处理JSON数据 } else { Debug.LogError("请求失败: " + request.error); } } } } ``` - **关键点**:URL用于完整地址(如 `https://domain.com`),URI是标识符;建议使用异步协程避免阻塞主线程[^5]。 ### 总结 在Unity中,不同程序级调用方法的实现需根据场景选择: - **简单延时/重复**:使用 `Invoke` 或协程[^2]。 - **模块化与性能**:优先用程序集(.asmdef)管理跨脚本调用[^3]。 - **异步与外部集成**:协程或HTTP请求更合适[^1][^5]。 - **兼容性需求**:C#/JS互调时注意文件夹规则[^4]。 最佳实践是避免主线程阻塞,结合事件系统(如UnityEvent)提升灵活性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值