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 4 of 6 of this article. Click here to view the first page.

Creating Commands

You need to tell Vote what options are available for voting. To do this, you'll create a script called VoteOption that stores an option's properties and instructions on what should happen if the option wins the vote.

In RW/Scripts/Battle, create a script called VoteOption.

Before you open VoteOption, navigate to RW/Prefabs/Battle in the Project pane. Open ActionButton.

Attach VoteOption to the prefab. Save the changes and close the Prefab Editor. Now all the ActionButtons in the scene have this script. You can find the ActionButtons in the scene hierarchy under UI/BattleCanvas/Player/Actions.

Open the VoteOption script.

Import UnityEngine.UI and add System.Serializable to VoteOption like this:

using UnityEngine.UI;
[System.Serializable]
public class VoteOption : MonoBehaviour
{  
    //...

Using System.Serializable lets you store the class in a List.

Next, declare the following public variables inside VoteOption:

public int optionNumber; // 1
public Text voteCounter; // 2 
public int voteCount; // 3  
public Text optionNumberLabel; // 4
public bool voteActionExecuted; // 5 
public IVoteAction voteAction; // 6            

These fields store:

  1. The option's number used for voting. For example, in the command !vote 1, 1 is the optionNumber.
  2. A red label on the ActionButton, used to display the current number of votes for this option.
  3. The total number of votes for this option.
  4. A green label on the ActionButton, used to display the option number for this VoteOption.
  5. Whether this VoteOption executed its in-game action.
  6. An IVoteAction that executes if this option receives the most votes.
Note: I created the IVoteActions for you. You'll find them in RW/Scripts/Battle.

After Start, create a method called SetOptionNumberText:

public void SetOptionNumberText()
{
    optionNumberLabel.text = "#" + optionNumber.ToString();
}

This sets the option number label to the optionNumber.

Mark Start as private and implement it as below:

private void Start()
{
    SetOptionNumberText();
    voteAction = GetComponent<IVoteAction>();
}

Here, this code calls SetOptionNumberText and sets voteAction to the IVoteAction attached to the same VoteOption GameObject.

Now you need a method to call when VoteOption receives a vote. Create a method called AddVote:

public void AddVote() 
{
    voteCount++;
    voteCounter.text = voteCount.ToString();
}

This method increases the voteCount by one and updates voteCounter.text with voteCount's value.

Nex, create a method called ClearVotes, which clears votes before a new vote phase:

public void ClearVotes()
{
    voteCount = 0; 
    voteCounter.text = voteCount.ToString(); 
    voteActionExecuted = false; 
}

This sets voteCount to 0, updates voteCounter.text and sets voteActionExecuted to false.

Finally, create a method called ExecuteOption, which checks if voteActionExecuted is false:

public void ExecuteOption()
{
    if (!voteActionExecuted) 
    {
        voteAction.Execute(); 
        voteActionExecuted = true; 
    }
}

If voteActionExecuted is false, this method calls voteAction.Execute and sets voteActionExecuted to true. Checking if voteActionExecuted is false prevents you from accidentally executing the same action twice.

Return to Unity. In the hierarchy, go through each ActionButton in UI/BattleCanvas/Player/Actions and give each a unique Option Number from 1 to 4. Expand each button and drag the Text inside OptionNumberLabel into Option Number Label. Drag the Text inside VoteCount into VoteCounter.

Setup for each ActionButton in the Inspector.

Leave Vote Count at 0 and Vote Action Executed as false.

Expanding the Vote Script

Time to expand Vote to use VoteOption.

Open Vote.

Import UnityEngine.UI:

using UnityEngine.UI;   

Declare the following public variables underneath ShortString:

public float votePhaseLength; // 1  
public bool isVotingOpen; // 2  
public List<VoteOption> voteOptions; // 3 
public GameObject voteTimerObject; // 4 
public Text voteTimerLabel; // 5 
public VoteOption selectedOption; // 6 
public BattleManager battleManager; // 7 
private float votePhaseTimer; // 8 

Here's a variable breakdown:

  1. How long in seconds the audience has to submit a vote.
  2. Allow voting during the vote phase, as well as prevent voting after the vote phase ends.
  3. Available VoteOptions.
  4. Voting timer in the VoteTimer text box, shown in the center of the scene.
  5. Text UI element in VoteTimer.
  6. The VoteOption that won the majority vote.
  7. The BattleManager.
  8. Remaining time of the vote phase.

In Execute, replace what you currently have with:

public bool Execute(string username, List<string> arguments, GameManager gm = null)
{
    if (isVotingOpen) // 1
    {
        if(int.TryParse(arguments[0], out int optionVoted))  //2
        {
            foreach (VoteOption option in voteOptions) // 3
            {
                if (option.optionNumber == optionVoted) 
                {
                    option.AddVote(); 
                }
            }
            return true; // 4
        }
    }
    return false; // 5
}

This updated code:

