# Battle Pass

**URL:** https://heroiclabs.com/docs/hiro/guides/gameplay-mechanics/battle-pass/
**Keywords:** battle pass, hiro
**Categories:** hiro, battle-pass, gameplay-mechanics

---


# Battle Pass

In this guide, we'll explore how to use [Achievements](../../../concepts/achievements/) and [Sub Achievements](../../../concepts/achievements/sub-achievements/) to create a Battle Pass. It has become an extremely common feature in almost all live-service style games where a player is rewarded incrementally for progress they make in the game.

For our example, we will define a parent [Achievement](../../../concepts/achievements/), and many [Sub Achievements](../../../concepts/achievements/sub-achievements/) to act as the steps in the Battle Pass. Each will have a dependency on the previous [Sub Achievement](../../../concepts/achievements/sub-achievements/) to create a chain for the player to progress through sequentially.

1. The pass starts at level 1.
![Initial Pass]({{< fingerprint_image "/images/pages/hiro/guides/battle-pass/pass-start.png" >}})

2. After levelling up, the pass has progressed! 
![Pass After Level Up]({{< fingerprint_image "/images/pages/hiro/guides/battle-pass/pass-level-up.png" >}})

## Prerequisites

To follow this guide you'll need to:

* [Install Nakama](../../../../nakama/getting-started/install/docker/)
* [Install Hiro](../../../../hiro/concepts/getting-started/install/)
* [Install Unity](https://unity3d.com/get-unity/download)

## Server-side

Let's start by taking a look at the server-side code that we'll be using to implement the Battle Pass, beginning with creating the config file where we can define the pass and sub-steps.

### `main.go`

In our `main.go` file where Hiro is initialized, we need to make sure to add the required systems.

#### `InitModule` function

```go
// ...
systems, err := hiro.Init(ctx, logger, nk, initializer, binPath, hiroLicense,
    hiro.WithBaseSystem(fmt.Sprintf("base-system-%s.json", env), true),
    hiro.WithAchievementsSystem(fmt.Sprintf("base-achievements-%s.json", env), true))
if err != nil {
    return err
}
// ...
```

### Hiro system definitions

Next, we create our Hiro system definitions to implement a Battle Pass in our game. While each registered system needs a config file, you can set up `base-system-dev1.json` however you like, we only need to focus on `achievements` for this example in the `base-achievements-dev1.json` file.

#### Achievements

For this example, we create a parent [Achievement](../../../concepts/achievements/) to act as the Battle Pass itself. We then create multiple [Sub Achievements](../../../concepts/achievements/sub-achievements/) to act as the steps of the Battle Pass. Each step has a `precondition_ids` array with the previous step's id. This is what allows us to have the sequential progression through the steps. We also set the `max_count` of the steps to define how much xp/progress is required to complete that step.

You will also want to assign rewards to these steps based on your game. Maybe some steps reward currencies, while others might reward items.

```json
{
    "achievements": {
        "battlePass": {
            "auto_reset": false,
            "category": "pass",
            "count": 0,
            "description": "The Battle Pass.",
            "max_count": 1,
            "name": "Battle Pass",
            "sub_achievements": {
                "step1": {
                    "max_count": 400
                },
                "step2": {
                    "max_count": 400,
                    "precondition_ids": [
                        "step1"
                    ]
                },
                "step3": {
                    "max_count": 400,
                    "reward": {
                        "guaranteed": {
                            "currencies": {
                                "gold": {
                                    "min": 100
                                }
                            }
                        }
                    },
                    "precondition_ids": [
                        "step2"
                    ]
                },
                "step4": {
                    "max_count": 400,
                    "precondition_ids": [
                        "step3"
                    ]
                },
                "step5": {
                    "max_count": 400,
                    "precondition_ids": [
                        "step4"
                    ]
                },
                // ...
            }
        }
    }
}
```

## Client-side

### `BattlePassGameCoordinator`

This file bootstraps our game with a list of systems to be used, and provides a list of systems for deterministic start-up. In our case, we're initializing the `Achievements` system from Hiro.

```csharp
// ...
systems.Add(nakamaSystem);

// Add the Achievements system
var achievementsSystem = new AchievementsSystem(logger, nakamaSystem);
systems.Add(achievementsSystem);

return Task.FromResult(systems);
// ...
```

### `BattlePassDisplay`

The `BattlePassDisplay` will update the UI whenever the `AchievementsSystem` is refreshed.

```csharp
public class BattlePassDisplay : MonoBehaviour
{
    [SerializeField] private Slider progressSlider;
    
    private AchievementsSystem _achievementsSystem;
    private IDisposable _achievementsDisposer;

    private IAchievement _battlePass;

    public IEnumerator Start()
    {
        // Get the relevant systems and listen for updates.
        _achievementsSystem = this.GetSystem<AchievementsSystem>();

        _achievementsDisposer = SystemObserver<AchievementsSystem>.Create(_achievementsSystem, OnAchievementsSystemChanged);

        // Wait until achievements are ready to begin.
        yield return new WaitUntil(() => _achievementsSystem.IsInitialized);

        // Refresh the system to get the latest data.
        _ = _achievementsSystem.RefreshAsync();
    }

    public async void LevelUp()
    {
        // Progress the pass by 400xp.
        var subAchievements = _battlePass.SubAchievements.Select(x => x.Key);
        await _achievementsSystem.UpdateAchievementsAsync(subAchievements, 400);
    }

    private void OnAchievementsSystemChanged(AchievementsSystem system)
    {
        // Load the first pass we find as we only have one.
        _battlePass = system.GetAvailableAchievements("pass").FirstOrDefault();

        if (_battlePass == null)
        {
            Debug.LogWarning("No available battle pass.");
            return;
        }

        foreach (var step in _battlePass.SubAchievements)
        {
            // Create UI element to display the step's reward(s).
            Debug.Log(step.Value.Reward);
        }

        // Update the pass slider element to visualize progress.
        var completedSteps = _battlePass.SubAchievements.Count(x => x.Value.Count >= x.Value.MaxCount);
        progressSlider.value = completedSteps;
    }

    private void OnDestroy()
    {
        _achievementsDisposer?.Dispose();
    }
}
```
## Additional information

- [Hiro Achievements sample project](../../../../sample-projects/unity/hiro-achievements/index.md)