How to Create a Twitch Chat Game with Unity

Learn to integrate the Twitch Chat API into your Unity game, so viewers can interact with the game during streaming. By Harrison Hutcheon.

4.7 (3) · 3 Reviews

Download materials
Save for later
Share
You are currently viewing page 2 of 6 of this article. Click here to view the first page.

Parsing and Storing Commands

Create a script in RW/Scripts/Common called IGameCommand. It’s short and sweet:

using System.Collections.Generic;
public interface IGameCommand
{
    string CommandString { get; } 
    string ShortString { get; }  
}

IGameCommand has a CommandString field, which stores the string required to execute it. For example, Battle has users vote for in-game options, so a CommandString for a vote command would be !vote.

Additionally, IGameCommand has a ShortString field, holding a shorthand version of the command. For example, a command with a CommandString of !vote could have the ShortString !v.

In RW/Scripts/Common, create a script called ChatCommand.

If a ChatMessage contains a valid command, its data is stored in a ChatCommand. Import the System namespace (and System.Collections.Generic if it wasn’t added by default). Then give ChatCommand the [System.Serializable] decorator and create the fields shown below:

using System.Collections.Generic;
using System;

[System.Serializable]
public class ChatCommand
{
    public IGameCommand command; // 1
    public string username; // 2 
    public DateTime timestamp; // 3 
    public List<string> arguments; // 4 

    //...

These fields store the:

  1. Command script for the command the user submitted.
  2. Username of the user that submitted the command.
  3. Exact time the user submitted the command, which can be useful for logging user actions and debugging gameplay issues.
  4. Arguments submitted with the command.

ChatCommand needs a constructor and two methods: Execute and ParseCommandArguments. Insert these below the public fields:

public ChatCommand(ChatMessage message, IGameCommand submittedCommand) // 1
{
    arguments = new List<string>(); // 2
    command = submittedCommand; // 3
    username = message.user; 
    timestamp = DateTime.Now; 
    ParseCommandArguments(message); // 4
}

public void ParseCommandArguments(ChatMessage message)
{
    string[] splitMessage = message.message.Split();
  
    if (splitMessage.Length > 1)
    {
        for (int i = 1; i < splitMessage.Length; i++)
        {
            arguments.Add(splitMessage[i]);
        }
    }
}

Here's a code breakdown:

  1. The constructor takes two arguments: ChatMessage and IGameCommand.
  2. Then it instantiates arguments to a new List.
  3. Next, it assigns submittedCommand to command, message.user to username and sets timestamp to DateTime.Now.
  4. Finally, it calls ParseCommandArguments and passes in message.

ParseCommandArguments splits the message to isolate any values after the command string and adds them to arguments. For example, if a user submits !vote 1, then 1 is the stored argument.

The Game Manager

The GameManager is your app's the brain. It uses your scripts to read incoming messages, parse commands and execute valid commands in-game.

In RW/Scripts/Common, create GameManager. Then attach GameManager to Managers/GameManager in the scene hierarchy.

Shows attaching the GameManager script to the GameManager GameObject.

Open the GameManager script. Above the GameManager class, declare an enum called GameState. Give it two values: Playing and GameOver:

public enum GameState
{
    Playing,
    GameOver
}

In the GameManager class, add:

public GameState gameState; // 1
public ChatCommand currentCommand; // 2 

private TwitchChat chat; // 3
private List<ChatCommand> newCommands; // 4
private IGameCommand[] gameCommands; // 5

These fields hold:

