Tactics RPG Conversations

This week we will implement the UI components from the previous lesson in a Conversation UI element which would appear as part of a cut-scene before and/or after battles. The panels will hold a little bit of text along with a sprite showing who is speaking. A bouncing arrow will indicate when there is more to read and using the input “Fire” event will cause the conversation to move on to the next message or the next character who will speak, etc. These panels can appear in any corner of the screen (which could indicate the direction of a speaking character relative to the player), and will animate in and out of the screen as needed.

Speaker Data

One of the first things we will need is a data model. Create a new class named SpeakerData in the Scripts/Model folder. I didn’t specify a class to inherit from, and I marked the class Serializable so that we can see and configure it using the Editor’s inspector. Model classes can often be quite simple – like this one. I have merely declared a few fields:

  • A list of string called messages will contain all of the “pages” of text that any one character will speak.
  • A reference to a sprite will be used to show who is speaking.
  • A TextAnchor enum is used to determine which corner of the screen the panel will display in.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[System.Serializable]
public class SpeakerData 
{
	public List<string> messages;
	public Sprite speaker;
	public TextAnchor anchor;
}

Conversation Data

Since this lesson isn’t about creating a monologue, let’s create another script to hold a sequence (list) of Speaker Data instances. This way, we can have multiple different people speaking and interacting with each other. Create a script named ConversationData and place it in the Scripts/Model folder.

This time our class will inherit from something. Even though you can see Serializeable classes in the editor, you can only see them when they are attached to an asset. It just so happens that you can make project assets out of ScriptableObject so we will specify it here.

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

public class ConversationData : ScriptableObject 
{
	public List<SpeakerData> list;
}

How to Create Conversation Assets

Prototype Approach

Initially, you are unlikely to have a full grasp of all of the features your project will need. You may want to just create a few simple assets to test with and see if you like your architecture decisions. In this sort of scenario, it would be really handy to just be able to create an instance of a scriptable object using a menu action, just like when you want to create any other kind of GameObject.

The Unity Community has provided a handy little script for that called ScriptableObjectUtility, which you should download and add to the project. Make sure to stick it in an Editor folder, because it uses the UnityEditor namespace.

This script can’t do anything by itself- we must add another script which actually triggers it and tells it what kind of asset we want to make. So, create another script called AssetCreator and place it alongside the utility script in the Editor folder.

using UnityEngine;
using UnityEditor;

public class YourClassAsset
{
	[MenuItem("Assets/Create/Conversation Data")]
	public static void CreateConversationData ()
	{
		ScriptableObjectUtility.CreateAsset<ConversationData> ();
	}
}

Now that we have these two scripts, we can create a ConversationData asset by using the file menu (Assets->Create->Conversation Data). Go ahead and create an instance of our Conversation Data and place it in the Resources/Conversations folder (Create the Conversations subfolder). Name the asset IntroScene and give it some sort of implementation:

Conversation Example photo Conversation Example_zpsenzhc7ls.png

Note that in this example, I duplicated the Avatar sprite which already existed in the project and then recolored it and saved it as EvilAvatar. This way I could test out the dynamic speaker image feature of my panel.

Production Approach

In a fully realized project, I would actually want to be creating my conversation data in some external form – this could be anything you want, but ideally it should allow you to easily see and edit all of your text, have a spell-checker, etc. The inspector in Unity doesn’t have these sorts of features. Imagine how tedious it could be to try to track down a spelling mistake if you had hundreds of Conversation assets, each having multiple speakers with multiple pages. Or suppose you changed a commonly used character or location name and needed to propogate the change across the entire game’s script. If all of the text existed in an external source, like a .csv for example, then it would be as easy as running a Find and Replace command.

If you are interested in the full production-level approach, I have written a separate post called Bestiary Management and Scriptable Objects which should be able to help you get a good head start on the process.

Conversation Panel

Now that we have a data model to store our conversation, we need a view to display it to a user. Add a new script named ConversationPanel to the Scripts/View Model Component folder. The implementation follows:

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

public class ConversationPanel : MonoBehaviour 
{
	public Text message;
	public Image speaker;
	public GameObject arrow;
	public Panel panel;

