Scene integration
The SDK root in the Login scene provides every screen contract and presenter. Each game scene only registers its own UI components in a scope derived from an SDK base class, and receives what it needs by injection.
1. Assembly referencesโ
Code in Assembly-CSharp sees the SDK types without any reference. A game
.asmdef references SilverTiger.ProjectV.Unity, plus VContainer when it
injects:
{
"name": "ProjectV.Game.Presentation",
"references": [
"SilverTiger.ProjectV.Unity",
"VContainer",
"UniTask"
]
}
Do not reference other SDK assemblies, SilverTiger.CrossSDK.* or Nakama from
game code.
2. One root in the Login sceneโ
The Login scene contains the one SDK root created by Setup. It stays alive when other scenes load. Never add another root, and never register screen contracts or presenters yourself in a scene scope.
3. Login scene scopeโ
Derive the Login scope from ProjectVLoginSceneScope and register the Login UI:
using SilverTiger.ProjectV.Unity.Presentation;
using VContainer;
using VContainer.Unity;
public sealed class LoginSceneScope : ProjectVLoginSceneScope
{
protected override void ConfigureLogin(IContainerBuilder builder)
{
builder.RegisterComponentInHierarchy<PopupLogin>();
}
}
4. Authenticated scene scopesโ
Lobby, Shop, Character, Alliance and Campaign derive from
ProjectVAuthenticatedSceneScope. When nobody is signed in (for example when
the scene is opened directly in the Editor), the base class sends the player
to Login.
using SilverTiger.ProjectV.Unity.Presentation;
using UnityEngine;
using UnityEngine.SceneManagement;
using VContainer;
using VContainer.Unity;
public sealed class LobbySceneScope : ProjectVAuthenticatedSceneScope
{
protected override void ConfigureAuthenticated(IContainerBuilder builder)
{
builder.RegisterComponentInHierarchy<LobbyUIManager>();
builder.RegisterComponentInHierarchy<HeaderUIManager>();
}
protected override void DisableScenePresentation()
{
var lobby = FindFirstObjectByType<LobbyUIManager>(FindObjectsInactive.Include);
if (lobby != null)
{
lobby.enabled = false;
}
}
protected override void NavigateToLogin(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
}
DisableScenePresentation must disable every UI script of the scene before
going to Login, so none of them sends a command. Override LoginSceneName
only if the Login scene is not called Login.
5. Inject into viewsโ
Ask for the screen contract (or the presenter, for screens without one) in an
[Inject] method:
using System;
using SilverTiger.ProjectV.Unity.Presentation.Lobby;
using UnityEngine;
using VContainer;
public sealed class LobbyUIManager : MonoBehaviour
{
private IProjectVLobbyScreen lobby = null!;
[Inject]
public void Construct(IProjectVLobbyScreen screen)
{
lobby = screen ?? throw new ArgumentNullException(nameof(screen));
}
}
Do not look them up with FindObjectOfType, create them with new or keep
them in static fields.
6. Startup sequence on Loginโ
Subscribe to Authentication and Startup snapshots
-> StartAsync(contentReady)
Ready -> go to Lobby (session restored)
AwaitingLogin -> show login choices
Maintenance / UpdateRequired / ContractMismatch / Offline / Failed -> blocking UI
-> authenticate (Guest, password, Google or Apple)
-> StartAsync(contentReady) again
-> Ready -> go to Lobby
Pass contentReady: true only after the game's own content (Addressables,
localization) has loaded; until then startup waits in WaitingForContent.
Never let the player past a blocking status.
7. Mission sceneโ
Campaign calls IProjectVCampaignScreen.BeginMissionAsync(missionId); when it
succeeds, the game loads the gameplay scene. That scene must contain
exactly one component implementing IProjectVGameplayDataConsumer
(components on inactive objects count too).
The SDK calls its BindGameplayData(presenter, startingData) with the run the
server started, and UnbindGameplayData when the run ends. If the scene
cannot load, call CancelSceneLoadAsync so the run is released. See
Gameplay data.
8. Page lifetimeโ
Call Observe (or SubscribeAsync) when a page opens and dispose that exact
IDisposable when it closes. Never poll from Update().
Continue with the Presenter reference and Presentation recipes.