Chuyển tới nội dung chính

Khởi động (Startup)

Startup chạy trong scene Login. Nó kiểm tra server có chấp nhận bản build game này không, đăng nhập lại cho người chơi từ lần chơi trước khi có thể, và tải dữ liệu game của người chơi. Scene Login hiện một màn cho mỗi bước và mở game khi startup báo Ready.

Bạn dùngNó cho bạn
IProjectVStartupPresenterBắt đầu startup và theo dõi từng bước
ProjectVStartupSnapshotCần vẽ gì: bước hiện tại và lý do nó dừng
ProjectVStartupStatusCác bước, mỗi bước một màn hoặc một thông báo
ProjectVStartupErrorLý do startup dừng: Category, Code, RequestId, LocalizationKey
ProjectVStartupCommandResultKết quả của StartAsync

Startup không có screen contract. Scene Login dùng trực tiếp presenter, cùng với presenter Authentication cho các nút đăng nhập.

Bắt đầu nhanh​

using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using SilverTiger.ProjectV.Unity.Presentation.Startup;
using UnityEngine;
using VContainer;

public sealed class LoginStartupView : MonoBehaviour
{
private IProjectVStartupPresenter startup = null!;
private IDisposable? subscription;
private CancellationTokenSource? lifetime;

[Inject]
public void Construct(IProjectVStartupPresenter presenter) => startup = presenter;

private async void OnEnable()
{
lifetime = new CancellationTokenSource();
var token = lifetime.Token;
var startupSub = await startup.SubscribeAsync(OnStartupChanged);
if (token.IsCancellationRequested)
{
// The page closed while subscribing.
startupSub.Dispose();
return;
}
subscription = startupSub;
// Call this once your own content (Addressables and so on) has loaded.
await startup.StartAsync(contentReady: true, token);
}

private void OnDisable()
{
subscription?.Dispose();
subscription = null;
lifetime?.Cancel();
lifetime?.Dispose();
lifetime = null;
}

// May be called off the Unity main thread.
private void OnStartupChanged(ProjectVStartupSnapshot snapshot) => RenderAsync(snapshot).Forget();

private async UniTaskVoid RenderAsync(ProjectVStartupSnapshot snapshot)
{
await UniTask.SwitchToMainThread();
if (this == null)
{
return;
}

if (snapshot.IsReady)
{
OpenLobby();
return;
}
if (snapshot.RequiresLogin)
{
ShowSignInOptions();
return;
}
if (snapshot.IsBlocking)
{
ShowBlocking(snapshot.Status, snapshot.Error?.LocalizationKey);
return;
}
ShowLoading(snapshot.Status);
}

// Retry button on the blocking screen.
public async void OnRetryClicked()
{
if (lifetime != null)
{
await startup.StartAsync(contentReady: true, lifetime.Token);
}
}

private void OpenLobby() { }
private void ShowSignInOptions() { }
private void ShowBlocking(ProjectVStartupStatus status, string? localizationKey) { }
private void ShowLoading(ProjectVStartupStatus status) { }
}

Đó là toàn bộ luồng startup: đăng ký nhận (subscribe), bắt đầu, vẽ (render) từng bước. Sau khi người chơi đăng nhập, gọi lại StartAsync(true, token) và listener sẽ đi qua Hydrating tới Ready.

Main thread

Listener của SubscribeAsync có thể chạy ngoài main thread của Unity. Hãy chuyển về main thread trước khi đụng vào object Unity, như RenderAsync làm với UniTask.SwitchToMainThread().

Cần vẽ gì​

Listener nhận một ProjectVStartupSnapshot:

MemberKiểuDùng để
StatusProjectVStartupStatusBước hiện tại; chọn màn theo bảng bên dưới
IsReadyboolStatus là Ready: mở game
RequiresLoginboolStatus là AwaitingLogin: hiện các lựa chọn đăng nhập
IsBlockingboolStartup đã dừng và không tự đi tiếp được. Hiện thông báo tương ứng
ErrorProjectVStartupError?Lý do startup dừng, hoặc null. LocalizationKey chọn thông báo, RequestId đưa vào báo cáo hỗ trợ

startup.Current giữ cùng snapshot đó; giá trị ban đầu là WaitingForContent.

Với mỗi ProjectVStartupStatus:

StatusHiện gì
WaitingForContentMàn đang tải của bạn; startup chưa bắt đầu
Admitting"Connecting…" (đang kết nối)
RestoringSessionMàn đang tải; người chơi đang được đăng nhập lại
AwaitingLoginCác lựa chọn đăng nhập (Guest, Google, Apple, mật khẩu)
HydratingMàn đang tải; dữ liệu game của người chơi đang tải
ReadyKhông gì cả; mở game
MaintenanceThông báo bảo trì; người chơi thử lại sau
UpdateRequiredYêu cầu người chơi cập nhật từ store
ContractMismatchYêu cầu người chơi cập nhật game
OfflineThông báo mất kết nối kèm nút thử lại (Retry)
FailedThông báo lỗi kèm nút Retry

