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

Campaign

Màn Campaign liệt kê các nhiệm vụ, cho thấy nhiệm vụ nào đang bị khoá, số sao và mốc của lượt chơi tốt nhất, những gì một lượt chơi có thể rơi ra, và bắt đầu nhiệm vụ trước khi scene gameplay được tải. Nhiệm vụ, khoá, chi phí stamina (thể lực) và sao đều do server quyết định.

Bạn dùngNó cho bạn
IProjectVCampaignScreenScreen contract: theo dõi các nhiệm vụ, bắt đầu một nhiệm vụ, giải phóng nó
ProjectVCampaignViewNhững gì cần vẽ: trạng thái đang tải, nhiệm vụ, tổng số sao
ProjectVCampaignMissionMột nhiệm vụ: trạng thái khoá, chi phí vào, sao, xem trước vật rơi
ProjectVCampaignMissionStarsMục tiêu sao, các mốc và lượt chơi tốt nhất của một nhiệm vụ
ProjectVResultKết quả của một command (lệnh)

Bản thân lượt chơi do scene gameplay chạy; xem Dữ liệu gameplay.

Bắt đầu nhanh​

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SilverTiger.ProjectV.Unity.Presentation.Campaign;
using UnityEngine;
using VContainer;

public sealed class CampaignPage : MonoBehaviour
{
private IProjectVCampaignScreen campaign = null!;
private IDisposable? observation;
private bool starting;

[Inject]
public void Construct(IProjectVCampaignScreen screen) => campaign = screen;

private void OnEnable() => observation = campaign.Observe(Render);

private void OnDisable()
{
observation?.Dispose();
observation = null;
}

private void Render(ProjectVCampaignView view)
{
if (view.IsLoading)
{
ShowLoading(view.LoadError); // LoadError is null while the first load is running
return;
}
ShowMissions(view.Missions, view.TotalStars);
}

public async void OnStartClicked(ProjectVCampaignMission mission)
{
// A second tap would get the first tap's result; start and load only once.
if (starting)
{
return;
}
starting = true;
var result = await campaign.BeginMissionAsync(mission.MissionId.Value);
if (this == null)
{
return;
}
if (!result.IsSuccess)
{
starting = false;
ShowMessage(result.Message);
return;
}

var sceneName = FindScene(mission.GameplayKey.Value);
if (string.IsNullOrEmpty(sceneName))
{
// No scene for this mission: release the run so it costs nothing.
await campaign.CancelSceneLoadAsync();
starting = false;
return;
}
LoadScene(sceneName); // your scene loader; on failure call CancelSceneLoadAsync
}

private void ShowLoading(string? error) { }
private void ShowMissions(IReadOnlyList<ProjectVCampaignMission> missions, long totalStars) { }
private void ShowMessage(string message) { }
private string? FindScene(string gameplayKey) => null;
private void LoadScene(string sceneName) { }
}

Đó là toàn bộ trang: theo dõi, vẽ, bắt đầu nhiệm vụ. Sau khi một lượt chơi kết thúc bạn không phải làm gì; khi người chơi quay lại, IsCompleted, Stars và stamina đã được cập nhật sẵn. Screen contract cho thấy cùng luồng này từ ProjectV-Client.

Vẽ gì​

Render nhận một ProjectVCampaignView:

MemberKiểuDùng để
IsLoadingboolTrue cho tới khi danh sách nhiệm vụ về lần đầu. Hiện spinner
LoadErrorstring?Được gán khi lần tải đầu tiên đó thất bại. Hiện nó kèm nút thử lại (Retry) đóng rồi mở lại trang
MissionsIReadOnlyList<ProjectVCampaignMission>Các nhiệm vụ, kèm tiến độ của người chơi ở từng nhiệm vụ
TotalStarslongSố sao đạt được trên tất cả nhiệm vụ
Find(missionId)ProjectVCampaignMission?Tra một nhiệm vụ, ví dụ để cập nhật popup nhiệm vụ đang mở

Với mỗi ProjectVCampaignMission:

Để hiệnĐọc
Scene cần tảiGameplayKey.Value, ánh xạ sang một scene trong bảng của riêng bạn
Chi phí vàoEntryStamina
Giới hạn thời gianDurationSeconds
Đã hoàn thànhIsCompleted
Đang có lượt chơi mởIsActive
Bị khoáIsLocked: đã đóng, chưa hoàn thành nhiệm vụ bắt buộc, hoặc chưa đủ tổng số sao. Tắt nút Start
Chưa mởIsClosed; nhiệm vụ đã đóng thì luôn IsLocked
"Hoàn thành X trước"RequiredMissionIds
Một lượt chơi có thể rơi gìDropPreview: Kind, ResourceId, Amount (số tối đa một lượt chơi có thể rơi)
SaoStars (bên dưới)

Với Stars, một ProjectVCampaignMissionStars:

Để hiệnĐọc
"2/3 sao"BestStars / MaxStars
Các mục tiêuObjectives: ObjectiveId, Kind, Threshold; đánh dấu các mục có trong BestRunObjectiveIds
Phần thưởng mốcMilestones: StarsRequired, Rewards, IsClaimed
"Cần 12 sao"RequiresTotalStars, cùng IsLocked khi TotalStars còn thấp hơn mức đó

Kind của mục tiêu là một trong complete_stage, duration_at_most_ms, enemies_defeated_min, health_percent_min và stages_cleared_min; Threshold tính theo đơn vị mà kind đó nêu, và là null với complete_stage. Dùng kind để chọn text cho mục tiêu. Nhiệm vụ không có sao thì có ProjectVCampaignMissionStars.None.

