Cuellarjmcg

Hi :)

First of all, I have to say that I'm very happy with the purchase of Spine, is an amazing piece of software, great work! :D

I have been playing with creating a skeleton with a skin, so far everything works as expected in Spine. And then, I want to set the skin on my game with libgdx.

I load the SkeletonData from json, load the animation file, and then I create a new Skeleton for every Actor. And then I use the method setSkin(String arg) from Skeleton but nothing happens :( I debugged my app, so I encountered that I can set a defaultSkin to the SkeletonData, I set one of my skins and then it shows correctly.

If I set another skin as the defaultSkin it works perfectly. But this is set to the SkeletonData, not to the skeleton independently.

The main problem: setSkin method from Skeleton isnt working. I even tried using the setSkin(Skin arg) method, I pass the skin with SkeletonData.findSkin(String arg) but doesn't work too :(

That's first. Second question is: How can I use pooling with Skeletons?

As far as I got, it seems that I can have 1 SkeletonData per file, so that's not a problem. The problem comes when I want to have just one "ScenarioActor" that use skeletons, so I have a skeleton property. This needs to create a Skeleton from a SkeletonData, which will create another instance, and it will be bad with the evil Garbage Collector :(

Does I need to use a Pool for every type of SkeletonData that I would use for my game? (That's doesn't seem like an elegant solution :S As I want to create several different types of skeletons).

Thanks for your help in advance.
Cuellarjmcg

Kickstarter Backer
  • Сообщения: 92

Krøllebølle

I have been thinking of the same, since I have up to ~20-30 Actors of the same Skeleton, SkeletonData and Animations. Creating new instances of all these objects for every Actor is not an efficient solution. What I am thinking is to create a class that contains the Skeleton, SkeletonData, Animations and RootBone for a given Actor and only create this once, also including getter methods. Such classes can then be wrapped in a total container class (e.g. SkeletonContainer) and each Actor may then keep references to its given Skeleton instance and its Animations and poll for Animation updates.

Bear in mind that I have not tried such approach yet, but I am going to sometime this week. Does it seem like a feasible way of doing this?
Krøllebølle

Kickstarter Backer
  • Сообщения: 36

Cuellarjmcg

I have been thinking about somewhat the same approach (draw only the animation update for the Actor), and that implies that every Actor, before it's drawn, will update the skeleton x/y, scale, etc. and apply an animation to the skeleton given a stateTime (which would be stored with the Actor of course).

But maybe there's a better solution, so I any suggestions would be welcome :D

Also, the setSkin problem persist :( so... before trying anything, I will wait for a solution for that.
Cuellarjmcg

Kickstarter Backer
  • Сообщения: 92

Nate

Javadocs for setSkin say:
Attachments from the new skin are attached if the corresponding attachment from the old skin is currently attached.
Since a newly created skeleton has only a default skin, setting a skin won't attach anything to the slots. You need to call skeleton.setToBindPose(); after setting the skin.

As far as loading goes...

SkeletonData holds skeleton setup pose information. This only needs to be loaded once (unless you need to change the setup pose per Skeleton). You can create as many Skeletons as you want from a single SkeletonData.

Animation holds animation data. This only needs tobe loaded once, you can pose any number of Skeletons with the same animation (as long as the Animation is designed for that Skeleton of course).

Skeleton holds the individual skeleton state: the current pose, what slots have what attachments, etc. You'll probably need one per on-screen skeleton.

I don't think you'll need any pooling.
Аватара пользователя
Nate

Nate
  • Сообщения: 9288

Cuellarjmcg

setSkin problem solved, I was using skeleton.setBonesToBindPose() instead of setToBindPose(), my bad ._. works perfectly now, thanks a lot.

With the skeleton question, for example: I have two skeletons, one is for some trees and one for some bushes.

So I have an "ScenarioActor", which holds a skeleton. Can I switch from one skeleton (trees or bushes) to another without creating a new Skeleton instance?

The ScenarioActor is pooled, so if I can switch skeletons without creating new instances, all will be well in this world :D

Thanks in advance for your help.
Cuellarjmcg

Kickstarter Backer
  • Сообщения: 92

Nate

A Skeleton holds state for a SkeletonData. You can't make a Skeleton with SkeletonData A be compatible with SkeletonData B. You can pool Skeletons for SkeletonData A and separately pool Skeletons for SkeletonData B, but I doubt you need to do this. It's unlikely you are creating an actor or Skeleton every frame, so you don't need to do pooling for these. Just create one when you need it. Pooling is to avoid GC on crappy VMs (Android) for short lived objects.
Аватара пользователя
Nate

Nate
  • Сообщения: 9288

Cuellarjmcg

I see, it is clear now. Now I can think of the best approach when creating my Skeletons :) Nice thing we can skin them, that will help a lot (some of the best things of Spine)

Thank you for your answers :).
Cuellarjmcg

Kickstarter Backer
  • Сообщения: 92

Krøllebølle

I'll give you my take on this here. Any comments and suggestions for improvements are appreciated. The following code is tested and works fine.

