mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
250 lines
8.5 KiB
ActionScript
250 lines
8.5 KiB
ActionScript
package spine {
|
|
import spine.animation.Animation;
|
|
import spine.animation.AttachmentTimeline;
|
|
import spine.animation.ColorTimeline;
|
|
import spine.animation.CurveTimeline;
|
|
import spine.animation.RotateTimeline;
|
|
import spine.animation.ScaleTimeline;
|
|
import spine.animation.Timeline;
|
|
import spine.animation.TranslateTimeline;
|
|
import spine.attachments.Attachment;
|
|
import spine.attachments.AttachmentLoader;
|
|
import spine.attachments.AttachmentType;
|
|
import spine.attachments.RegionAttachment;
|
|
|
|
public class SkeletonJson {
|
|
static public const TIMELINE_SCALE:String = "scale";
|
|
static public const TIMELINE_ROTATE:String = "rotate";
|
|
static public const TIMELINE_TRANSLATE:String = "translate";
|
|
static public const TIMELINE_ATTACHMENT:String = "attachment";
|
|
static public const TIMELINE_COLOR:String = "color";
|
|
|
|
public var attachmentLoader:AttachmentLoader;
|
|
public var scale:Number = 1;
|
|
|
|
public function SkeletonJson (attachmentLoader:AttachmentLoader = null) {
|
|
this.attachmentLoader = attachmentLoader;
|
|
}
|
|
|
|
public function readSkeletonData (json:String, name:String) : SkeletonData {
|
|
if (json == null)
|
|
throw new ArgumentError("json cannot be null.");
|
|
|
|
var skeletonData:SkeletonData = new SkeletonData();
|
|
skeletonData.name = name;
|
|
|
|
var root:Object = JSON.parse(json);
|
|
|
|
// Bones.
|
|
var boneData:BoneData;
|
|
for each (var boneMap:Object in root["bones"]) {
|
|
var parent:BoneData = null;
|
|
var parentName:String = boneMap["parent"];
|
|
if (parentName) {
|
|
parent = skeletonData.findBone(parentName);
|
|
if (!parent)
|
|
throw new Error("Parent bone not found: " + parentName);
|
|
}
|
|
boneData = new BoneData(boneMap["name"], parent);
|
|
boneData.length = (boneMap["length"] || 0) * scale;
|
|
boneData.x = (boneMap["x"] || 0) * scale;
|
|
boneData.y = (boneMap["y"] || 0) * scale;
|
|
boneData.rotation = (boneMap["rotation"] || 0);
|
|
boneData.scaleX = boneMap["scaleX"] || 1;
|
|
boneData.scaleY = boneMap["scaleY"] || 1;
|
|
skeletonData.addBone(boneData);
|
|
}
|
|
|
|
// Slots.
|
|
for each (var slotMap:Object in root["slots"]) {
|
|
var boneName:String = slotMap["bone"];
|
|
boneData = skeletonData.findBone(boneName);
|
|
if (!boneData)
|
|
throw new Error("Slot bone not found: " + boneName);
|
|
var slotData:SlotData = new SlotData(slotMap["name"], boneData);
|
|
|
|
var color:String = slotMap["color"];
|
|
if (color) {
|
|
slotData.r = toColor(color, 0);
|
|
slotData.g = toColor(color, 1);
|
|
slotData.b = toColor(color, 2);
|
|
slotData.a = toColor(color, 3);
|
|
}
|
|
|
|
slotData.attachmentName = slotMap["attachment"];
|
|
|
|
skeletonData.addSlot(slotData);
|
|
}
|
|
|
|
// Skins.
|
|
var skins:Object = root["skins"];
|
|
for (var skinName:String in skins) {
|
|
var skinMap:Object = skins[skinName];
|
|
var skin:Skin = new Skin(skinName);
|
|
for (var slotName:String in skinMap) {
|
|
var slotIndex:int = skeletonData.findSlotIndex(slotName);
|
|
var slotEntry:Object = skinMap[slotName];
|
|
for (var attachmentName:String in slotEntry) {
|
|
var attachment:Attachment = readAttachment(skin, attachmentName, slotEntry[attachmentName]);
|
|
if (attachment != null)
|
|
skin.addAttachment(slotIndex, attachmentName, attachment);
|
|
}
|
|
}
|
|
skeletonData.addSkin(skin);
|
|
if (skin.name == "default")
|
|
skeletonData.defaultSkin = skin;
|
|
}
|
|
|
|
// Animations.
|
|
var animations:Object = root["animations"];
|
|
for (var animationName:String in animations)
|
|
readAnimation(animationName, animations[animationName], skeletonData);
|
|
|
|
return skeletonData;
|
|
}
|
|
|
|
private function readAttachment (skin:Skin, name:String, map:Object) : Attachment {
|
|
name = map["name"] || name;
|
|
|
|
var type:AttachmentType = AttachmentType.valueOf(map["type"] || "region");
|
|
var attachment:Attachment = attachmentLoader.newAttachment(skin, type, name);
|
|
|
|
if (attachment is RegionAttachment) {
|
|
var regionAttachment:RegionAttachment = attachment as RegionAttachment;
|
|
regionAttachment.x = (map["x"] || 0) * scale;
|
|
regionAttachment.y = (map["y"] || 0) * scale;
|
|
regionAttachment.scaleX = map["scaleX"] || 1;
|
|
regionAttachment.scaleY = map["scaleY"] || 1;
|
|
regionAttachment.rotation = map["rotation"] || 0;
|
|
regionAttachment.width = (map["width"] || 32) * scale;
|
|
regionAttachment.height = (map["height"] || 32) * scale;
|
|
regionAttachment.updateOffset();
|
|
}
|
|
|
|
return attachment;
|
|
}
|
|
|
|
private function readAnimation (name:String, map:Object, skeletonData:SkeletonData) : void {
|
|
var timelines:Vector.<Timeline> = new Vector.<Timeline>();
|
|
var duration:Number = 0;
|
|
|
|
var bones:Object = map["bones"];
|
|
for (var boneName:String in bones) {
|
|
var boneIndex:int = skeletonData.findBoneIndex(boneName);
|
|
if (boneIndex == -1)
|
|
throw new Error("Bone not found: " + boneName);
|
|
var boneMap:Object = bones[boneName];
|
|
|
|
for (var timelineName:Object in boneMap) {
|
|
var timelineMap:Object = boneMap[timelineName];
|
|
if (timelineName == TIMELINE_ROTATE) {
|
|
var timeline:RotateTimeline = new RotateTimeline(count(timelineMap));
|
|
timeline.boneIndex = boneIndex;
|
|
|
|
var frameIndex:int = 0;
|
|
for each (var valueMap:Object in timelineMap) {
|
|
timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]);
|
|
readCurve(timeline, frameIndex, valueMap);
|
|
frameIndex++;
|
|
}
|
|
timelines.push(timeline);
|
|
duration = Math.max(duration, timeline.frames[timeline.frameCount * 2 - 2]);
|
|
|
|
} else if (timelineName == TIMELINE_TRANSLATE || timelineName == TIMELINE_SCALE) {
|
|
var timeline1:TranslateTimeline;
|
|
var timelineScale:Number = 1;
|
|
if (timelineName == TIMELINE_SCALE)
|
|
timeline1 = new ScaleTimeline(count(timelineMap));
|
|
else {
|
|
timeline1 = new TranslateTimeline(count(timelineMap));
|
|
timelineScale = scale;
|
|
}
|
|
timeline1.boneIndex = boneIndex;
|
|
|
|
var frameIndex1:int = 0;
|
|
for each (var valueMap1:Object in timelineMap) {
|
|
var x:Number = (valueMap1["x"] || 0) * timelineScale;
|
|
var y:Number = (valueMap1["y"] || 0) * timelineScale;
|
|
timeline1.setFrame(frameIndex1, valueMap1["time"], x, y);
|
|
readCurve(timeline1, frameIndex1, valueMap1);
|
|
frameIndex1++;
|
|
}
|
|
timelines.push(timeline1);
|
|
duration = Math.max(duration, timeline1.frames[timeline1.frameCount * 3 - 3]);
|
|
|
|
} else
|
|
throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
|
|
}
|
|
}
|
|
|
|
var slots:Object = map["slots"];
|
|
for (var slotName:String in slots) {
|
|
var slotMap:Object = slots[slotName];
|
|
var slotIndex:int = skeletonData.findSlotIndex(slotName);
|
|
|
|
for (var timelineName2:Object in boneMap) {
|
|
var timelineMap2:Object = boneMap[timelineName2];
|
|
if (timelineName2 == TIMELINE_COLOR) {
|
|
var timeline2:ColorTimeline = new ColorTimeline(count(timelineMap2));
|
|
timeline2.slotIndex = slotIndex;
|
|
|
|
var frameIndex2:int = 0;
|
|
for each (var valueMap2:Object in timelineMap2) {
|
|
var color:String = valueMap["color"];
|
|
var r:Number = toColor(color, 0);
|
|
var g:Number = toColor(color, 1);
|
|
var b:Number = toColor(color, 2);
|
|
var a:Number = toColor(color, 3);
|
|
timeline2.setFrame(frameIndex2, valueMap2["time"], r, g, b, a);
|
|
readCurve(timeline2, frameIndex2, valueMap);
|
|
frameIndex2++;
|
|
}
|
|
timelines.push(timeline2);
|
|
duration = Math.max(duration, timeline2.frames[timeline2.frameCount * 5 - 5]);
|
|
|
|
} else if (timelineName2 == TIMELINE_ATTACHMENT) {
|
|
var timeline3:AttachmentTimeline = new AttachmentTimeline(count(timelineMap2));
|
|
timeline3.slotIndex = slotIndex;
|
|
|
|
var frameIndex3:int = 0;
|
|
for each (var valueMap3:Object in timelineMap2) {
|
|
timeline3.setFrame(frameIndex3++, valueMap3["time"], valueMap3["name"]);
|
|
}
|
|
timelines.push(timeline);
|
|
duration = Math.max(duration, timeline3.frames[timeline3.frameCount - 1]);
|
|
|
|
} else
|
|
throw new Error("Invalid timeline type for a slot: " + timelineName2 + " (" + slotName + ")");
|
|
}
|
|
}
|
|
|
|
skeletonData.addAnimation(new Animation(name, timelines, duration));
|
|
}
|
|
|
|
private function readCurve (timeline:CurveTimeline, frameIndex:int, valueMap:Object) : void {
|
|
var curve:Object = valueMap["curve"];
|
|
if (curve == null)
|
|
return;
|
|
if (curve == "stepped")
|
|
timeline.setStepped(frameIndex);
|
|
else if (curve is Array) {
|
|
timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]);
|
|
}
|
|
}
|
|
|
|
static private function toColor (hexString:String, colorIndex:int) : Number {
|
|
if (hexString.length != 8)
|
|
throw new ArgumentError("Color hexidecimal length must be 8, recieved: " + hexString);
|
|
return parseInt(hexString.substring(colorIndex * 2, 2), 16) / 255;
|
|
}
|
|
|
|
static private function count (map:Object) : int {
|
|
var count:int = 0;
|
|
for (var key:String in map)
|
|
count++;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
}
|