“Shoot foul beasts in your wake as you charge the local keep to reach and kill its master”
About Crimson Keep
Crimson Keep was the third game project at TGA and the first game we made in the schools custom engine TGE. We were given the task of making a 2D platformer. We chose Abuse (1996) as our reference game because we enjoyed playing it and liked the combat.
Elias Andersson Kim Betsgren Milla Jaakkola Landin
Animators:
Tim Lee Viktoriia Shvydak
Level Designers
Eric Stockselius Ivan Mårtensson Blomberg Oliver Riem Vis
My Contribution
Animation Controller
While TGE provides a way to render sprites it’s does not have a built in support for 2D animation using sprite sheets. I took it upon myself to create an animation controller choose what animation to play and play it.
Sprite Sheet First off I needed a way to render part of a texture. Luckily TGE provides the possibility to render part of a texture as a sprite using Texture Rext which are just min and max UV coordinates. I made a Sprite sheet class that holds a texture with the sprite sheet and a list of Texture Rext one for each frame of the animation. The sprite sheet is used to get the correct Texture Rext by sending in what frame of the animation I want to render and using it to render part of the sprite sheet.
Controller The controller is initialized using a JSON file that hold all the data needed for an animated object. The controller contains a list of animations.
structAnimation
{
std::stringmyName;
SpriteSheetmySpriteSheet;
floatmyFPS;
boolmyLooping;
boolmyHasExitTime;
boolmyCanFlip;
boolmyHasPriority;
std::map<std::string,bool>myConditions;
std::vector<std::string>myConnections;
};
Each controller has a state machine that is controlled by a set of conditions, each animation has a list of conditions that have to be true for it to be able to play. Each animation also contains a list of connections which are what animations can play after the animation. If any condition changes the controller checks if the current animation is still valid. If not it will go through the list of connections and check if any are valid and will swap the animation being played if necessary.
Player Animation
When we chose Abuse (1996) as our reference game we realized that we were going to have to split the player sprite into two parts. One for the upper body and one for the lower body. The upper body swaps between different sprite sheets depending on where the cursor is aiming each of which match the running animation of the legs and by matching the current frame for both upper and lower body we were able to make it relatively seamless to aim anywhere without the upper and lower body feeling disconnected. Except no.
Looking closely you can see that the hips move up and down, but because the upper body doesn’t follow they look disconnected. We were able to solve it quite simply, I made it so the upper body moves up and down using a sin wave with a matching frequency to match the two again.
Crimson Keep
“Shoot foul beasts in your wake as you charge the local keep to reach and kill its master”
About Crimson Keep
Crimson Keep was the third game project at TGA and the first game we made in the schools custom engine TGE. We were given the task of making a 2D platformer. We chose Abuse (1996) as our reference game because we enjoyed playing it and liked the combat.
Elias Andersson Kim Betsgren Milla Jaakkola Landin
Animators:
Tim Lee Viktoriia Shvydak
Level Designers
Eric Stockselius Ivan Mårtensson Blomberg Oliver Riem Vis
My Contribution
Animation Controller
While TGE provides a way to render sprites it’s does not have a built in support for 2D animation using sprite sheets. I took it upon myself to create an animation controller choose what animation to play and play it.
Sprite Sheet First off I needed a way to render part of a texture. Luckily TGE provides the possibility to render part of a texture as a sprite using Texture Rext which are just min and max UV coordinates. I made a Sprite sheet class that holds a texture with the sprite sheet and a list of Texture Rext one for each frame of the animation. The sprite sheet is used to get the correct Texture Rext by sending in what frame of the animation I want to render and using it to render part of the sprite sheet.
Controller The controller is initialized using a JSON file that hold all the data needed for an animated object. The controller contains a list of animations.
structAnimation
{
std::stringmyName;
SpriteSheetmySpriteSheet;
floatmyFPS;
boolmyLooping;
boolmyHasExitTime;
boolmyCanFlip;
boolmyHasPriority;
std::map<std::string,bool>myConditions;
std::vector<std::string>myConnections;
};
Each controller has a state machine that is controlled by a set of conditions, each animation has a list of conditions that have to be true for it to be able to play. Each animation also contains a list of connections which are what animations can play after the animation. If any condition changes the controller checks if the current animation is still valid. If not it will go through the list of connections and check if any are valid and will swap the animation being played if necessary.
Player Animation
When we chose Abuse (1996) as our reference game we realized that we were going to have to split the player sprite into two parts. One for the upper body and one for the lower body. The upper body swaps between different sprite sheets depending on where the cursor is aiming each of which match the running animation of the legs and by matching the current frame for both upper and lower body we were able to make it relatively seamless to aim anywhere without the upper and lower body feeling disconnected. Except no.
Looking closely you can see that the hips move up and down, but because the upper body doesn’t follow they look disconnected. We were able to solve it quite simply, I made it so the upper body moves up and down using a sin wave with a matching frequency to match the two again.