Bones are sort of better than PointAttachments because they are stateful. Changing a PointAttachment position affects all other skeletons that use that PointAttachment.
But if you prefer not to have extra bones in Spine editor, you really could just store your own object/struct type.
A Bone is just a bunch of transformation values. (a scale/skew/rotation matrix + position), but arranged in the skeleton's tree structure.
You can create your own offset point/offset transform independent of the skeleton tree, and transform it according to the bone you want, so that it follows the bone at runtime, just like an extra bone would.
The extra benefit here is that you can reparent it to any bone you want, then call update()
to position it correctly, so you don't need an extra bone per bone that could possibly have an arrow attached to it.
Here's pseudocode.
class AttachedToBone {
// Set these values. This is the Bone it's attached to, and the offsets from that bone.
public Spine.Bone bone;
public float x, y;
public float rotation;
float worldX, worldY;
float worldRotation;
// Call this after your skeleton has already updated.
public void update () {
// Parent bone matrix.
float pa = bone.a;
float pb = bone.b;
float pc = bone.c;
float pd = bone.d;
worldX = pa * x + pb * y + bone.worldX;
worldY = pc * x + pd * y + bone.worldY;
float la = cos(rotation * degreesToRadians); // local x axis x
float lc = sin(rotation * degreesToRadians); // local x axis y
float ix = pa * la + pb * lc; // world x axis x
float iy = pc * la + pd * lc; // world x axis y
worldRotation = atan2(iy, ix) * radiansToDegrees;
// You can now use worldX, worldY, and worldRotation in your external class.
}
// Use these values to transform your arrow.
public float getWorldX () { return worldX; } // you may have to add these to the skeleton's world position to get gameworld position.
public float getWorldY () { return worldY; }
public float getWorldRotation () { return worldRotation; }
}