|

Dynamic Objective System Tutorial for Unity

unity objective system

Learn how to create a dynamic objective system for your game in this Unity Tutorial. An objective system provide players with goals and structure and can be a good way to guide players through your game. In this guide, we’ll look at how to structure and define objectives and create a basic UI system to show the objectives. Source code and github project will be provided as well.

Difficulty: Medium – You should have an intermediate knowledge of Unity and C# programming.

Understanding the Objective System

In games, objectives are tasks or goals assigned to players. These can range from simple actions like collecting a certain number of items to complex sequences such as solving puzzles or defeating bosses. An effective objective system not only boosts player engagement but also enhances the game’s flow and user experience.

The Core Scripts

Our objective system comprises two main scripts: Objective and ObjectiveManager. Let’s break these down:

Objective Class

This class represents a single objective within our game. Here’s a brief overview of how it works. The rest are explained in-line with comments.

  • EventTrigger: An optional string that triggers progress of the objective. An objective can be progressed through a direct reference or through the event trigger string. More on this in the implementation section.
  • OnComplete/OnValueChange: Action delegates that can be used to listen for state changes to update things like the UI and objective manager.
  • StatusText: Used to do basic formatting to display the objective to the player. Uses string.Format() to provide the CurrentValue and MaxValue as format parameters. Example: “Kill {0} of {1} enemies”
public class Objective {
    // Invoked when the objective is completed
    public Action OnComplete;
    // Invoked when the objective's progress changes
    public Action OnValueChange;

    // Used to AddProgress from ObjectiveManager.
    // Can be empty if objective progress is managed elsewhere.
    public string EventTrigger { get; }
    public bool IsComplete { get; private set; }
    public int MaxValue { get; }
    public int CurrentValue { get; private set; }

    private readonly string _statusText;

    // Status text can have 2 parameters {0} and {1} for current and max value
    // Example: "Kill {0} of {1} enemies"
    public Objective(string eventTrigger, string statusText, int maxValue) {
        EventTrigger = eventTrigger;
        _statusText = statusText;
        MaxValue = maxValue;
    }

    public Objective(string statusText, int maxValue) : this("", statusText, maxValue) {}

    private void CheckCompletion() {
        if (CurrentValue >= MaxValue) {
            IsComplete = true;
            OnComplete?.Invoke();
        }
    }

    public void AddProgress(int value) {
        if (IsComplete) {
            return;
        }
        CurrentValue += value;
        if (CurrentValue > MaxValue) {
            CurrentValue = MaxValue;
        }
        OnValueChange?.Invoke();
        CheckCompletion();
    }

    public string GetStatusText() {
        return string.Format(_statusText, CurrentValue, MaxValue);
    }
}

Objective Manager Class

This class acts as a controller for all objectives in the game, handling their creation, progress tracking, and completion.

  • AddObjective(): Adds new objectives to the system.
  • AddProgress(): Updates progress based on event triggers.
public class ObjectiveManager {
    public Action<Objective> OnObjectiveAdded;

    public List<Objective> Objectives { get; } = new();
    private readonly Dictionary<string, List<Objective>> _objectiveMap = new();

    // Adds an objective to the objective manager.
    // If the objective has an EventTrigger, it's progress will be incremented
    // by AddProgress when the event is triggered. Multiple objectives can have
    // the same EventTrigger (i.e. MobKilled, ItemCollected, etc)
    public void AddObjective(Objective objective) {
        Objectives.Add(objective);
        if (!string.IsNullOrEmpty(objective.EventTrigger)) {
            if (!_objectiveMap.ContainsKey(objective.EventTrigger)) {
                _objectiveMap.Add(objective.EventTrigger,
                                  new List<Objective>());
            }

            _objectiveMap[objective.EventTrigger].Add(objective);
        }

        OnObjectiveAdded?.Invoke(objective);
    }

    public void AddProgress(string eventTrigger, int value) {
        if (!_objectiveMap.ContainsKey(eventTrigger))
            return;
        foreach (var objective in _objectiveMap[eventTrigger]) {
            objective.AddProgress(value);
        }
    }
}

Implementing the Objective System