	void Start ()
	{
		Vector3 pos = arrow.transform.localPosition;
		arrow.transform.localPosition = new Vector3(pos.x, pos.y + 5, pos.z);
		Tweener t = arrow.transform.MoveToLocal(new Vector3(pos.x, pos.y - 5, pos.z), 0.5f, EasingEquations.EaseInQuad);
		t.easingControl.loopType = EasingControl.LoopType.PingPong;
		t.easingControl.loopCount = -1;
	}

	public IEnumerator Display (SpeakerData sd)
	{
		speaker.sprite = sd.speaker;
		speaker.SetNativeSize();

		for (int i = 0; i < sd.messages.Count; ++i)
		{
			message.text = sd.messages[i];
			arrow.SetActive( i + 1 < sd.messages.Count );
			yield return null;
		}
	}
}

This is also a pretty simple script. We need a reference to the Text component so we can update it with dynamic messages from our conversation. We need a reference to the speaker so we can show who is speaking. We will need a reference to the arrow so we can turn it on or off based on whether or not more “pages” of text exist in the current speaker’s dialog. Finally we need a reference to the Panel component so we can make the view tween into or out of the screen.

In the Start method we get the current local position of the arrow. Using that as a base point, we move it up by a fixed amount and then tell it to tween down just as far – this way the animation is centered around the original point. After completing the tween the animation will play in reverse so that you see it animate back up to where it started (due to the PingPong setting). I set the tween to loop infinitely by setting the loopCount to -1. This little bit of animation should help indicate to the user that there is more to read.

I created an IEnumerator method called Display. This will not be the target of a MonoBehaviour’s StartCoroutine. Instead, we will manually move through the method based on “Fire” input events. This process may be new for many of you, but it is fairly simple to use, and can be a very powerful feature.

Conversation Controller

The real meat of this lesson is found in the Conversation Controller. Its job is to handle the process of a conversation including making sure that an appropriate panel is used and positioned correctly, tweening the panel into view, showing the speaker and the speaker’s messages one at a time based on user input, tweening the panel out when the speaker is finished speaking, and then tweening in another panel (if necessary) for a new speaker, etc. The complex part of this is deciding how to maintain state between all of the different speakers and pages of a speaker’s dialogue.

Begin by creating a script named ConversationController in the Scripts/Controller folder.

using UnityEngine;
using System;
using System.Collections;

public class ConversationController : MonoBehaviour 
{
	
}

I usually begin something complex like this by defining my fields and properties. I like to know what is going to be needed first and then implement the details later. First, I have decided to create two separate Conversation Panels – one for display on the left side of the screen, and one for display on the right side of the screen. It would have been possible to use a single panel, and simply modify assets as necessary to show it on the different sides, but this is simpler.

[SerializeField] ConversationPanel leftPanel;
[SerializeField] ConversationPanel rightPanel;

I will also want the ability to turn on and off the canvas based on whether or not a conversation is actually taking place. Disabling the canvas when it is not used should allow the rendering speed to run at peak performance.

Canvas canvas;

I will maintain a reference to an IEnumerator which steps through all of the speakers and their messages in a conversation. This will make it easy to maintain state without adding a bunch of other properties.

IEnumerator conversation;

Finally I will maintain a reference to a Tweener, which is used to animate the current panel into or out of the screen. I maintain this reference, because while the transitions are active, I dont want the user to be able to advance the conversations position.

Tweener transition;

If you remember from the previous lesson, you are able to specify panel positions using string names. These names will be set in the inspector, but in code, it generally isn’t a good idea to use strings (due to typo errors etc). One common practice is to define constants at the top of your script so that you know you will be using the same text anywhere the constant itself is used. This practice can help to alleviate potentially confusing bugs.

const string ShowTop = "Show Top";
const string ShowBottom = "Show Bottom";
const string HideTop = "Hide Top";
const string HideBottom = "Hide Bottom";

In the start method, I will connect some references, set the default off screen position for both panels, and then disable the canvas (until it is needed).

