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
@@ -255,9 +255,45 @@ namespace AIGame.EditorTools
Fail(vr, "events 节点不存在: " + n);
}
}
ValidateNineSliceControls(root, nodes, vr);
return vr;
}
private static void ValidateNineSliceControls(JObject root, JArray nodes, ValidationResult vr)
{
var checkedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (JToken node in nodes)
{
foreach (JToken comp in (node["components"] as JArray) ?? new JArray())
{
string type = (string)comp["type"];
string[] fields = type == "Image" ? new[] { "sprite" }
: type == "Button" ? new[] { "spriteHighlighted", "spritePressed", "spriteDisabled" }
: Array.Empty<string>();
foreach (string field in fields)
{
string name = (string)comp[field];
if (string.IsNullOrEmpty(name)) continue;
string path = (string)root["assets"]?["sprites"]?[name]
?? TextureRoot + "/Common/" + name + ".png";
if (!UiSpriteNineSliceRules.IsStandardControl(path)) continue;
string imageType = (string)comp["imageType"];
if (type == "Image" && !string.IsNullOrEmpty(imageType)
&& !string.Equals(imageType, "Sliced", StringComparison.OrdinalIgnoreCase))
Fail(vr, "标准控件图必须使用 imageType: Sliced: " + (string)node["name"] + " / " + name);
if (!checkedPaths.Add(path)) continue;
Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path);
if (sprite == null)
Fail(vr, "标准控件 Sprite 缺失: " + path);
else if (sprite.border.x <= 0 || sprite.border.y <= 0 || sprite.border.z <= 0 || sprite.border.w <= 0
|| sprite.border.x + sprite.border.z >= sprite.rect.width
|| sprite.border.y + sprite.border.w >= sprite.rect.height)
Fail(vr, "标准控件 Sprite 的九宫格 Border 无效,请执行 Tools/UI/Apply Standard Control Nine-Slice: " + path);
}
}
}
}
public static string BuildFromJsonFile(string jsonPath)
{
ValidationResult vr = Validate(jsonPath);
@@ -531,6 +567,9 @@ namespace AIGame.EditorTools
Sprite sprite = ResolveSprite(spriteName, fileName);
img.sprite = sprite;
string imageType = (string)comp["imageType"];
if (string.IsNullOrEmpty(imageType) && sprite != null
&& UiSpriteNineSliceRules.IsStandardControl(AssetDatabase.GetAssetPath(sprite)))
imageType = "Sliced";
if (string.Equals(imageType, "Sliced", StringComparison.OrdinalIgnoreCase))
{
img.type = Image.Type.Sliced;
@@ -569,6 +608,16 @@ namespace AIGame.EditorTools
Button btn = GetOrAdd<Button>(go);
Image target = go.GetComponent<Image>();
if (target != null) btn.targetGraphic = target;
string targetGraphicName = (string)comp["targetGraphic"];
if (!string.IsNullOrEmpty(targetGraphicName))
{
deferred.Add(() =>
{
if (!map.TryGetValue(targetGraphicName, out GameObject graphicGo) || graphicGo.GetComponent<Graphic>() == null)
throw new InvalidOperationException("Button targetGraphic not found: " + targetGraphicName);
btn.targetGraphic = graphicGo.GetComponent<Graphic>();
});
}
if (string.Equals((string)comp["transition"], "SpriteSwap", StringComparison.OrdinalIgnoreCase))
{
btn.transition = Selectable.Transition.SpriteSwap;
@@ -581,6 +630,14 @@ namespace AIGame.EditorTools
else
{
btn.transition = Selectable.Transition.ColorTint;
ColorBlock colors = btn.colors;
colors.normalColor = ParseColor((string)comp["normalColor"], colors.normalColor);
colors.highlightedColor = ParseColor((string)comp["highlightedColor"], colors.highlightedColor);
colors.pressedColor = ParseColor((string)comp["pressedColor"], colors.pressedColor);
colors.selectedColor = ParseColor((string)comp["selectedColor"], colors.selectedColor);
colors.disabledColor = ParseColor((string)comp["disabledColor"], colors.disabledColor);
if (comp["fadeDuration"] != null) colors.fadeDuration = (float)comp["fadeDuration"];
btn.colors = colors;
}
// onClick 仅作事件标记,不在 Prefab 写死回调
break;