SiO2 bullet-char demo question


#1

Hi @pspeed

I have a question regarding MobAnimationState.

There is Mobility component and CharacterAction component.

I have trouble understanding how they are different. It seems both are referring to an animation.

Would you mind explaining the use case for each one, please ?

Thanks


#2

One describes the type of movement that a character should do when it’s moving… say walking, swimming, flying, etc.

The other describes an action like punching, pressing a button, hitting a rock, etc.


#3

Say walking while holding a weapon in hand… Do you suppose this as a case of multiple mobility states at a time ?


#4

Also based on ActionContainer implementation, are you supposing we should create a new action entity each time an action is triggered on parent and remove it when action ends playing ? Why not do this with an state on the entity ? for example START and STOP state.


#5

Yes, for example, walking + aiming are two mobility states at once. One is animated from position and the other from view orientation.

Yes.

Which entity? Start/stop what? Are you thinking only one action at a time? What advantage does this have?


#6

I mean on the (child) entity with CharacterAction component we add an other component ActionState and update state instead of adding/removing the entity.

But never mind, for a moment I thought it as a Persistent Entity. I see that it is just a transient entity which should be decayed when done.


#7

@pspeed

For my need I want to modify MobAnimationState to be based on my tween animation. So that not only it plays bone animation but also there can be audio, particle, dialogue,… played by chaining any kind of tween.

So while keeping the principles I want to change the way animation is played.

In MobAnimationState you play the animation with AnimComposer. I am considering to modify that part and use Lemur EffectControl to play the TweenAnimation which will in turn call the AnimComposer inside the tween (the CallMethod tween).

I am curious to also know your idea. I read in wiki that EffectControl is used by GUI elements but it also can be used for any spatial, so do you think I am in right place to use it for playing my tween animations ?

Will appreciate any comments.


#8

Well, as far as I know, AnimComposer was based on Tweens and stuff… so I thought it was already setup to handle effects and audio and so on. I always figured that stuff would be bundled with the rest of the animation.

EffectControl is very basic with respect to the types of 3D mob state management one might want to do. I kind of wish @nehon had finished his monkanim stuff because that seemed to be heading in the right direction. EffectControl really only has two kinds of state transitions: on and off.


#9

I was supposing that AnimComposer does not accept any arbitrary tweens and filters out only the Action tweens.

After your hint I read the code more precisely and it seems we can include any arbitrary tweens and it will interpolate them as well. I am going to test it to get sure about it.

As always thanks so much for your help :slightly_smiling_face:


#10

One more question,
What about the cases when we have no bone animation, for example I have some ghost characters that fly in the air and they have no bone animation, and I just want to play particle tween and sound tween on them. Would this be possible to use AnimComposer ?


#11

That is my understanding.


#12

@pspeed about Walk mobility state I want to go further and do some more advanced stuff but there is something I am confused about.

I want to use BlendAction to create a Move action containing Walk and Run clips.

moveAction = composer.actionBlended("Move", new LinearBlendSpace(0, 1),

"Walk", "Run");

I need to update BlendSpace each frame based on entity move speed (when walking the blend value is 0, when running with max speed the blend value is 1 otherwise it’s something between them).

Do you think I need to create another tween for it ?
for example a BlendBySpeed tween which is running in parallel with moveAction and updates blend space based on entity speed.

Or I should use a Control to do that ? for example an AnimBlendBySpeedControl.

There is similar concern about sound, a walk audio speed and volume will be updated based on entity move speed.

Do you think they need to be done in Tween or in Control or you have different approach ?


#13

I don’t know the animation API well so I may be the wrong person to ask.

…but I also don’t immediately see why a control would be helpful.


#14

@pspeed thinking the whole day, finally I came up with the idea of TweenInterpolators.

public interface TweenInterpolator<T> {
 
    public void doInterpolate(double t, T tween);

}

Here is initial scratch of my AudioTween

public class AudioTween extends AbstractTween {

    private AudioNode audioNode;
    private boolean instanced;
    
    private TweenInterpolator<AudioTween> interpolators [];
   

    public AudioTween(AudioNode audioNode, boolean instanced, double length, TweenInterpolator<AudioTween>... interpolators) {
        super(length);
        this.audioNode = audioNode;
        this.instanced = instanced;
        this.interpolators = interpolators;
    }

    @Override
    protected void doInterpolate(double t) {
        if (audioNode.getStatus() != AudioSource.Status.Playing) {
            if (instanced) {
                audioNode.playInstance();
            } else {
                audioNode.play();
            }
        }
        
        for (TweenInterpolator<AudioTween> interpolator : interpolators) {
            interpolator.doInterpolate(t, this);
        }    
                
        if(t == 1){
            // Done
            audioNode.pause();
        }
    }
}

one can add any audio interpellator, for example AudioSpeedInterpolator, VolumeInterpolator, … and they will be only called while audio is playing.

Looking forward to hear your idea about this approach :slightly_smiling_face:


#15

I’m not sure I understand what it’s for.


#16

For example if you want to increase audio playback speed when character move speed increase and decrease it when character move speed decrease you can add an AudioSpeedInterpolateWithMotion or you may want the audio to get louder when character speed increases so you add an AudioVolumeInterpolateWithMotion.


