Simple dialogue manager Part2 - nicloay/Node-Inspector GitHub Wiki

In this part we will bind our node config to scene objects.

Let's start from the beginning how our objects will interact with each other and how they will play specific animations.

Dialogue Trigger

We will create a custom (trigger) collider that covers an area. If a player enters this area our component will trigger our animation. The object with the collider and the player game object will be supplied as parameters to the Execute method.

##DialogueTrigger.cs

    public class DialogueTrigger : MonoBehaviour {
        public LayerMask OpponentLayerMask;
        public DialogueSequence Dialogue;

        void OnTriggerEnter(Collider other){
            if (((1 << other.gameObject.layer) & OpponentLayerMask.value ) != 0){                                
                Dialogue.StartItem.Execute(gameObject, other.gameObject);
            }
        }
    }

We won't lock user input here, but in a real game you would need to lock user input or implement OnTriggerExit and terminate the dialogue in this case. Otherwise your player could just walk out, and the dialogue would still stay open.

You can also see that we use a LayerMask so this trigger won't work if something like a hostile monster enters the area. You need to create a separate layer for your player and use it as a layer mask for this trigger.

Here is how our Bender object will looks like with the collider and the DialogueTrigger component. screen shot 2016-03-18 at 11 06 25

Handling Animation

Let's start with the more complex part. Our NPC should open the door, and we want to configure that through our dialogue system. It's not possible to add the Animation component to the game object directly. If our NPC should be able to interact with external objects we need to store these object references somewhere. Alternatively we could access them by tag or separate layers.

I don't like tags because we could have several doors at the level and if we provide just one tag we won't know which door we need to open.

I don't like layers because of the same reason as tag and additionally we have limits for the total number of layers.

This is why I decided to create a new component which will keep all the information about all interactable objects. Each object will be references by a string key. Unfortunately this way we will lose the ability to use something like a combobox in our dialogue config.

InteractableObjects.cs

    public class InteractableObject{
        public string Key;
        public GameObject GameObject;
    }

    public class InteractableObjects : MonoBehaviour {
        public List<InteractableObject> Objects;

        public GameObject GetObjectBykey(string key){
            return Objects.First((arg) => arg.Key.ToLower() == key.ToLower()).GameObject;
        }
    } 

So right now we could attach this component to our NPC (Bender) object and give it a link with the key "door" to it. As you can see we don't support multiple results here and return just the first object with a matching key. If we can't find any object with the given key we will return null.

With our component ready we need to modify our PlayAnimation node.

##PlayAnimation.cs

 public class PlayAnimation : DialogueNode {
        public Owner         Owner;
        public string        ObjectKey;
        public AnimationClip Animation;
        [OneWay]
        public DialogueNode NextStep;

        public override void Execute(GameObject actor, GameObject opponent)
        {
            GameObject actualOwner = Owner == Owner.Actor ? actor : opponent;
            InteractableObjects objects = actualOwner.GetComponent<InteractableObjects>();
            if (objects == null){
                Debug.LogError("Cant find InteractableObjects on the object", actualOwner);
            } else {
                GameObject actualObject = objects.GetObjectBykey(ObjectKey);
                if (actualObject == null){
                    Debug.LogErrorFormat("Can't find object with key {0} on the object {1}", ObjectKey, actualObject.name );
                } else {
                    Animation animation = actualObject.GetComponent<Animation>();
                    animation.clip = Animation;
                    animation.wrapMode = WrapMode.Once;
                    animation.Play();
                }
            }
        }
    }

As you can see I added properties for Owner and ObjectKey to this node.

It's time to test this component. What we should do first is mark this node as the default node for this graph. To do that open the node inspector, right click on the node, and choose Make Default.

Default node

After that you will see "Start Node" at the top right corner of the node.

Let's create an "open door" animation. I won't go into detail how to do that, it's a standard process and more information can be found in the official Unity docs. I'll just move this door up and store the animation file in the Animation folder.

Don't forget to setup our PlayAnimation node. You need to choose Actor as the Owner, door as the ObjectKey and set the Animation property to the animation you created.

We need to somehow control our character, so let's create a simple controller which can go left and right. Add a rigid body to your Player game object and lock everything except X Position.

##CharacterController.cs

    public class CharacterController : MonoBehaviour {
        public float Force = 0.5f;
        float xInput = 0.0f;
        
        void Update () {
            xInput = Input.GetAxis("Horizontal");
        }
        
        void FixedUpdate(){        
            GetComponent<Rigidbody>().AddForce(new Vector3(xInput*Force, 0 ,0), ForceMode.VelocityChange);        
        }
    }

I've also added a kinematic rigid body to our door. If you did everything right, when you go to Bender he will open the door.

Message boxes

For this tutorials it would be easiest to use the regular Unity UI and show all text in a simple box at the bottom of the screen. We will need one text box and a maximum of 2 buttons. So let's create UI Stuff and write a single controller which will control all UI elements.

UI

Here is my layout

UI layout

And here is a controller that will control all UI elements. It also has a static variable referencing itself, making it a pseudo singleton.

##UNDER_CONSTRUCTION Please see the Demo project in the repo. It is finished and contains all required changes. Documentation coming soon

⚠️ **GitHub.com Fallback** ⚠️