[cpp] Port latest libgdx timeline and sequence follow-up fixes

This commit is contained in:
Mario Zechner 2026-03-14 15:35:48 +01:00
parent 74520d21f0
commit 9749e52c05
15 changed files with 183 additions and 171 deletions

View File

@ -579,7 +579,7 @@ namespace spine {
/// @param attachments False when: 1) the attachment timeline is mixing out, 2) mix < attachmentThreshold, and 3) the timeline
/// is not the last timeline to set the slot's attachment. In that case the timeline is applied only so subsequent
/// timelines see any deform.
void applyAttachmentTimeline(AttachmentTimeline *attachmentTimeline, Skeleton &skeleton, float animationTime, MixBlend pose,
void applyAttachmentTimeline(AttachmentTimeline *attachmentTimeline, Skeleton &skeleton, float animationTime, MixBlend pose, bool out,
bool attachments);
/// Returns true when all mixing from entries are complete.

View File

@ -47,6 +47,10 @@ namespace spine {
virtual Attachment &copy() = 0;
Attachment *getTimelineAttachment();
void setTimelineAttachment(Attachment *attachment);
int getRefCount();
void reference();
@ -55,6 +59,7 @@ namespace spine {
private:
const String _name;
Attachment *_timelineAttachment;
int _refCount;
};
}

View File

@ -100,7 +100,7 @@ namespace spine {
_setupIndex = setupIndex;
}
bool getPathSuffix() {
bool hasPathSuffix() {
return _pathSuffix;
}

View File

@ -96,7 +96,6 @@ namespace spine {
Array<int> _bones;
Array<float> _vertices;
size_t _worldVerticesLength;
Attachment *_timelineAttachment;
private:
const int _id;

View File

@ -569,7 +569,7 @@ bool AnimationState::apply(Skeleton &skeleton) {
for (size_t ii = 0; ii < timelineCount; ++ii) {
Timeline *timeline = timelines[ii];
if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, blend, attachments);
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, blend, false, attachments);
else
timeline->apply(skeleton, animationLast, applyTime, applyEvents, alpha, blend, MixDirection_In, false);
}
@ -591,7 +591,7 @@ bool AnimationState::apply(Skeleton &skeleton) {
applyRotateTimeline(static_cast<RotateTimeline *>(timeline), skeleton, applyTime, alpha, timelineBlend, timelinesRotation,
ii << 1, firstFrame);
else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, blend, attachments);
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, blend, false, attachments);
else
timeline->apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend, MixDirection_In, false);
}
@ -798,13 +798,15 @@ Animation *AnimationState::getEmptyAnimation() {
return &ret;
}
void AnimationState::applyAttachmentTimeline(AttachmentTimeline *attachmentTimeline, Skeleton &skeleton, float time, MixBlend blend,
void AnimationState::applyAttachmentTimeline(AttachmentTimeline *attachmentTimeline, Skeleton &skeleton, float time, MixBlend blend, bool out,
bool attachments) {
Slot *slot = skeleton.getSlots()[attachmentTimeline->getSlotIndex()];
if (!slot->getBone().isActive()) return;
Array<float> &frames = attachmentTimeline->getFrames();
if (time < frames[0]) {
if (out) {
if (blend == MixBlend_Setup) setAttachment(skeleton, *slot, slot->getData().getAttachmentName(), attachments);
} else if (time < frames[0]) {
if (blend == MixBlend_Setup || blend == MixBlend_First) setAttachment(skeleton, *slot, slot->getData().getAttachmentName(), attachments);
} else {
setAttachment(skeleton, *slot, attachmentTimeline->getAttachmentNames()[Animation::search(frames, time)], attachments);
@ -986,7 +988,7 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle
if (!shortestRotation && (timeline->getRTTI().isExactly(RotateTimeline::rtti))) {
applyRotateTimeline((RotateTimeline *) timeline, skeleton, applyTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame);
} else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) {
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, timelineBlend,
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, timelineBlend, true,
attachments && alpha >= from->_alphaAttachmentThreshold);
} else {
if (drawOrder && timeline->getRTTI().isExactly(DrawOrderTimeline::rtti) && timelineBlend == MixBlend_Setup)

View File

@ -35,7 +35,7 @@ using namespace spine;
RTTI_IMPL_NOPARENT(Attachment)
Attachment::Attachment(const String &name) : _name(name), _refCount(0) {
Attachment::Attachment(const String &name) : _name(name), _timelineAttachment(this), _refCount(0) {
assert(_name.length() > 0);
}
@ -46,6 +46,14 @@ const String &Attachment::getName() const {
return _name;
}
Attachment *Attachment::getTimelineAttachment() {
return _timelineAttachment;
}
void Attachment::setTimelineAttachment(Attachment *attachment) {
_timelineAttachment = attachment;
}
int Attachment::getRefCount() {
return _refCount;
}

View File

@ -281,6 +281,7 @@ void RGBA2Timeline::setFrame(int frame, float time, float r, float g, float b, f
void RGBA2Timeline::_apply(Slot &slot, SlotPose &pose, float time, float alpha, MixBlend blend) {
Color &light = pose._color;
Color &dark = pose._darkColor;
float r2, g2, b2;
if (time < _frames[0]) {
SlotPose &setup = slot._data._setup;
Color &setupLight = setup._color;
@ -291,83 +292,84 @@ void RGBA2Timeline::_apply(Slot &slot, SlotPose &pose, float time, float alpha,
dark.r = setupDark.r;
dark.g = setupDark.g;
dark.b = setupDark.b;
/* Fall through. */
default:
return;
case MixBlend_First:
light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha,
(setupLight.a - light.a) * alpha);
dark.r += (setupDark.r - dark.r) * alpha;
dark.g += (setupDark.g - dark.g) * alpha;
dark.b += (setupDark.b - dark.b) * alpha;
return;
default:
return;
r2 = dark.r + (setupDark.r - dark.r) * alpha;
g2 = dark.g + (setupDark.g - dark.g) * alpha;
b2 = dark.b + (setupDark.b - dark.b) * alpha;
}
}
float r, g, b, a, r2, g2, b2;
int i = Animation::search(_frames, time, ENTRIES);
int curveType = (int) _curves[i >> 3];
switch (curveType) {
case LINEAR: {
float before = _frames[i];
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
a = _frames[i + A];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
float t = (time - before) / (_frames[i + ENTRIES] - before);
r += (_frames[i + ENTRIES + R] - r) * t;
g += (_frames[i + ENTRIES + G] - g) * t;
b += (_frames[i + ENTRIES + B] - b) * t;
a += (_frames[i + ENTRIES + A] - a) * t;
r2 += (_frames[i + ENTRIES + R2] - r2) * t;
g2 += (_frames[i + ENTRIES + G2] - g2) * t;
b2 += (_frames[i + ENTRIES + B2] - b2) * t;
break;
}
case STEPPED: {
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
a = _frames[i + A];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
break;
}
default: {
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER);
r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER);
g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER);
b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER);
break;
}
}
if (alpha == 1) {
light.set(r, g, b, a);
dark.r = r2;
dark.g = g2;
dark.b = b2;
} else {
if (blend == MixBlend_Setup) {
SlotPose &setup = slot._data._setup;
light.set(setup._color);
Color &setupDark = setup._darkColor;
dark.r = setupDark.r;
dark.g = setupDark.g;
dark.b = setupDark.b;
float r, g, b, a;
int i = Animation::search(_frames, time, ENTRIES);
int curveType = (int) _curves[i >> 3];
switch (curveType) {
case LINEAR: {
float before = _frames[i];
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
a = _frames[i + A];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
float t = (time - before) / (_frames[i + ENTRIES] - before);
r += (_frames[i + ENTRIES + R] - r) * t;
g += (_frames[i + ENTRIES + G] - g) * t;
b += (_frames[i + ENTRIES + B] - b) * t;
a += (_frames[i + ENTRIES + A] - a) * t;
r2 += (_frames[i + ENTRIES + R2] - r2) * t;
g2 += (_frames[i + ENTRIES + G2] - g2) * t;
b2 += (_frames[i + ENTRIES + B2] - b2) * t;
break;
}
case STEPPED: {
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
a = _frames[i + A];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
break;
}
default: {
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER);
r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER);
g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER);
b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER);
break;
}
}
if (alpha == 1)
light.set(r, g, b, a);
else if (blend == MixBlend_Setup) {
SlotPose &setup = slot._data._setup;
Color &setupLight = setup._color;
light.set(setupLight.r + (r - setupLight.r) * alpha, setupLight.g + (g - setupLight.g) * alpha, setupLight.b + (b - setupLight.b) * alpha,
setupLight.a + (a - setupLight.a) * alpha);
Color &setupDark = setup._darkColor;
r2 = setupDark.r + (r2 - setupDark.r) * alpha;
g2 = setupDark.g + (g2 - setupDark.g) * alpha;
b2 = setupDark.b + (b2 - setupDark.b) * alpha;
} else {
light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
r2 = dark.r + (r2 - dark.r) * alpha;
g2 = dark.g + (g2 - dark.g) * alpha;
b2 = dark.b + (b2 - dark.b) * alpha;
}
light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
dark.r += (r2 - dark.r) * alpha;
dark.g += (g2 - dark.g) * alpha;
dark.b += (b2 - dark.b) * alpha;
}
dark.r = r2 < 0 ? 0 : (r2 > 1 ? 1 : r2);
dark.g = g2 < 0 ? 0 : (g2 > 1 ? 1 : g2);
dark.b = b2 < 0 ? 0 : (b2 > 1 ? 1 : b2);
}
RTTI_IMPL(RGB2Timeline, SlotCurveTimeline)
@ -394,6 +396,7 @@ void RGB2Timeline::setFrame(int frame, float time, float r, float g, float b, fl
void RGB2Timeline::_apply(Slot &slot, SlotPose &pose, float time, float alpha, MixBlend blend) {
Color &light = pose._color;
Color &dark = pose._darkColor;
float r, g, b, r2, g2, b2;
if (time < _frames[0]) {
SlotPose &setup = slot._data._setup;
Color &setupLight = setup._color;
@ -406,83 +409,84 @@ void RGB2Timeline::_apply(Slot &slot, SlotPose &pose, float time, float alpha, M
dark.r = setupDark.r;
dark.g = setupDark.g;
dark.b = setupDark.b;
return;
case MixBlend_First:
light.r += (setupLight.r - light.r) * alpha;
light.g += (setupLight.g - light.g) * alpha;
light.b += (setupLight.b - light.b) * alpha;
dark.r += (setupDark.r - dark.r) * alpha;
dark.g += (setupDark.g - dark.g) * alpha;
dark.b += (setupDark.b - dark.b) * alpha;
return;
/* Fall through. */
default:
return;
case MixBlend_First:
r = light.r + (setupLight.r - light.r) * alpha;
g = light.g + (setupLight.g - light.g) * alpha;
b = light.b + (setupLight.b - light.b) * alpha;
r2 = dark.r + (setupDark.r - dark.r) * alpha;
g2 = dark.g + (setupDark.g - dark.g) * alpha;
b2 = dark.b + (setupDark.b - dark.b) * alpha;
}
}
float r, g, b, r2, g2, b2;
int i = Animation::search(_frames, time, ENTRIES);
int curveType = (int) _curves[i / ENTRIES];
switch (curveType) {
case LINEAR: {
float before = _frames[i];
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
float t = (time - before) / (_frames[i + ENTRIES] - before);
r += (_frames[i + ENTRIES + R] - r) * t;
g += (_frames[i + ENTRIES + G] - g) * t;
b += (_frames[i + ENTRIES + B] - b) * t;
r2 += (_frames[i + ENTRIES + R2] - r2) * t;
g2 += (_frames[i + ENTRIES + G2] - g2) * t;
b2 += (_frames[i + ENTRIES + B2] - b2) * t;
break;
}
case STEPPED: {
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
break;
}
default: {
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER);
g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER);
b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER);
break;
}
}
if (alpha == 1) {
light.r = r;
light.g = g;
light.b = b;
dark.r = r2;
dark.g = g2;
dark.b = b2;
} else {
if (blend == MixBlend_Setup) {
SlotPose &setup = slot._data._setup;
light.r = setup._color.r;
light.g = setup._color.g;
light.b = setup._color.b;
dark.r = setup._darkColor.r;
dark.g = setup._darkColor.g;
dark.b = setup._darkColor.b;
int i = Animation::search(_frames, time, ENTRIES);
int curveType = (int) _curves[i / ENTRIES];
switch (curveType) {
case LINEAR: {
float before = _frames[i];
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
float t = (time - before) / (_frames[i + ENTRIES] - before);
r += (_frames[i + ENTRIES + R] - r) * t;
g += (_frames[i + ENTRIES + G] - g) * t;
b += (_frames[i + ENTRIES + B] - b) * t;
r2 += (_frames[i + ENTRIES + R2] - r2) * t;
g2 += (_frames[i + ENTRIES + G2] - g2) * t;
b2 += (_frames[i + ENTRIES + B2] - b2) * t;
break;
}
case STEPPED: {
r = _frames[i + R];
g = _frames[i + G];
b = _frames[i + B];
r2 = _frames[i + R2];
g2 = _frames[i + G2];
b2 = _frames[i + B2];
break;
}
default: {
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER);
g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER);
b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER);
break;
}
}
if (alpha != 1) {
if (blend == MixBlend_Setup) {
SlotPose &setup = slot._data._setup;
Color &setupLight = setup._color;
r = setupLight.r + (r - setupLight.r) * alpha;
g = setupLight.g + (g - setupLight.g) * alpha;
b = setupLight.b + (b - setupLight.b) * alpha;
Color &setupDark = setup._darkColor;
r2 = setupDark.r + (r2 - setupDark.r) * alpha;
g2 = setupDark.g + (g2 - setupDark.g) * alpha;
b2 = setupDark.b + (b2 - setupDark.b) * alpha;
} else {
r = light.r + (r - light.r) * alpha;
g = light.g + (g - light.g) * alpha;
b = light.b + (b - light.b) * alpha;
r2 = dark.r + (r2 - dark.r) * alpha;
g2 = dark.g + (g2 - dark.g) * alpha;
b2 = dark.b + (b2 - dark.b) * alpha;
}
}
light.r += (r - light.r) * alpha;
light.g += (g - light.g) * alpha;
light.b += (b - light.b) * alpha;
dark.r += (r2 - dark.r) * alpha;
dark.g += (g2 - dark.g) * alpha;
dark.b += (b2 - dark.b) * alpha;
}
}
light.r = r < 0 ? 0 : (r > 1 ? 1 : r);
light.g = g < 0 ? 0 : (g > 1 ? 1 : g);
light.b = b < 0 ? 0 : (b > 1 ? 1 : b);
dark.r = r2 < 0 ? 0 : (r2 > 1 ? 1 : r2);
dark.g = g2 < 0 ? 0 : (g2 > 1 ? 1 : g2);
dark.b = b2 < 0 ? 0 : (b2 > 1 ? 1 : b2);
}

