65.9K
CodeProject 正在变化。 阅读更多。
Home

将聊天机器人集成到 Unity 中(C#)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2020年5月7日

CPOL

8分钟阅读

viewsIcon

32229

downloadIcon

612

使用设备上的机器人开发框架将聊天机器人集成到 Unity 中。

引言

本文采用将聊天机器人集成到Unity设备中的方法。我们不会调用在线API来为用户消息生成响应,而是直接集成一个离线聊天机器人框架,这样就不需要依赖任何在线服务或订阅来生成智能响应。为此,我们将使用一个流行的设备机器人开发工具Oscova,并编写一个极其简单的“你好机器人”对话来测试我们的Unity机器人集成。

必备组件

本文将是我上一篇文章的良好后续阅读,在那篇文章中我创建了一个模拟的桌子预订机器人。您可以利用这些知识,将其应用于本文的知识库创建部分,并进行扩展。本文显然假设读者在Unity游戏开发方面具有一定的专业知识。同时,我将假设您满足以下条件:

  • Unity脚本基础知识
  • 在Visual Studio或Visual Studio Code中工作的经验
  • C#/.NET编程基础知识

安装Unity

如果您还没有安装Unity

  • 访问 https://unity3d.com/get-unity/download 并单击 Download Unity Hub 下载Unity Hub,它将指导您安装Unity。
  • 确保在“Add-On”窗口中,选择 **Microsoft Visual Studio Community 2019**,如果您还没有安装它的话。

创建Unity项目

  • 启动Unity,选择3D项目模板,创建一个新的3D项目。
  • 您可以将项目命名为MyFirstUnityBot

  • 单击 Create 按钮。
  • 您的Unity项目现在将被创建。

创建聊天机器人UI

我们需要三个重要的UI元素来创建Unity中的聊天机器人界面。

  • 显示面板 - 所有聊天/消息都将显示在这里
  • 输入框 - 用户将在此输入内容
  • 发送按钮 - 用户点击以提交消息的按钮

创建我们的显示系统

  • Hierarchy窗口中右键单击,选择UI,然后选择Canvas
  • 保持Canvas选中状态,右键单击,选择UI,然后选择Scroll View
  • Scroll View的宽度和高度分别更改为500300

    Unity Chat bot panel

一旦生成用户或机器人的消息,我们需要将其添加到Scroll View中。为此,我们将创建Text UI元素的预制件,并在每次需要将消息添加到Scroll View时使用它。

  • 再次,在ViewportContent项内右键单击,选择UI,然后选择Text
  • Text UI元素的对齐方式设置为Bottom-Left,并将字体大小增加到16
  • Vertical Overflow下选择Overflow
  • 将该项拖动到Assets中将其转换为预制件,并删除Hierarchy窗口中的该项。

在上图的截图中,您可以看到我添加了两个新组件,名为Content Size FilterVertical Layout Group,并调整了它们的属性以帮助消息正确地适应Scroll View。

创建一个新的Input Field和一个Button,并将它们并排放置,如下所示。我们将使用这两个UI元素将用户消息发送到我们的机器人。

导入OSCOVA - Syn Bot框架

与我们在C#项目中通过NuGet导入库不同,Unity项目在每次更改脚本时都会重新编译,因此我们不能直接使用NuGet包。我们将改为使用一个包含框架库及其依赖项的Unity包。

下载 Syn.Bot Unity Package

  • Assets中右键单击,选择Import Package,然后选择Custom Package
  • 当导入窗口出现时,请确保包中的所有项都已选中。
  • 单击Import

Syn.Bot Unity Bot package

编写机器人接口脚本

为了将UI元素与我们的机器人绑定,我们将继续编写脚本。

  • 在Hierarchy窗口中创建一个名为GameManagerEmpty Object
  • 向其添加一个Script组件,并将其命名为相同。
  • 在Asset Panel中双击新添加的GameManager脚本。
  • 这将打开一个Visual Studio项目。

GameManager script to work with Oscova

还记得我们之前提到的添加到Scroll ViewMessage对象吗?好吧,让我们创建一个类来保存用户和机器人的消息,并使其能够通过Text预制件在Unity UI中显示。

下面的Message类将以string格式保存用户/机器人消息,对Text预制件的引用,以及一个可选的MessageType枚举,这将帮助我们用不同的颜色来装饰消息。

using Syn.Bot.Oscova;
using Syn.Bot.Oscova.Attributes;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Message
{
    public string Text;
    public Text TextObject;
    public MessageType MessageType;
}

public enum MessageType
{
    User, Bot
}

public class GameManager : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }
}

现在消息持有者已准备好,我们将创建一些公共字段,这些字段将在Unity中绑定。我们还将创建一个OscovaBot对象,该对象将在游戏运行时实例化。将下面的代码放在Start()方法上方。

OscovaBot MainBot;

List<Message> Messages = new List<Message>();

public GameObject chatPanel, textObject;
public InputField chatBox;
public Color UserColor, BotColor;

保存并关闭Visual Studio项目,然后在Hierarchy窗口中选择GameManager。我们现在将字段与相应的组件连接起来。

  • Chat Panel对象设置为场景中的Content
  • Text Object设置为Assets中的Text预制件
  • Chat Box字段设置为场景中的Input Field

我们在Message类中创建了一个MessageType属性,以便用户可以在视觉上区分两者。将User和Bot颜色对象的值设置为您选择的任何颜色,但请记住将Alpha值设置为255,以便它保持可见。

显示用户/机器人消息

为了在Scroll View的内容属性中显示用户和机器人的消息,我们需要将对象放置在其变换中。此外,我们还将为每种消息类型分配颜色。

在下面的代码中,我们创建了一个Message类的新实例,将其消息字符串分配给它,并在chatPanel的变换中实例化它。Messages列表用于跟踪消息数量。如果数量超过25,我们只删除第25个 last 项目。最后,根据MessageType,我们为text.<text>选择一种颜色。

public void AddMessage(string messageText, MessageType messageType)
{
    if (Messages.Count >= 25)
    {
        //Remove when too much.
        Destroy(Messages[0].TextObject.gameObject);
        Messages.Remove(Messages[0]);
    }

    var newMessage = new Message { Text = messageText };

    var newText = Instantiate(textObject, chatPanel.transform);

    newMessage.TextObject = newText.GetComponent<Text>();
    newMessage.TextObject.text = messageText;
    newMessage.TextObject.color = messageType == MessageType.User ? UserColor : BotColor;

    Messages.Add(newMessage);
}

处理用户消息

在下一个方法中,我们将创建一个逻辑来控制用户将消息发送到机器人后会发生什么。简而言之,我们将把消息发送给机器人进行处理。我们将此方法称为SendMessageToBot()

public void SendMessageToBot()
{
    var userMessage = chatBox.text;

    if (!string.IsNullOrEmpty(userMessage))
    {
        Debug.Log($"OscovaBot:[USER] {userMessage}");
        AddMessage($"User: {userMessage}", MessageType.User);        
        var request = MainBot.MainUser.CreateRequest(userMessage);
        var evaluationResult = MainBot.Evaluate(request);
        evaluationResult.Invoke();

        chatBox.Select();
        chatBox.text = "";
    }
}

在上面的方法中,我们首先从Input Field的文本属性中获取用户消息。然后,我们检查消息是否为空字符串。

接下来,我们创建一个包含用户消息的用户请求,获取评估结果,并对其调用Invoke()方法。这将迫使机器人选择匹配度最高的意图并执行它。反过来,这将生成一个响应,我们稍后将为此添加一个事件处理程序。

在最后一部分,我们只选择Input Field (chatBox) 并通过将其文本值设置为空字符串来清除其内容。

连接到按钮点击

每当用户单击Send按钮或在Input Field中按下Enter键时,就需要调用此SendMessageToBot()方法。

  • 在Hierarchy中选择按钮元素。
  • On Click()下,在场景中选择GameManager
  • GameManager对象下,选择SendMessageToBot()

Unity Bot message to Oscova Bot

连接到Enter键按下

除了在按下Send按钮时处理用户消息外,当用户在Input Field中按下Enter键时,我们也需要处理用户消息。为此,我们只需将Update()方法更改为以下内容:

void Update()
{
    if (Input.GetKeyDown(KeyCode.Return))
    {
        SendMessageToBot();
    }
}

简单的“Hello Bot”对话

我们使用Oscova来构建机器人的知识库(KB)。您可以在SIML、Syn Workspace(通过Oryzer Studio)或直接用纯C#代码中创建KB。为了简单起见,并避免文章过于冗长,我们将直接在脚本中用C#创建一个简单的对话。

我们将首先创建一个继承Dialog类的BotDialog类,在该类中我们将创建一个void Hello()方法,并用Expression属性装饰它,在该属性中我们将指定示例用户消息为Hello Bot。然后,该方法将返回Hello User!作为机器人响应。

public class BotDialog: Dialog
{
    [Expression("Hello Bot")]
    public void Hello(Context context, Result result)
    {
        result.SendResponse("Hello User!");
    }
}

如果上面的代码不太容易理解,那是因为有几个概念您需要熟悉。最近,我发布了一篇更深入的文章,介绍了如何在不编写太多C#代码的情况下创建机器人的知识库。