Khớp một nhiệm vụ với art và text của bạn bằng MissionId.Value trong một bảng của riêng bạn; SDK không cung cấp tên hay icon.

Bắt đầu nhiệm vụ​

Task<ProjectVResult> BeginMissionAsync(string missionId)
Tham sốKiểuGiá trị
missionIdstringmission.MissionId.Value của một nhiệm vụ không bị khoá trong view hiện tại

Server bắt đầu một lượt chơi và kiểm tra stamina vào. Stamina bị trừ khi lượt chơi kết thúc, không phải lúc này.

Trả về một ProjectVResult:

IsSuccessMessageĐiều gì đã xảy ra
truerỗngServer đã bắt đầu lượt chơi. Tải scene gameplay
falselý do, sẵn để hiệnLượt chơi không bắt đầu, hoặc không thể xác nhận (xem bên dưới)

Khi thất bại — hiện Message nguyên văn. Các lý do thường gặp:

Tình huốngNgười chơi thấy gì
Không đủ staminaThông báo của server (code STAMINA_INSUFFICIENT)
Nhiệm vụ bị khoáThông báo của server (code MISSION_RUN_LOCKED)
Chưa đủ tổng số saoThông báo của server (code MISSION_RUN_LOCKED_STARS)
missionId null hoặc rỗng"The request was not confirmed. Please try again."
Không có kết nối hoặc timeoutMột thông báo mạng. Không tự động bắt đầu lại

Mỗi lần chỉ một lượt bắt đầu, cho mọi nhiệm vụ. Bấm lần hai vào cùng nhiệm vụ trong lúc nó đang bắt đầu sẽ nhận cùng kết quả. Bấm vào nhiệm vụ khác trong lúc một nhiệm vụ đang bắt đầu sẽ thất bại với "Another mission is starting." và không gửi gì. Chỉ tải scene gameplay một lần, cho nhiệm vụ đã bắt đầu; kết quả cũng có thể về sau khi trang đã đóng.

Giải phóng lượt chơi không tải được​

Task<ProjectVResult> CancelSceneLoadAsync()

Gọi nó sau một lần BeginMissionAsync thành công khi bảng của bạn không có scene cho GameplayKey của nhiệm vụ, hoặc khi scene tải thất bại. Lượt chơi đã giải phóng không tốn stamina.

Trả về một ProjectVResult:

IsSuccessMessageĐiều gì đã xảy ra
truerỗngServer đã giải phóng lượt chơi. Danh sách nhiệm vụ tự vẽ lại
falselý do, sẵn để hiệnLượt chơi chưa được giải phóng; hiện Message và để người chơi thử lại

Lần gọi thứ hai trong lúc một lần gọi đang chạy nhận cùng kết quả.

Stamina và sao​

  • Stamina vào bị trừ khi lượt chơi kết thúc: thắng, thua hoặc bỏ cuộc. Chơi lại trong scene gameplay, lượt chơi đã giải phóng, hoặc app bị tắt hay bị crash thì không tốn gì.
  • Server chấm sao khi lượt chơi kết thúc. Campaign hiện BestStars và các mốc đã nhận; scene gameplay hiện số sao của lượt chơi vừa chơi.
  • Hiện khoá từ IsLocked và Stars.IsLocked. Đừng bao giờ tự tính từ tiến độ local; dù sao server cũng từ chối bắt đầu nhiệm vụ bị khoá.
  • Cho QA, backend staging có thể mở khoá mọi nhiệm vụ; khi đó Campaign hiện tất cả đều đã mở.

Lỗi thường gặp​

  • Tải scene gameplay hai lần. Một lần bấm được gộp có thể trả kết quả sau khi việc tải đã bắt đầu. Giữ một cờ và chỉ tải một lần.
  • Bấm nhiệm vụ thứ hai khi nhiệm vụ đầu đang bắt đầu. Nó thất bại với "Another mission is starting."; hãy bỏ qua lượt bấm đó thay vì hiện lỗi.
  • Để lượt chơi đã bắt đầu treo khi scene không tải được. Gọi CancelSceneLoadAsync.
  • Trừ stamina trên màn hình ngay lúc Start. Stamina bị trừ khi lượt chơi kết thúc; Header tự cập nhật.
  • Tự tính khoá hay sao từ dữ liệu local. Dùng view.
  • Dùng kết quả sau khi trang đã đóng. Kiểm tra this != null sau await.
  • Không dispose observation. Giữ IDisposable từ Observe và dispose (giải phóng) nó trong OnDisable hoặc OnDestroy.

Presenter​

IProjectVCampaignPresenter là interface cấp thấp hơn nằm sau screen contract. Chỉ dùng nó khi bạn cần thứ mà screen không cung cấp:

MemberDùng để
SubscribeAsync(listener)Mọi ProjectVCampaignSnapshot: Status (Empty, Refreshing, Ready, Beginning, Failed), Recovery, Data
Current.ErrorMột ProjectVCampaignError đầy đủ với Code và RequestId, ví dụ để tự chọn text theo từng code
RefreshAsync(token)Tải danh sách nhiệm vụ (trả về ngay nếu đã tải)
BeginAsync(missionId, token)Dạng presenter của BeginMissionAsync
CancelSceneLoadAdmissionAsync(token)Dạng presenter của CancelSceneLoadAsync

Các command trả về một ProjectVCampaignCommandResult.

Listener của SubscribeAsync có thể chạy ngoài Unity main thread; chuyển về main thread trước khi đụng tới Unity object. Observe của screen luôn vẽ trên main thread.