View File

@ -156,7 +156,7 @@ Attachment &MeshAttachment::copy() {
MeshAttachment &MeshAttachment::newLinkedMesh() {
MeshAttachment *copy = new (__FILE__, __LINE__) MeshAttachment(getName(), new (__FILE__, __LINE__) Sequence(*_sequence));
copy->_timelineAttachment = _timelineAttachment;
copy->setTimelineAttachment(getTimelineAttachment());
copy->_path = _path;
copy->_color.set(_color);
copy->setParentMesh(_parentMesh != NULL ? _parentMesh : this);

View File

@ -175,6 +175,7 @@ Color &RegionAttachment::getColor() {
Attachment &RegionAttachment::copy() {
RegionAttachment *copy = new (__FILE__, __LINE__) RegionAttachment(getName(), new (__FILE__, __LINE__) Sequence(*_sequence));
copy->setTimelineAttachment(getTimelineAttachment());
copy->_path = _path;
copy->_x = _x;
copy->_y = _y;

View File

@ -83,9 +83,7 @@ void SequenceTimeline::apply(Skeleton &skeleton, float lastTime, float time, Arr
Attachment *slotAttachment = pose.getAttachment();
if (slotAttachment != (Attachment *) _attachment) {
if (slotAttachment == NULL || !slotAttachment->getRTTI().instanceOf(VertexAttachment::rtti) ||
((VertexAttachment *) slotAttachment)->getTimelineAttachment() != (Attachment *) _attachment)
return;
if (slotAttachment == NULL || slotAttachment->getTimelineAttachment() != (Attachment *) _attachment) return;
}
Sequence *sequence = NULL;
if (((Attachment *) _attachment)->getRTTI().instanceOf(RegionAttachment::rtti)) sequence = &((RegionAttachment *) _attachment)->getSequence();

View File

@ -474,7 +474,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
setError("Parent mesh not found: ", linkedMesh->_parent.buffer());
return NULL;
}
linkedMesh->_mesh->_timelineAttachment = linkedMesh->_inheritTimelines ? static_cast<VertexAttachment *>(parent) : linkedMesh->_mesh;
linkedMesh->_mesh->setTimelineAttachment(linkedMesh->_inheritTimelines ? static_cast<VertexAttachment *>(parent) : linkedMesh->_mesh);
linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
linkedMesh->_mesh->updateSequence();
}

View File

@ -559,7 +559,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
if (skin == NULL) SKELETON_JSON_ERROR(root, "Skin not found: ", linkedMesh->_skin.buffer());
Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent);
if (parent == NULL) SKELETON_JSON_ERROR(root, "Parent mesh not found: ", linkedMesh->_parent.buffer());
linkedMesh->_mesh->_timelineAttachment = linkedMesh->_inheritTimelines ? static_cast<VertexAttachment *>(parent) : linkedMesh->_mesh;
linkedMesh->_mesh->setTimelineAttachment(linkedMesh->_inheritTimelines ? static_cast<VertexAttachment *>(parent) : linkedMesh->_mesh);
linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
linkedMesh->_mesh->updateSequence();
}

View File

@ -72,13 +72,8 @@ Attachment *SlotPose::getAttachment() {
void SlotPose::setAttachment(Attachment *attachment) {
if (_attachment == attachment) return;
// Check if we need to clear deform based on timeline attachment
if (!attachment || !_attachment || !attachment->getRTTI().instanceOf(VertexAttachment::rtti) ||
!_attachment->getRTTI().instanceOf(VertexAttachment::rtti) ||
static_cast<VertexAttachment *>(attachment)->getTimelineAttachment() !=
static_cast<VertexAttachment *>(_attachment)->getTimelineAttachment()) {
_deform.clear();
}
// Check if we need to clear deform based on timeline attachment.
if (!attachment || !_attachment || attachment->getTimelineAttachment() != _attachment->getTimelineAttachment()) _deform.clear();
_attachment = attachment;
_sequenceIndex = -1;
}

View File

@ -38,7 +38,7 @@ using namespace spine;
RTTI_IMPL(VertexAttachment, Attachment)
VertexAttachment::VertexAttachment(const String &name) : Attachment(name), _worldVerticesLength(0), _timelineAttachment(this), _id(getNextID()) {
VertexAttachment::VertexAttachment(const String &name) : Attachment(name), _worldVerticesLength(0), _id(getNextID()) {
}
VertexAttachment::~VertexAttachment() {
@ -146,11 +146,11 @@ void VertexAttachment::setWorldVerticesLength(size_t inValue) {
}
Attachment *VertexAttachment::getTimelineAttachment() {
return _timelineAttachment;
return Attachment::getTimelineAttachment();
}
void VertexAttachment::setTimelineAttachment(Attachment *attachment) {
_timelineAttachment = attachment;
Attachment::setTimelineAttachment(attachment);
}
int VertexAttachment::getNextID() {
@ -162,5 +162,5 @@ void VertexAttachment::copyTo(VertexAttachment &other) {
other._bones.clearAndAddAll(this->_bones);
other._vertices.clearAndAddAll(this->_vertices);
other._worldVerticesLength = this->_worldVerticesLength;
other._timelineAttachment = this->_timelineAttachment;
other.setTimelineAttachment(this->getTimelineAttachment());
}

View File

@ -2605,7 +2605,7 @@ namespace spine {
_json.writeValue(obj->getHeight());
_json.writeName("sequence");
if (!obj->getSequence().getPathSuffix()) {
if (!obj->getSequence().hasPathSuffix()) {
_json.writeNull();
} else {
writeSequence(&obj->getSequence());
@ -3128,7 +3128,7 @@ namespace spine {
_json.writeValue(obj->getPath());
_json.writeName("sequence");
if (!obj->getSequence().getPathSuffix()) {
if (!obj->getSequence().hasPathSuffix()) {
_json.writeNull();
} else {
writeSequence(&obj->getSequence());