  1. Checks if isVotingOpen is true.
  2. If true, it gets the number of the option that received a vote by parsing the first argument into an int.
  3. For each VoteOption in voteOptions, it checks if it's optionNumber matches the number submitted. If it does, it calls AddVote for that option.
  4. Outside the loop, it returns true to indicate that the command executed successfully.
  5. If a user submits a vote while voting is closed, or if the option passed in wasn't parsed to an int, you want Execute to indicate that it failed. So, return false outside the isVotingOpen check.

Create a method called OpenVoting:

public void OpenVoting() 
{
    isVotingOpen = true; // 1
    voteTimerObject.SetActive(true); // 2
    foreach (VoteOption option in voteOptions) 
    {
        option.ClearVotes(); // 3
    }
    votePhaseTimer = votePhaseLength; // 4
}

OpenVoting opens a new vote phase by:

Expanding BattleManageer

Next, create a method called CloseVoting and implement it as below:

Here's a code breakdown:

Finally, add the following code in Update:

Here, you:

Next, open RW/Scripts/Battle/BattleManager.cs. It has several commented out references to Vote. I didn't want them sending errors to the console while you worked on getting Vote ready.

Now Vote is ready, uncomment:

  1. Setting isVotingOpen to true.
  2. Activating voteTimerObject.
  3. Clearing all votes.
  4. Resetting votePhaseTimer.
  5. Next, create a method called CloseVoting and implement it as below:

    public void CloseVoting() 
    {
        isVotingOpen = false; // 1
        voteTimerObject.SetActive(false); // 2
        voteOptions.Sort((VoteOption a, VoteOption b) => a.voteCount < b.voteCount ? 1 : -1);
        selectedOption = voteOptions[0];  // 3
        if (battleManager.battlePhase == 0) 
        {
            battleManager.battlePhase = 1; // 4
        }
    }
    

    Here's a code breakdown:

    1. CloseVoting sets isVotingOpen to false to prevent counting new votes.
    2. Then it also deactivates voteTimerObject.
    3. Next, it sorts the VoteOptions from highest voteCount to lowest and selects the most voted option.
    4. Finally, it sets the battle phase to 1. When battlePhase is 0, BattleManager waits for voting end. When battlePhase is 1, BattleManager knows to proceed by announcing the chosen action.

    Finally, add the following code in Update:

    // count down vote phase timer
    if (battleManager.battlePhase == 0 && !isVotingOpen) // 1
    {
        OpenVoting(); 
    }
    if (isVotingOpen) // 2
    {
        if (votePhaseTimer > 0) 
        {
            votePhaseTimer -= Time.deltaTime; 
            voteTimerLabel.text = votePhaseTimer.ToString("0"); 
        }
        else // 3
        {
            CloseVoting(); 
        }
    }
    

    Here, you:

    1. Check if battleManager.battlePhase is 0 and if isVotingOpen is false. If it is, you call OpenVoting.
    2. Since voting is open, you check if votePhaseTimer is greater than 0. If so, you reduce the time by Time.deltaTime and update voteTimerLabel.
    3. Then, if votePhaseTimer is less than or equal to 0, you call CloseVoting.

    Expanding BattleManageer

    Next, open RW/Scripts/Battle/BattleManager.cs. It has several commented out references to Vote. I didn't want them sending errors to the console while you worked on getting Vote ready.

    Now Vote is ready, uncomment:

  1. CloseVoting sets isVotingOpen to false to prevent counting new votes.
  2. Then it also deactivates voteTimerObject.
  3. Next, it sorts the VoteOptions from highest voteCount to lowest and selects the most voted option.
  4. Finally, it sets the battle phase to 1. When battlePhase is 0, BattleManager waits for voting end. When battlePhase is 1, BattleManager knows to proceed by announcing the chosen action.
  1. Check if battleManager.battlePhase is 0 and if isVotingOpen is false. If it is, you call OpenVoting.
  2. Since voting is open, you check if votePhaseTimer is greater than 0. If so, you reduce the time by Time.deltaTime and update voteTimerLabel.
  3. Then, if votePhaseTimer is less than or equal to 0, you call CloseVoting.
public void CloseVoting() 
{
    isVotingOpen = false; // 1
    voteTimerObject.SetActive(false); // 2
    voteOptions.Sort((VoteOption a, VoteOption b) => a.voteCount < b.voteCount ? 1 : -1);
    selectedOption = voteOptions[0];  // 3
    if (battleManager.battlePhase == 0) 
    {
        battleManager.battlePhase = 1; // 4
    }
}
// count down vote phase timer
if (battleManager.battlePhase == 0 && !isVotingOpen) // 1
{
    OpenVoting(); 
}
if (isVotingOpen) // 2
{
    if (votePhaseTimer > 0) 
    {
        votePhaseTimer -= Time.deltaTime; 
        voteTimerLabel.text = votePhaseTimer.ToString("0"); 
    }
    else // 3
    {
        CloseVoting(); 
    }
}
  • The Vote field on line 38.
  • AnnouncePlayerAction on lines 84-90/.
  • ExecutePlayerAction on lines 95-102.

The BattleManager code shares a lot of functionality with Vote, but controls the opponent rather than the player character. It also controls the battle's pace and displays the current action of the player or opponent.

Save your scripts and go back into Unity. With the comments removed, BattleManager now has a public Vote field in the inspector.

Click BattleManager in the hierarchy. Then drag the GameManager object into the Vote field of the BattleManager script.

BattleManager setup in the Inspector

Select GameManager in the hierarchy and look at the Vote component. Set the following values:

  1. Vote Phase Length to 20. Feel free to play around with this value.
  2. Drag each of the VoteOption buttons you set up earlier onto the list of Vote Options.
  3. Vote Timer Object to UI/BattleCanvas/VoteTimer.
  4. Vote Timer Label to UI/BattleCanvas/VoteTimer/VoteTimerLabel.
  5. Battle Manager to Managers/BattleManager.

The setup for the Vote component in the Game Manager.