Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace XGame
|
||||
{
|
||||
// 账号鉴权 HTTP 客户端:POST /login、/register,拿 {token,pid}。
|
||||
public static class AuthClient
|
||||
{
|
||||
[Serializable] class AuthReq { public string username; public string password; }
|
||||
[Serializable] class AuthResp { public string token; public int pid; }
|
||||
|
||||
public static IEnumerator Login(string user, string pwd, Action<bool, string, int, string> done)
|
||||
=> Post("/login", user, pwd, done);
|
||||
|
||||
public static IEnumerator Register(string user, string pwd, Action<bool, string, int, string> done)
|
||||
=> Post("/register", user, pwd, done);
|
||||
|
||||
static IEnumerator Post(string path, string user, string pwd, Action<bool, string, int, string> done)
|
||||
{
|
||||
string url = GlobalData.ProductionAuthBase + path;
|
||||
string body = JsonUtility.ToJson(new AuthReq { username = user, password = pwd });
|
||||
using (var req = new UnityWebRequest(url, "POST"))
|
||||
{
|
||||
req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body));
|
||||
req.downloadHandler = new DownloadHandlerBuffer();
|
||||
req.SetRequestHeader("Content-Type", "application/json");
|
||||
yield return req.SendWebRequest();
|
||||
|
||||
if (req.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
string err = req.responseCode == 401 ? "账号或密码错误"
|
||||
: req.responseCode == 409 ? "用户名已被占用"
|
||||
: ("网络错误: " + req.error);
|
||||
done(false, null, 0, err);
|
||||
yield break;
|
||||
}
|
||||
AuthResp resp = null;
|
||||
try { resp = JsonUtility.FromJson<AuthResp>(req.downloadHandler.text); } catch { }
|
||||
if (resp == null || string.IsNullOrEmpty(resp.token))
|
||||
{
|
||||
done(false, null, 0, "响应解析失败");
|
||||
yield break;
|
||||
}
|
||||
done(true, resp.token, resp.pid, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1658466d5399e3241b93b004568852d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42ca20d79634abb42b014dd1d9fcfb0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f5f6a77b5a84e6fa8d4a3a4bc53c0f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace XGame
|
||||
{
|
||||
// UI 层级,对应 UIRoot 下的四个子节点;顺序即渲染顺序(后者在上):HUD < Normal < Dialog < Tips
|
||||
public enum UILayer
|
||||
{
|
||||
HUD = 0, // 抬头信息:血条、状态、摇杆、小地图等常驻 HUD
|
||||
Normal = 1, // 普通功能界面:背包、设置、商城、登录/大厅等常规面板
|
||||
Dialog = 2, // 弹出对话框:确认框等模态弹窗
|
||||
Tips = 3, // 弹出文字信息:飘字、Toast 轻提示
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI 挂载管理:维护一个统一的 UI 画布 UIRoot,其下有 HUD/Normal/Dialog/Tips 层级节点,
|
||||
/// 把 UI 预设按类型挂到对应层。规则见 Doc/Rule/UnityProject.md「UI 挂载规则」。
|
||||
///
|
||||
/// UIRoot 本身是 Canvas(ScreenSpaceOverlay + ScaleWithScreenSize 2048x1024)。这很关键:
|
||||
/// 面板预设自带的 Canvas 挂进来后变成"嵌套子画布",嵌套 Canvas 不会驱动其 RectTransform,
|
||||
/// 面板根节点在预设里设置的 Anchor/尺寸才会生效(否则顶层 Overlay 画布会把根强制拉成全屏、锚点失效)。
|
||||
/// 参考分辨率与各面板自带 CanvasScaler 一致(2048x1024, match 0.5),故缩放不变。
|
||||
/// </summary>
|
||||
public static class UIManager
|
||||
{
|
||||
private const string UIRootName = "UIRoot";
|
||||
private static readonly string[] LayerNames = { "HUD", "Normal", "Dialog", "Tips" };
|
||||
private static readonly Vector2 DesignResolution = new Vector2(2048f, 1024f);
|
||||
|
||||
/// <summary>把 UI 挂到指定层(worldPositionStays=false 保留本地布局/锚点)。</summary>
|
||||
public static void AttachTo(UILayer layer, GameObject ui, bool worldPositionStays = false)
|
||||
{
|
||||
if (ui == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
RectTransform layerRt = GetLayer(layer);
|
||||
if (layerRt != null)
|
||||
{
|
||||
ui.transform.SetParent(layerRt, worldPositionStays);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>获取指定层的 RectTransform(确保 UIRoot 画布与该层存在)。</summary>
|
||||
public static RectTransform GetLayer(UILayer layer)
|
||||
{
|
||||
Transform root = EnsureUIRoot();
|
||||
string name = LayerNames[(int)layer];
|
||||
Transform t = root.Find(name);
|
||||
if (t == null)
|
||||
{
|
||||
t = CreateFullStretchChild(root, name);
|
||||
}
|
||||
return t as RectTransform;
|
||||
}
|
||||
|
||||
private static Transform EnsureUIRoot()
|
||||
{
|
||||
EnsureEventSystem();
|
||||
GameObject go = GameObject.Find(UIRootName);
|
||||
if (go == null)
|
||||
{
|
||||
go = new GameObject(UIRootName, typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||||
Canvas canvas = go.GetComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = 100; // 位于主界面之上
|
||||
|
||||
CanvasScaler scaler = go.GetComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = DesignResolution;
|
||||
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
|
||||
// 按 HUD→Normal→Dialog→Tips 顺序创建,子节点顺序即渲染层级(后建的在上)
|
||||
for (int i = 0; i < LayerNames.Length; i++)
|
||||
{
|
||||
CreateFullStretchChild(go.transform, LayerNames[i]);
|
||||
}
|
||||
}
|
||||
return go.transform;
|
||||
}
|
||||
|
||||
private static void EnsureEventSystem()
|
||||
{
|
||||
if (EventSystem.current != null || Object.FindObjectOfType<EventSystem>() != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
|
||||
}
|
||||
|
||||
private static Transform CreateFullStretchChild(Transform parent, string name)
|
||||
{
|
||||
GameObject go = new GameObject(name, typeof(RectTransform));
|
||||
RectTransform rt = go.GetComponent<RectTransform>();
|
||||
rt.SetParent(parent, false);
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
rt.pivot = new Vector2(0.5f, 0.5f);
|
||||
rt.localScale = Vector3.one;
|
||||
return go.transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1c655b3a9179e3479e4066be0525792
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user