【AI学习笔记】基于Unity+DeepSeek开发的一些BUG记录解决方案

【AI学习笔记】基于Unity+DeepSeek开发的一些BUG记录&解决方案

背景前摇:(省流可不看)

Unity是大学学的,AI是研究生学的,DeepSeek是第一份实习偷师的,三合一的梦是最近开始做的,BUG是今天遇到并且解决的。

关于Unity和大模型结合的教程网上并不多,正好符合Unity+DeepSeek的我目前只看到这一篇《【Unity+AI01】在Unity中调用DeepSeek大模型!实现AI对话功能!》:https://blog.csdn.net/leoysq/article/details/139547284
传送门
阅读全文后,发现这篇文章工程量不大,涉及的知识点(UGUI,C#编程,DeepSeek的API接入)都是我目前比较熟悉的,这个项目可以跟着做,于是就愉快地开始了尝试。
在复现工程的时候我遇到了一些奇奇怪怪的问题,查了CSDN和百度以后发现相关的解释帖子还很少,决定自己开个帖子记录下来。

————————————————————————————————

BUG集锦:

Unity版本:Unity6

1.Unity报错Error: HTTP/1.1 401 Unauthorized

解决方法:看看API有没有写对。
UnityEngine.Debug:LogError (object)
DeepSeekChat/d__8:MoveNext () (at Assets/Scripts/DeepSeekChat.cs:93)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr),
这种联网报错我处理经验不多,看红感叹号出来人都麻了,GPT查了一下,这个报错表示未授权
在这里插入图片描述
上网搜了一圈,没有我这种情境下报错的解决方案,我先在原作者的评论区下方留言等待好心人答复,也厚着脸皮通过DeepSeek官网的聊天渠道反馈了BUG。
很快我就收到了邮件和聊天界面双重回复——未授权一般是API没写对
在这里插入图片描述
我觉得DeepSeek的这一点做得特别好,聊天界面和邮件都可以看到过往记录并且回复,这样沟通就很方便自由。
在这里插入图片描述
我检查了一下我的代码,确实,API抄错了,写成隔壁智谱清言的了。
同时我也获得了教程原作者的友善回复:
在这里插入图片描述

2.base_url连接报错Error: HTTP/1.1 404 Not Found

解决方法:根据需要,换形如https://api.deepseek.com/chat/completions 这样的完整接口地址
UnityEngine.Debug:LogError (object)
DeepSeekChat/d__8:MoveNext () (at Assets/Scripts/DeepSeekChat.cs:94)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
当时在跟着写代码的时候,我就发现一件很奇怪的事情——原教程作者写的url很特别。
我之前用Python+DeepSeek的时候,用的是官网的base_url,一切都很顺利。
https://platform.deepseek.com/api-docs/zh-cn/
DeepSeek官网传送门
在这里插入图片描述
但是我发现,如果Unity里面也用这个链接,Unity会报错。
在这里插入图片描述
但如果用教程作者老师的这个我没见过的“野生”链接,就没事。
在这里插入图片描述
在这里插入图片描述
这我就不明白了,因为我之前用python做过DeepSeek开发,一直照着官方文档这么用都没问题。
于是我又厚着脸皮请教了DeepSeek的老师们。
在这里插入图片描述
很快我得到了详细的答案:
在这里插入图片描述
我理解的一句话总结是:使用Openai python sdk最省事,base_url能应付所有问题。
在这里插入图片描述在这里插入图片描述
但如果是其他平台,就得根据功能需要换具体的链接了。
比如我现在用Unity做的功能可以归类为【对话补全】,那么就要需要这个页面。
在这里插入图片描述
https://platform.deepseek.com/api-docs/zh-cn/api/create-chat-completion/
传送门
复制下图红框所示的链接,也就是原教程的”野生“链接:
在这里插入图片描述
在这里插入图片描述
这样就可以在Unity里顺利调用DeepSeek的API啦!

3.TextMeshPro不显示AI的回复