void Start ()
{
	canvas = GetComponentInChildren<Canvas>();
	if (leftPanel.panel.CurrentPosition == null)
		leftPanel.panel.SetPosition(HideBottom, false);
	if (rightPanel.panel.CurrentPosition == null)
		rightPanel.panel.SetPosition(HideBottom, false);
	canvas.gameObject.SetActive(false);
}

The public interface for this script will have two main options. Initially you will tell it to Show and will pass along an instance of ConversationData as a parameter. This method will set everything up and bring out the first panel and display the initial message of the first speaker. From then on, new messages and speakers must be manually triggered using a Next method call. This process will continue until the final message has been dismissed. When the panel has completed its animation offscreen an event will be posted.

I could have allowed the conversation panel itself to receive input and manage the entire sequence itself, but in the end I decided it would make the most sense to keep all of the input routed through a single source – the game state, which displays the conversation in the first place.

Add the following event for when the conversation controller has finished:

public static event EventHandler completeEvent;

Here are the public methods mentioned above:

public void Show (ConversationData data)
{
	canvas.gameObject.SetActive(true);
	conversation = Sequence (data);
	conversation.MoveNext();
}

public void Next ()
{
	if (conversation == null || transition != null)
		return;
	
	conversation.MoveNext();
}

The Sequence call is to a method with an IEnumerator return type. This is a bit of a lengthy method, but essentially, it loops over all of the speakers in a conversation, and in a nested loop, iterates over each of the speaker’s messages via another IEnumerator from the current panel.

IEnumerator Sequence (ConversationData data)
{
	for (int i = 0; i < data.list.Count; ++i)
	{
		SpeakerData sd = data.list[i];

		ConversationPanel currentPanel = (sd.anchor == TextAnchor.UpperLeft || sd.anchor == TextAnchor.MiddleLeft || sd.anchor == TextAnchor.LowerLeft) ? leftPanel : rightPanel;
		IEnumerator presenter = currentPanel.Display(sd);
		presenter.MoveNext();

		string show, hide;
		if (sd.anchor == TextAnchor.UpperLeft || sd.anchor == TextAnchor.UpperCenter || sd.anchor == TextAnchor.UpperRight)
		{
			show = ShowTop;
			hide = HideTop;
		}
		else
		{
			show = ShowBottom;
			hide = HideBottom;
		}

		currentPanel.panel.SetPosition(hide, false);
		MovePanel(currentPanel, show);

		yield return null;
		while (presenter.MoveNext())
			yield return null;

		MovePanel(currentPanel, hide);
		transition.easingControl.completedEvent += delegate(object sender, EventArgs e) {
			conversation.MoveNext();
		};

		yield return null;
	}

	canvas.gameObject.SetActive(false);
	if (completeEvent != null)
		completeEvent(this, EventArgs.Empty);
}

For each speaker, I determine what entry and exit point to use for the panel based on the SpeakerData’s anchor setting. I store those in a temporary local variable so that later I can just tell the panel to show or hide.

Before I animate a panel on screen, I first snap it offscreen (no animation). This way if it had previously been in a different vertical location, you wont see it tween on the Y axis (it would enter along a diagonal animation path).

Once I have animated a panel onto the screen, I create a pause in the sequence using a yield statement. All of the state will be preserved at this spot until I tell it to continue moving via the MoveNext function (called by the public Next method I exposed earlier).

As the user provides input, I will loop through the messages of the speaker in a while loop. When the presenter has no further yield statements the while loop will terminate. At this point I will move the current panel off screen. I use an anonymous delegate to automatically continue the conversation once it completes. The yield statement immediately after it is the one it will skip.

Once all of the speakers have completed their dialogue, the canvas is disabled, and the event is fired.

The MovePanel method is below:

void MovePanel (ConversationPanel obj, string pos)
{
	transition = obj.panel.SetPosition(pos, true);
	transition.easingControl.duration = 0.5f;
	transition.easingControl.equation = EasingEquations.EaseOutQuad;
}

Cut Scene State

In order to see our conversation as a part of the game, we will add another game state called CutSceneState in the Scripts/Controller/Battle States folder. This script loads the sample conversation asset we created earlier and displays it. When the animation is complete it moves on to the next state of the game. Of course, a more complete implementation would not hard-code the conversation to load. There would probably be additional data, perhaps in some sort of Mission data model which contained all sorts of information, like what conversations to play, what enemies to load, what rewards you get for winning, etc.

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

