Getting Started

Introduction

Installation

Project Configuration

Testing in Your Sandbox

Samples & Examples

Core Concepts & Glossary

Package Types

Spaces & Space Templates

Custom Avatars

Custom Avatar Animations

Avatar Attachments

Custom Prefab Objects

Embedded Packages

Drivable Vehicles

Scene Setup

Testing In Unity vs Sandbox

Controlling the Camera

Custom Collision, Layers, and Tags

Audio Mixers and Groups

Key Differences from Standard Unity3D development

Economy

Quests and Rewards

Economy Overview

Monetization

Items

Consumable Items

Rewarding Items

World Currency

Selling Items

Scripting

Components

Entrance Point

Camera Passthrough

Interactable

Trigger Event

Point Of Interest

Environment Settings Overrides

Render Pipeline Settings Overrides

Movement Materials

Climbable

Avatar Teleporter

Empty Frame

Projector Surface

Seat Hotspot

Guidelines

Supported Features and Limitations

Performance Guidelines

Lighting

Publishing to Spatial

Finding Published Packages

Support

FAQs

Help and Support

Release Notes

Asset Import Settings

Key Differences: Spatial and Standard Unity3D development

Using Third-Party Packages

Unity Packages with C# code require some changes to work with the Spatial Creator Toolkit, and there are some important limitations.

You can find a list of third-party packages with porting instructions here, including packages such as tweening and car control packages.

Camera

Spatial does not provide access to Camera.Main; instead i provides its own camera API. For more details on this, and on using Cinemachine, see the Camera documentation.

PlayerPrefs, Save Data, and the Data Store

Spatial does not support PlayerPrefs. Instead we have a cloud-save system we call the DataStore.

Saving to the DataStore creates a server request so we rate limit writes. This means inside your game-loop you should be reading/writing your save data to an intermediary C# object which you periodically write to the DataStore.

For more information view the DataStore API Reference.

Resources

A common technique for referencing assets in Unity is to drop them all inside a /Resources folder then load them at runtime with Resources.Load(.... Unfortunately, Unity3D does not allow Asset Bundles to add contents to Resources, so Spatial does not allow Creator Toolkit projects to use Resources.

Strategies for Referencing Assets

Instead, you can re-create the Resources.Load workflow with ScriptableObjects. Create an object that holds a collection of UnityEngine.Objects and then add the assets you want to reference to it.

[CreateAssetMenu(fileName = "NewAssetCollection", menuName = "AssetCollection")]
public class AssetCollection : ScriptableObject
{
    public UnityEngine.Object[] assets;
}

You can then search through the AssetCollection.assets by object name similar to Resources.Load.

<aside> <img src="/icons/light-bulb_red.svg" alt="/icons/light-bulb_red.svg" width="40px" /> Remember that packages uploaded only contains the dependencies of a scene. If your AssetCollection is not directly referenced by a GameObject in the scene it will not be included.

</aside>

Scene Management

A key limitation of Spatial Creator Toolkit projects is they can only have one scene. This means that if you’re used to creating several scenes for different levels, or even reloading the same scene to reset the game, you’ll have to design around this limitation.

Here are some useful strategies that we’ve used internally and with teams that help work around this limitation:

Create levels as prefabs and instantiate/destroy them.

For Buddy Blitz we used this strategy, and the levels were loaded and unloaded via scripting. While on development, we actually had different scenes with the prefab we wanted to test.

Untitled

Untitled

As you can see, using this strategy you will be able to subdivide your content and load/unload it accordingly.

Create a SceneManager to handle top level “scenes”

Group your “scenes” as a top level game objects and create a SceneManager script that handles loading/unloading. This is similar to the first strategy, but you could potentially have everything on the scene. In the following sample, the Scene script was just created to recreate the scene object when the Restart() function is called.

Untitled

Untitled

Untitled

Avatar control

When you load your space on Spatial, you will notice that Spatial already provides an avatar controller that allows users to control their character from any of our provided platforms. However, there are times where we want to be able to control how the Spatial avatar moves, where it moves, how it looks, or how it animates. Unlike a direct Unity project, Spatial doesn’t provide direct access to the avatar controller game object, but rather it exposes key functionality thru the use of Spatial Services. All of these services are part of our SpatialBridge and we’ll go thru them:

Hiding the avatar / other avatars

Sometimes you want the be able to hide the user avatars for different reasons. For example, hide the avatar when riding a vehicle, or a cutscene where you want the other avatars on the space to disappear while you show it. We can do this using the avatar’s visibleLocally and visibleRemotely variables. These variables will allow you to control how your avatar is presented on your device, on other user’s devices, and how other user’s avatars look on your device.

public void ToggleRemoteVisibility()
{
    // Change visibility on my avatar to everyone's client
    SpatialBridge.actorService.localActor.avatar.visibleRemotely = !SpatialBridge.actorService.localActor.avatar.visibleRemotely;
}

public void ToggleLocalVisibility()
{
    // Change visibility of my avatar only on my client
    SpatialBridge.actorService.localActor.avatar.visibleLocally = !SpatialBridge.actorService.localActor.avatar.visibleLocally;
}

public void ToggleLocalVisibilityOtherAvatars()
{
    // Change visibility of other avatars on my client
    foreach (var actor in SpatialBridge.actorService.actors.Values)
    {
        if (actor != SpatialBridge.actorService.localActor)
        {
            actor.avatar.visibleLocally = !actor.avatar.visibleLocally;
        }
    }
}

Changing avatar mesh / visuals

To control the local avatar’s visuals, you can use the local avatar’s SetAvatarBody function (see https://cs.spatial.io/reference/SpatialSys.UnitySDK.IAvatar.SetAvatarBody). This function allows you to change the avatar to a SpatialAvatar that’s either uploaded to your spatial world as a separate package, or change it to a SpatialAvatar embedded within your space package.

public void ChangeAvatarBody()
{
    // Change the user's avatar to an embedded asset avatar
    SpatialBridge.actorService.localActor.avatar.SetAvatarBody(AssetType.EmbeddedAsset, "OgreAvatar");

    // Change the user's avatar to a Spatial Studio avatar
    SpatialBridge.actorService.localActor.avatar.SetAvatarBody(AssetType.Package, "ITEM_ID_FROM_SPATIAL_STUDIO");

    // Reset the user's avatar to the default
    SpatialBridge.actorService.localActor.avatar.ResetAvatarBody();
}

As you can see, there are multiple ways of creating the body. See https://docs.spatial.io/components/custom-avatars to understand how to create a custom avatar.

Changing avatar animation

Sometimes you’d like to change the avatar’s animation. You can do this using the PlayEmote and StopEmote functions on your local avatar (see https://cs.spatial.io/reference/SpatialSys.UnitySDK.IAvatar.PlayEmote).

<aside> 💡 Note: Spatial Avatars don’t support custom animator controllers for the time being, so you can only control animations via PlayEmote and StopEmote. Ideally sometime in the future we have more advanced avatar controller support that’s you can properly sync between clients.

</aside>

public void StartSwimming()
{
    // Play the swim animation on the local actor's avatar
    SpatialBridge.actorService.localActor.avatar.PlayEmote(AssetType.EmbeddedAsset, "SwimAnimation", immediately: true, loop: true);
}

public void StopSwimming()
{
    SpatialBridge.actorService.localActor.avatar.StopEmote();
}

Moving your avatar