解决方案:看看UGUI的排列、颜色
按代码逻辑,成功发送消息以后,AI应该回复,并且在背景板上显示出来。
但现在什么也没有,我首先排除了最不应该的UI层级错误(背景白色image把text盖住了),不是这个原因。
在这里插入图片描述
然后也Debug.Log了各个核心环节,发现AI其实回复了,但我的TextMeshPro没有正常显示内容。
在这里插入图片描述
所以大概BUG是从下面这一步开始的。
在这里插入图片描述
我按GPT给的办法加了个存在与否的判断,但我感觉不该是这种错误,因为确实把按钮拖进了脚本对应位置,要是没找到对象的话早ERROR了。
在这里插入图片描述
在这里插入图片描述
我觉得还是UI本身的问题,想着会不会还是我层级搞错了,于是就尝试把image隐藏看看是不是字在背后,结果——
破案了,原来是白色字体和背景融为一体了……
啥好玩意默认字体是白色啊,我以为默认黑色字来着,真是给我整的哭笑不得。
在这里插入图片描述
于是我给image换了个底色,问题解决了。
在这里插入图片描述
以及,因为要显示聊天记录,这个TextMeshPro不会在发信息后清空原来的内容,要美观的话记得把默认的New Text删除了。

4.安装Unity的可集成HTTP请求库(原作者的NuGet For Unity插件)

解决方法:从GitHub下载NuGet For Unity插件:https://github.com/GlitchEnzo/NuGetForUnity

Unity的HTTP请求库有UnityWebRequest,或第三方库如LitJson、Newtonsoft.Json等,可以将这三者类比为Python的Request、BeautifulSoup、Scrapy库,它们可以实现(指定数据格式)+(指定目标网页)的情况下爬取网站内容的任务
-UnityWebRequest:Unity内置的HTTP客户端库,不需要额外安装,集成在Unity引擎中。其他的第三方库需要自己安装,比如原作者使用的NuGet For Unity。
在这里插入图片描述
第一步的下载链接:https://github.com/GlitchEnzo/NuGetForUnity
传送门
这个下载下来是个Unitypackage,直接导入Unity就行。
第4步的详细操作如下:
在这里插入图片描述

其他可能的优化方案

已实现:

1.点击Send按钮以后自动清空InputField的内容。

2.对话界面也加上用户的输入。

在这里插入图片描述
未实现:

1.显示历史记录的TextMeshPro添加滚轮滑动条。

