Space Content Service

The space content service provides access to all objects in a space, including some built-in object types like Avatars. It is also used to spawn and destroy objects in the space.

Accessing Space Content

All space content is represented as IReadOnlySpaceObject and ISpaceObject interfaces. You can access all objects in the space with SpatialBridge.spaceContentService.allObjects or specific object types with SpatialBridge.spaceContentService.avatars, SpatialBridge.spaceContentService.networkObjects, etc.

// Loop through all spaceObject's in the space
foreach (IReadOnlySpaceObject obj in SpatialBridge.spaceContentService.allObjects.Values)
{
Debug.Log($"ID: {obj.objectID} Owner: {obj.ownerActorNumber} ObjectType: {obj.objectType}");
}
// Loop through all spaceObject's in the space
foreach (IReadOnlySpaceObject obj in SpatialBridge.spaceContentService.allObjects.Values)
{
Debug.Log($"ID: {obj.objectID} Owner: {obj.ownerActorNumber} ObjectType: {obj.objectType}");
}

Casting ReadOnly Interfaces

Notice how the allObjects dictionary contains objects of type IReadOnlySpaceObject. This read-only interface is used to communicate that, by default, you should be treating this object as read-only (as if you don't own it).

When you want to modify a SpaceObject you can cast it to an ISpaceObject. However, this cast only changes the interface and does not modify the ownership of the object. Trying to modify an ISpaceObject when the local client is not the owner will result in an error, or in most cases will be a no-op.

IReadOnlySpaceObject readOnlyObj = SpatialBridge.spaceContentService.allObjects.Values.First();

// Cast the readonly interface to the writable interface
ISpaceObject spaceObject = readOnlyObj as ISpaceObject;

// Casting doesn't dictate object ownership. Make sure we are the owner before writing
if (!spaceObject.isMine)
{
// Taking ownership is an async operation because it might require server to validate the request
yield return SpatialBridge.spaceContentService.TakeOwnership(spaceObject.objectID);

// Check if we are now the owner
if (!spaceObject.isMine)
{
Debug.LogError("Failed to take ownership of object");
yield break;
}
}

// Update position for everyone
spaceObject.position = new Vector3(0, 0, 0);
IReadOnlySpaceObject readOnlyObj = SpatialBridge.spaceContentService.allObjects.Values.First();

// Cast the readonly interface to the writable interface
ISpaceObject spaceObject = readOnlyObj as ISpaceObject;

// Casting doesn't dictate object ownership. Make sure we are the owner before writing
if (!spaceObject.isMine)
{
// Taking ownership is an async operation because it might require server to validate the request
yield return SpatialBridge.spaceContentService.TakeOwnership(spaceObject.objectID);

// Check if we are now the owner
if (!spaceObject.isMine)
{
Debug.LogError("Failed to take ownership of object");
yield break;
}
}

// Update position for everyone
spaceObject.position = new Vector3(0, 0, 0);

Creating Space Objects

Each client is able to create new SpaceObject's at any time.

private ISpaceObject mySpaceObject;

private IEnumerator CreateNewObjectCoroutine()
{
// Create a new space object
SpawnSpaceObjectRequest request = SpatialBridge.spaceContentService.SpawnSpaceObject();
yield return request;

if (request.succeeded)
{
// The new object we created on the server
ISpaceObject spaceObject = request.spaceObject;
// Write custom state to the object
spaceObject.SetVariable(0, "hello world!");
// Cache it in a local class variable for later access
mySpaceObject = spaceObject;
}
}
private ISpaceObject mySpaceObject;

private IEnumerator CreateNewObjectCoroutine()
{
// Create a new space object
SpawnSpaceObjectRequest request = SpatialBridge.spaceContentService.SpawnSpaceObject();
yield return request;

if (request.succeeded)
{
// The new object we created on the server
ISpaceObject spaceObject = request.spaceObject;
// Write custom state to the object
spaceObject.SetVariable(0, "hello world!");
// Cache it in a local class variable for later access
mySpaceObject = spaceObject;
}
}

Each SpaceObject has a unique ID, which can be used to reference it later. If you access a SpaceObject frequently however, it is recommended to cache it locally.

// Get our object by ID
if (SpatialBridge.spaceContentService.allObjects.TryGetValue(mySpaceObject.objectID, out IReadOnlySpaceObject spaceObject))
Debug.Log($"Found object with ID: {spaceObject.objectID}; Equals: {spaceObject == mySpaceObject}");
// Get our object by ID
if (SpatialBridge.spaceContentService.allObjects.TryGetValue(mySpaceObject.objectID, out IReadOnlySpaceObject spaceObject))
Debug.Log($"Found object with ID: {spaceObject.objectID}; Equals: {spaceObject == mySpaceObject}");

Destroying Space Objects

You can destroy SpaceObject's if you are the owner using the spaceContentService.DestroySpaceObject

When objects are destroyed, their interface will be marked as Disposed and should no longer be used.

private IEnumerator DestroyMyObjectCoroutine()
{
// You must be the owner of the object to be able to destroy it
ISpaceObject spaceObject = //..;

// Destroy the object (this is async because the server needs to validate the request)
DestroySpaceObjectRequest request = SpatialBridge.spaceContentService.DestroySpaceObject(spaceObject.objectID);
yield return request;

if (request.succeeded)
{
// The interface is now marked as disposed
Debug.Log($"Object destroyed! Disposed: {spaceObject.isDisposed}");
}
}
private IEnumerator DestroyMyObjectCoroutine()
{
// You must be the owner of the object to be able to destroy it
ISpaceObject spaceObject = //..;

// Destroy the object (this is async because the server needs to validate the request)
DestroySpaceObjectRequest request = SpatialBridge.spaceContentService.DestroySpaceObject(spaceObject.objectID);
yield return request;

if (request.succeeded)
{
// The interface is now marked as disposed
Debug.Log($"Object destroyed! Disposed: {spaceObject.isDisposed}");
}
}