Skip to content

v1.0.4

Latest

Choose a tag to compare

@Alessio-Marchese Alessio-Marchese released this 02 Dec 11:39
· 2 commits to master since this release

In this Release the following things were added:

Animation System

Let’s briefly talks about the new Models.

  • Animation, it’s a new component. It contains a dictionary of Clips (see later), the key of the Current clip, an integer FrameIndex (to keep track of the current frame of the current clip) and float Time (to keep track of the time, usefull to switch frames over time)
  • Clip, it’s a generic abstract model it contains a list of Frames (see later) and an Integer Priority (Used to understand which animation can cancel another animation, based on priority)
  • AnimationFrame, it’s a generic model, it contain a TextureHandle (the key used to obtain the texture of a specific frame) and float Duration, used inside the system to understand for how long this frame should stay.
  • LoopClip and OneShotClip, those classes extends Clip, the first one simply extend and doesn’t change anything, the second one include a new variable (string FollowUpClip, the key of the next clip that we want to play after this one is done) The idea is to implement Clips that goes on in a loop, always the same Clip.. and Clips that run once and then the animation goes back to it’s normal state, for example Run → Loop, Attack → OneShot → then i set that after attack the next Clip must be Idle.
  • Direction (Component) and FacingDirection (Enum) i created this to add the Direction behaviour to a GameObject, this behaviour is usefull for animations, if i keep track of the last direction of a specific GameObject i can Play the correct Animation based on his last Direction..
    For example after i run to the right, i expect that the next animation Idle is going to be on the right, not on the left.

Now let’s talk about the System, it gets all the game objects in a world that contains Animation and Sprite, and if the elapsed Time of the current animation is higher than the Duration of the Frames[FrameIndex], a.k.a the current frame, it means that i can swap frame and go to the next one..
Resets the time to 0, increase the FramIndex by one, and then check, if it’s the last frame, the Clip is done, then if it’s a OneShotClip i set the Animation to play the FollowUpClip, otherwise i set the FrameIndex to 0 = start again the current animation.

Here’s an example of how i implemented the system inside the Sandbox.
Inside Sandbox i extended the behaviour of the Event system by adding new Events and new Subscribers, i thought that the responsibility of which animation exists and how it’s called it’s more related to the game than to the Engine itself.
So i added, PlayerRunEvent, PlayerIdleEvent and PlayerAttackEvent. Then i added the Subscribers that subscribe for those events and Extracts the components: Animation and Direction, then based on the direction the correct animation is played on the Animation component.

Those events are published in Game1.cs, basically, inside the Update method i do these checks to understand if i need to publish any of these events:

if (k.IsKeyDown(Keys.Space))
{
    _world.Events.Publish(new PlayerAttackEvent(_player));
}
else
{
    if (movement.DirX == 1)
    {
        _world.Events.Publish(new PlayerRunEvent(_player, false));
    }
    if (movement.DirX == -1)
    {
        _world.Events.Publish(new PlayerRunEvent(_player, true));
    }
    if (movement.DirX == 0)
    {
        _world.Events.Publish(new PlayerIdleEvent(_player));
    }
}

So that’s the flow:
The Input of the user can create one of these Events → Subscriber invoked, it updates the Animation component → the System adapt to this change and start animating the new animation.

Here’s an example of how i configured the GameObject:

_player = _world.AddGameObject()
	.AddComponent(new PlayerTag()) // A tag used to recognize the player
	.AddComponent(new Collider(100, 200)) // This game object can collide with any other collider object
	.AddComponent(new Transform { X = 0, Y = 0 }) // Can have a position in the world
	.AddComponent(new Movement { Speed = 300 }) // Can move
	.AddComponent(new Sprite(playerIdleRight0, 100, 200, zIndex: 1)) // Can be rendered on the world
	.AddComponent(new Direction()) // Can have a direction (this component is used to keep track of the last direction of the game object, usefull for Animation)
	.AddComponent(new Animation("idle_right") // Can have animations + Configuration of the followings + Configuration of the default
	    .AddClip("attack_right",
	    // Add a new Clip inside the Clips Dictionary of this Animation inside this GameObj
	    // The Clip is of type OneShot (it requires to specify the follow up clip key)
	    // Can add a priority, and then i can add N frames, specifying the handle
	    // Of each frame and the Duration
	        new OneShotClip("idle_right", 2)
	            .AddFrame(playerAttackRight0, 0.15f)
	            .AddFrame(playerAttackRight1, 0.1f)
	            .AddFrame(playerAttackRight2, 0.05f)
	            .AddFrame(playerAttackRight3, 0.15f)
	    )
	    .AddClip("attack_left",
	        new OneShotClip("idle_left", 2)
	            .AddFrame(playerAttackLeft0, 0.15f)
	            .AddFrame(playerAttackLeft1, 0.1f)
	            .AddFrame(playerAttackLeft2, 0.05f)
	            .AddFrame(playerAttackLeft3, 0.15f)
	    )
	    .AddClip("idle_right",
	        new LoopClip(1)
	            .AddFrame(playerIdleRight0, 0.05f)
	            .AddFrame(playerIdleRight1, 0.05f)
	            .AddFrame(playerIdleRight2, 0.05f)
	            .AddFrame(playerIdleRight3, 0.05f)
	            .AddFrame(playerIdleRight4, 0.05f)
	            .AddFrame(playerIdleRight5, 0.05f)
	            .AddFrame(playerIdleRight6, 0.05f)
	            .AddFrame(playerIdleRight7, 0.05f)
	    ));

Minor fix to the Camera Zoom:

The zoom wasn’t actually working properly, now it should be fine! i accidentally removed the Rendering of the Textures based on texture.Scale * camera.Zoom.

Clip of the Animation System:

Credits for the Animation: https://pixelfrog-assets.itch.io/

Sandbox4.mp4