Understanding/creating code - Button to change displayText UI asset

I’m pretty new to Unity and C# (though I’ve done some Python and VBA, so I’m not completely lost). I’ve been following the Text Adventures tutorial and I can do that successfully. But I’m trying to wrap my head around some of the bits of code and understand what each line does and how it relates to one another, as well as create a script that will change the displayText with a button click instead of with user input in an input field. (Sort of like taking it apart and putting it back together.)

I’ve copied the Text Input, Input Action, and Go scripts from the tutorial here with some of my comments that I think hit on what I need to change for a button. If I understand correctly I know I need to create a script to attach to an on_click event on the button that will also talk to the other scripts - RoomNavigation and Game Controller. But I also know there could be a better way. I do know that I could do simple enable/disable object with all of the different texts, but I really want to understand code better.

Any help is appreciated, even if it is just redirecting me to another resource (and I apologize if this isn’t in the best section). TIA!

TextInput

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TextInput : MonoBehaviour
{
    public InputField inputField; // I think this would be changed to the button.

    GameController controller;

    void Awake()
    {
        controller = GetComponent<GameController> ();
        inputField.onEndEdit.AddListener (AcceptStringInput); // I don't think I need this.
    }

    void AcceptStringInput(string userInput) // I'm sure this needs to be changed to take the button input and somehow I will need to embed each button with a string to call the proper text. But I'm having issues.
    {
        userInput = userInput.ToLower ();
        controller.LogStringWithReturn (userInput);

        char[] delimiterCharacters = { ' ' };
        string[] separatedInputWords = userInput.Split (delimiterCharacters);

        for (int i = 0; i < controller.inputActions.Length; i++) // I'm not sure what this loop does.
        {
            InputAction inputAction = controller.inputActions [i];
            if (inputAction.keyWord == separatedInputWords [0])
            {
                inputAction.RespondToInput (controller, separatedInputWords);
            }
        }

        InputComplete ();

    }

    void InputComplete() // I'm pretty sure I don't need this function since I'm not doing text input.
    {
        controller.DisplayLoggedText ();
        inputField.ActivateInputField ();
        inputField.text = null;
    }

}

InputAction

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class InputAction : ScriptableObject
{
    public string keyWord;

    public abstract void RespondToInput (GameController controller, string[] separatedInputWords);
}

Go

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName= "TextAdventure/InputActions/Go")] //
public class Go : InputAction // I don't know if I need this script since it relates directly to the user text input. But I also feel like I could be missing an connection between these three scripts.
{
    public override void RespondToInput (GameController controller, string[] separatedInputWords)
    {
        controller.roomNavigation.AttemptToChangeRooms (separatedInputWords [1]);
    }
}

Well, the loop looks like it’s checking each input action (whatever that is) :slight_smile:

I can answer a more core portion of your question. Buttons and assigning a string value that is used to call a method:

Setup the button in the scene, and if you want to use the method you posted in the script, change the method access to ‘public’.
Now, you can add an OnClick unity event in the inspector, and drag that game object into the game object slot. You can use the drop down menu to find the script, and then find the method you want to use. It will allow you to input a string there in the inspector.

Your post says that you want to change the text input based on the string. Do you also want to try to change rooms?
Some of the code already in the method looks like it tries to check about changing rooms, so if you want that in addition to changing the text, you’d keep it, otherwise change it.

For the record, if you’ve not seen them already, there are other more basic tutorials to help any get started, that don’t involve nearly as much code, and are more focused on a smaller (or single) set of material to learn, eg: using buttons. If that’s of interest to you. :slight_smile:

Hope that helps a little.

Thanks! I think I figured out where I wasn’t digesting stuff correctly (and continued watching more lessons and tutorials). I had been trying to get the button to change the displayed text (from the UI) when really I needed the button to change a game asset that contains text.

I’ve altered the code from the tutorial to just what I think I need at this point and put it below. I’ve created a script for a button to change the scenario. I add it to my game controller, then drag the game controller to an on click event on the button and select the function RunEncounter. Then I drag over one of the scenarios in the value section.

But when I go to run, clicking the button doesn’t call the new scenario. I thought maybe it wasn’t overriding the original scenario, so I took off that first scenario so it wouldn’t be overriding or replacing one, but that didn’t do anything either. So I still haven’t figured out how to get the button to actually activate or change the scenario.

Button code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ChangeScenario : MonoBehaviour {

    GameController controller;

    private void Awake()
    {
        controller = GetComponent<GameController>();
    }

    public void RunEncounter(Scenario changeScenario)
    {
        controller.DisplayScenarioText();
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "Game/scenario")]
public class Scenario : ScriptableObject
{
    [TextArea]
    public string description;
    public string scenarioName;
    public Encounter[] possibleEncounter;

}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


