This document is about: QUANTUM 2
SWITCH TO

Map Baking

Overview

In Quantum, a map consists of two parts: the Unity Scene and the MapData.

The Unity Scene is loaded when the map is loaded and contains the visible elements of the map such as the EntityViews. These Unity GameObjects are responsible for rendering the entities in the game.

The MapData, on the other hand, contains information about the map that is used by the deterministic simulation to drive gameplay.

Map Baking

By default, Quantum automatically bakes maps whenever a scene is saved, when exiting edit mode (prior to entering play mode), or before building the application.

In the QuantumEditorSettings asset in the project under the Editor Features section, automatic map baking for scene saving, playmode changes and building the app can be enabled or disabled.

While disabling automatic map baking may be useful in certain scenarios, it is important to note that manual map baking can be time-consuming and may introduce human error into the pipeliene. As a result, it is generally recommended to keep automatic map baking enabled for most projects.

QuantumEditorSettings MapBaking
QuantumEditorSettings MapBaking

For a Scene to be baked in Quantum, it is necessary to have a MapData component present on a GameObject in the scene. Additionally, if navmesh is present, a MapNavMeshUnity component is needed as well. The Game scene provided with the Quantum SDK comes with the necessary MapData setup already in place.

The MapData MonoBehaviour component also includes buttons that can be used to manually trigger the baking process if needed. The Bake All Mode can be adjusted to skip certain steps in the baking process.

MapData Component
MapData Component

MapData

When Quantum bakes a map, it generates a MapData asset that can be found under Resources/DB/Configs. However, it is possible to move these assets to another location if desired.

It's important to note that the values of the MapData fields should generally not be changed manually, as re-baking the map will override any manual changes made to the MapData asset.

The User Asset field in the MapData component can be used to inject any asset into the map, which can then be retrieved on the simulation side. This can be done either by manually linking an asset in the inspector or by assigning one from a custom map baking callback. An example for this is shown below.

Quantum Editor Settings BakeMapData

Map baking callbacks in Quantum allow you to inject custom steps into the MapData baking process. To implement a map baking callback, you can create a class that derives from MapDataBakerCallback. Here's an example implementation:

C#

using Quantum;
using UnityEngine;

public class ExampleMapDataBaker : MapDataBakerCallback
{
    public override void OnBeforeBake(MapData data)
    {
    }

    public override void OnBake(MapData data)
    {
    }
}

The MapDataBakerCallback attribute in Quantum can be used to specify the order in which multiple custom map baking callbacks are executed. To use this attribute, you can add it to your custom map baking callback class and specify a invokeOrder value. The lower the value, the earlier the callback will be executed during the map baking process.

Here's an example:

C#

[MapDataBakerCallback(invokeOrder:5)]
public class ExampleMapDataBaker : MapDataBakerCallback

OnBeforeBake is called before any other MapData baking is executed. It allows to adjust the Unity scene by adding or removing components, for example.

OnBake is called after all built-in baking steps for the map have been executed, but before the MapAsset is saved to the Unity asset. This allows you to adjust the MapData while accessing all the data of the baked map.

There are additional virtual callbacks that can be overridden if needed. OnBeforeBakeNavmesh, OnCollectNavMeshBakeData, OnCollectNavMeshes, OnBakeNavMesh and an overload of OnBeforeBake that provides the bake flags and what triggered the baking.

Adding Custom Data to a Map

Aside from colliders and navmeshes, game maps may include other elements that are significant for gameplay. When immutable data needs to be included, it's not mandatory to add it in entities on the map. In such cases, the User Asset field in the MapData Unity component can be used to pass any form of data into the simulation. This makes it possible to include things like object locations, game rules and more to the map. The data assigned to the User Asset field can be accessed and used within the simulation.

Example: Spawn Points

An example of utilizing custom data when baking a map is to include spawn points in the baked map. Designers can then place and freely move these spawn points in the Unity Scene.

In a DSL file add an asset declaration:

C#

asset MapCustomData;

Then, create a new class in the Quantum project to store the spawnpoint data:

C#

using System;
using Photon.Deterministic;
using Quantum.Core;
using Quantum.Inspector;

namespace Quantum {
    public unsafe partial class MapCustomData {
        [Serializable]
        public struct SpawnPointData {
            public FPVector3 Position;

            [Degrees]
            public FPQuaternion Rotation;
        }

        public SpawnPointData   DefaultSpawnPoint;
        public SpawnPointData[] SpawnPoints;

        public void SetEntityToSpawnPoint(Frame f, EntityRef entity, Int32? index) {
            var transform  = f.Unsafe.GetPointer<Transform3D>(entity);
            var spawnPoint = index.HasValue && index.Value < SpawnPoints.Length ? SpawnPoints[index.Value] : DefaultSpawnPoint;
            transform->Position = spawnPoint.Position;
            transform->Rotation = spawnPoint.Rotation;
        }
    }
}

To make the asset available in the Unity project, it is necessary to build the Quantum project first. After building the Quantum project, switch to the Unity project and create a new class that will handle the baking of spawnpoints:

C#


public class SpawnPointBaker : MapDataBakerCallback
{
    public override void OnBeforeBake(MapData data)
    {
    }

    public override void OnBake(MapData data)
    {
        var customData = UnityDB.FindAsset<MapCustomDataAsset>(data.Asset.Settings.UserAsset.Id);
        var spawnPoints = GameObject.FindGameObjectsWithTag("SpawnPoint");
        
        if (customData == null || spawnPoints.Length == 0)
        {
            return;
        }

        var defaultSpawnPoint = spawnPoints[0];
        if (customData.Settings.DefaultSpawnPoint.Equals(default(MapCustomData.SpawnPointData)))
        {
            customData.Settings.DefaultSpawnPoint.Position = defaultSpawnPoint.transform.position.ToFPVector3();
            customData.Settings.DefaultSpawnPoint.Rotation = defaultSpawnPoint.transform.rotation.ToFPQuaternion();
        }

        customData.Settings.SpawnPoints = new MapCustomData.SpawnPointData[spawnPoints.Length];
        for (var i = 0; i < spawnPoints.Length; i++)
        {
            customData.Settings.SpawnPoints[i].Position = spawnPoints[i].transform.position.ToFPVector3();
            customData.Settings.SpawnPoints[i].Rotation = spawnPoints[i].transform.rotation.ToFPQuaternion();
        }


#if UNITY_EDITOR
        EditorUtility.SetDirty(customData);
#endif
    }
}

This baker is relatively simple. It gathers all GameObjects with a SpawnPoint tag in the scene and extracts their position and rotation, which are then saved into the custom asset. Finally, the asset is marked dirty so that the changes are stored to the disk.

To use the custom data, add GameObjects with the SpawnPoint tag to the scene. Then create a MapCustomDataAsset and assign it to the UserAsset field of the map. To make use of the spawn points in the simulation, employ the following code:

C#

var data = f.FindAsset<MapCustomData>(f.Map.UserAsset);
data.SetEntityToSpawnPoint(f, entity, spawnPointIndex);
Back to top