It’s hard to describe, but I’ll give a shot.
My full interactive tutorial code. This is for reference to the following explanation below this:
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Tutorial {
public interface IDialogAction {
void Run();
}
[Serializable]
public struct DialogArrow {
public Vector2 position;
public float angle;
public DialogArrow(float x, float y, float angle) {
this.position = new Vector2(x, y);
this.angle = angle;
}
}
[Serializable]
public struct DialogText {
public Vector2 position;
public string message;
public DialogText(float x, float y, string message) {
this.position = new Vector2(x, y);
this.message = message;
}
}
[Serializable]
public struct DialogProperty {
public DialogText dialogText;
public DialogArrow dialogArrow;
public bool changePosition;
public bool showArrow;
public float elapsedTime;
public IDialogAction action;
public DialogProperty(DialogText text, DialogArrow arrow, bool shouldChangePositionOnNextDialog, bool showArrowOnNextDialog, float time, IDialogAction action) {
this.dialogText = text;
this.dialogArrow = arrow;
this.changePosition = shouldChangePositionOnNextDialog;
this.showArrow = showArrowOnNextDialog;
this.elapsedTime = time;
this.action = action;
}
}
public class TutorialDialog : MonoBehaviour {
public RectTransform dialogTransform;
public RectTransform arrowTransform;
public GameObject mainDialog;
public GameObject arrow;
public List<DialogProperty> properties;
public int propertyIterator;
public bool runCoroutine;
void Start() {
if (this.mainDialog == null) {
Debug.LogError("Dialog: Something is wrong.");
}
if (this.arrow == null) {
Debug.LogError("Arrow: Something is wrong.");
}
this.dialogTransform = this.mainDialog.GetComponent<RectTransform>();
this.arrowTransform = this.arrow.GetComponent<RectTransform>();
this.properties = new List<DialogProperty>();
this.runCoroutine = false;
Initialize();
}
void OnGUI() {
if (this.propertyIterator < this.properties.Count) {
DialogProperty property = this.properties[this.propertyIterator];
Text text = this.mainDialog.GetComponentInChildren<Text>();
if (text != null) {
text.text = property.dialogText.message;
}
if (property.changePosition) {
if (this.dialogTransform != null) {
this.dialogTransform.anchoredPosition = property.dialogText.position;
}
}
if (property.showArrow) {
this.arrowTransform.anchoredPosition = property.dialogArrow.position;
this.arrowTransform.localRotation = Quaternion.Euler(0f, 0f, property.dialogArrow.angle);
}
else {
this.arrowTransform.anchoredPosition = new Vector2(0f, 260f);
}
}
}
void Update() {
if (this.runCoroutine) {
this.StartCoroutine(CR_DialogSwitch());
}
}
private IEnumerator CR_DialogSwitch() {
DialogProperty property = this.properties[this.propertyIterator];
if (property.elapsedTime > 0f) {
if (this.mainDialog.activeSelf) {
this.mainDialog.SetActive(false);
}
if (this.arrow.activeSelf) {
this.arrow.SetActive(false);
}
this.properties[this.propertyIterator] = property;
yield return new WaitForSeconds(property.elapsedTime);
}
if (!this.mainDialog.activeSelf) {
this.mainDialog.SetActive(true);
}
if (!this.arrow.activeSelf) {
this.arrow.SetActive(true);
}
this.runCoroutine = false;
}
public void OnTutorialButtonClick() {
this.propertyIterator++;
if (this.propertyIterator < this.properties.Count) {
this.runCoroutine = true;
}
}
private void Initialize() {
this.properties.Add(new DialogProperty(new DialogText((Screen.width - 300f) / 2f, -(Screen.height + 95f) / 2f, "Hello! Let's get started on how to play this game."), new DialogArrow(0f, 0f, 135f), true, false, 0f));
this.properties.Add(new DialogProperty(new DialogText(83.82f, -259.75f, "This is your unit."), new DialogArrow(-193.24f, -91.92f, 110.5f), true, true, 0f));
this.properties.Add(new DialogProperty(new DialogText(83.82f, -259.75f, "There are no other units."), new DialogArrow(218f, -313.3f, 135f), true, false, 0f));
this.properties.Add(new DialogProperty(new DialogText(400f, -259.75f, "This is your opponent's unit."), new DialogArrow(174.92f, 101.9f, 287.9f), true, true, 0f));
this.properties.Add(new DialogProperty(new DialogText((Screen.width - 300f) / 2f, -(Screen.height + 95f) / 2f, "All units behave the same, regardless of faction."), new DialogArrow(0f, 0f, 0f), true, false, 0f));
this.properties.Add(new DialogProperty(new DialogText((Screen.width - 300f) / 2f, -(Screen.height + 95f) / 2f, "To select your unit, left click on the unit."), new DialogArrow(0f, 0f, 0f), true, false, 0f));
this.properties.Add(new DialogProperty(new DialogText((Screen.width - 300f) / 2f, -(Screen.height + 95f) / 2f, "You can also select your unit by dragging a box."), new DialogArrow(0f, 0f, 0f), true, false, 0f));
this.properties.Add(new DialogProperty(new DialogText((Screen.width - 300f) / 2f, -(Screen.height + 95f) / 2f, "To deselect, click anywhere else away from your unit."), new DialogArrow(0f, 0f, 0f), true, false, 0f));
}
}
}
Say that you have a button the user can press. This button is used for triggering the next tutorial sequence, as well as some actions that the game needs to do in order to unlock a tutorial sequence (such as a flag that blocks the user from using advance features before the user was allowed to do so). The tutorial sequence is placed inside a list (see near top of class TutorialDialog). When the user presses a button, it increments an iterator index that goes through the whole list.
I wanted to extend this by having the user trigger an action while incrementing to the next tutorial sequence, and I’m currently thinking of C# delegates. I wanted to know if there’s a way to pass delegates into and set them in the structs given, something like the following pseudo-code:
public struct Container {
public delegate DoAction action;
public Container(DoAction a) {
this.action = a;
}
}
DoAction Something() {
//Action...
}
this.tutorial.Add(new Container(Something()));
I know that delegates act as function pointers in C#, so I was wondering if it’s possible to have a function pointer in a struct, and have it either point to a function, or null.
Is it possible? Or I’m mistaken…