  1. An enum that identifies the game's current state.
  2. The command the game manager is currently executing.
  3. A reference to TwitchChat so GameManager can read in chat messages.
  4. The list of user submitted commands.
  5. An array of valid game commands, attached to the same object as the GameManager.

Next, implement Start and a private SetGameState as shown below:

void Start()
{
    gameCommands = GetComponents<IGameCommand>();
  
    newCommands = new List<ChatCommand>();
    chat = gameObject.GetComponent<TwitchChat>(); 
  
    SetGameState(GameState.Playing); 
}

private void SetGameState(GameState newGameState)
{
    gameState = newGameState;
}

Start initializes some of the class's fields and sets the default game state. SetGameState takes a GameState and sets the gameState field.

Now, create a public method called ResetGame:

public void ResetGame()
{
    if (gameState != GameState.Playing)
    {
        SetGameState(GameState.Playing);
    }
}

If it's not already set to Playing, ResetGame sets gameState to Playing. Later, this method will return the game to its original state, so a new game can start.

Create a public method called EndGame and implement it as below:

public void EndGame()
{
    if (gameState != GameState.GameOver)
    {
        SetGameState(GameState.GameOver);
    }
}

If it's not already set to GameOver, EndGame sets gameState to GameOver. Later, this method will do everything needed when a game is over, such as displaying a scoreboard.

Next, open IGameCommand. Add this method signature to the bottom of the interface:

bool Execute(string username, List<string> arguments, GameManager gm = null);

Now IGameCommand has a method called Execute that returns a boolean and takes three parameters:

  • A string storing the username of the player that submitted the command.
  • The List of strings storing arguments passed with the command. For example, an argument for !vote would be the option's number. So, the user would submit the command !vote 1.
  • A GameManager, set to null by default.

Now, go back into ChatCommand. Add the following at the bottom of the class:

public bool Execute(GameManager gm)
{
    return command.Execute(username, arguments, gm);
}

Execute calls command.Execute, passing in the required variables: username, arguments and GameManager. It also returns a boolean indicating if the command was successful.

Of course, you need to check that any incoming chat message matches a valid command.

Validating Commands

Back in GameManager, create a private method called CommandIsValid that takes a ChatMessage and returns an IGameCommand, as shown below:

private IGameCommand CommandIsValid(ChatMessage chatMessage) 
{
    if(chatMessage != null) 
    {
        string commandString = chatMessage.message.Split()[0]; // 1

        foreach (IGameCommand command in gameCommands) // 2
        {
            if (commandString == command.CommandString || commandString == command.ShortString) // 3
            {
                return command;
            }
        }
    }       
    return null; // 4
}

In this code:

  1. CommandIsValid splits the ChatMessage and gets the first string to check if its a valid command string, for example !vote.
  2. It loops through each command in gameCommands.
  3. If the command string matches a IGameCommand's CommandString or ShortString, then it returns the command.
  4. If it doesn't find a match, it returns null.

Now, replace Update with FixedUpdate. Declare a private method ProcessCommands after it. Implement them both as below:

void FixedUpdate()
{
    if (gameState == GameState.Playing) //1
    {
        ChatMessage recentMessage = chat.ReadChat(); //2

        IGameCommand command = CommandIsValid(recentMessage); //3
        if (command != null) //4
        {
            ChatCommand newCommand = new ChatCommand(recentMessage, command); //5
            newCommands.Add(newCommand);
        }

        ProcessCommands(); //6
                  
    }
}

private void ProcessCommands()
{
    if (newCommands.Count > 0)
    {
        newCommands[0].Execute(this); //7
        newCommands.RemoveAt(0);
    }
}

In FixedUpdate, the code:

  1. Checks if gameState is GameState.Playing.
  2. Calls chat.ReadChat and stores the returned ChatMessage in a variable called recentMessage.
  3. Then checks if the command is valid by calling CommandIsValid, passing in recentMessage. It stores what returns in a IGameCommand called command.
  4. Checks if command isn't null, meaning your app received a valid command.
  5. Then creates a new ChatCommand, passing in the required variables.
  6. Calls ProcessCommands to execute the stored commands.

In ProcessCommands, the code:

  1. Gets the oldest command in newCommands and calls Execute, then removes that command from the list.

That's all you need to read and execute incoming commands! Time to test all this code!