AR Marker Manager component
Understand how to configure and use the AR Marker Manager component
The ARMarkerManager is a trackable manager that enables you to detect and track AR markers in your app. Add the ARMarkerManager
component to your XR Origin GameObject to enable marker tracking.
An AR marker is a known visual pattern in the physical environment, such as a QR code or an ArUco tag, that a device can recognize and track. This manager detects these markers and creates GameObjects with ARMarker components to represent them.
AR Marker Manager component inspector
Property | Description |
---|---|
Trackables Changed | Invoked when markers have changed (been added, updated, or removed). |
Marker Prefab | If not null , this prefab is instantiated for each detected AR marker. If the prefab does not contain an AR Marker component, ARMarkerManager will add one. |
Use markers in your project
The primary way to use markers is to subscribe to the ARMarkerManager.trackablesChanged
event. This event is invoked whenever markers are added, updated, or removed, and it provides lists of all the markers that have changed since the last frame.
The following code example shows how to subscribe to this event to respond to detected markers:
void SubscribeToMarkerChanges(ARMarkerManager manager)
{
manager.trackablesChanged.AddListener(OnMarkersChanged);
}
void OnMarkersChanged(ARTrackablesChangedEventArgs<ARMarker> changes)
{
foreach (var added in changes.added)
{
// Handle added markers, initial pose and marker ID
// or encoded data
}
foreach (var updated in changes.updated)
{
// Handle updated markers, typically changes in pose
}
foreach (var removed in changes.removed)
{
// Handle removed markers
}
}
Once you have a reference to an ARMarker
from the added or updated lists, you can query its properties (such as markerId
or size
) or retrieve its encoded data if the marker type supports it.
Get the marker's encoded data
Some markers support encoded data that can be decoded with TryGetStringData and TryGetBytesData for the respective data types. To learn more about which markers support encoded data refer to Marker types.
The primary advantage of markers like QR codes is their ability to encode data into the marker. To access this data, you first need a reference to the ARMarker, which you can get when the ARMarkerManager.trackablesChanged
event is invoked.
Check the ARMarker.dataBuffer.bufferType to determine what kind of data the marker contains, and then call the appropriate method to retrieve it as shown in the following code example:
void GetMarkerDataFromManager(ARMarkerManager manager, ARMarker marker)
{
if (marker.markerType == XRMarkerType.QRCode ||
marker.markerType == XRMarkerType.MicroQRCode)
{
switch (marker.dataBuffer.bufferType)
{
case XRSpatialBufferType.String:
GetStringDataFromManager(manager, marker);
break;
case XRSpatialBufferType.Uint8:
GetBytesDataFromManager(manager, marker);
break;
}
}
}
void GetStringDataFromManager(ARMarkerManager manager, ARMarker marker)
{
var result = manager.TryGetStringData(marker);
if (result.status.IsError())
return;
string stringData = result.value;
}
void GetBytesDataFromManager(ARMarkerManager manager, ARMarker marker)
{
var result = manager.TryGetBytesData(marker);
if (result.status.IsError())
return;
byte[] bytesData = result.value;
}
Performance considerations
Retrieving marker's encoded data can have performance implications due to memory allocations. For performance critical applications, it is important to understand how to minimize this overhead. TryGetStringData
and TryGetBytesData
allocate new managed memory (string
and byte[]
respectively) on every successful call. In performance critical applications, especially those that retrieve markers' encoded data frequently, this can lead to garbage collection pressure and impact performance.
For the best performance, you should retrieve the encoded data as a NativeArray<byte>
by calling the subsystem level API directly. This approach works for all data types and avoids the managed heap allocation.
The following code example shows how to get the raw encoded data into a NativeArray
using a temporary allocator, which results in no garbage collection:
void GetNativeArrayBytesData(ARMarkerManager manager, ARMarker marker)
{
if (manager.subsystem is XRMarkerSubsystem markerSubsystem)
{
var result = markerSubsystem.TryGetBytesData(
marker.dataBuffer, Allocator.Temp);
if (result.status.IsError())
{
// Handle error
return;
}
NativeArray<byte> bytesData = result.value;
}
}
Once you have the data in a NativeArray<byte>
, the next step depends on the data type.
Processing binary data
If the bufferType
is Uint8
, the NativeArray<byte>
you retrieved contains the raw binary data. You can now process this data directly without any further conversions or allocations.
Processing string data (UTF-8)
If the bufferType
is String
, the NativeArray<byte>
contains the UTF-8 encoded string data. While you could convert this to a C# string, doing so would cause a new allocation and defeat the purpose of the efficient retrieval.
The key to performance is to avoid creating the managed string object altogether by processing the raw UTF-8 bytes.