Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / artificial-intelligence

Integrating a Chatbot into Unity in C#

5.00/5 (4 votes)
7 May 2020CPOL8 min read 30.9K   567  
Using an on-device bot development framework to integrate a chatbot into unity
In this post, we'll integrate a conversational bot development framework into Unity that will work offline. We'll first create the chat interface and then bind it to Oscova to do the bot magic. We'll then create a simple Hello Bot dialog into our bot and see how to generate and display our response in Unity.

Introduction

This article takes the approach of on-device chatbot integration into Unity. Instead of calling some online API to generate a response for a user message, here we will directly integrate an offline chatbot framework so we don't have to rely on any online service or subscription for intelligent response generation. For this, we'll be using a popular on-device bot development tool called Oscova and we'll write an extremely simple Hello Bot dialog to test drive our unity bot integration.

Prerequisites

This article would be a good follow-up read to my previous article in which I created a mock-up table reservation bot. You could use that knowledge and apply it in the knowledge-base creation section of this article and expand on it. The article obviously assumes that the reader has some expertise in unity game development. In the meantime, I'll assume you satisfy the following:

  • Basic knowledge of Unity scripting
  • Knowledge of working in Visual Studio or Visual Studio Code
  • Basic knowledge of programming in C#/.NET

Installing Unity

If you haven't got Unity installed:

  • Visit https://unity3d.com/get-unity/download and click on Download Unity Hub to download Unity Hub which will guide you into installing Unity.
  • Make sure in the Add-On window, you choose Microsoft Visual Studio Community 2019 if you don't have it already.

Creating a Unity Project

  • Launch unity and create a new 3D Project by selecting the 3D project template.
  • You can name your project MyFirstUnityBot.

    Image 1

  • Click on the Create button.
  • Your unity project will now be created.

    Image 2

Creating the Chat Bot UI

We'll need three important UI elements to create our Chatbot interface in Unity.

  • Display Panel - where all the chats/messages are going to be held
  • Input Box - where the user will type in stuff
  • Send Button - Button the user will press to submit his message

To create our display system:

  • Right click in Hierarchy window, select UI and choose Canvas.
  • Keep the Canvas selected, right click, select UI and choose Scroll View.
  • Change the width and the height of the Scroll View to 500 and 300 respectively.

    Unity Chat bot panel

Once a user or bot message is generated, we'll need to add that message to the Scroll View. For this, we'll create a prefab of the Text UI element and use it whenever a message is to be added to the Scroll View.

  • Again, right click within Content item of the Viewport, choose UI and select Text
  • Set the orientation of the Text UI element to Bottom-Left, increase font size to 16
  • Under Vertifical Overflow select Overflow
  • Drag the item to Assets to convert it into a prefab and delete the item in the Hierarchy window.

Image 4

In the above screenshot, you can see that I've added two new components called Content Size Filter and Vertical Layout Group and adjusted their properties to help fit the messages properly within the Scroll View.

Image 5

Create a new Input Field and a Button and place them side by side as shown below. We'll use these two UI elements to send the user message to our bot.

Image 6

Importing OSCOVA - Syn Bot Framework

Unlike how we import libraries in a C# project say via NuGet, a unity project is recompiled every time the script gets changed so we cannot simply work with a NuGet package here. We'll instead deal with a Unity package that contains the framework library and its dependencies.

Download the Syn.Bot Unity Package.

  • Right click in Assets, select Import Package and choose Custom Package.
  • When the import window shows up, ensure that all the items within the package are selected.
  • Click on Import.

Syn.Bot Unity Bot package

Scripting the Bot Interface

To bind the UI elements with our bot, we'll now go ahead and script that in.

  • Create an Empty Object named GameManager in Hierarchy window
  • Add a Script component to it and name it the same.
  • Double click on the newly added GameManager script in the Asset Panel.
  • This will open a Visual Studio project.

GameManager script to work with Oscova

Remember the Message object we talked about adding to the Scroll View? Well, let's create a class that will hold the user and bot message and will make it possible for us to display it in Unity UI via the Text prefab.

The Message class below will hold the user/bot message as string, a reference to a Text prefab and optionally a MessageType enumeration which will help us decorate the message a bit with different colors.

C+
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()
    {

    }
}

