inkle / ink

inkle's open source scripting language for writing interactive narrative.
http://www.inklestudios.com/ink
MIT License
4.06k stars 486 forks source link

Observe Variables (beginner question) #244

Open TLCMunchkin opened 7 years ago

TLCMunchkin commented 7 years ago

Sorry for a probably very novice question, but I'm very new to both unity and ink.

I'm trying to learn by modifying the ink demos (The Intercept & the demos from the Unite Europe talk) and I'm getting along pretty well, especially because ink has been a real blessing for interactive fiction.

(Apologies in advance if my description of the issue is horrible, but I'm still learning all the correct terms) Something I can't figure out for the life of me right now is how the Observe Variables function works. I've added a test variable ("fullness") into the ink script that starts on 0 and then depending on your first branching choice it gets set to 1, later in the text 2, then 3. I've added a Text object to the UI of The Intercept that is supposed to show the progress of that variable. In it I've set up a variable, names "full" and an if statement that tells the text object that if the variable is 0, it puts out "- - -" in the UI. If it's 1, it puts out "I - -". If it's 3, it puts out "I I I" into the UI. This works if I manually set variable (independently form the ink file so far). So that part works.

Then I've tried the variablesState, so that the variable is being set by the according variable in the ink file. I did this in the same script and I'm not sure if that's the right way to do it, since I had to load the full ink story file into the script to make it work. BUT once I added that link to the story in the beginning I was able to use variablesState and when I started the game it loaded up the correct output ("- - -", "I - -", "I I I"). But it didn't track the progress of the variable throughout the choices.

And I know that I need the ObserveVariable delegate function to make that work, but I just can't seem to get it to work no matter what I try. I've tried to adapt this example:

void Update(){
int full = 0;

story.ObserveVariable("fullness", (string varName, object newValue) =>
            {
                full = (int)newValue;
            });
}

I don't know what I'm doing wrong. Do I need to have the variable observed in another script and then just feed it into the text game object? Maybe the GameState script? Maybe just not in the "void Update()" part of that script? Or does it not matter and I'm just messing this up in another way?

I've tried deleting the "int full = 0;" line and replaced it by the previous variablesState version: int full = (int) story.variablesState["fullness"]; and this way it would at least read my first "VAR fullness = 0" in the ink story. But it still wouldn't update when the variable changed throughout the game.

Thanks for any help or pointers in advance. Even seeing a working example of the ObserveVariable for a UI command would be a huge help.

~Gabriela

PS: This is the code, for reference:

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

using Debug = UnityEngine.Debug;

public class TextUI : MonoBehaviour {

    public Text fullnessUI;
    public int full;

       public TextAsset storyJSON;
    // The ink runtime story object
    private Story story;

    // Use this for initialization
    void Start () {
    }

    // Update is called once per frame
    void Update () {
        alphaTween.Loop();
                int full = (int) story.variablesState["fullness"];

                story.ObserveVariable("fullness", (string varName, object newValue) =>
                      {
                            full = (int)newValue;
                      });

        if (full == 1) {
            fullnessUI.text = ("I - -").ToString ();
        } 
        if (full == 2) {
            fullnessUI.text = ("I I -").ToString ();
        }
        if (full == 3) {
            fullnessUI.text = ("I I I").ToString ();
        }
        else {
            fullnessUI.text = ("- - -︎").ToString ();
        }
    }

    public FloatTween alphaTween = new FloatTween();

    private CanvasGroup canvasGroup {
        get {
            return GetComponent<CanvasGroup>();
        }
    }

    private void Awake () {
        alphaTween.OnChange += OnChangeAlphaTween;
    }

    void OnChangeAlphaTween (float currentValue) {
        canvasGroup.alpha = currentValue;
    }

    public void FadeIn () {
        alphaTween.Tween(0, 1, 5, AnimationCurve.EaseInOut(0,0,1,1));
    }

    public void Show () {
        canvasGroup.alpha = 1;
    }

    public void Hide () {
        alphaTween.Stop();
        canvasGroup.alpha = 0;
    }
}
cduquesne commented 7 years ago

Hi ! I see two issues.

First, the 'ObserveVariable' method needs to be called only once in the 'Start' method of your script.

Second, you don't need the "int full = (int) story.variablesState["fullness"];" line at all. More than that, redeclaring this variable in the Update with "int full" implies that it is not the public variable you declared but a new variable in the local scope of the method. (so it was impossible that it stored a value durably)

TLCMunchkin commented 7 years ago

That makes a lot of sense. I made both of those changes:

But, so far, it doesn't work, and I'm getting the warning that the variable 'full' is assigned but its value is never used. Without the 'variablesState' it doesn't even seem to register the variable in the story. I connected the story JSON file in the inspector of the game object. I also got the Error "NullReferenceException: Object reference not set to an instance of an object TextUI.Start() (at Assets/Scripts/Main/States/Game/View/TextUI.cs:22)".

The Text object is called 'FullnessUI', the Script is called 'TextUI'.

screen shot 2016-12-21 at 14 10 56 screen shot 2016-12-21 at 14 14 32
joethephish commented 7 years ago

Try something like this - the ObserveVariable and variablesState approaches are two ways to solve the same problem, so you only need one of them.

public class TextUI : MonoBehaviour {

       public Text fullnessUI;

       // This isn't needed
       //public int full;

      public TextAsset storyJSON;
       // The ink runtime story object
       private Story story;

       // Use this for initialization
       void Start () {

            story.ObserveVariable("fullness", (string varName, int full) =>
            {
                if (full == 1) {
                       fullnessUI.text = ("I - -").ToString ();
               }
               if (full == 2) {
                       fullnessUI.text = ("I I -").ToString ();
               }
               if (full == 3) {
                       fullnessUI.text = ("I I I").ToString ();
               }
               else {
                       fullnessUI.text = ("- - -︎").ToString ();
               }
            });
       }

       // etc!
TLCMunchkin commented 7 years ago

I did that and get this:

- Assets/Scripts/Main/States/Game/View/TextUI.cs(24,58): error CS1678: Parameter `2' is declared as type `int' but should be `object'
- Assets/Scripts/Main/States/Game/View/TextUI.cs(24,37): error CS1661: Cannot convert `lambda expression' to delegate type `Ink.Runtime.Story.VariableObserver' since there is a parameter mismatch

changed 'int' to 'object', but then the "==" operator gives me an error message: Operator '==' cannot be applied to operands of type 'object' and 'int'

So I tried the NewValue thing as a middle man like this, which at least gets rid of the compiler error, but the Text object just says "New Text", so it's not picking up anything.

public class TextUI : MonoBehaviour {

    public Text fullnessUI;
    // public int full;

    public TextAsset storyJSON;
    // The ink runtime story object
    private Story story;

    // Use this for initialization
    void Start () {
        story.ObserveVariable("fullness", (string varName, object newValue) =>
            {
                int full = (int)newValue;
                if (full == 1) {
                    fullnessUI.text = ("I - -").ToString ();
                }
                if (full == 2) {
                    fullnessUI.text = ("I I -").ToString ();
                }
                if (full == 3) {
                    fullnessUI.text = ("I I I").ToString ();
                }
                else {
                    fullnessUI.text = ("- - -︎").ToString ();
                }
            });
    }
    // Update is called once per frame
    void Update () {
        alphaTween.Loop ();
    }

Any idea where to go next? Thank you for all your support so far.

~Gabriela

joethephish commented 7 years ago

I did notice that story isn't being assigned to in your class? Not sure if that's related...

TLCMunchkin commented 7 years ago

Hmm, you mean like: story = new Story(storyJSON.text);

I've now added that into the start method, right before the Observe Variable. No change. :( I'm sorry, thanks for your help though! hugs