如果您想了解更多关于对话创建的信息,以下文章将对您更有帮助:

现在我已经为您指明了进一步学习的方向,让我们继续更新我们的Start()方法来完成所有工作。

初始化机器人实例

我们需要将一些东西整合在一起才能让我们的机器人工作。首先,我们需要实例化我们的机器人对象,然后我们需要处理机器人生成响应时的情况。

在我们的代码中,我们创建了OscovaBot的一个新实例,并将其分配给MainBot字段。然后,我们将之前创建的BotDialog添加到机器人的Dialogs集合中,并开始训练。最后,我们向ResponseReceived事件添加一个事件处理程序。该事件处理程序不做任何事情,只是调用AddMessage()方法并传递机器人的响应。

void Start()
{
    try
    {
        MainBot = new OscovaBot();
        OscovaBot.Logger.LogReceived += (s, o) =>
        {
            Debug.Log($"OscovaBot: {o.Log}");
        };

        MainBot.Dialogs.Add(new BotDialog());
        MainBot.Trainer.StartTraining();

        MainBot.MainUser.ResponseReceived += (sender, evt) =>
        {
            AddMessage($"Bot: {evt.Response.Text}", MessageType.Bot);
        };
    }
    catch (Exception ex)
    {
        Debug.LogError(ex);
    }
}

好了,编码完成!

整体代码

完成后,您的代码(希望)看起来会像这样:

using Syn.Bot.Oscova;
using Syn.Bot.Oscova.Attributes;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Message
{
    public string Text;
    public Text TextObject;
    public MessageType MessageType;
}

public enum MessageType
{
    User, Bot
}

public class BotDialog: Dialog
{
    [Expression("Hello Bot")]
    public void Hello(Context context, Result result)
    {
        result.SendResponse("Hello User!");
    }
}

public class GameManager : MonoBehaviour
{
    OscovaBot MainBot;

    public GameObject chatPanel, textObject;
    public InputField chatBox;

    public Color UserColor, BotColor;

    List<Message> Messages = new List<Message>();

    // Start is called before the first frame update
    void Start()
    {
        try
        {
            MainBot = new OscovaBot();
            OscovaBot.Logger.LogReceived += (s, o) =>
            {
                Debug.Log($"OscovaBot: {o.Log}");
            };

            MainBot.Dialogs.Add(new BotDialog());
            //MainBot.ImportWorkspace("Assets/bot-kb.west");
            MainBot.Trainer.StartTraining();

            MainBot.MainUser.ResponseReceived += (sender, evt) =>
            {
                AddMessage($"Bot: {evt.Response.Text}", MessageType.Bot);
            };
        }
        catch (Exception ex)
        {
            Debug.LogError(ex);
        }
    }

    public void AddMessage(string messageText, MessageType messageType)
    {
        if (Messages.Count >= 25)
        {
            //Remove when too much.
            Destroy(Messages[0].TextObject.gameObject);
            Messages.Remove(Messages[0]);
        }

        var newMessage = new Message { Text = messageText };

        var newText = Instantiate(textObject, chatPanel.transform);

        newMessage.TextObject = newText.GetComponent<Text>();
        newMessage.TextObject.text = messageText;
        newMessage.TextObject.color = messageType == MessageType.User ? UserColor : BotColor;

        Messages.Add(newMessage);
    }

    public void SendMessageToBot()
    {
        var userMessage = chatBox.text;

        if (!string.IsNullOrEmpty(userMessage))
        {
            Debug.Log($"OscovaBot:[USER] {userMessage}");
            AddMessage($"User: {userMessage}", MessageType.User);
            var request = MainBot.MainUser.CreateRequest(userMessage);
            var evaluationResult = MainBot.Evaluate(request);
            evaluationResult.Invoke();

            chatBox.Select();
            chatBox.text = "";
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Return))
        {
            SendMessageToBot();
        }
    }
}

试用机器人

闲话不多说,让我们保存并关闭Visual Studio项目,以便Unity重新编译并提交更改。

Game模式下,在Input Field中输入Hello Bot,然后按Send按钮。看!您的机器人的响应已显示在屏幕上。

关注点

哇!构建这个很有趣。游戏平台中的聊天机器人打开了很多新的大门。看到开发者在他们的游戏中构建什么,以及智能聊天机器人如何改变游戏体验,这将是令人兴奋的。为了避免冗余,我没有为机器人创建更大的知识库,因为我之前的几篇文章已经讨论和解释了如何创造性地在应用程序中使用机器人。然而,在游戏环境中创建一个聊天机器人感觉很有趣,可以尝试一下。

历史

  • 2020年5月6日:首次发布
© . All rights reserved.