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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Create a Twitch Chat Game with Unity
45 mins
- Getting Started
- The Twitch Chat Script
- Reading in Chat Messsages From Twitch
- Parsing Chat Commands
- Parsing and Storing Commands
- The Game Manager
- Validating Commands
- Testing Commands
- Your Twitch Audience and You
- Audience vs Audience
- Audience vs Game
- Audience vs Streamer
- Common Design Challenges
- Delay
- Audience Interaction
- Streamer Interaction
- Where's the Fun?
- Exploring the Sample Scene
- Creating Commands
- Expanding the Vote Script
- Game Over and New Game Conditions
- Game Over Check
- Game Over Action
- New Game Action
- Revisiting the GameManager
- Using What You've Learned
- Where to Go From Here?
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:
- Command script for the command the user submitted.
- Username of the user that submitted the command.
- Exact time the user submitted the command, which can be useful for logging user actions and debugging gameplay issues.
- 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:
- The constructor takes two arguments:
ChatMessage
andIGameCommand
. - Then it instantiates
arguments
to a newList
. - Next, it assigns
submittedCommand
tocommand
,message.user
tousername
and setstimestamp
toDateTime.Now
. - Finally, it calls
ParseCommandArguments
and passes inmessage
.
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.
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:
- An enum that identifies the game's current state.
- The command the game manager is currently executing.
- A reference to
TwitchChat
soGameManager
can read in chat messages. - The list of user submitted commands.
- 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:
-
CommandIsValid
splits theChatMessage
and gets the first string to check if its a valid command string, for example !vote. - It loops through each command in
gameCommands
. - If the command string matches a
IGameCommand
'sCommandString
orShortString
, then it returns the command. - 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:
- Checks if
gameState
isGameState.Playing
. - Calls
chat.ReadChat
and stores the returnedChatMessage
in a variable calledrecentMessage
. - Then checks if the command is valid by calling
CommandIsValid
, passing inrecentMessage
. It stores what returns in aIGameCommand
calledcommand
. - Checks if
command
isn't null, meaning your app received a valid command. - Then creates a new
ChatCommand
, passing in the required variables. - Calls
ProcessCommands
to execute the stored commands.
In ProcessCommands
, the code:
- Gets the oldest command in
newCommands
and callsExecute
, then removes that command from the list.
That's all you need to read and execute incoming commands! Time to test all this code!