Add prototype gameplay loop with character/battlefield assets and server game APIs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-10-20 18:50:00 +08:00
co-authored by Cursor
parent 79369e4473
commit 6c64210331
833 changed files with 998079 additions and 1042 deletions
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
@@ -14,28 +15,46 @@ namespace HotUpdate.UI
readonly Dictionary<string, GameObject> _opened = new Dictionary<string, GameObject>(StringComparer.Ordinal);
Transform _root;
UiPrefabLoader _loader;
readonly HashSet<string> _loading = new HashSet<string>(StringComparer.Ordinal);
public string Theme { get; private set; } = UiThemeCatalog.Candy;
public void Init(Transform root, UiPrefabLoader loader)
public void Init(Transform root, UiPrefabLoader loader, string theme = UiThemeCatalog.Candy)
{
Instance = this;
_root = root;
_loader = loader ?? new UiPrefabLoader();
Theme = UiThemeCatalog.Normalize(theme);
}
public GameObject Open(string prefabName, int siblingIndex = -1)
public IEnumerator OpenAsync(string prefabName, Action<GameObject> onOpened, int siblingIndex = -1)
{
if (string.IsNullOrEmpty(prefabName)) return null;
if (string.IsNullOrEmpty(prefabName)) { onOpened?.Invoke(null); yield break; }
while (_loading.Contains(prefabName)) yield return null;
if (_opened.TryGetValue(prefabName, out var existing) && existing != null)
{
existing.SetActive(true);
return existing;
onOpened?.Invoke(existing);
yield break;
}
var prefab = _loader.Load(prefabName);
_loading.Add(prefabName);
GameObject prefab = null;
try
{
var assetName = UiThemeCatalog.Resolve(prefabName, Theme);
yield return _loader.LoadAsync(assetName, loaded => prefab = loaded);
if (prefab == null && assetName != prefabName)
{
Debug.LogWarning($"[UI] Missing {assetName}; falling back to {prefabName}. Include both themes in the resource package.");
yield return _loader.LoadAsync(prefabName, loaded => prefab = loaded);
}
}
finally { _loading.Remove(prefabName); }
if (prefab == null)
{
Debug.LogError($"[UiManager] prefab not found: {prefabName}");
return null;
onOpened?.Invoke(null);
yield break;
}
var go = Instantiate(prefab, _root, false);
@@ -43,13 +62,7 @@ namespace HotUpdate.UI
if (siblingIndex >= 0)
go.transform.SetSiblingIndex(siblingIndex);
_opened[prefabName] = go;
return go;
}
public T Open<T>(string prefabName) where T : Component
{
var go = Open(prefabName);
return go != null ? go.GetComponentInChildren<T>(true) : null;
onOpened?.Invoke(go);
}
public void Close(string prefabName, bool destroy = false)
@@ -72,6 +85,8 @@ namespace HotUpdate.UI
void OnDestroy()
{
StopAllCoroutines();
_loader?.Dispose();
if (Instance == this) Instance = null;
}
}