Creating an animation manager control

We will create a control that will handle the animations of a character. It will follow jMonkeyEngine's control pattern and extend AbstractControl. We won't actually use most of the functions of AbstractControl right away, but it's a neat way to offset some of the code from a possible Character class. It will also be easy to add functionalities later on.

How to do it...

To create a control that will handle the animations of a character, perform the following steps:

  1. Create a class called CharacterAnimationManager and have it extend AbstractControl. This class should also implement AnimEventListener, which AnimControl uses to tell our class when animations have finished playing.
  2. We're going to map Jaime's animations into an enum. This is so we don't have to do a lot of string comparisons. While we're at it, we'll add some basic logic to the enum as well. The name of the animation, whether the animation should loop or not, and the time AnimControl should take to blend to a new animation using the following code:
    public enum Animation{
      Idle(LoopMode.Loop, 0.2f),
      Walk(LoopMode.Loop, 0.2f),
      Run(LoopMode.Loop, 0.2f),
      ...
      SideKick(LoopMode.DontLoop, 0.1f);
    
      Animation(LoopMode loopMode, float blendTime){
        this.loopMode = loopMode;
        this.blendTime = blendTime;
      }
      LoopMode loopMode;
      float blendTime;
    }

    We need two fields as well: an AnimControl field called animControl and an AnimChannel called mainChannel.

  3. We set these in the setSpatial method, as shown in the following code. Don't forget to add the class to the AnimControl field as a listener, or we won't receive any calls when animations are finished:
    public void setSpatial(Spatial spatial) {
      super.setSpatial(spatial);
      animControl = spatial.getControl(AnimControl.class);
      mainChannel = animControl.createChannel();
      animControl.addListener(this);
    }
  4. We define a new method called setAnimation in the following code. Inside this, we set the supplied animation to be mainChannel as the current one if it's not the same as the one playing now. We also set loopMode according to how it's defined in the enum:
    public void setAnimation(Animation animation) {
      if(mainChannel.getAnimationName() == null || !mainChannel.getAnimationName().equals(animation.name())){
        mainChannel.setAnim(animation.name(), animation.blendTime);
        mainChannel.setLoopMode(animation.loopMode);
      }
    }
  5. In the onAnimCycleDone method, we create a control so that all animations that don't loop return to the idle animation, with the exception of JumpStart, which should switch to Jumping (as in midair) as shown in the following code:
    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
      if(channel.getLoopMode() == LoopMode.DontLoop){
        Animation newAnim = Animation.Idle;
        Animation anim = Animation.valueOf(animName);
        switch(anim){
          case JumpStart:
            newAnim = Animation.Jumping;
            break;
        }
        setAnimation(newAnim);
      }
    }
  6. That's all that's needed to create a class that manages animations! To set this up from an application, we just need to load a model in the application and add the following line:
    jaime.addControl(new AnimationManagerControl());

How it works…

The AnimControl class is responsible for playing and keeping track of the animations. AnimChannel has a list of Bones that the animation should affect.

Since we let the enum decide the animation parameters for us, we don't need much code in the setAnimation method. We do however need to make sure we don't set the same animation if it is already playing or it could get stuck, repeating the first frame in a loop.

The onAnimCycleDone method is called from AnimControl whenever an animation reaches the end. Here, we decide what will happen when this occurs. If the animation is not looping, we must tell it what to do next. Playing the idle animation is a good choice.

We also have one special case. If you look at the animation list, you will notice that Jaime's jump animation is split into three parts. This is to make it easier to handle jumps of different lengths or the falling animation.

We will tell AnimControl to change the animation to a jumping action once JumpStart is done. We never change to JumpEnd once the jumping action has taken place however. Instead, this should be called from elsewhere when Jaime hits the ground after he jumps. How this is measured is dependent on the game logic, but since we're using the Control pattern, we could use controlUpdate to check Jaime's whereabouts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.104.183