说来惭愧,学了这么久Unity,做个滑动查看的文本框还一直没有清晰的解决方案。
知道要加Scrollbar组件,并且把TextMeshPro这个要滑动查看的内容放在Content下面,但是经常不显示滑动效果。这历史遗留问题都很久了,我都还不是很确定该怎么做。
UGUI这个操作我会:在这里插入图片描述
今天在GPT的帮助下加了段代码,算是勉强实现了能滑动的功能,不过不是很美观,只能说将就。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
而且如果输入的文本太长了,还是会出现滑动条显示不全的问题,并且Unity会报错:Assertion failed on expression: ‘!(o->TestHideFlag(Object::kDontSaveInEditor) && (options & kAllowDontSaveObjectsToBePersistent) == 0)’
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
在这里插入图片描述
在这里插入图片描述
这个问题我目前还不知道怎么解决。
二次编辑:
上b站搜集了一些教程,发现这个问题很早之前就有人提了。
在这里插入图片描述
随机尝试后,发现这位UP的方法跟着做完全程以后,能实现我需要的功能:
https://www.bilibili.com/video/BV1Wv41137ZU?p=2&vd_source=cdfd0a0810bcc0bcdbcf373dafdf6a82
传送门
在这里插入图片描述
在这里插入图片描述
具体操作直接看这位大佬的视频就行,这里我补记几个我个人觉得比较关键的地方:
第一,用滑动条滑动的比例 x 可滑动的总长度 = 新的文字展示区域y轴相对父物体的坐标
在这里插入图片描述
这段代码有点抽象,让AI智谱清言帮忙解释一下:
在这里插入图片描述
第二,Text组件本身Vertical Overflow这里要设置为Overflow,这样多余的文字才能在下方显示出来,而不是就被原来的大小框死不显示, 这样就没法滑动了啊。
在这里插入图片描述
Area会添加一个Content Size Fitter组件,这个VerticalFit要设置为Preferred Size,不然这个Area没法和Text一样随地大小变,然后又会滑动了个寂寞。
在这里插入图片描述
第三,给Mask对象添加一个Mask组件,超出Area的文字就看不到了,这样就实现了限定范围内滑动的效果。
在这里插入图片描述
外面这一圈突兀的Mask只是用来直观展示效果的,如果是自己做项目的话把Area和Mask设置为同样大小(见下图),添加完Mask组件就是实际使用的效果了。
在这里插入图片描述
第四,在第三步的基础上,给Area上的VerticalLayout Group组件适当加一些Padding,会让文字和Area范围四周有一些空隙,显得更加美观。
在这里插入图片描述
TextArea2完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class TextArea2 : MonoBehaviour
{private RectTransform maskRectTra;private RectTransform areaRectTra;private Slider slider;private void Awake(){maskRectTra = this.transform.Find("Mask").GetComponent<RectTransform>();areaRectTra = this.transform.Find("Mask/Area").GetComponent<RectTransform>();slider = this.transform.Find("Slider").GetComponent<Slider>();// 为滑动条的值改变事件添加监听器slider.onValueChanged.AddListener(delegate { ToDrag(); });}public void ToDrag(){//当滑动变化时,修改Area的y轴位置//获取滑动值(一个0-1的比例)float tempSliderValue = slider.value;//获取总滑动高度(一个具体的长度值)float tempTotalHeight = areaRectTra.sizeDelta.y - maskRectTra.sizeDelta.y;//x轴不变,设置y轴的新坐标(相对父物体的)等于前两者相乘,注意位置用的锚点坐标areaRectTra.anchoredPosition = new Vector2(areaRectTra.anchoredPosition.x, tempSliderValue * tempTotalHeight);}
}

2.加入中文字库允许中文交互。

中文字体这个网上有很多解决方案,我之前查了一下觉得很麻烦,暂时没精力(懒得)搞。
一句话我的理解就是要自己导入个字体,而且要尽可能包含多的中文字,不然遇到一个生字就会显示为下图这种方框。
在这里插入图片描述
第二份实习做过一些编辑工作,使用了两款商用字体:中文——阿里普惠体,英文——Monsterrate(拼写不确定),有精力的码友可以试试这两款字体。
————————————————————————————————————————————

添加注释&功能后的完整代码:

工程我发到了CSDN个人资源,有需要的朋友可以自行下载。
https://download.csdn.net/download/bailichen800/89822761
传送门

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Networking;
using Newtonsoft.Json;
using System.Text;
using TMPro;
using UnityEngine.UI;public class DeepSeekChat : MonoBehaviour
{//参考链接:https://blog.csdn.net/leoysq/article/details/139547284//DeepSeek API配置private string apikey = "你的API"; //注意不要粘贴成别家api了private string apiURL = "https://api.deepseek.com/chat/completions";// private string apiURL = "https://api.deepseek.com";//Unity UI元素public TMP_InputField userInputField;public TextMeshProUGUI chatOutputText;public Button sendButton;public ScrollRect scrollRect; // 引用 ScrollRect 组件//存储对话历史private List<Dictionary<string, string>> messages = new List<Dictionary<string, string>>();// Start is called once before the first execution of Update after the MonoBehaviour is createdvoid Start(){// 配置 ScrollRectscrollRect.content = chatOutputText.rectTransform; // 将 TextMeshProUGUI 的 RectTransform 设置为 ContentscrollRect.vertical = true; // 启用垂直滚动if (sendButton != null){sendButton.onClick.AddListener(OnSendButtonClicked);Debug.Log("Click Send Button!");}//初始化系统消息messages.Add(new Dictionary<string, string> { { "role", "system" }, { "content", "You are a helpful assistant." } });}public void OnSendButtonClicked(){//用户没说话,returnstring userMessage = userInputField.text;if (string.IsNullOrEmpty(userMessage)){Debug.Log("Empty Input!");return;}//显示响应if (chatOutputText != null){chatOutputText.text += "\nUser: " + userMessage;//将AI的回复追加到聊天输出文本框中,以显示给用户。}else{Debug.LogError("TextMeshPro component not assigned.");}// 清空输入框userInputField.text = "";//添加用户消息到对话历史messages.Add(new Dictionary<string, string> { { "role", "user" }, { "content", userMessage } });//调用DeepSeek APIStartCoroutine(CallDeepSeekAPI());}private IEnumerator CallDeepSeekAPI(){Debug.Log("CallDeepSeekAPI!");//创建请求数据var requestData = new  //定义一个匿名对象requestData{model = "deepseek-chat", //表示使用的模型,这里是"deepseek-chat"messages = messages, //一个变量,应该是一个包含消息的数组,用于与聊天模型交互stream = false //一个布尔值,表示是否以流的形式接收响应,这里设置为false};string jsonData = JsonConvert.SerializeObject(requestData); //将requestData对象序列化为JSON格式的字符串jsonData。//创建UnityWebRequest 向指定的API URL发送一个POST请求,请求体为JSON格式的数据,并设置必要的请求头UnityWebRequest request = new UnityWebRequest(apiURL, "POST"); //创建一个网络请求对象request,指定请求的URL和请求方法(POST)byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData); //将序列化后的JSON数据转换为字节数组bodyRawrequest.uploadHandler = new UploadHandlerRaw(bodyRaw); //创建一个UploadHandlerRaw对象,并将其赋值给request的uploadHandler属性,这样就将JSON数据作为请求体发送request.downloadHandler = new DownloadHandlerBuffer(); //创建一个DownloadHandlerBuffer对象作为request的downloadHandler,用于接收服务器响应的数据request.SetRequestHeader("Content-Type", "application/json");  //通过SetRequestHeader方法设置请求的Content-Type为application/json,表明发送的是JSON数据request.SetRequestHeader("Authorization", "Bearer " + apikey); //同时设置Authorization请求头,包含一个API密钥apiKey,用于身份验证。//发送请求  发送一个网络请求到服务器,获取AI模型的回复,并将该回复显示在聊天界面中,同时更新对话历史。如果请求失败,则输出错误信息。yield return request.SendWebRequest(); //使用yield return来异步发送网络请求,而不会阻塞主线程if (request.result == UnityWebRequest.Result.Success) //检查网络请求是否成功{Debug.Log("Success!");//解析响应var response = JsonConvert.DeserializeObject<DeepSeekResponse>(request.downloadHandler.text); //将下载的响应文本(JSON格式)反序列化为一个DeepSeekResponse类型的对象string botMessage = response.choices[0].message.content; //从反序列化后的响应对象中提取AI回复的消息内容。Debug.Log("botMessage = " + botMessage);//显示响应if (chatOutputText != null){chatOutputText.text += "\nAI: " + botMessage;//将AI的回复追加到聊天输出文本框中,以显示给用户。}else{Debug.LogError("TextMeshPro component not assigned.");}//添加AI消息到对话历史messages.Add(new Dictionary<string, string> { { "role", "assistant" }, { "content", botMessage } }); // 将AI的回复添加到消息列表中}else{Debug.LogError("Error: " + request.error);}}// 使用这些类,Unity将能够将JSON响应反序列化为一个DeepSeekResponse对象,你可以通过DeepSeekResponse对象访问choices数组,然后访问第一个Choice对象的message属性,最后得到content字符串,即AI的回复。[System.Serializable] //标记为[System.Serializable]的公共类,表示它可以被Unity的序列化系统序列化和反序列化。public class DeepSeekResponse{public Choice[] choices; //包含一个名为choices的数组,该数组应该包含多个Choice对象}[System.Serializable]public class Choice{public Message message; //包含一个名为message的属性,该属性是Message类型}[System.Serializable]public class Message{public string content; //包含一个名为content的字符串属性,该属性用于存储消息的实际内容}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】什么是哈希表(散列表)?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;哈希表的概念 &#x1f4cc;哈希函数的构造方法 &#x1f38f;直接定址法 &#x1f38f;除留余数法 &#x1f38f;平方取中法 &#x1f38f;折叠法 &#x…

Bolt.new:终极自动化编程工具

兄弟们&#xff0c;终极写代码工具来了—— Bolt.new&#xff01;全方位的编程支持&#xff1a; StackBlitz 推出了 Bolt․new&#xff0c;这是一款结合了 AI 与 WebContainers 技术的强大开发平台&#xff0c;允许用户快速搭建并开发各种类型的全栈应用。 它的主要特点是无需…

前端reactvue3——实现滚动到底加载数据

文章目录 ⭐前言⭐react 实现滚动加载⭐vue3 实现滚动加载⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享 前端react&vue3——实现滚动加载&#xff08;到底部加载&#xff09; scrollTop 属性 一个双精度浮点值&#xff0c;表示元素当前从原点垂直…

安全运营中心 (SOC) 团队对其安全工具感到失望

Vectra AI 表示&#xff0c;安全运营中心 (SOC) 从业人员认为&#xff0c;由于太多孤立的工具和缺乏准确的攻击信号&#xff0c;他们在检测和确定真实威胁的优先级方面正在失败。 人们对供应商的不信任感日益加深&#xff0c;认为供应商的工具在发现真正的攻击方面起的阻碍作用…

ctfshow-web入门(信息收集,持续更新中。。)

写在之前:近期打了个比赛,备受打击,入手了vip账号进修,加油! 文章目录 ctfshow-web1查看源代码ctfshow-web2burp抓包ctfshow-web3burp抓包ctfshow-web4访问robots.txtctfshow-web5dirscarch扫描PHPS文件泄露ctfshow-web6dirscarch扫描ctfshow-web7dirscarch扫描ctfshow-w…

【STM32开发之寄存器版】(六)-通用定时器中断

一、前言 STM32定时器分类 STM32103ZET6具备8个定时器TIMx(x 1,2,...,8)。其中&#xff0c;TIM1和TIM8为高级定时器&#xff0c;TIM2-TIM6为通用定时器&#xff0c;TIM6和TIM7为基本定时器&#xff0c;本文将以TIM3通用定时器为例&#xff0c;分析STM32定时器工作的底层寄存器…

You must konw JS!!(超详细的javascript套餐,适合计算机专业有基础的,包含常见前端开发面试题)

1.起源 JavaScript 起源于 1995 年&#xff0c;当时它主要是为了满足网页交互的需求而被创建。它最初的设计目的是为了让网页开发者能够在网页中添加一些简单的交互效果和动态内容。在那个时期&#xff0c;网页大多是静态的&#xff0c;而 JavaScript 的出现为网页带来了新的活…

jmeter学习(7)beanshell

beanshell preprocessor 发送请求前执行 beanshell postprocessor 发送请求前执行 获取请求相关信息 String body sampler.getArguments().getArgument(0).getValue(); String url sampler.getPath(); 获取响应报文 String responseprev.getResponseDataAsString(); 获…

CMake 教程跟做与翻译

目录 STEP 1: 入门与理解 cmake_minimum_required设置CMake版本的最小值 project声明工程属性 add_executable添加可执行文件 使用CMake构建工程 根据自己的构建工具自行构建 Reference STEP 1: 入门与理解 我们起手的&#xff0c;最基本的 CMake 项目是从单个源代码文件…

【Blender Python】1.概述和基础使用

概述 众所周知&#xff0c;Blender是一款开源免费的3D建模软件&#xff08;当然不限于3D建模&#xff09;。在Blender中&#xff0c;可以使用其内置的Python解释器执行Python代码&#xff0c;用于程序化的生成网格以及其他内容。你可以基于此创建Blender插件。 这个系列就是快…

Electron桌面应用打包现有的vue项目

1 环境准备 Node&#xff1a;v16.20.2&#xff08;本地vue项目nodejs版本&#xff09;Electron&#xff1a;22.3.7vue&#xff1a;2 版本管理 2 Vue项目准备 更新相关依赖npm install --registry https://registry.npmmirror.com/npm run dev 3、引入Electorn 安装指定版…

算法剖析:双指针

文章目录 双指针算法一、 移动零1. 题目2. 算法思想3. 代码实现 二、 复写零1. 题目2. 算法思想3. 代码实现 三、 快乐数1. 题目2. 算法思想3. 代码实现 四、 盛水最多的容器1. 题目2. 算法思想3. 代码实现 五、有效三角形的个数1. 题目2. 算法思想3. 代码实现 六、 和为 s 的两…

UART驱动学习三(TTY驱动部分源码解析)

目录 全局框架图一、tty_io.c 分析1. 关键数据结构和定义2. 文件操作结构体3. 初始化和注册4. 读写操作5. 挂起和恢复6. 信号处理7. 设备类8. 控制台通知9. 辅助函数10. 代码功能11. 带有注释的部分tty_io.c源码 二、tty_ldisc.c 分析1. 关键数据结构和定义2. 行规程操作函数3.…

Android车载——VehicleHal运行流程(Android 11)

1 概述 本篇主要讲解VehicleHal的主要运行流程&#xff0c;包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。 2 获取属性流程 2.1 获取属性流程源码分析 作为服务注册到hwServiceManager中的类是VehicleHalManager&#xff0c;所以&#xff0c;CarServic…

判断是否为二叉排序树(二叉搜索树,二叉查找树)

1.判断给定的二叉树是否为二叉排序树&#xff0c;如果是返回1&#xff0c;不是返回0。 思想&#xff1a; 二叉树是左子树<根<右子树。中序遍历是递增有序&#xff0c;可以通过比较当前结点与前驱关系来进行判断。 代码&#xff1a; //pre为全局变量&#xff0c;保存当…

数学与生活

多学科交叉 信号处理 小波 经济 政策 计算机 统计 信号处理与市场分析 经济与数据分析 政策与统计 过去的数学家没有一个是纯粹的数学家&#xff1b;生活中各方面工程的&#xff0c;物理的&#xff0c;天文&#xff0c;地理的&#xff0c;赌博&#xff0c;政治的&#xff1b…

5-25 JQuery

jQuery简介 jQuery是什么 jQuery基本语法 测试jQuery <head> <meta charset"utf-8"> <title>无标题文档</title><script type"text/javascript" src"jquery-3.5.1.js"></script><script type"tex…

FastAdmin Apache下设置伪静态

FastAdmin Apache下设置伪静态 一、引言 FastAdmin 是一个基于ThinkPHP和Bootstrap框架开发的快速后台开发框架&#xff0c;它以其简洁、高效、易于扩展的特点&#xff0c;广受开发者的喜爱。在部署FastAdmin项目时&#xff0c;为了提高访问速度和用户体验&#xff0c;我们通…

Redis介绍及整合Spring

目录 Redis介绍 Spring与Redis集成 Redis介绍 Redis是内存数据库&#xff0c;Key-value型NOSQL数据库&#xff0c;项目上经常将一些不经常变化并且反复查询的数据放入Redis缓存&#xff0c;由于数据放在内存中&#xff0c;所以查询、维护的速度远远快于硬盘方式操作数据&#…

【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解

目录 1、FileChannel (1&#xff09;获取 FileChannel (2&#xff09;读取文件 (3&#xff09;写入文件 (4&#xff09;关闭通道 (5&#xff09;当前位置与文件大小 (6&#xff09;强制写入磁盘 2、两个 FileChannel 之间的数据传输 (1&#xff09;使用 transferTo()…