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.
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
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