Advance Animators
In this tutorial you will learn more about animators. if you haven't read the basic animators tutorial, be sure to read it first.
Before starting this tutorial be sure to setup an empty project with the Setup Tutorial.
Please note!!!
From ness-engine version 1.4.5 there is the improved Sprite Character Animator you can use to create smart sprites animations. there is no tutorial for it yet but check out the api in animator_sprite_character.h.
what are we going to get?
at the end of this tutorial we will have this code:
That's it. :)
Now that you know about animators, it's time to learn about text!
continue to next tutorial -->
#include <NessEngine.h>
int _tmain(int argc, _TCHAR* argv[])
{
Ness::init();
Ness::Renderer renderer("new project", Ness::Sizei(800,600));
// create the scene and the player sprite
Ness::ScenePtr scene = renderer.create_scene();
Ness::SpritePtr player = scene->create_sprite("ness-engine/resources/gfx/wizard.png");
player->set_source_from_sprite_sheet(Ness::Pointi(0, 0), Ness::Sizei(4, 4), true);
player->set_blend_mode(Ness::BLEND_MODE_BLEND);
player->set_scale(2.0f);
player->set_position(Ness::Point(100, 100));
player->set_anchor(Ness::Point::HALF);
const float PlayerSpeed = 150.0f;
// create player animator
const float PlayerAnimSpeed = 10.0f;
Ness::Animators::AnimatorSpritePtr playerAnim =
ness_make_ptr<Ness::Animators::AnimatorSprite>
(player, Ness::Sizei(4, 4), Ness::Sizei(0, 0), 4, PlayerAnimSpeed, Ness::Animators::SPRITE_ANIM_END_REPEAT);
renderer.register_animator(playerAnim);
// event handlers
Ness::Utils::EventsPoller EventsPoller;
Ness::Utils::ApplicationEvents app;
EventsPoller.add_handler(app);
Ness::Utils::Keyboard keyboard;
EventsPoller.add_handler(keyboard);
// main loop
while( !app.got_quit() )
{
EventsPoller.poll_events();
// move player up
if (keyboard.key_state(SDLK_UP) && player->get_position().y > 0)
{
player->set_position(player->get_position() - Ness::Point(0, renderer.time_factor() * PlayerSpeed));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 3))
playerAnim->reset(Ness::Pointi(0, 3), 4, PlayerAnimSpeed);
}
// move player down
else if (keyboard.key_state(SDLK_DOWN) && player->get_position().y < renderer.get_screen_size().y)
{
player->set_position(player->get_position() + Ness::Point(0, renderer.time_factor() * PlayerSpeed));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 0))
playerAnim->reset(Ness::Pointi(0, 0), 4, PlayerAnimSpeed);
}
// move player left
else if (keyboard.key_state(SDLK_LEFT) && player->get_position().x > 0)
{
player->set_position(player->get_position() - Ness::Point(renderer.time_factor() * PlayerSpeed, 0));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 1))
playerAnim->reset(Ness::Pointi(0, 1), 4, PlayerAnimSpeed);
}
// move player down
else if (keyboard.key_state(SDLK_RIGHT) && player->get_position().x < renderer.get_screen_size().x)
{
player->set_position(player->get_position() + Ness::Point(renderer.time_factor() * PlayerSpeed, 0));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 2))
playerAnim->reset(Ness::Pointi(0, 2), 4, PlayerAnimSpeed);
}
// not moving
else
{
playerAnim->pause_animation(true);
}
// render
renderer.start_frame();
scene->render();
renderer.end_frame();
}
}

const float PlayerAnimSpeed = 10.0f;
Ness::Animators::AnimatorSpritePtr playerAnim = ness_make_ptr<Ness::Animators::AnimatorSprite>
(player,
Ness::Sizei(4, 4),
Ness::Sizei(0, 0),
4,
PlayerAnimSpeed,
Ness::Animators::SPRITE_ANIM_END_REPEAT);
renderer.register_animator(playerAnim);
// create the scene and the player sprite
Ness::ScenePtr scene = renderer.create_scene();
Ness::SpritePtr player = scene->create_sprite("ness-engine/resources/gfx/wizard.png");
player->set_source_from_sprite_sheet(Ness::Pointi(0, 0), Ness::Sizei(4, 4), true);
player->set_blend_mode(Ness::BLEND_MODE_BLEND);
player->set_scale(2.0f);
player->set_position(Ness::Point(100, 100));
player->set_anchor(Ness::Point::HALF);
const float PlayerSpeed = 150.0f;

