199 lines
6.7 KiB
C#
199 lines
6.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Newtonsoft.Json.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace AibisDream.FrameAnimation.Editor
|
|
{
|
|
internal static class AsepriteJsonParser
|
|
{
|
|
public static bool TryParse(
|
|
string json,
|
|
string sourceId,
|
|
out AsepriteSourceDocument document,
|
|
out FrameAnimationImportIssue issue)
|
|
{
|
|
document = null;
|
|
issue = null;
|
|
try
|
|
{
|
|
if (string.IsNullOrWhiteSpace(json))
|
|
{
|
|
issue = Error(FrameAnimationImportIssueCode.JsonInvalid, sourceId, "Aseprite JSON 为空。");
|
|
return false;
|
|
}
|
|
|
|
var root = JObject.Parse(json);
|
|
var framesToken = root["frames"];
|
|
if (framesToken == null ||
|
|
(framesToken.Type != JTokenType.Object && framesToken.Type != JTokenType.Array))
|
|
{
|
|
issue = Error(FrameAnimationImportIssueCode.FramesMissing, sourceId, "JSON 缺少 Object 或 Array 格式的 frames。");
|
|
return false;
|
|
}
|
|
|
|
var frames = ParseFrames(framesToken);
|
|
var meta = root["meta"] as JObject;
|
|
var size = ParseSize(meta?["size"]);
|
|
var imageName = meta?.Value<string>("image") ?? string.Empty;
|
|
var tags = ParseTags(meta?["frameTags"]);
|
|
document = new AsepriteSourceDocument(frames, tags, size, imageName);
|
|
return true;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
issue = Error(
|
|
FrameAnimationImportIssueCode.JsonInvalid,
|
|
sourceId,
|
|
$"Aseprite JSON 解析失败:{exception.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool TryExpandTag(
|
|
AsepriteSourceTag tag,
|
|
int frameCount,
|
|
string sourceId,
|
|
out IReadOnlyList<int> indices,
|
|
out FrameAnimationImportIssue issue)
|
|
{
|
|
indices = Array.Empty<int>();
|
|
issue = null;
|
|
if (tag == null || tag.From < 0 || tag.To < tag.From || tag.To >= frameCount)
|
|
{
|
|
issue = Error(
|
|
FrameAnimationImportIssueCode.TagRangeInvalid,
|
|
sourceId,
|
|
$"Tag '{tag?.Name}' 的范围 {tag?.From}..{tag?.To} 无效。");
|
|
return false;
|
|
}
|
|
|
|
var result = new List<int>();
|
|
switch ((tag.Direction ?? string.Empty).ToLowerInvariant())
|
|
{
|
|
case "forward":
|
|
AddAscending(result, tag.From, tag.To);
|
|
break;
|
|
case "reverse":
|
|
AddDescending(result, tag.To, tag.From);
|
|
break;
|
|
case "pingpong":
|
|
AddAscending(result, tag.From, tag.To);
|
|
AddDescending(result, tag.To - 1, tag.From + 1);
|
|
break;
|
|
case "pingpong_reverse":
|
|
AddDescending(result, tag.To, tag.From);
|
|
AddAscending(result, tag.From + 1, tag.To - 1);
|
|
break;
|
|
default:
|
|
issue = Error(
|
|
FrameAnimationImportIssueCode.TagDirectionInvalid,
|
|
sourceId,
|
|
$"Tag '{tag.Name}' 使用了不支持的 direction '{tag.Direction}'。");
|
|
return false;
|
|
}
|
|
|
|
indices = result;
|
|
return true;
|
|
}
|
|
|
|
private static List<AsepriteSourceFrame> ParseFrames(JToken token)
|
|
{
|
|
var frames = new List<AsepriteSourceFrame>();
|
|
if (token is JObject objectFrames)
|
|
{
|
|
foreach (var property in objectFrames.Properties())
|
|
{
|
|
frames.Add(ParseFrame(property.Name, property.Value));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var item in (JArray)token)
|
|
{
|
|
frames.Add(ParseFrame(item.Value<string>("filename") ?? string.Empty, item));
|
|
}
|
|
}
|
|
return frames;
|
|
}
|
|
|
|
private static AsepriteSourceFrame ParseFrame(string frameName, JToken token)
|
|
{
|
|
return new AsepriteSourceFrame(
|
|
frameName,
|
|
ParseRect(token["frame"]),
|
|
token.Value<int?>("duration") ?? 0,
|
|
token.Value<bool?>("rotated") ?? false,
|
|
token.Value<bool?>("trimmed") ?? false,
|
|
ParseRect(token["spriteSourceSize"]),
|
|
ParseSize(token["sourceSize"]));
|
|
}
|
|
|
|
private static List<AsepriteSourceTag> ParseTags(JToken token)
|
|
{
|
|
var tags = new List<AsepriteSourceTag>();
|
|
if (!(token is JArray array))
|
|
{
|
|
return tags;
|
|
}
|
|
|
|
foreach (var item in array)
|
|
{
|
|
tags.Add(new AsepriteSourceTag(
|
|
item.Value<string>("name") ?? string.Empty,
|
|
item.Value<int?>("from") ?? -1,
|
|
item.Value<int?>("to") ?? -1,
|
|
item.Value<string>("direction") ?? string.Empty));
|
|
}
|
|
return tags;
|
|
}
|
|
|
|
private static RectInt ParseRect(JToken token)
|
|
{
|
|
return token == null
|
|
? default
|
|
: new RectInt(
|
|
token.Value<int?>("x") ?? 0,
|
|
token.Value<int?>("y") ?? 0,
|
|
token.Value<int?>("w") ?? 0,
|
|
token.Value<int?>("h") ?? 0);
|
|
}
|
|
|
|
private static Vector2Int ParseSize(JToken token)
|
|
{
|
|
return token == null
|
|
? default
|
|
: new Vector2Int(token.Value<int?>("w") ?? 0, token.Value<int?>("h") ?? 0);
|
|
}
|
|
|
|
private static void AddAscending(List<int> target, int from, int to)
|
|
{
|
|
for (var index = from; index <= to; index++)
|
|
{
|
|
target.Add(index);
|
|
}
|
|
}
|
|
|
|
private static void AddDescending(List<int> target, int from, int to)
|
|
{
|
|
for (var index = from; index >= to; index--)
|
|
{
|
|
target.Add(index);
|
|
}
|
|
}
|
|
|
|
private static FrameAnimationImportIssue Error(
|
|
FrameAnimationImportIssueCode code,
|
|
string sourceId,
|
|
string message)
|
|
{
|
|
return new FrameAnimationImportIssue(
|
|
FrameAnimationValidationSeverity.Error,
|
|
code,
|
|
sourceId,
|
|
sourceId,
|
|
message);
|
|
}
|
|
}
|
|
}
|