SiO2 bullet-char demo question

@pspeed are you thinking of Idle state as a Mobility state or a CharacterAction ?

Edit:
I hope you can add support for layering stuff in MobAnimationState, I am having hard time trying to figure out how should I do it. :confounded:

Currently there are

private Set<Mobility> mobility = new HashSet<>();

private Mobility primary;

but seems they are not used ?

Also it seems Walking/Jumping/Idle states are managed separately than Mobility and CharacterAction ? Shouldn’t walk and jump mobility states set by CharInputDriver ?

Regarding setBaseAnimation() what do you mean by base animation ? How is setting action (in your comment below) is different than setBaseAnimation() ? :

 public void addAction( String a ) {
            if( Objects.equals(a, this.action) ) {
                return;
            }
            this.action = a;
            
            // Set the animation on the character
        }

by // Set the animation on the character, do you mean it should be handled differently from setBaseAnimation() ?

Sorry if my questions are noobish. Will appreciate any help to figure out them. :slightly_smiling_face:

If “Idle” is a state then that means that a character in the “Idle” state would just slide around as it moves?

No, Idle is an animation of the movement state. Mobility state = “what should I do while I’m moving and not moving”. Am I standing at Idle and then walking/running when I move? Or am I treading water and then swimming when I move? Those are two different mobility states… one at a time.

I never added layering because when I wrote the original classes the new animation API didn’t support it. That’s also why no strafe animation is blended with the walk animation (if it exists).

So you are thinking punching would be a base animation? I think of walking as a base animation and then punching might be layered on top of it (might override it, too… since monkanim is not finished there is no proper state machine).

1 Like

Got that. Thanks for the explanation.

Is ControlDriver (for example CharSteerDriver in my case) where I should set Mobility components on entity (at server side) ?

No.

While these might align, they may not always align.

Then do I need some kind of MobilitySystem at server side to manage them ?

A system to manage the systems of managing the systems of your systems? Yo dawg!

Or, just set the appropriate components together when you want them together and set them different when you want them different. No system required.

When you want the mobility to be walking and use the char input driver… then you set those two components. When you want the mobility to be swimming… but still use the char input driver then you change just the mobility component.

That’s what I mean

See here

I made a CharMobility

public final class CharMobility {
    
    public static final String LAYER = "Default";

    private final EntityData ed;
    private final EntityId characterId;
    private EntityId mobilityId;
    private CharMobilityState currentState;

    public CharMobility(EntityData ed, EntityId characterId) {
        this.ed = ed;
        this.characterId = characterId;
    }

    public void initialize() {
        TransientBuff buff = new TransientBuff(characterId, BuffTypes.MOBILITY);
        mobilityId = ed.createEntity();
        ed.setComponent(mobilityId, buff);
    }

    public boolean isIdle() {
        return currentState == CharMobilityState.IDLE;
    }

    public boolean isWalking() {
        return currentState == CharMobilityState.WALKING;
    }

    public void applyIdleMobility() {
        ed.setComponent(mobilityId, Mobility.create(CharMobilityState.IDLE.getName(), LAYER, true, 1, ed));
        currentState = CharMobilityState.IDLE;
    }

    public void applyWalkingMobility() {
        ed.setComponent(mobilityId, Mobility.create(CharMobilityState.WALKING.getName(), LAYER, true, 1, ed));
        currentState = CharMobilityState.WALKING;
    }

    public void cleanup() {
        ed.removeEntity(mobilityId);
    }

    private enum CharMobilityState {
        IDLE("char-idle"),
        WALKING("char-walk");
        
        String name;

        private CharMobilityState(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }    
    }
}

which I pass it to ControlDriver and inside update

 @Override
    public void update(SimTime time, EntityPhysicsObject object) {
        body.getPhysicsLocation(vTemp);
        agent.setWorldTranslation(vTemp);
        body.getPhysicsRotation(qTemp);
        agent.setWorldRotation(qTemp);
        //agent.updateCell(grid);

        steer.updateAI((float) time.getTpf());

        //body.getPhysicsRotation(qTemp);
        body.getAngularVelocity(vTemp);

        // Note: apparently killing angular velocity is actually needed.
        // Otherwise we tip and intercollide with ramps.
        // Kill any non-yaw rotation
        if (vTemp.x != 0 && vTemp.z != 0) {
            vTemp.x = 0;
            vTemp.y *= 0.95f; // Let's see if we can dampen the spinning
            vTemp.z = 0;
            body.setAngularVelocity(vTemp);
        }

        desiredVelocity.set(agent.getVelocity()).normalizeLocal().multLocal(walkSpeed);

        // See how much our velocity has to change to reach the
        // desired velocity
        body.getLinearVelocity(vTemp);

        // Calculate a force that will either break or accelerate in the
        // appropriate direction to achieve the desired velocity
        force.set(desiredVelocity).subtractLocal(vTemp);
        force.y = 0;

        // We are going to apply force by a threshold to make characer less slippy 
        // so it wont look like a vehicle.
        if (force.lengthSquared() > 0.80) {
            body.applyCentralForce(force.multLocal(groundImpulse));

            qTemp.set(agent.getPredictedRotation());
            // Kill any non-yaw orientation
            qTemp.toAngles(angles);
            angles[0] = 0;
            angles[2] = 0;
            body.setPhysicsRotation(qTemp.fromAngles(angles));

            if (!charMobility.isWalking()) {
                charMobility.applyWalkingMobility();
            }

        } else {
            // We need to kill non-yaw orientation for capsule shapes  
            // to prevent body fall down when no force is applied.
            body.getPhysicsRotation(qTemp);
            qTemp.toAngles(angles);
            if (angles[0] != 0 || angles[2] != 0) {
                angles[0] = 0;
                angles[2] = 0;
                body.setPhysicsRotation(qTemp.fromAngles(angles));
            }

            if (!charMobility.isIdle()) {
                charMobility.applyIdleMobility();
            }
        }
        // Note: for non-capsule shapes something else would have to
        // be done so that the environment affects orientation.
        // Player input orientation then becomes more of a suggestion
        // that needs to be reconciled with environment influences.         
    }

I change mobility components based on speed .

In my mind, that makes no sense. If you want to play a different animation when the character moves very fast then that is still part of the “moving with his feet” mobility.

I have no idea what “CharMobility” is for. The fact that you are tying your input to your mobility means that we are talking about apples and comets. Two totally different concepts.

So maybe you should just forge your own path and delete all of the code related to my way… because you are 100% doing your own thing. Which is fine… just no reason to saddle yourself with my approach.

Yes, If he moves fast then inside my Walk (“moving with his feet”) tween (which is a blend of Walk and Run animation) I will sync the blend value with character move speed so it will blend toward run animation.

I define all mobility states a human character can do.

Idle, Walk ( “moving with his feet”), Treading Water, Swimming, Holding sth in hand

for a ghost character : I will have GhostMobility
which will have :
Idle, Fly other mobilities may be added later

The reason I am using a final class CharMobility to change the mobility instead of directly doing it in CharSteerDrive is because I do not want to let CharSteerDriver to have access to EntityData.

I might get confused a bit, but aren’t you doing the same thing based on character forward speed in MobAnimationState:

 double animSpeed = forward / normalWalkSpeed; 
 if (!onGround) {
                setBaseAnimation("Jumping", 1);
                return;
            } else {
                // Clamp the animation speed at some minimum threshold that
                // won't look silly.            
                if (Math.abs(animSpeed) > 0.2) {
                    setBaseAnimation("Walk", animSpeed);
                    return;
                }
            }
            //} 
            //} else {
//System.out.println("rawSpeed:" + rawSpeed);            
//            }

            // If nothing else set an animation then go back to idle
            setBaseAnimation("Idle", 1);
        }

I am doing the similar thing in CharSteerDriver at server side except that I do not have jumping included yet. So if he is not moving I apply idle state else I apply walking state.

Note, I have not Swimming/Jumping yet (I may do not need them in my game) for this reason I am not including them yet.

I really don’t mean to do it, I just was able to come up with above ideas based on my understanding of how you are doing in MobSnimationState. But most certainly my understanding is wrong (or may be I couldn’t explain them well in English, because it is not my native language and it takes a lot energy for me to write what i want to say) and I want to correct my understanding with your help, please bear with me :slightly_smiling_face:

In my approach, there are two separate things.

  1. On the back end, there is how the character moves the physical object. This may be forces from player input. This may be steering from AI selected steering targets… or something else entirely.

  2. On the front end, completely separate from how the object gets from position to position + 1, is the idea of ‘mobility animation’. This is how the particular character controls its animation based on the movement that it detects.

These are completely orthogonal concepts. They do not communicate. There is some third thing that coordinates which of (1) the entity is using and which of (2) the entity is using. But for example, any walking character may have the “Walk” mobility but they may have “player input” driver or steering drivers, etc. on the back end.

The idea being that instead of micromanaging every footstep, IK foot placement, etc. on the backend… the backend just moves the entity. The front end determines how to render that particular character based on a variety of inputs: their current mobility state (walking, sliding on their butt, swimming, skipping, whatever), how far they’ve moved, where they are standing (if foot placement is done with IK), which direction the wind is blowing, and so on.

These two pieces (1) and (2) never communicate except through the motion of the player… else you might as well just tightly couple them, come up with some twitch networking protocol for animation frames, and skip my idea.

Admittedly, it’s a bit of an experiment on my part but it has some really cool side-effects that I like.

1 Like

Thanks so much for thorough explanation.

I will try to keep it in my mind and do my implementation based on your approach as far as my capabilities let me to do. I am not an smart guy like you are but I have learned so much from your advices and codes so far. :slightly_smiling_face:

1 Like