Let’s start implementing our system in a Unity project. Follow these steps:

  1. Create a New Script: In Unity, create two new C# scripts named Objective and ObjectiveManager.
  2. Script Integration: Copy the provided script contents into the corresponding scripts in Unity.

Creating Objectives

Create objectives in code with different parameters, like this:

Objective coinObjective = new Objective("CollectCoin", "Coins Collected: {0}/{1}", 10);
objectiveManager.AddObjective(coinObjective);

This creates an objective that completes after 10 coins are collected.

Tracking Objectives

Using the objective we just created, the progress can be incremented either through the Objective Manager or the Objective itself.

objectiveManager.AddProgress("CollectCoin", 1); // adds +1 progress to all objectives with the event trigger "CollectCoin"
coinObjective.AddProgress(1); // adds +1 progress ONLY to coinObjective

Depending on the type of objective you would use a different method of adding progress. For example, if the objective is to discover a specific location in the game world, you would just directly add progress to the objective instead of through objective manager since there is just 1 instance of objective that cares about that progress event.

However, if the objective is to mine 50 ores in the game world, then it would make more sense to AddProgress() from the Objective Manager instead, since there could be many different types of ores that trigger this event progress.

Simple UI Objectives Display

We’ll create a simple UI to display our objectives. The UI will update state as Objectives are added, progressed, and completed.

public class ObjectiveDisplay : MonoBehaviour {
    [SerializeField]
    private TextMeshProUGUI _objectiveText;

    private Objective _objective;

    public void Init(Objective objective) {
        _objective = objective;
        _objectiveText.text = objective.GetStatusText();
        objective.OnValueChange += OnObjectiveValueChange;
        objective.OnComplete += OnObjectiveComplete;
    }

    private void OnObjectiveComplete() {
        _objectiveText.text = $"<s>{_objective.GetStatusText()}</s>";
    }

    private void OnObjectiveValueChange() {
        _objectiveText.text = _objective.GetStatusText();
    }
}

ObjectiveDisplay handles the text for a single objective. It updates the status text when the objective progress is updated or the objective is completed.

Simple UI display for objective system
Completed objectives are striked out
public class ObjectivePanel : MonoBehaviour {
    [SerializeField]
    private ObjectiveDisplay _objectiveDisplayPrefab;

    [SerializeField]
    private Transform _objectiveDisplayParent;

    private readonly List<ObjectiveDisplay> _listDisplay = new();

    void Start() {
        // Assumes you have a GameManager Singleton with the ObjectiveManager
        foreach (var objective in GameManager.Instance.Objectives.Objectives) {
            AddObjective(objective);
        }

        GameManager.Instance.Objectives.OnObjectiveAdded += AddObjective;
    }

    private void AddObjective(Objective obj) {
        var display =
            Instantiate(_objectiveDisplayPrefab, _objectiveDisplayParent);
        display.Init(obj);
        _listDisplay.Add(display);
    }

    public void Reset() {
        for (var i = _listDisplay.Count - 1; i >= 0; i--) {
            Destroy(_listDisplay[i].gameObject);
        }
        _listDisplay.Clear();
    }
}

ObjectivePanel is the overall parent for holding objective displays. As objectives get created, it will Instantiate objective display’s as children. This implementation assumes you have a singleton GameManager with the Objective Manager inside but you could change it as you see fit.

Github Resources

Github Repository

Code Tests – could be useful for seeing how to use the Objective System as well.

Conclusion

Congratulations! You’ve now implemented a basic objective system in your Unity game. This system can be further customized and expanded to fit the specific needs of your game. Experiment with different types of objectives and feedback mechanisms to create a more engaging experience for your players.

I hope this tutorial has been helpful. If you have any questions, feel free to drop a comment below. Check out my other Unity Tutorials and Happy developing!

Jonathan

Jonathan

Hey there, I’m Jon! I love landscape photography which is why I travel all the time! On my blog I’ll share all the best spots for epic sunsets and sunrises as well as some photography tips and tricks. I also have a interest in game design so you’ll find the occasional post about that too. Thanks for visiting!

Similar Posts

guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Emad

can you make video tutorial, that’s will be great