A really easy way to handle player's objectives in Unity3D is by taking advantage of these features:
- Game Object hierarchy
- Using
public
fields in scripts - Drag and Drop objects into
public
fields
With that said, this is what we can do:
- Create an empty game object
- Create a new child object per objective we want to represent
- Move each objective to the corresponding position (To make it easier, you can click on the cube in the inspector once you have selected the objective, this will show the icon and name of the object in the Scene view)
- Attach a collider marked with trigger so that it does not block the player movement.
- Create a custom Objective script which will contain its Name, Description, and other aspects required to handle its behavior, including the action to perform when collider is reached. One of the important keys here is that each objective would have a "
NextObjective
" field, which is of the same type of the script (Objective
). This helps in reducing harcoded logic string
s to go into the next objective. - Create a
Objectives
script, which will contain a field for CurrentObjective
and internally retrieves the list of all of the objectives in the hierarchy. - Attach the
Objectives
script into the player's object, and drag your initial objective into the CurrentObjective
field.
These are the sample scripts:
Objective.cs
using UnityEngine;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
public class Objective : MonoBehaviour
{
public enum ObjectiveType
{
Reach = 0,
Talk = 1,
Defeat = 2,
}
public enum ObjectiveStatus
{
Pending = 0,
Achieved = 1,
}
public enum ActionOnReach
{
MarkAsAchieved = 0,
PlayCinematic = 1,
PlayAnimation = 2,
SetTrigger = 3,
}
public string Name;
[Multiline(10)]
public string Description;
public ObjectiveType Kind;
public ObjectiveStatus Status;
public GameObject Target;
public Objective NextObjective;
public ActionOnReach[] ActionsOnReach;
public Animator animator;
public MovieTexture ClipToPlay;
public string TriggerName;
private void OnReach()
{
if (this.ActionsOnReach.Contains(ActionOnReach.MarkAsAchieved))
this.Status = ObjectiveStatus.Achieved;
if (this.ActionsOnReach.Contains(ActionOnReach.PlayCinematic))
this.PlayCinematic();
if (this.ActionsOnReach.Contains(ActionOnReach.PlayAnimation))
this.PlayAnimation();
if (this.ActionsOnReach.Contains(ActionOnReach.SetTrigger))
this.NextObjective.Target.GetComponentInParent<animator>().SetTrigger(this.TriggerName);
ParentScript.CurrentObjective = this.NextObjective;
}
private void PlayAnimation()
{
Debug.Log("On PlayAnimation: Not implemented yet");
}
private void PlayCinematic()
{
Debug.Log("On PlayCinematic: Not implemented yet ");
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player" && this.ParentScript.CurrentObjective.name == this.name)
{
OnReach();
}
}
public Objectives ParentScript { get; set; }
}
Objectives.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Objectives : MonoBehaviour {
public Objective CurrentObjective;
private Objective[] PlayerObjectives;
public Image CurrentObjectiveArrow;
public Text CurrentObjectiveDescription;
void Start()
{
var objectiveParentGameObject = this.CurrentObjective.transform.parent.gameObject;
if (objectiveParentGameObject != null)
{
this.PlayerObjectives = objectiveParentGameObject.GetComponentsInChildren<Objective>();
if (this.PlayerObjectives != null)
{
Debug.Log("Successfully found all player objectives");
foreach (Objective singleObjective in PlayerObjectives)
{
if (singleObjective != null)
{
singleObjective.ParentScript = this;
}
}
}
else
Debug.LogError("Unable to find objectives");
}
}
void OnGUI()
{
this.CurrentObjectiveDescription.text = this.CurrentObjective.Description;
}
}
This is just one way to do it, although it is not the only way, there are always other ways, some easier, some more complex, and complexity would be based on your game specific needs.