首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-02-16 20:41
  • 2564
  • 9870
blog.csdn.net

【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.对话界面也加上用户的输入。

在这里插入图片描述

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

这个问题我困扰很久,于是上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组件就是实际使用的效果了。
在这里插入图片描述
特别说明:Mask物体上的Image组件不能删除,否则会失去遮挡效果。文字会溢出Mask的范围。
在这里插入图片描述
第四,在第三步的基础上,给Area上的VerticalLayout Group组件适当加一些Padding,会让文字和Area范围四周有一些空隙,显得更加美观。

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

中文字体这个网上有很多解决方案,我之前查了一下觉得很麻烦,暂时没精力(懒得)搞。
一句话我的理解就是要自己导入个字体,而且要尽可能包含多的中文字,不然遇到一个生字就会显示为下图这种方框。
在这里插入图片描述
第二份实习做过一些编辑工作,使用了两款商用字体:中文——阿里普惠体,英文——Monsterrate(拼写不确定),有精力的码友可以试试这两款字体。
但如果想偷懒的话,这件事情其实很好办——不要用TextMeshPro系列的UI,就用老款的Legacy的就没事了。
在这里插入图片描述
项目运行效果如图,文本框滑动效果还不是很完美,但比起之前好多了。
在这里插入图片描述
我个人总结了一下,自适应的核心就是放文本的Text+ContentSizeFitter,然后装Text的容器也加ContentSizeFitter,配合水平或者垂直的LayoutGroup。这样就已经有了自适应的文本框了。
在这个基础上再添加
Slider配合代码
,就是可滑动的自适应文本框了。
关于这个自适应文本框的其他学习视频链接:
https://www.bilibili.com/list/watchlater?oid=531535469&bvid=BV1Bu411G7Rj&spm_id_from=333.1296.top_right_bar_window_view_later.content.click

https://www.bilibili.com/list/watchlater?oid=723054334&bvid=BV1hS4y1Z7zD&spm_id_from=333.1296.top_right_bar_window_view_later.content.click
————————————————————————————————————————————

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

工程我发到了CSDN个人资源,有需要的朋友可以自行下载。

把参考的原博主的DeepSeekChat.cs代码和B站视频里的TextArea2.cs代码整合了,并且把所有按钮都换成了代码内找引用和绑定事件,就不需要每次改一下UI或者脚本还需要重新拖动UI了。

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
{
    //参考链接:http://iyenn.com/rec/1642689.html

    //DeepSeek API配置
    private string apikey = "你的apiKey"; //注意不要粘贴成别家大模型api了
    private string apiURL = "https://api.deepseek.com/chat/completions";
    // private string apiURL = "https://api.deepseek.com";

    //Unity UI元素
    private InputField userInputField;
    private Text chatOutputText;
    private Button sendButton;
    private RectTransform maskRectTra;
    private RectTransform areaRectTra;
    private Slider slider;

    //存储对话历史
    private List<Dictionary<string, string>> messages = new List<Dictionary<string, string>>();
    private void Awake()
    {
        userInputField = GameObject.Find("UserInputField").GetComponent<InputField>();
        chatOutputText = GameObject.Find("Mask/Area/chatOutputText").GetComponent<Text>();
        sendButton = GameObject.Find("SendButton").GetComponent<Button>();

        maskRectTra = GameObject.Find("Mask").GetComponent<RectTransform>();
        areaRectTra = GameObject.Find("Area").GetComponent<RectTransform>();
        slider = GameObject.Find("Slider").GetComponent<Slider>();

        // 为滑动条的值改变事件添加监听器
        slider.onValueChanged.AddListener(delegate { ToDrag(); });
    }
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
       
        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 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);
    }

    public void OnSendButtonClicked()
    {
        //用户没说话,return
        string userMessage = userInputField.text;
        if (string.IsNullOrEmpty(userMessage))
        {
            Debug.Log("Empty Input!");
            return;
        }
        //显示响应
        if (chatOutputText != null)
        {
            chatOutputText.text += "\nUser: " + userMessage;//将AI的回复追加到聊天输出文本框中,以显示给用户。
        }
        else
        {
            Debug.LogError("Text component not assigned.");
        }
        // 清空输入框
        userInputField.text = "";

        //添加用户消息到对话历史
        messages.Add(new Dictionary<string, string> { { "role", "user" }, { "content", userMessage } });

        //调用DeepSeek API
        StartCoroutine(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数据转换为字节数组bodyRaw
        request.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的字符串属性,该属性用于存储消息的实际内容
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
注:本文转载自blog.csdn.net的百里香酚兰的文章"https://blog.csdn.net/bailichen800/article/details/142655426"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

134
游戏
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top