Player Animator

namespace Sandbox
{
    public class StandardPlayerAnimator : PlayerAnimator
    {
        TimeSince TimeSinceFootShuffle = 60;

        public override void Tick()
        {
            DoRotation();
            DoWalk();

            //
            // Let the animation graph know some shit
            //
            SetParam( "b_grounded", GroundEntity != null );

            //
            // Look in the direction what the player's input is facing
            //
            SetLookAt( "lookat_pos", Player.EyePos + Input.Rot.Forward * 1000 );
        }

        public virtual void DoRotation()
        {
            //
            // Our ideal player model rotation is the way we're facing
            //
            var idealRotation = Rotation.LookAt( Input.Rot.Forward.WithZ( 0 ), Vector3.Up );

            //
            // If we're moving, rotate to our ideal rotation
            //
            Rot = Rotation.Slerp( Rot, idealRotation, WishVelocity.Length * Time.Delta * 0.01f );

            //
            // Clamp the foot rotation to within 120 degrees of the ideal rotation
            //
            Rot = Rot.Clamp( idealRotation, 120, out var change );

            //
            // If we did restrict, and are standing still, add a foot shuffle
            //
            if ( change > 1 && WishVelocity.Length <= 1 ) TimeSinceFootShuffle = 0;

            SetParam( "b_shuffle", TimeSinceFootShuffle < 0.1 );
        }

        void DoWalk()
        {
            //
            // These tweak the animation speeds to something we feel is right,
            // so the foot speed matches the floor speed. Your art should probably
            // do this - but that ain't how we roll
            //
            SetParam( "walkspeed_scale", 2.0f / 190.0f );
            SetParam( "runspeed_scale", 2.0f / 320.0f );

            //
            // Work out our movement relative to our body rotation
            //
            var moveDir = WishVelocity.Normal;
            var forward = Rot.Forward.Dot( moveDir );
            var sideward = Rot.Right.Dot( moveDir );

            //
            // Set our speeds on the animgraph
            //
            SetParam( "forward", forward );
            SetParam( "sideward", sideward );
            SetParam( "wishspeed", WishVelocity.Length );
        }

        public override void OnEvent( string name )
        {
            DebugOverlay.Text( 5.0f, Pos + Vector3.Up * 100, name );

            if ( name == "jump" )
            {
                Trigger( "b_jump" );
            }

            base.OnEvent( name );
        }

    }
}
10 Likes

Oh boy, class access and we can feed our own data directly into the anim graph! Will we be able to handle custom data set in the graph? I see a lot of Set but no Get

Also, I’m assuming OnEvent is general I/O related? What’s the extent of that? IE: Map Triggers, code based “DoEvent” etc

I didn’t know you could read any useful data back from the graph?

OnEvent is just a way to abstract the player controller interacting with the animator… so we don’t have them talking to each other and relying too much on that connection. Trigger just calls SetParam( "b_jump", true ).

3 Likes

Thank you for clearing that OnEvent method up. Also by custom data I meant the motors, parameters, or AnimTags. Initially I thought OnEvent would maybe be a listener for AnimTags, but without the additional arguments. For sure you can access those though.

Parameters would just be a handy thing considering map triggers can also change anim parameters on entities, as it may be difficult to keep track of what’s going on if you wanted to have more contextual responses ( or you wanted to use those param values for some other part of the player )

1 Like

Just a quick note here that DoRotation() is a virtual function, if someone else comes along an inherits from this class and re-implements it, how will you make sure that it will still perform what the function says?

This is a general rub because its a problem with all inheritance based systems in the first place. To make it safer, you could make it non virtual or ideally non-inheritable, that way if the user inherits from the class he/she can’t change it. Furthermore, combining inheritance and modders/programmers is a recipy for disaster, take java for example, java used inheritance and now its a horrible mess becuase inheritance is complicated to get right. More generally, this is all to do with an interesting topic on how to extend existing code bases. Inheritance is not what you’re looking for to do this, Inheritance is not for extending (this is a common misconception) it’s for subtyping/concept heirachies and you may argue that you need to give modders flexibility but, If we look at sfml it provides a stable platform but doesn’t encourage inheritance, this is how to make a system that is extendable without using inheritance. In conclusion, it’s almost certainly better to provide modders with has-a’s instead of is-a’s.

To be fair it’s virtual to encourage overriding. There’s nothing wrong with inheritance.

4 Likes

David just came cause he spent like 2 years trying to make custom anims for gmod and this is just his wettest dream. (ALCS 2.0 when?)

1 Like