public class CutSceneState : BattleState 
{
	ConversationController conversationController;
	ConversationData data;

	protected override void Awake ()
	{
		base.Awake ();
		conversationController = owner.GetComponentInChildren<ConversationController>();
		data = Resources.Load<ConversationData>("Conversations/IntroScene");
	}

	protected override void OnDestroy ()
	{
		base.OnDestroy ();
		if (data)
			Resources.UnloadAsset(data);
	}

	public override void Enter ()
	{
		base.Enter ();
		conversationController.Show(data);
	}

	protected override void AddListeners ()
	{
		base.AddListeners ();
		ConversationController.completeEvent += OnCompleteConversation;
	}

	protected override void RemoveListeners ()
	{
		base.RemoveListeners ();
		ConversationController.completeEvent -= OnCompleteConversation;
	}

	protected override void OnFire (object sender, InfoEventArgs<int> e)
	{
		base.OnFire (sender, e);
		conversationController.Next();
	}

	void OnCompleteConversation (object sender, System.EventArgs e)
	{
		owner.ChangeState<SelectUnitState>();
	}
}

Init Battle State

To finish plugging in our Cut Scene, we need to make it the target of the InitBattleState. Change the last line of the Init method to the following:

owner.ChangeState<CutSceneState>();

Scene Setup

Open the game’s Battle scene. Create an empty GameObject named Conversation Controller. Make this object a child of Battle Controller. This object will be made into a special prefab to hold and manage our conversation panels. An example of the final result appears below:

 photo CanvasControllerSceneView_zps9vdslmgl.png

This prefab has several gameobjects arranged in a particular hierarchy as shown in the image below:

 photo CanvasControllerHierarchy_zpsqrk76gfb.png

Add our ConversationController script to the root Conversation Controller object and make sure you dont forget to set its dependencies when you finish creating them in a bit:

 photo ConversationControllerInspector_zpserlccgjn.png

Create and add a Canvas object as indicated by the hierachy example above. Note that I customized the Canvas Scaler component so that my UI elements can scale with different screen sizes. Even with different sizes and aspect ratios, our setup will always work as expected.

 photo CanvasInspector_zpsg8fmyhxr.png

Create an empty GameObject named Right Edge Conversation Panel. Add the Panel component to this object and it will automatically make the Transform a RectTransform and add the Layout Anchor for us. Make this object a child of the Canvas. Next, add the ConversationPanel script. Make sure you dont forget to set its dependencies when you finish creating them. The image below, as well as all of the next series of images show the parallel settings (one for the left panel and one for the right panel). I would recommend creating just the right side first, and then duplicating it and then just make the few changes necessary.

 photo ConversationPanels_zpsytbzgcnb.png

Create an image object for the Background:

 photo LeftRightBackground_zpskfarcumi.png

Create another image object for the Speaker:

 photo LeftRightSpeaker_zpsjmk0go39.png

Create one final image object for the More Arrow:

 photo LeftRightArrow_zpszh6topkr.png

Finally create a Text object for the Message:

 photo LeftRightMessage_zps2p85nog1.png

Make sure to make a prefab out of your Conversation Controller. If you were working on a large project with other people, this allows you to make changes to the prefab itself without needing to modify the scene. Since you added the prefab to the scene for the first time though, you will need to save the scene this time. Also save the project to make sure the prefab is properly saved.

At this point you should be able to run the scene and test everything out. The conversation will automatically appear and wait for your input to skip from message to message. When the conversation completes, you should be able to select and move a unit on the board as you were able to do before.

Summary

In this lesson we implemented the UI components from the previous lesson in a Conversation UI element which appears as part of a cut-scene before and/or after battles. We made use of Scriptable objects to store the conversation messages and avatar sprites. We used a free library to easily create project assets out of our Scriptable Object which we could configure in the inspector. We also used an IEnumerator natively (not through a StartCoroutine call) to see how easy it can be to drive the conversation updates through events.

Don’t forget that the project repository is available online here. If you ever have any trouble getting something to compile, or need an asset, feel free to use this resource.