#include <NessEngine.h>
int _tmain(int argc, _TCHAR* argv[])
{
Ness::init();
Ness::Renderer renderer("new project", Ness::Sizei(800,600));
// create the scene and the player sprite
Ness::ScenePtr scene = renderer.create_scene();
Ness::SpritePtr player = scene->create_sprite("ness-engine/resources/gfx/wizard.png");
player->set_source_from_sprite_sheet(Ness::Pointi(0, 0), Ness::Sizei(4, 4), true);
player->set_blend_mode(Ness::BLEND_MODE_BLEND);
player->set_scale(2.0f);
player->set_position(Ness::Point(100, 100));
player->set_anchor(Ness::Point::HALF);
const float PlayerSpeed = 150.0f;
// create player animator
const float PlayerAnimSpeed = 10.0f;
Ness::Animators::AnimatorSpritePtr playerAnim = ness_make_ptr<Ness::Animators::AnimatorSprite>
(player, Ness::Sizei(4, 4), Ness::Sizei(0, 0), 4, PlayerAnimSpeed, Ness::Animators::SPRITE_ANIM_END_REPEAT);
renderer.register_animator(playerAnim);
// event handlers
Ness::Utils::EventsPoller EventsPoller;
Ness::Utils::ApplicationEvents app;
EventsPoller.add_handler(app);
Ness::Utils::Keyboard keyboard;
EventsPoller.add_handler(keyboard);
// main loop
while( !app.got_quit() )
{
EventsPoller.poll_events();
// render
renderer.start_frame();
scene->render();
renderer.end_frame();
}
}
// move player up
if (keyboard.key_state(SDLK_UP) && player->get_position().y > 0) {
player->set_position(player->get_position() - Ness::Point(0, renderer.time_factor() * PlayerSpeed));
}
// move player down
else if (keyboard.key_state(SDLK_DOWN) && player->get_position().y < renderer.get_screen_size().y) {
player->set_position(player->get_position() + Ness::Point(0, renderer.time_factor() * PlayerSpeed));
}
// move player left
else if (keyboard.key_state(SDLK_LEFT) && player->get_position().x > 0) {
player->set_position(player->get_position() - Ness::Point(renderer.time_factor() * PlayerSpeed, 0));
}
// move player down
else if (keyboard.key_state(SDLK_RIGHT) && player->get_position().x < renderer.get_screen_size().x) {
player->set_position(player->get_position() + Ness::Point(renderer.time_factor() * PlayerSpeed, 0));
}
// move player up
if (keyboard.key_state(SDLK_UP) && player->get_position().y > 0) {
player->set_position(player->get_position() - Ness::Point(0, renderer.time_factor() * PlayerSpeed));
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 3))
playerAnim->reset(Ness::Pointi(0, 3), 4, PlayerAnimSpeed);
}
// move player up
if (keyboard.key_state(SDLK_UP) && player->get_position().y > 0)
{
player->set_position(player->get_position() - Ness::Point(0, renderer.time_factor() * PlayerSpeed));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 3))
playerAnim->reset(Ness::Pointi(0, 3), 4, PlayerAnimSpeed);
}
// move player down
else if (keyboard.key_state(SDLK_DOWN) && player->get_position().y < renderer.get_screen_size().y)
{
player->set_position(player->get_position() + Ness::Point(0, renderer.time_factor() * PlayerSpeed));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 0))
playerAnim->reset(Ness::Pointi(0, 0), 4, PlayerAnimSpeed);
}
// move player left
else if (keyboard.key_state(SDLK_LEFT) && player->get_position().x > 0)
{
player->set_position(player->get_position() - Ness::Point(renderer.time_factor() * PlayerSpeed, 0));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 1))
playerAnim->reset(Ness::Pointi(0, 1), 4, PlayerAnimSpeed);
}
// move player down
else if (keyboard.key_state(SDLK_RIGHT) && player->get_position().x < renderer.get_screen_size().x)
{
player->set_position(player->get_position() + Ness::Point(renderer.time_factor() * PlayerSpeed, 0));
playerAnim->pause_animation(false);
if (playerAnim->get_starting_step_point() != Ness::Pointi(0, 2))
playerAnim->reset(Ness::Pointi(0, 2), 4, PlayerAnimSpeed);
}
else
{
playerAnim->pause_animation(true);
}
playerAnim->pause_animation(false);
class MyAnimator : public Ness::Animators::AnimatorAPI
{
public:
virtual void do_animation(Ness::Renderer* renderer)
{
// ANIMATOR CODE HERE
}
};
which will produce the following window:
when pressing the arrow keys the character will move around with neat walking animation. now let's break down this code.
Step 1: creating the player character
First we create the scene and the player character. you should already be familiar with this code:
Step 2: creating the animator
now it's time to create the sprite animator:
As you read in this tutorial, Animators are objects that animate entities and nodes.
Ness-Engine comes with a set of built-in animators you can use, or you can create your own animators. you can even create a self-animating sprite, by creating a class that inherit from both the AnimatorAPI and from the Sprite entity, and implement the animation function inside.
Let's go over the code we wrote above:
Ness::Animators::AnimatorSpritePtr: the namespace Ness::Animators contains everything related to animator objects and API. AnimatorSpritePtr is a pointer to one of the built-in animators, called AnimatorSprite.
AnimatorSprite: this animator is used to animate a sprite entity based on animation steps from a sprite sheet, like in this example:
ness_make_ptr<Ness::Animators::AnimatorSprite>: will create a SharedPtr of an AnimatorSprite entity. this is an alias for std::make_shared<>.
AnimatorSprite() is created with the following values:
player - the sprite we are animating
Ness::Sizei(4, 4) - how many total steps we got in the sprite sheet (4 rows, 4 columns)
Ness::Pointi(0, 0) - starting step (top left corner, facing down)
4 - how many steps in this animation: 4 steps (the entire walking down animation)
PlayerAnimSpeed - speed to run this animation (defined above)
Ness::Animators::SPRITE_ANIM_END_REPEAT - what to do when animation cycle ends (replay it)
renderer.register_animator(playerAnim): register the new animator to our renderer animators queue.
if you run the following code now:
you should see the player character playing the walking down animation in place, repeatedly.
Now it's time to add keyboard controls.
Step 3: creating keyboard controls
Let's add basic keyboard controls that will move the player character around. add the following code to the main loop:
nothing new here so we won't go over this code, but now you should be able to move your character around, while always playing the "walk down" animation.
Time to match the animation to the movement direction!
Step 4: matching animation to walking direction
Now it's time to match the animation direction to the walking direction. let's replace the code to walk up with this:
what we do here is this: whenever our character walks up, we ask the animator if the current starting step is at position (0, 3), which is the begining of the animation to walk up. if the answer is no, we reset the animation to that starting point.
Now lt's do the same for the rest of the directions:
if you run the code now, you will see the animation changes based on walking direction. but there is still one last thing to solve: the character keeps playing the walking animation even when not walking. let's fix this.
Step 5: pause animation when standing
we want to pause the animation when the player is not walking. so let's add an 'else' to the walking conditions, that whenever none of the arrow keys are down, the animation will be paused:
and whenever the player do walk, we disable the animation pause with this:
That's it. your code should now look like the code from the begining of the tutorial.
Creating your own animator
As mentioned before, you can create your own animators and register them to the renderer animators queue. to create your own custom animator, use the following syntax:
and if you want the animator to remove itself from the animators queue, you can simply call 'this->remove_from_animation_queue()' from inside do_animation().