#17

Just feels weird to start nesting tweens in this way. Like “Oh, I want to rotate my spatial while I move it so I will add the rotation as a child of the move tween…” Not right.


#18

I feel I screwed up the whole day :grin:
I will come back with better solution, other day :slightly_smiling_face:


#19

Okay,

@pspeed I came up with new design in 2nd iteration , I am taking similar approach to the SpatialTweens you made.

For example AudioTween:

Initial implementation of AudioTweens :

public class AudioTweens {

    public static Tween play(AudioNode audioNode, boolean instanced, double length) {
        return new PlayAudio(audioNode, instanced, length);
    }

    public static Tween syncSpeed(AudioNode audioNode, PositionAdapter pa, double baseSpeed, double length) {
        return new SyncSpeed(audioNode, pa, baseSpeed, length);
    }

    public static Tween syncVolume(AudioNode audioNode, PositionAdapter pa, double baseSpeed, double length) {
        return new SyncVolume(audioNode, pa, baseSpeed, length);
    }

    private static class PlayAudio extends AbstractTween {

        private final AudioNode audioNode;
        private final boolean instanced;

        public PlayAudio(AudioNode audioNode, boolean instanced, double length) {
            super(length);
            this.audioNode = audioNode;
            this.instanced = instanced;
        }

        @Override
        protected void doInterpolate(double t) {
            if (audioNode.getStatus() != AudioSource.Status.Playing) {
                if (instanced) {
                    audioNode.playInstance();
                } else {
                    audioNode.play();
                }
            }

            if (t == 1) {
                // Done
                audioNode.pause();
            }
        }
    }

    private static class SyncSpeed extends AbstractTween {

        private final AudioNode audioNode;
        private final PositionAdapter pa;
        private final double baseSpeed;

        private final Filterd lowPass = new SimpleMovingMean(10); // 1/6th second of data   

        public SyncSpeed(AudioNode audioNode, PositionAdapter pa, double baseSpeed, double length) {
            super(length);
            this.audioNode = audioNode;
            this.pa = pa;
            this.baseSpeed = baseSpeed;
        }

        @Override
        protected void doInterpolate(double t) {
            float forward = pa.getOrientation().mult(Vector3f.UNIT_Z).dot(pa.getVelocity());
            double pitch = forward / baseSpeed;
            // Pass the pitch through a low-pass filter using
            // a moving average
            lowPass.addValue(pitch);
            pitch = lowPass.getFilteredValue();
            pitch = Math.round(pitch * 10) / 10.0;
            pitch = FastMath.clamp((float) pitch, 0.5f, 2.0f);
            if (pitch != audioNode.getPitch()) {
                audioNode.setPitch((float) pitch);
            }
        }
    }

    private static class SyncVolume extends AbstractTween {

        private final AudioNode audioNode;
        private final PositionAdapter pa;
        private final double baseSpeed;

        public SyncVolume(AudioNode audioNode, PositionAdapter pa, double baseSpeed, double length) {
            super(length);
            this.audioNode = audioNode;
            this.pa = pa;
            this.baseSpeed = baseSpeed;
        }

        @Override
        protected void doInterpolate(double t) {
            //TODO: implement when needed!
        }

    }
}

I will add similar one for AnimTweens.

public class AnimTweens {

    public static Action action(AnimComposer ac, String name) {
        return ac.action(name);
    }

    public static BaseAction actionSequence(AnimComposer ac, String name, Tween... tweens) {
        return ac.actionSequence(name, tweens);
    }

    public static BlendAction actionBlended(AnimComposer ac, String name, BlendSpace blendSpace, String... clips) {
        return ac.actionBlended(name, blendSpace, clips);
    }

    public static Tween blendAnim(PositionAdapter pa, BlendSpace blendSpace, double baseSpeed, double length) {
        return new BlendAnim(pa, blendSpace, baseSpeed, length);
    }

    public static Tween syncSpeed(PositionAdapter pa, Action action, double baseSpeed, double length) {
        return new SyncSpeed(pa, action, baseSpeed, length);
    }

    private static class BlendAnim extends AbstractTween {

        private final PositionAdapter pa;
        private final BlendSpace blendSpace;
        private final double baseSpeed;

        public BlendAnim(PositionAdapter pa, BlendSpace blendSpace, double baseSpeed, double length) {
            super(length);
            this.pa = pa;
            this.blendSpace = blendSpace;
            this.baseSpeed = baseSpeed;
        }

        @Override
        protected void doInterpolate(double t) {
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        }
    }

    private static class SyncSpeed extends AbstractTween {

        private final PositionAdapter pa;
        private final Action action;
        private final double baseSpeed;

        public SyncSpeed(PositionAdapter pa, Action action, double baseSpeed, double length) {
            super(length);
            this.pa = pa;
            this.action = action;
            this.baseSpeed = baseSpeed;
        }

        @Override
        protected void doInterpolate(double t) {
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        }
    }
}

Then next step would be to hook them to my TweenEditor.

Looking forward to hear your idea about new approach.


#20

Without diving in deeply, it sounds like a better approach.