Hướng dẫn: lên cấp cho nhân vật
Trong hướng dẫn này bạn dựng một trang Character: trang liệt kê các nhân vật của người chơi, hiện nhân vật đang chọn, chọn vật phẩm EXP bằng nút "+1" và "Max", xem trước kết quả và lên cấp. Cuối cùng, trang còn nâng một node của cây kỹ năng. Bạn sẽ dùng năm thứ:
IProjectVCharacterScreen: thứ trang giao tiếp;ProjectVCharacterView: thứ trang vẽ;ProjectVCharacterLevelPolicy: chọn vật phẩm EXP giúp bạn;ProjectVCharacterXpUpgradePreview: một lượt lên cấp sẽ làm gì;ProjectVResult: thứ một command (lệnh) trả về.
Trước khi bắt đầu: người chơi đăng nhập và vào được Lobby
(Hướng dẫn: Dựng luồng đăng nhập), và scene Character có một scope kế thừa từ
ProjectVAuthenticatedSceneScope
(Tích hợp scene).
Hướng dẫn Shop dùng cùng cách làm trên một màn đơn giản hơn.
1. Đăng ký trang
Trang là một MonoBehaviour trong scene Character. Đăng ký nó trong scene
scope. Khi chưa ai đăng nhập, scope ẩn trang trước khi trang kịp theo dõi gì,
rồi đưa người chơi về Login.
public sealed class CharacterSceneScope : ProjectVAuthenticatedSceneScope
{
[SerializeField] private CharacterPage page = null!;
protected override void ConfigureAuthenticated(IContainerBuilder builder)
{
builder.RegisterComponentInHierarchy<CharacterPage>();
}
protected override void DisableScenePresentation()
{
page.gameObject.SetActive(false);
}
protected override void NavigateToLogin(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
}
2. Nhận screen và theo dõi nó
Thêm một method [Inject] và giữ IProjectVCharacterScreen. Theo dõi khi
trang đang mở: Observe(render) gọi Render ngay lập tức, rồi gọi lại mỗi
khi dữ liệu thay đổi, luôn trên Unity main thread. Dispose (giải phóng)
observation khi trang đóng.
private IProjectVCharacterScreen character = null!;
private IDisposable? observation;
[Inject]
public void Construct(IProjectVCharacterScreen screen)
{
character = screen ?? throw new ArgumentNullException(nameof(screen));
}
private void OnEnable()
{
observation = character.Observe(Render);
}
private void OnDisable()
{
observation?.Dispose();
observation = null;
}
Bạn không tự tải gì cả. Nếu dữ liệu chưa được tải, việc theo dõi sẽ tải nó, và screen tự đồng bộ lại sau một command thất bại. Bạn không cần nút Sync.
3. Vẽ danh sách nhân vật và nhân vật đang chọn
Khi view.IsLoading là true, view.Data là null: hiện spinner, hoặc
view.LoadError khi lần tải đầu tiên thất bại. Sau đó vẽ Data.Characters
thành một danh sách và nhớ nhân vật đang chọn theo Id.Value.
private void Render(ProjectVCharacterView view)
{
loadingPanel.SetActive(view.IsLoading && view.LoadError == null);
if (view.IsLoading || view.Data == null)
{
messageText.text = view.LoadError ?? string.Empty;
return;
}
if (view.Data.IsEmpty)
{
messageText.text = "No characters yet.";
return;
}
var selected = Find(view.Data, selectedId) ?? view.Data.Characters[0];
if (selected.Id.Value != selectedId)
{
selectedId = selected.Id.Value;
selectedItems = NoItems;
}
var upgrade = view.UpgradeState(selectedId)!;
DrawRoster(view.Data);
DrawSelected(selected, upgrade);
DrawLevelUp(view, upgrade);
DrawSkill(view.Data);
}
DrawSelected hiện Level, thanh EXP và vài chỉ số trong Stats. Thanh EXP
là upgrade.Experience trên upgrade.Requirements.RequiredExperience; cả hai
đều tính từ cấp 1. Requirements là null khi server không gửi, và
RequiredExperience là 0 ở cấp tối đa.
private void DrawSelected(ProjectVCharacterEntry entry, ProjectVCharacterUpgradeState upgrade)
{
levelText.text = $"Lv.{entry.Level}";
xpText.text = upgrade.Requirements is { RequiredExperience: > 0 } next
? $"EXP {upgrade.Experience}/{next.RequiredExperience}"
: $"EXP {upgrade.Experience}";
var stats = entry.Stats;
statsText.text = $"HP {stats.MaxHealth} · ATK {stats.AttackDamage} · Armor {stats.Armor}";
}
Ánh xạ Id.Value sang tên và art của riêng bạn; SDK chỉ gửi ID và con số.
4. Chọn vật phẩm EXP bằng "+1" và "Max"
Bạn không bao giờ tự tính lượng EXP cần. SelectForNextLevel chọn các vật
phẩm người chơi đang có đủ cho cấp kế tiếp; SelectMaximum chọn các vật phẩm
cho cấp cao nhất mà chúng cộng lại đạt được. Cả hai trả về danh sách rỗng khi
không có gì để chọn, và các vật phẩm được chọn luôn hợp lệ để gửi.
private void Pick(bool maximum)
{
var data = character.Current.Data;
if (data == null)
{
return;
}
var id = new ProjectVCharacterId(selectedId);
selectedItems = maximum
? ProjectVCharacterLevelPolicy.SelectMaximum(data, id)
: ProjectVCharacterLevelPolicy.SelectForNextLevel(data, id);
Render(character.Current);
}
character.Current chính là view mà Observe đưa cho bạn, nên trang vẽ lại
ngay với lựa chọn mới. SelectForNextLevel có thể không đủ cho cấp kế tiếp
khi người chơi có quá ít vật phẩm.