First I create an interface for my Actors. The methods included in this interface must be implemented to achieve the polymorph behavior (are there other ways of achieving the same?).
public interface Animatable {
public Animation getAnimation(int state); //Each Actor must poll Animations
public SkeletonData getSkeletonData(); //Each Actor must get a SkeletonData object
}
Then I make the Actor implement this interface. Also, the Actor has an instance of a Skeleton and an Animation instance (currentAnimation) that will be changed based on states. This will thus just be a reference to objects in an AnimationsContainer object (see below).
public abstract class ActorBaseClass extends Actor implements Animatable{
protected Skeleton skeleton;
protected Animation currentAnimation;
public ActorbaseClass(AssetsContainer assetsContainer){
this.assetsContainer = assetsContainer; //Reference to (protected) object containing AnimationsContainer object
skeleton = new Skeleton(getSkeletonData());
skeleton.setToBindPose();

currentAnimation = getAnimation(state);
}
//Stuff, note that this in an abstract Actor so the interface methods need not be implemented here.
}
Then I implement the two interface methods in an Actor of instance ActorBaseClass:
public class Figure2 extends ActorBaseClass{
public Figure2( AssetsContainer assetsContainer) {
super(assetsContainer);
}

@Override
public Animation getAnimation(int state) {
return assetsContainer.getAnimationsContainer().getFigure2Animation(state);
}

@Override
public SkeletonData getSkeletonData() {
return assetsContainer.getAnimationsContainer().getFigure2SkeletonData();
}

}
The AnimationsContainer class looks like:
public class AnimationsContainer {

private Figure2Animations figure2Animations;
//TODO: Add classes for other Actors

public AnimationsContainer( AssetsContainer assetsContainer ){
figure2Animations = new Figure2Animations(assetsContainer);
//TODO: Create instances of other classes
}


public Animation getFigure2Animation( int state ){
return figure2Animations.getAnimation(state);
}

public SkeletonData getFigure2SkeletonData(){
return figure2Animations.getSkeletonData();
}

}
where the figure2Animations instance is of the following class:
public class Figure2Animations {

private static final String name = GameParameters.FigureData.Figure2Data.name;

private SkeletonData skeletonData;
private Animation walkAnimation, //Animation for STATE_WALKING
dragAnimation, //Animation for STATE_DRAGGED
fallAnimation, //Animation for STATE_FALLING
bounceAnimation, //Animation for STATE_BOUNCING
stealAnimation, //Animation for STATE_STEALING
tapAnimation, //Animation for STATE_TAPPED
dieAnimation, //Animation for STATE_DEAD
dodgeAnimation, //Animation for STATE_DODGE
landAnimation; //Animation for STATE_LANDING

public Figure2Animations( AssetsContainer assetsContainer ){
SkeletonJson json = new SkeletonJson(assetsContainer.getTextureAtlas());
skeletonData = json.readSkeletonData(Gdx.files.internal("data/spine/"+name+"/"+name+"-skeleton.json"));

walkAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-walk.json"), skeletonData);
dragAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-drag.json"), skeletonData);
fallAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-fall.json"), skeletonData);
bounceAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-bounce.json"), skeletonData);
stealAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-steal.json"), skeletonData);
tapAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-walk.json"), skeletonData);
dieAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-walk.json"), skeletonData);
dodgeAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-walk.json"), skeletonData);
landAnimation = json.readAnimation(Gdx.files.internal("data/spine/"+name+"/"+name+"-land.json"), skeletonData);

}

public SkeletonData getSkeletonData(){
return skeletonData;
}

public Animation getAnimation( int state ){

switch ( state ) {
case ActorBaseClass.STATE_NONE:
return null; //TODO: Remove?

case ActorBaseClass.STATE_TOUCHED:
return null; //TODO: Remove?

case ActorBaseClass.STATE_WALKING:
return walkAnimation;

case ActorBaseClass.STATE_DRAGGED:
return dragAnimation;

case ActorBaseClass.STATE_FALLING:
return fallAnimation;

case ActorBaseClass.STATE_BOUNCING:
return bounceAnimation;

case ActorBaseClass.STATE_LANDING:
return landAnimation;

case ActorBaseClass.STATE_FLINGED:
return fallAnimation; //TODO: Do something else?

case ActorBaseClass.STATE_TAPPED:
return tapAnimation;

case ActorBaseClass.STATE_DODGE:
return dodgeAnimation;

case ActorBaseClass.STATE_STEALING:
return stealAnimation;

case ActorBaseClass.STATE_DEAD:
return dieAnimation;

case ActorBaseClass.STATE_STILL:
return null; //TODO: Do something else?

default:
return null; //TODO: Do something else? Throw?
}
}
}
Does this seem like a reasonable approach? I realize that for instance the Figure2Animations class may be refactored to a template class somehow.
Krøllebølle

Kickstarter Backer
  • Сообщения: 36

Nate

You could probably simplify it a bit (your actor can just have a Skeleton and Animation field with getters/setters), but that looks fine. I need to design a nice way of keeping animation state (with mixing) so I can show it in the coding workshop.
Аватара пользователя
Nate

Nate
  • Сообщения: 9288


Вернуться в Editor