Documentation

Cloud Code

Game organization in CloudBehaviours

Split your game state across CloudBehaviours, keep domain logic private, and call between behaviours when needed.
Read time 3 minutesLast updated 15 days ago

Important
This page describes an experimental feature that might change significantly before release. It's not recommended to use or rely on experimental features in production environments due to potential instability.
CloudBehaviours carries expectations about how you structure your game's objects. A game with players, hands, and cards doesn't map to a single CloudBehaviour class the way a MonoBehaviour hierarchy might. Decide what becomes its own CloudBehaviour, and put game logic that doesn't cross the client boundary in the right place.

Decide what becomes its own CloudBehaviour

Give a piece of state its own CloudBehaviour when it has its own owner or lifetime. State that belongs to one player and outlasts any single match, such as a profile or an inventory, is a
Player
-scoped CloudBehaviour. State that belongs to a match and ends with it lives on a
MultiplayerSession
-scoped CloudBehaviour.
Inside a CloudBehaviour, what clients need to see decides how you declare each piece of state:
  • State that every player sees the same way is a public field or property, which synchronizes.
  • State that looks different to each player is exposed through a per-player state view.
  • State that clients never see is a private field.
For example, in a card game with players, hands, and cards:
  • The match belongs to the session and ends with it. It's a
    MultiplayerSession
    -scoped CloudBehaviour.
  • Each player's hand also ends with the match, so it stays on the match CloudBehaviour. Each player sees only their own hand, so the match exposes it through a per-player state view.
  • A single card has no owner or lifetime of its own. It stays a plain C# class referenced by the match CloudBehaviour.

Where real game logic lives

A shared model type crossing the client boundary is data-only: it carries public fields and properties, and nothing else. Methods on a model type never regenerate on the client, and a public property that can be read but not publicly assigned, such as a computed property, causes a compile-time error.
Keep your actual game logic in a private domain layer inside the CloudBehaviour, where ordinary object-oriented code works normally. Expose only data-only projections and events to the client:
[StateScope(Scope.MultiplayerSession)]public class CardGame : CloudBehaviour{ [CloudCodeSerializeProperty] private Deck _deck = new(); // Real logic: shuffling, dealing [CloudCodeSerializeProperty] private Dictionary<string, List<Card>> _hands = new(); // Private: only the server knows each hand [SyncView("Hand")] internal List<Card> ProjectHand(string playerId) => // Each player receives only their own hand _hands.TryGetValue(playerId, out var hand) ? hand : new List<Card>(); public int Round; // Data-only, synchronized public event Action<string> CardDrawnBy; // Push channel: says who drew, never which card public void DrawCard() { var playerId = Context.PlayerId; // The caller, never a client-supplied ID if (!_hands.TryGetValue(playerId, out var hand)) _hands[playerId] = hand = new List<Card>(); hand.Add(_deck.Draw()); // Domain logic runs here CardDrawnBy?.Invoke(playerId); }}
[CloudCodeSerializeProperty]
on
_deck
persists the field, but a private field inside
Deck
also needs its own
[CloudCodeSerializeProperty]
to be saved. Refer to Player and match persistence in CloudBehaviours.
Persistence rules apply inside
Deck
too:
[CloudCodeSerializeProperty]
on
_deck
saves the deck object, but only its public members and the private ones that also carry
[CloudCodeSerializeProperty]
. Mark
Deck
's private card list with the attribute, or its cards reset after the behaviour is reloaded. Refer to Player and match persistence in CloudBehaviours.
This shape works for a turn-based game with moderately complex mechanics: private domain logic, data-only projections, and an event stream for presentation.

Coordinate between CloudBehaviours

When one behaviour needs to call another, such as a match behaviour awarding a reward from a player's inventory behaviour, refer to Cross-behaviour calls in CloudBehaviours.

Additional resources