[System.Serializable]
public class Encounter
{
    public int keyInt;
    public Scenario valueScenario;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScenarioNavigation : MonoBehaviour
{
    public Scenario currentScenario; 

    GameController controller;

    void Awake()
    {
        controller = GetComponent<GameController>();
    }
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    public Text displayText;

    [HideInInspector] public ScenarioNavigation scenarioNavigation; // links ScenarioNavigation with GameControler

    // Use this for initialization
    void Awake ()
    {
        scenarioNavigation = GetComponent<ScenarioNavigation>(); // auto-calls ScenarioNavigation
    }

    void Start()
    {
        DisplayScenarioText();
    }

    public void DisplayScenarioText()
    {
        string combinedText = scenarioNavigation.currentScenario.description;
        displayText.text = combinedText;
    }

    // Update is called once per frame
    void Update () {
       
    }
}

If you put a print statement (aka. Debug.Log or print) in the DisplayScenerioText, and click the button do you get any output in the console? This way we can verify if the method is being called or not…

Thank you so much for your help! I added Debug.Log to my DisplayScenarioText function (so it looks like below).

 public void DisplayScenarioText()
    {
        string combinedText = scenarioNavigation.currentScenario.description;
        displayText.text = combinedText;
        Debug.Log("Hello");
    }

If I have a scenario attached to the game controller it brings up my Hello text upon play as well as each time the button is clicked (though it doesn’t switch scenarios).

If I remove the Scenario from the Game Controller, then I get this error in the console:
NullReferenceException: Object reference not set to an instance of an object
GameController.DisplayScenarioText () (at Assets/Scripts/GameController.cs:26)
GameController.Start () (at Assets/Scripts/GameController.cs:21)

Right, the text and error are understandable.

I’m not really sure how that game changes scenarios off-hand; the code looks like it’s just fetching and displaying text, so I can understand that nothing is really ‘changed’.

Is attempt to change rooms what you were looking for or something? Sorry, I am a bit tired, and not immediately familiar with that game :slight_smile:

Don’t apologize, you’ve been a huge help. :slight_smile:

In the original tutorial, you create an asset Room (Scenario) that augments UI text to display the text of the Room. Then the Exit script (Encounter), augments the Room asset so you can choose the number of exits in the Inspector tab. From there, you put in a description for the exit that would be displayed, a key string, and the Room asset that is linked with that exit.

For the Exits, the key string and the Room are turned into a dictionary, which interacts with the user input and allows a new room to be called when the player types “go north” (north being the key string) and then calls the room that is associated with that key string from the current room (if any are available) and adds it to the UI text. The original tutorial also logs the players input so the user input is mirrored back to the player between Rooms and Exits.

Instead of user input and strings I want to change the room (scenario) based off the button input. And at this point it’s just a Scenario A → Scenario B relationship, but if I can figure this bit out I hope to try and figure out how to do Scenario A → B or C or D (all with different buttons).

Perhaps it is the lack of dictionary? I didn’t think the dictionary code would be necessary if the button was explicitly calling a new scenario.

I see, I see. :slight_smile: Sounds cool…

So, I found the tutorial and scripts online…

public void AttemptToChangeRooms(string directionNoun)
   {
       if (exitDictionary.ContainsKey (directionNoun)) {
           currentRoom = exitDictionary [directionNoun];
           controller.LogStringWithReturn ("You head off to the " + directionNoun);
           controller.DisplayRoomText ();
       } else
       {
           controller.LogStringWithReturn ("There is no path to the " + directionNoun);
       }
   }

You could make it so the button calls that function with a direction. I mean in the inspector, you could set the direction as a parameter.

I didn’t read every bit of the code, but the dictionary does seem to have the direction exits paired with rooms. That way you’d have the room for other places it’s used throughout the code.
I guess I don’t know how much you’ve been changing this, so I’m not sure if that fits for you, but it’s an idea.

Yes! That works. Small oddity that it wouldn’t work if I change the key to an integer instead of a string, but progress! It works! Thank you so much for your help! I never would have thought of putting just that part of the script on the button.

That was just the simplest way I could see it :slight_smile: You could change it to an int, or even a reference.
If it were an int, you’d probably want to reorganize the code so it has an array (or list) instead of a dictionary.
If the string is working okay, though, you can just leave it :slight_smile:

For a reference it could be more direct, but you’d have to add some code for the button’s script, I think, because I don’t think you could drop a room reference there in OnClicked.

Anyways, glad I could help :slight_smile: