Loading...
Loading...
Integrate SpacetimeDB with Unity game projects. Use when building Unity clients with MonoBehaviour lifecycle, FrameTick, and PlayerPrefs token persistence.
npx skill4agent add clockworklabs/spacetimedb unitycsharp-serverhttps://github.com/clockworklabs/com.clockworklabs.spacetimedbsdk.gitspacetime generate --lang csharp --out-dir Assets/SpacetimeDB/module_bindings --module-path PATH_TO_MODULEusing UnityEngine;
using SpacetimeDB;
using SpacetimeDB.Types;
public class SpacetimeManager : MonoBehaviour
{
private const string TOKEN_KEY = "SpacetimeAuthToken";
private const string SERVER_URI = "http://localhost:3000";
private const string DATABASE_NAME = "my-game";
public static SpacetimeManager Instance { get; private set; }
public DbConnection Connection { get; private set; }
public Identity LocalIdentity { get; private set; }
void Awake()
{
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this;
DontDestroyOnLoad(gameObject);
}
void Start()
{
string savedToken = PlayerPrefs.GetString(TOKEN_KEY, null);
Connection = DbConnection.Builder()
.WithUri(SERVER_URI)
.WithDatabaseName(DATABASE_NAME)
.WithToken(savedToken)
.OnConnect(OnConnected)
.OnConnectError(err => Debug.LogError($"Connection failed: {err}"))
.OnDisconnect((conn, err) => {
if (err != null) Debug.LogError($"Disconnected: {err}");
})
.Build();
}
void Update()
{
Connection?.FrameTick();
}
void OnDestroy()
{
Connection?.Disconnect();
}
private void OnConnected(DbConnection conn, Identity identity, string authToken)
{
LocalIdentity = identity;
PlayerPrefs.SetString(TOKEN_KEY, authToken);
PlayerPrefs.Save();
Debug.Log($"Connected as: {identity}");
conn.SubscriptionBuilder()
.OnApplied(OnSubscriptionApplied)
.SubscribeToAllTables();
}
private void OnSubscriptionApplied(SubscriptionEventContext ctx)
{
Debug.Log("Subscription applied: game state loaded");
}
}FrameTick()Update()FrameTick()Update()FrameTick()conn.Db.WithAutomaticReconnect()OnConnectedBuild()OnConnectedFrameTick()SpacetimeDBNetworkManagerIsActiveOnAppliedConnectionIdIsReconnectingOnDisconnect((conn, error, next) => ...)OnConnectError((error, next) => ...)NextReconnect?AttemptDelayMinDelayMaxDelay.WithAutomaticReconnect(new AutomaticReconnectOptions { MinDelay = ..., MaxDelay = ... })Disconnect()Status.UnknownResultUnknownResultExceptionSELECT * FROM view_name(literal_args)void RegisterCallbacks()
{
Connection.Db.Player.OnInsert += (EventContext ctx, Player player) => {
SpawnPlayerObject(player);
};
Connection.Db.Player.OnDelete += (EventContext ctx, Player player) => {
DestroyPlayerObject(player.Id);
};
Connection.Db.Player.OnUpdate += (EventContext ctx, Player oldPlayer, Player newPlayer) => {
UpdatePlayerObject(newPlayer);
};
}FrameTick()OnConnectedOnSubscriptionAppliedpublic class GameUI : MonoBehaviour
{
public void OnMoveButtonClicked(Vector2 direction)
{
SpacetimeManager.Instance.Connection.Reducers.MovePlayer(direction.x, direction.y);
}
public void OnSendChat(string message)
{
SpacetimeManager.Instance.Connection.Reducers.SendMessage(message);
}
}SpacetimeManager.Instance.Connection.Reducers.OnSendMessage += (ReducerEventContext ctx, string text) => {
if (ctx.Event.Status is Status.Committed)
Debug.Log($"Message sent: {text}");
else if (ctx.Event.Status is Status.Failed(var reason))
Debug.LogError($"Send failed: {reason}");
else if (ctx.Event.Status is Status.UnknownResult)
Debug.LogWarning("Connection lost before the result arrived; the message may have been sent.");
};// Find by primary key
if (Connection.Db.Player.Id.Find(playerId) is Player player)
{
Debug.Log($"Player: {player.Name}");
}
// Iterate all
foreach (var p in Connection.Db.Player.Iter())
{
Debug.Log(p.Name);
}
// Filter by index
foreach (var p in Connection.Db.Player.Level.Filter(5))
{
Debug.Log($"Level 5: {p.Name}");
}
// Count
int total = Connection.Db.Player.Count;FrameTickconn.DbDontDestroyOnLoad(gameObject)link.xmlPlayerPrefsOnConnectOnConnect.WithToken(initialToken).WithAutomaticReconnect().WithTokenProvider(() => RefreshTokenAsync())Task<string>Disconnect()