Using generative AI for NPC dialogs
Tags: ai, npc, react, gamedev
Published: 2024-04-10 09:43:16

Hi there, it's time to share my latest open-source gamedev project, and the first time I'm working with the OpenAI API to build a component for my game engine.

# The main idea

Using OpenAI's chat API, we can generate the dialog for an NPC in an RPG, making it more realistic and diverse. On top of that, with unscripted conversation, the NPC could branch into new topic of conversation that haven't been planned by the game designers, opening new gameplay possibilities.

# Current project state

As an initial prototype, I'm using AI to produce an initial dialog given the description of a creature. The result includes provide 4 possible choices from the user, one being the option to leave. The service is called through rest API, returning a JSON payload.

# Checking out the demo

Here's a video of the demo below:

{% embed https://www.youtube.com/watch?v=SGSDGGPVYyw %} 

You can try the demo at https://jacklehamster.github.io/open-ai-npc. The demo takes a minute to load, because it's hosted on a free host that shuts down the server when not used, and it takes about a minute to bring it back up.

In the demo, you can choose one fo the creatures to talk to:
- angel
- gremlins
- robot
- depressed software engineer
- alien

There will be 4 options to choose from, but you can also initially choose your own topic.

# How this was built

There are two main components to the project.
- The [dokui-menu](https://www.npmjs.com/package/dokui-menu) provides the library for the user interface. It's a menu system for choosing multiple options and have a retro look.
- The [open-ai-npc](https://www.npmjs.com/package/open-ai-npc) component, which provides the rest API.

The call to OpenAI includes a set of instructions on what to do and how to format the response. Those instructions can be seen in this call:

https://platform.openai.com/playground/p/S07MudwhhhloK1KY1CfJsjkj

The instructions had to be very specific to ensure that the response is in a consistent JSON format that can be parsed by the application.

# Using the API

Call: `https://open-ai-npc.onrender.com/api`

```json
[{
   creature: "You see a graceful, glowing angel with magnificent white wings.",
   player: {
      A: "Hello there!",
      B: "You look fascinating. What brings you here?",
      C: "I challenge you to a duel!",
      D: "Farewell for now."
   },
   attributes: {
      anger: 0,
      seduced: 0,
      trust: 0,
      fear: 0
   },
   actions: {
      fight: false,
      run away: false,
      trade: false,
      join party: false
   },
   info: {
      name: null
   }
}]
```

Subsequently, the user can call the API with a response, using one of the choices provided (A, B, C, D)

Call: `https://open-ai-npc.onrender.com/api?choice=A`

Or just write anything (like "about pizza") to choose a different topic.

Call: `https://open-ai-npc.onrender.com/api?choice=about%20pizza`.

Then subsequent choices can be made separated with the "|" character.

Call: `https://open-ai-npc.onrender.com/api?choice=A|B|C`

Since OpenAI API is stateless, we have to pass the entire dialog to it every time. The API provided from open-ai-npc stores that conversation, so that the caller doesn't need to pass it when calling the API.

Retaining those conversation also lets the API cache some calls to OpenAI that have already been made. This is crucial to keep the cost down, since each call to OpenAI costs a few cents, and I didn't want to run out of credits by having users make the same requests over and over.

# Some other API details

The API response also includes some extra information on the creature's feeling:
      anger, seduced, trust, fear
This could be used for two things. First of all, it can alter the image of the creature displayed to the user. Furthermore, those attributes also direct possible actions that the creature may take:
fight, "run away", trade, join party.

There is an "info" section that is initially empty, but can be filled as some information gets revealed. For instance, asking the creature about the name can reveal the "name" info.

# How this could be used in the future

By adding some context to the conversation, we can make the NPC conversation more real, and the gameplay interesting. Perhaps through the conversation, the player can unlock secrets that an NPC wants to conceal, or the player can convince the creature to give a particular item, or join the party. By giving the wrong answers, the creature could also be pushed to want to fight with the player.

By simply feeding information about the environment, we can produce emergent gameplay. For instance, an NPC knows the weakness of another creature, so it might be beneficial to uncover that information before fighting that creature.

# Links

Check out the open source repo:
https://github.com/jacklehamster/open-ai-npc

The [README](https://github.com/jacklehamster/open-ai-npc/blob/main/README.md) should have enough instructions to get you started.

You can also reuse the UI library:
https://github.com/jacklehamster/dokui-menu

All of this is a work in progress, so if you have issues using any of those, feel free to contact me for help:

Email: jacklehamster@gmail.com
Twitter: https://twitter.com/jacklehamster