Now that a message holder is ready, we'll go ahead and create some public fields to which we'll bind to from within Unity. We'll also create a OscovaBot object which will be instantiated when the game runs. Place the code below just above the Start() method.

C#
OscovaBot MainBot;

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

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

Save and close the Visual Studio project and select the GameManager in Hierarchy window. We'll now connect the fields with their respective components.

  • Set the Chat Panel object to Content in Scene
  • Text Object to Text prefab from within the Assets
  • Chat Box field to Input Field in Scene

We had created a MessageType property in the Message class so that users would visually be able to differentiate between the two. Set the User and Bot color object values to something of your choice but remember to set the Alpha value to 255 so it stays visible.

Image 9

Displaying the User/Bot Message

To display the user and bot message in the Scroll View's content property, we'll have to position the objects within its transforms. Also, we'll assign a color to each message type.

In the code below, we create a new instance of Message class, assign the message string to it, instantiate it within the transforms of the chatPanel. The list Messages is used to keep a check on the message count. If the count is above 25, we just remove the 25th last item. Lastly, based on the MessageType, we select a color for the text.<text>.

C#
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);
}

Processing the User Message

In our next method, we'll create a logic that will govern what happens after a user posts a message to the Bot. Long story short, we'll send the message to the bot for processing. We'll call this method SendMessageToBot().

C#
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 = "";
    }
}

In the method above, we first fetch the user message from the Input Field's text property. We then check if the message isn't an empty string.

Next, we create a user request with the user message, get the evaluation result and call the Invoke() method on it. This will compel the Bot to select the highest matching Intent and execute it. Which in-turn will generate a response, for which we'll add an event handler later.

In the last part, we just select the Input Field (chatBox) and clear its content by setting its text value to an empty string.

Connecting to Button Click

This SendMessageToBot() method needs to be called whenever the user clicks the Send button or presses Enter in the Input Field.

  • Select the button element in Hierarchy.
  • Under On Click(), select GameManager under Scene.
  • Under GameManager object, choose SendMessageToBot().

Unity Bot message to Oscova Bot

Connecting to Enter Press

Apart from processing the user message when the Send button is pressed, we would also need to process the user message when the user hits Enter in the Input Field. To do so, we'll just need to change the Update() method to the following:

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

Simple Hello Bot Dialog

We are using Oscova to build the knowledge-base (KB) of the Bot. You can create a KB in SIML, Syn Workspace via Oryzer Studio or directly in pure C# code. For simplicity and to prevent the article from going verbose, we will create a simple dialog in C# directly within our script.

We'll start by creating a BotDialog class which will inherit the Dialog class, within it we'll create a void Hello() method and decorate it with an Expression attribute in which we'll specify the sample user message as Hello Bot. This method will then return Hello User! as a bot response.

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

If the above code isn't making much sense, well, that's because there are a few concepts you'll have to familiarize yourself with. Recently, I posted an article that goes a little bit in-depth in creating a Bot's knowledge-base without much C# coding.

You will find the following article a lot more helpful if you wish to learn more about dialog creation:

Now that I've nudged you in the right direction for further learning, let's move on to updating our Start() method to wrap it all up.

Initializing the Bot Instance

We need to glue a couple of things together for our bot to work. Firstly, we'll need to instantiate our bot object and then we'll need to handle what happens when the bot generates a response.

In our code, we create a new instance of OscovaBot and assign it to the MainBot field. We then add our previously created BotDialog to the Dialogs collection of the bot and start the training. Finally, we add a event handler to the ResponseReceived event. Which does nothing but call the AddMessage() method with the bot's response.

C#
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);
    }
}

Well, that's that we are done coding!

Overall Code

Once done, your code should (hopefully) look something like the following:

C#
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();
        }
    }
}

Test Driving the Bot

Without further adieu, let's save and close the Visual Studio project so that Unity re-compiles and commits the changes.

In Game mode, type in Hello Bot in the Input Field and press the Send button. Voila! your bot's response is on your screen.

Image 11

Points of Interest

Phew! That was interesting to build. Chatbots in gaming platforms open a lot of new doors. It would be exciting to see what developers would build within their games and how intelligent chat bots would change the gaming experience. In order to be less redundant, I refrained from creating a larger knowledge-base for the bot as a few of my previous articles already discussed and explained how one can creatively use bots in their applications. However, a chat bot in a game environment felt something cool to experiment with.

History

  • 6th May, 2020: Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)