Advertisements

40 thoughts on “Tactics RPG Conversations

  1. Excellent !!!! Great stay !!!! Thank you so much for these tutorials !!! I am creating a game based on your tutorials Tactical RPG. There is no legal problem with that right? A query, now we are seeing the management UI, you know how it could be implemented inventory? Sorry if there are typos, I am using Google translator to write faster (I speak Spanish and my English is not very good). Regards !!!

    Like

    1. Hey Gustavo, I am happy to hear you are enjoying this series so much! There is no problem using my material however you wish, as it is released under the open source MIT license. As a personal request, if you complete your project I would love for you to share a link so I can see how it turned out!

      Although I have created inventory systems and menus in other games, I’m afraid that feature won’t appear in this series for awhile. Since I am working solo, I am trying to do things which are easy (read – little to no art or design required). I should have some decent architecture code-wise to support items and inventory but my initial focus on this project will be what happens within a battle, and managing your inventory is something I would expect to take place outside of battle.

      In the meantime, there are plenty of other authors out there who have shared inventory examples. This one might help,

      Like

  2. Hey excuse me, I forgot to respond to you. Of course I will show you my game when I finished. For now I’m involved with inventory. I found a very complete code and I’m studying to use as I want. Do you know the game Tactics Ogre for GBA? Well, it’s similar to FFTA in many ways, but includes some features that set it apart, and a more mature story. Features including two that I include in my games, one is the use of items and other medals system. If you do not know what I recommend you do not you will regret.

    Liked by 1 person

    1. Sounds good Gustavo! I’ve never played Tactics Ogre myself but it sounds like a good game and I have heard a bunch of people commenting on it. I’ll have to add it to my continually growing list of games I need to try πŸ˜‰

      Im curious about the code you found. Is it a public (free) link? Perhaps it would benefit other readers here as well if you wanted to share?

      Like

  3. “NullReferenceException: Object reference not set to an instance of an object
    ConversationController.MovePanel (.ConversationPanel obj, System.String pos) (at Assets/_Scripts/Controller/ConversationController.cs:66)
    ConversationController+c__Iterator3.MoveNext () (at Assets/_Scripts/Controller/ConversationController.cs:95)
    ConversationController+c__Iterator3.m__0 (System.Object sender, System.EventArgs e) (at Assets/_Scripts/Controller/ConversationController.cs:102)
    EasingControl.Tick (Single time) (at Assets/_Scripts/Common/EasingControl.cs:202)
    EasingControl+c__Iterator0.MoveNext () (at Assets/_Scripts/Common/EasingControl.cs:151”

    When I run the game I get an empty conversation box on the upper right hand side followed by that error and nothing appear. It just goes to the gameboard with my unit, of which I cannot move now. Thanks for any help πŸ™‚

    Like

    1. Assuming you copied the code exactly, it is probably a missing reference in the scene (like forgetting to assign a reference to one of the scripts), or perhaps the ConversationData asset was not able to be loaded. When I am hunting down issues like these I usually scatter a few calls such as “Debug.Log(sd == null);” and do one such line for each object which might be null. If it’s a code error don’t forget you can download my code from the repository and then you can compare from there.

      Like

    1. Also, I meant to ask – can you explain why you use the ++i version of the increment in your for loops? I understand the difference and have used it in things such as Debug.Logs and arguments, but what is the benefit of using it in a for loop? Thanks again for the awesome series!

      Like

  4. I followed the code exactly but I still get this when the second message is supposed to appear:

    NullReferenceException: Object reference not set to an instance of an object
    RectTransformAnimationExtensions.AnchorTo (UnityEngine.RectTransform t, Vector3 position, Single duration, System.Func`4 equation) (at Assets/Scripts/Common/Animation/RectTransformAnimationExtensions.cs:18)
    RectTransformAnimationExtensions.AnchorTo (UnityEngine.RectTransform t, Vector3 position, Single duration) (at Assets/Scripts/Common/Animation/RectTransformAnimationExtensions.cs:11)
    RectTransformAnimationExtensions.AnchorTo (UnityEngine.RectTransform t, Vector3 position) (at Assets/Scripts/Common/Animation/RectTransformAnimationExtensions.cs:7)
    LayoutAnchor.MoveToAnchorPosition (TextAnchor myAnchor, TextAnchor parentAnchor, Vector2 offset) (at Assets/Scripts/Common/UI/LayoutAnchor.cs:65)
    Panel.SetPosition (.Position p, Boolean animated) (at Assets/Scripts/Common/UI/Panel.cs:53)
    Panel.SetPosition (System.String positionName, Boolean animated) (at Assets/Scripts/Common/UI/Panel.cs:41)
    ConversationController.MovePanel (.ConversationPanel obj, System.String pos) (at Assets/Scripts/Controller/ConversationController.cs:74)
    ConversationController+c__Iterator4.MoveNext () (at Assets/Scripts/Controller/ConversationController.cs:55)
    ConversationController+c__Iterator4.m__0 (System.Object sender, System.EventArgs e) (at Assets/Scripts/Controller/ConversationController.cs:63)
    EasingControl.Tick (Single time) (at Assets/Scripts/Common/Utility/EasingControl.cs:202)
    EasingControl+c__Iterator1.MoveNext () (at Assets/Scripts/Common/Utility/EasingControl.cs:151)

    I will let you know if I discover the issue.

    Like

    1. Looking only at the output all I can tell you is that it expected to find an object reference and didn’t, but I can’t tell you “why” that happened. Some of the most frequent issues people encounter are usually related to forgetting to connect a reference in the Editor’s inspector pane, or using a wrong reference such as a prefab from the project rather than an instance of a prefab in the scene.

      If you checkout the project from the repository at commit “1c8787bf2e87f07cc66fabce733b5055d5a4e8e4” then you can see how the code and project looked at the time I wrote the post, and will perhaps be able to figure out the problem by comparing this against your own.

      It is also possible that you encountered a bug similar to one I found in the animation libraries. I refactored it due to some null references I was getting in the upcoming lesson on Magic. Hope that helps!

      Like

      1. I had connected all the prefabs and it works until the second message tries to appear, then it errors. And I tried to continue on anyway and now the Ability Menu won’t show up either. I’ll check out the commits and see if anything is different from your code at all.

        Like

      2. If one of the conversation messages appears, then it would be worth verifying your Conversation Data is properly configured. Sometimes if you forget to save the project then your settings can be lost.

        Like

      3. No errors anymore but still not showing the second message but the ability menu shows up now and it works to some degree (outside the menu options cloning themselves right now). I’ll continue on with your tutorials and compare with your repository as I go. Thanks for the help!

        Like

      4. The conversation itself is fine, or should be. I’ll be going back and seeing if I missed anything though.

        Like

  5. After writing the ConversationPanel script, ran into an error, maybe it has to do with some new unity update? I’m still a novice so it could definitely be user error. Anyway the error messages are as follows:

    Assets/Assets/Scripts/View Model Components/ConversationPanel.cs(17,19): error CS1061: Type `Tweener’ does not contain a definition for `easingControl’ and no extension method `easingControl’ of type `Tweener’ could be found (are you missing a using directive or an assembly reference?)

    Assets/Assets/Scripts/View Model Components/ConversationPanel.cs(18,19): error CS1061: Type `Tweener’ does not contain a definition for `easingControl’ and no extension method `easingControl’ of type `Tweener’ could be found (are you missing a using directive or an assembly reference?)

    I’ve looked at all the animation code I have compared to the scripts in your repo and it looks like everything is in line. Has anyone run into something similar or have any advice on further troubleshooting?

    Like

    1. I’m wondering if you happen to be using my updated animation code. I refactored it toward the end of this project by combining the Tweener component and the animation timer (which I had called easingControl) into just one component called Tweener. If so, you can probably just delete the easingControl reference but leave everything else and it will work. Otherwise, I would try checking out the commit for this step of the project and compare your code against it.

      Like

  6. hello, and thank you for this one of a kind on the inernet tutorial series of awesomeness. I am among the legions of readers who intend to use this lesson as a combat engine framework for a coming game! Now i havent looked ahead yet, and maybe you left it for us to put in, or its fixed after i read the next one, but the scripts above as written never trigger the event to move to the next state?

    Liked by 1 person

  7. I know this might be a stupid question, but when you set up the scene i don’t get where the Left(and right) Edge Conversation Panel Script come form or the other things, i’m pretty confused :/

    Like

    1. Hey Michael, if you look at the Hierarchy screen grab of the “Conversation Controller” prefab there will be two panels beneath its canvas. One is named “Right Edge Conversation Panel” and the other is named “Left Edge Conversation Panel”. Both objects are configured using the same kinds of components such as a “ConversationPanel” component, but are specifically configured via the inspector to look correct on the indicated side of the screen. These objects should be dragged into the relevant slots of the “Conversation Controller” root object.

      Like

      1. Hi thanks for the help but another problem arrived :/ i now get this error when i play the scene : “NullReferenceException: Object reference not set to an instance of an object” any help?

        Like

      2. If you copied and pasted the code examples, then the most likely culprit is that you forgot to connect a reference in the inspector window. There are lots of references, so make sure that that you closely follow and match all the screen grabs I included.

        Like

  8. The IntroScene conversation seems to be deleting itself whenever I test out the scripts in Unity (The conversation runs as planned, but after the program itself is run IntroScene’s size is set back to 0).

    Great tutorials, by the way! It’s helped me learn a lot about programming my own FFT-like, although I do plan on changing some systems along the way to make sure i’m actually learning πŸ™‚

    Like

    1. Make sure that you didn’t type out the conversation while in play mode, because exiting play mode will reset everything back to the way it was before you started. Also, after creating the conversation make sure you save the project. You should be able to close Unity and open it again and see the conversation data saved. If so then playing and stopping the scene shouldn’t be able to erase your data either.

      Like

  9. I’m using the most recent code for the animation scripts, and it looks like my arrows in the conversation panel no longer animate. I wonder what could be going on here?

    Like

    1. Good catch, I never noticed it had stopped working. I think we just need to tell it to “Play” – could be as simple as adding that to the “Start” method on the Tweener script, unless you want more control than that.

      Like

    2. It seems, once the gameobject Arrow is deactived, it lost his pingpong loop.

      I tried using Play() during start but it didn’t fixed the problem so i tried other way, i disable the image. The animation is running in background but you will not able to see the arrow.

      Liked by 1 person

    1. Sure, it should be relatively easy to skip a conversation. If I were to add that feature I would probably start with the “CutSceneState” by adding a listener for whichever button press I wanted to use for skipping. Much like we have an “OnFire” method for causing the conversation to continue to the next panel, I might use a “OnSkip” method to tell the conversationController to skip to the end. This will allow it a chance to dismiss any active panels before calling its own complete method, which then triggers the “OnCompleteConversation” in our “CutSceneState” which allows the game to continue.

      Liked by 1 person

  10. Hey Men

    Thank you for the great Tutorial. My friend and i try to do our best to follow your steps.
    we Downloaded the whole Projekt and created also a new one. The wohle Project is just to compare.
    We Work on the new One and did all your Steps. Now we cant find our mistake in the project. Maybe you can help us to solve it.

    NullReferenceException: Object reference not set to an instance of an object
    ConversationController+c__Iterator0.MoveNext () (at Assets/Skripts/Controller/ConversationController.cs:50)
    ConversationController.Show (.ConversationData data) (at Assets/Skripts/Controller/ConversationController.cs:37)
    CutSceneState.Enter () (at Assets/Skripts/Controller/Battle States/CutSceneState.cs:30)
    StateMachine.Transition (.State value) (at Assets/Skripts/Common/State Machine/StateMachine.cs:40)
    StateMachine.set_CurrentState (.State value) (at Assets/Skripts/Common/State Machine/StateMachine.cs:9)
    StateMachine.ChangeState[CutSceneState] () (at Assets/Skripts/Common/State Machine/StateMachine.cs:24)
    InitBattleState+c__Iterator0.MoveNext () (at Assets/Skripts/Controller/Battle States/InitBattleState.cs:22)
    UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Coroutines.cs:17)

    Thank you and best regards a Team from Switzerland

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s