Năm status cuối là các status chặn (IsBlocking là true).

Bắt đầu startup​

Task<ProjectVStartupCommandResult> StartAsync(bool contentReady, CancellationToken cancellationToken)
Tham sốKiểuGiá trị
contentReadybooltrue khi nội dung riêng của game đã tải xong. false không bắt đầu gì nếu startup chưa chạy
cancellationTokenCancellationTokenDừng việc bạn chờ kết quả. Bản thân startup vẫn chạy tiếp và vẫn gửi cập nhật tới listener

Trả về một ProjectVStartupCommandResult:

IsSuccessSnapshot.StatusChuyện gì đã xảy ra
trueReadyNgười chơi đã đăng nhập và dữ liệu game đã tải. Mở game
trueAwaitingLoginKhông có phiên đăng nhập đã lưu. Hiện các lựa chọn đăng nhập
trueWaitingForContentBạn truyền contentReady: false và startup chưa chạy; không có gì bắt đầu
falsemột status chặnStartup đã dừng. Hiện thông báo cho status đó (xem bên dưới)
  • Gọi StartAsync khi startup đang chạy sẽ nhập vào lượt chạy đó và trả về kết quả của nó; không bắt đầu lượt thứ hai, kể cả với contentReady: false.
  • Gọi lại StartAsync(true, token) để thử lại sau một status chặn, và sau khi đăng nhập thành công.
  • Khi token của bạn kích hoạt trước, kết quả là Failed với category Cancelled. Kết quả đó chỉ của riêng bạn: listener và Current vẫn theo lượt chạy thật.

Khi nó dừng — hiện thông báo cho status, dùng Error.LocalizationKey (projectv.startup.<category>) khi Error có giá trị:

Tình huốngStatusNgười chơi thấy gì
Server đang bảo trìMaintenanceThông báo bảo trì. Thử lại sau
Phiên bản game này quá cũUpdateRequiredYêu cầu người chơi cập nhật, kèm link store
Bản build này và server không khớpContractMismatchYêu cầu người chơi cập nhật. Khi phát triển, điều này nghĩa là bản build Client và môi trường backend lệch nhau; không phải lỗi của Client
Không kết nối được serverOfflineThông báo mất kết nối kèm Retry (Error.Category là Network hoặc Unavailable)
Mọi trường hợp khácFailedThông báo lỗi kèm Retry. Các code gồm PROJECTV_STARTUP_BACKEND_FAILED và PROJECTV_STARTUP_UNEXPECTED_FAILURE
Token của bạn kích hoạt trước khi startup xongFailed (chỉ ở kết quả)Không gì cả; trang đang đóng
Phiên đăng nhập đã lưu không còn được chấp nhậnAwaitingLogin với Error có giá trịCác lựa chọn đăng nhập. IsSuccess vẫn là true

Không bao giờ mở game khi đang ở một status chặn.

Quy tắc​

  • Chỉ truyền contentReady: true khi nội dung riêng của game đã tải xong.
  • Chuyển màn dựa trên IsReady, không bao giờ chỉ dựa trên việc đăng nhập thành công.
  • Startup không xếp hàng command (lệnh) như các presenter khác: mọi lời gọi đều nhập vào cùng một lượt chạy.
  • Error.Code dùng để rẽ nhánh và hỗ trợ, không phải chữ hiển thị cho người chơi. Hiện thông báo theo LocalizationKey, và RequestId trong một panel hỗ trợ.

Lỗi thường gặp​

  • Coi StartAsync bị huỷ là đã dừng startup. Nó chỉ dừng việc bạn chờ; hãy vẽ từ listener, không chỉ từ kết quả.
  • Gọi StartAsync từ mọi nút. Gọi một lần khi scene mở, lại một lần sau khi đăng nhập, và từ nút Retry.
  • Đụng vào object Unity trong listener mà không chuyển thread. Listener có thể chạy ngoài main thread.
  • Mở Lobby ngay sau khi đăng nhập. Gọi StartAsync(true, token) và chờ Ready trước, để dữ liệu của người chơi được tải.
  • Không dispose subscription. Dispose (giải phóng) nó trong OnDisable hoặc OnDestroy.

Helper gắn UI​

startup.SubscribeUiAsync(ProjectVUiBindingId.LoginStartup, apply, token) hoạt động như SubscribeAsync và còn đánh dấu trong log của SDK khi apply của bạn đã trả về (xem UI subscriptions và ProjectVUiSubscriptions). Nó không chuyển thread: apply chạy trên thread gửi bản cập nhật, nên bạn tự chuyển về main thread.