C++ Game Engine Development – Part 40 – First Friend

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
This week we are going to add a friend for the player. We’ll refractor the player creation code to reuse as much of it as possible and we’ll also create a new collision layer for the character and setup collisions between the player and his new friend.

If you look in the resources folder you’ll find a new sprite sheet for the character called Skeleton.png.

Skeleton.png
Skeleton.png
The layout is the same as the player’s sprite sheet with the walking sprites on lines 9 to 12, melee attack on lines 13 to 16, and projectile attack on lines 17 to 20. Each line has the animation for a separate direction. Unlike the player, the skeleton has a dagger and no bow. Using the same layout will allow us to use the same code to initialise the animations for both characters.
Before we write the code to add the skeleton to the game, we’ll move player creation to it’s own function
SceneGame.hpp
class SceneGame : public Scene
{
	…
    
private:
    void CreatePlayer();

	…
};
Cut and paste all the code associated with player creation from the OnCreate function into the new CreatePlayer function.
SceneGame.cpp
void SceneGame::CreatePlayer()
{
    std::shared_ptr<Object> player = std::make_shared<Object>(&context);
    
    player->transform->SetPosition(100, 700);
    
    auto sprite = player->AddComponent<C_Sprite>();
    sprite->SetDrawLayer(DrawLayer::Entities);
    
    player->AddComponent<C_KeyboardMovement>();
    
    auto animation = player->AddComponent<C_Animation>();
    
    int playerTextureID = textureAllocator.Add(workingDir.Get() + "Player.png");
    
    const unsigned int frameWidth =  64;
    const unsigned int frameHeight = 64;
    
    const FacingDirection directions[4] = {FacingDirection::Up, FacingDirection::Left, FacingDirection::Down, FacingDirection::Right};
    
    /*******************
     * Idle Animations *
     *******************/
    const bool idleAnimationLooped = false;
    
    unsigned int idleYFramePos = 512;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> idleAnimations;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> idleAnimation = std::make_shared<Animation>();
        
        idleAnimation->AddFrame(playerTextureID, 0, idleYFramePos, frameWidth, frameHeight, 0.f, idleAnimationLooped);
        
        idleAnimations.insert(std::make_pair(directions[i], idleAnimation));
        
        idleYFramePos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Idle, idleAnimations);
    
    
    /**********************
     * Walking Animations *
     **********************/
    const bool walkAnimationLooped = true;
    const int walkingFrameCount = 9;
    const float delayBetweenWalkingFramesSecs = 0.1f;
    
    unsigned int walkingYFramePos = 512;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> walkingAnimations;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> walkingAnimation = std::make_shared<Animation>();
        for (int i = 0; i < walkingFrameCount; i++)
        {
            walkingAnimation->AddFrame(playerTextureID, i * frameWidth, walkingYFramePos, frameWidth, frameHeight, delayBetweenWalkingFramesSecs, walkAnimationLooped);
        }
        
        walkingAnimations.insert(std::make_pair(directions[i], walkingAnimation));
        
        walkingYFramePos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Walk, walkingAnimations);
    
    
    /*************************
     * Projectile Animations *
     *************************/
    const bool projectileAnimationLooped = true;
    const int projectileFrameCount = 10;
    const float delayBetweenProjectileFramesSecs = 0.08f;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> projectileAnimations;
    
    unsigned int projFrameYPos = 1024;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> projAnimation = std::make_shared<Animation>();
        for (int i = 0; i < projectileFrameCount; i++)
        {
            projAnimation->AddFrame(playerTextureID, i * frameWidth, projFrameYPos, frameWidth, frameHeight, delayBetweenProjectileFramesSecs, projectileAnimationLooped);
        }
        projectileAnimations.insert(std::make_pair(directions[i], projAnimation));
        
        projFrameYPos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Projectile, projectileAnimations);
    
    auto collider = player->AddComponent<C_BoxCollider>();
    collider->SetSize(frameWidth * 0.4f, frameHeight * 0.5f);
    collider->SetOffset(0.f, 14.f);
    collider->SetLayer(CollisionLayer::Player);
    
    player->AddComponent<C_Camera>();
    player->AddComponent<C_ProjectileAttack>();
    player->AddComponent<C_Velocity>();
    player->AddComponent<C_MovementAnimation>();
    player->AddComponent<C_Direction>();
    
    objects.Add(player);
}
Call the function in OnCreate. So with the player creation code removed, OnCreate now looks like this:
SceneGame.cpp
void SceneGame::OnCreate()
{
    context.input = &input;
    context.objects = &objects;
    context.workingDir = &workingDir;
    context.textureAllocator = &textureAllocator;
    context.window = &window;
    
	// Call our new create player function.
    CreatePlayer();
    
    sf::Vector2i mapOffset(0, 180);
    std::vector<std::shared_ptr<Object>> levelTiles = mapParser.Parse(workingDir.Get() + "House Exterior.tmx", mapOffset);
    
    objects.Add(levelTiles);
}
Add a new function to create the skeleton.
SceneGame.cpp
class SceneGame : public Scene
{
	…
    
private:
    void CreateFriend();
	
	…
};
This will contain all of the code we use to instantiate the new character. Writing a separate function for each character is not sustainable for anything but the smallest game but it will do for now. We’ll look at different ways of doing this in future as we scale the game.
SceneGame.cpp
void SceneGame::CreateFriend()
{
    std::shared_ptr<Object> npc = std::make_shared<Object>(&context);
    
    npc->transform->SetPosition(160, 700);
    
    auto sprite = npc->AddComponent<C_Sprite>();
    sprite->SetDrawLayer(DrawLayer::Entities);
    
    auto animation = npc->AddComponent<C_Animation>();
    
    const int textureID = textureAllocator.Add(workingDir.Get() + "Skeleton.png");
    
    const unsigned int frameWidth =  64;
    const unsigned int frameHeight = 64;
    
    const FacingDirection directions[4] = {FacingDirection::Up, FacingDirection::Left, FacingDirection::Down, FacingDirection::Right};
    
    /*******************
     * Idle Animations *
     *******************/
    const bool idleAnimationLooped = false;
    
    unsigned int idleYFramePos = 512;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> idleAnimations;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> idleAnimation = std::make_shared<Animation>();
        
        idleAnimation->AddFrame(textureID, 0, idleYFramePos, frameWidth, frameHeight, 0.f, idleAnimationLooped);
        
        idleAnimations.insert(std::make_pair(directions[i], idleAnimation));
        
        idleYFramePos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Idle, idleAnimations);
    
    
    /**********************
     * Walking Animations *
     **********************/
    const bool walkAnimationLooped = true;
    const int walkingFrameCount = 9;
    const float delayBetweenWalkingFramesSecs = 0.1f;
    
    unsigned int walkingYFramePos = 512;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> walkingAnimations;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> walkingAnimation = std::make_shared<Animation>();
        for (int i = 0; i < walkingFrameCount; i++)
        {
            walkingAnimation->AddFrame(textureID, i * frameWidth, walkingYFramePos, frameWidth, frameHeight, delayBetweenWalkingFramesSecs, walkAnimationLooped);
        }
        
        walkingAnimations.insert(std::make_pair(directions[i], walkingAnimation));
        
        walkingYFramePos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Walk, walkingAnimations);
    
    
    /*************************
     * Projectile Animations *
     *************************/
    const bool projectileAnimationLooped = true;
    const int projectileFrameCount = 10;
    const float delayBetweenProjectileFramesSecs = 0.08f;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> projectileAnimations;
    
    unsigned int projFrameYPos = 1024;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> projAnimation = std::make_shared<Animation>();
        for (int i = 0; i < projectileFrameCount; i++)
        {
            projAnimation->AddFrame(textureID, i * frameWidth, projFrameYPos, frameWidth, frameHeight, delayBetweenProjectileFramesSecs, projectileAnimationLooped);
        }
        projectileAnimations.insert(std::make_pair(directions[i], projAnimation));
        
        projFrameYPos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Projectile, projectileAnimations);
    
    auto collider = npc->AddComponent<C_BoxCollider>();
    collider->SetSize(frameWidth * 0.4f, frameHeight * 0.5f);
    collider->SetOffset(0.f, 14.f);
    collider->SetLayer(CollisionLayer::Player);
    
    npc->AddComponent<C_Velocity>();
    npc->AddComponent<C_MovementAnimation>();
    npc->AddComponent<C_Direction>();
    
    objects.Add(npc);
}
You may notice that much of this code is similar to the code we use to create the player, we’ll deal with that shortly. For now, call the CreateFriend function in OnCreate straight after we call the CreatePlayer function.
SceneGame.cpp
void SceneGame::OnCreate()
{
    context.input = &input;
    context.objects = &objects;
    context.workingDir = &workingDir;
    context.textureAllocator = &textureAllocator;
    context.window = &window;
    
    CreatePlayer();
    CreateFriend(); // Create the friend directly after we create the player.

    //sf::Vector2i mapOffset(128, 128);
    //std::vector<std::shared_ptr<Object>> levelTiles = mapParser.Parse(workingDir.Get() + "House Exterior.tmx", mapOffset);
    //objects.Add(levelTiles);
}
I’ve also commented out the lines where we create the level so we can focus on the new player.
Player standing with his new friend.
Player standing with his new friend.
We don’t add as many components to the friend but it is very similar to that of CreatePlayer. In fact, the majority of each function is responsible for building animations and this is the same in both so we can refractor animation construction into its own function.
SceneGame.hpp
class SceneGame : public Scene
{
…
    
private:
    void AddAnimationComponent(std::shared_ptr<Object> object, const int textureID);

…
};
This function receives a shared pointer to the object and an id of the sprite sheet. Using this it will build the idle, walking, and projectile animations for the object.
SceneGame.cpp
void SceneGame::AddAnimationComponent(std::shared_ptr<Object> object, const int textureID)
{
    auto animation = object->AddComponent<C_Animation>();
    
    const unsigned int frameWidth =  64;
    const unsigned int frameHeight = 64;
    
    const FacingDirection directions[4] = {FacingDirection::Up, FacingDirection::Left, FacingDirection::Down, FacingDirection::Right};
    
    /*******************
     * Idle Animations *
     *******************/
    const bool idleAnimationLooped = false;
    
    unsigned int idleYFramePos = 512;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> idleAnimations;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> idleAnimation = std::make_shared<Animation>();
        
        idleAnimation->AddFrame(textureID, 0, idleYFramePos, frameWidth, frameHeight, 0.f, idleAnimationLooped);
        
        idleAnimations.insert(std::make_pair(directions[i], idleAnimation));
        
        idleYFramePos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Idle, idleAnimations);
    
    
    /**********************
     * Walking Animations *
     **********************/
    const bool walkAnimationLooped = true;
    const int walkingFrameCount = 9;
    const float delayBetweenWalkingFramesSecs = 0.1f;
    
    unsigned int walkingYFramePos = 512;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> walkingAnimations;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> walkingAnimation = std::make_shared<Animation>();
        for (int i = 0; i < walkingFrameCount; i++)
        {
            walkingAnimation->AddFrame(textureID, i * frameWidth, walkingYFramePos, frameWidth, frameHeight, delayBetweenWalkingFramesSecs, walkAnimationLooped);
        }
        
        walkingAnimations.insert(std::make_pair(directions[i], walkingAnimation));
        
        walkingYFramePos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Walk, walkingAnimations);
    
    
    /*************************
     * Projectile Animations *
     *************************/
    const bool projectileAnimationLooped = true;
    const int projectileFrameCount = 10;
    const float delayBetweenProjectileFramesSecs = 0.08f;
    
    std::unordered_map<FacingDirection, std::shared_ptr<Animation>, EnumClassHash> projectileAnimations;
    
    unsigned int projFrameYPos = 1024;
    
    for (int i = 0; i < 4; i++)
    {
        std::shared_ptr<Animation> projAnimation = std::make_shared<Animation>();
        for (int i = 0; i < projectileFrameCount; i++)
        {
            projAnimation->AddFrame(textureID, i * frameWidth, projFrameYPos, frameWidth, frameHeight, delayBetweenProjectileFramesSecs, projectileAnimationLooped);
        }
        projectileAnimations.insert(std::make_pair(directions[i], projAnimation));
        
        projFrameYPos += frameHeight;
    }
    
    animation->AddAnimation(AnimationState::Projectile, projectileAnimations);
}
Now we can remove the code that creates the animation component in CreatePlayer and CreateFriend and instead call AddAnimationComponent.
SceneGame.cpp
void SceneGame::CreatePlayer()
{
    std::shared_ptr<Object> player = std::make_shared<Object>(&context);
    
    player->transform->SetPosition(100, 700);
    
    auto sprite = player->AddComponent<C_Sprite>();
    sprite->SetDrawLayer(DrawLayer::Entities);
    
    player->AddComponent<C_KeyboardMovement>();
    
    int textureID = textureAllocator.Add(workingDir.Get() + "Player.png");
    
    AddAnimationComponent(player, textureID);
    
    auto collider = player->AddComponent<C_BoxCollider>();
    collider->SetSize(64 * 0.4f, 64 * 0.5f);
    collider->SetOffset(0.f, 14.f);
    collider->SetLayer(CollisionLayer::Player);
    
    player->AddComponent<C_Camera>();
    player->AddComponent<C_ProjectileAttack>();
    player->AddComponent<C_Velocity>();
    player->AddComponent<C_MovementAnimation>();
    player->AddComponent<C_Direction>();
    
    objects.Add(player);
}

void SceneGame::CreateFriend()
{
    std::shared_ptr<Object> npc = std::make_shared<Object>(&context);
    
    npc->transform->SetPosition(160, 700);
    
    auto sprite = npc->AddComponent<C_Sprite>();
    sprite->SetDrawLayer(DrawLayer::Entities);
    
    const int textureID = textureAllocator.Add(workingDir.Get() + "Skeleton.png");
    
    AddAnimationComponent(npc, textureID);
    
    auto collider = npc->AddComponent<C_BoxCollider>();
    collider->SetSize(64 * 0.4f, 64 * 0.5f);
    collider->SetOffset(0.f, 14.f);
    collider->SetLayer(CollisionLayer::Player);
    
    npc->AddComponent<C_Velocity>();
    npc->AddComponent<C_MovementAnimation>();
    npc->AddComponent<C_Direction>();
    
    objects.Add(npc);
}
This reduces the size of both functions considerably. We could continue to separate common code however as these functions are just temporary I don’t want to spend too much time refactoring.
Now if you run the game you may have noticed that the player can walk right through his friend. The friend has a box collider but its collision layer is currently set to ‘Player’. The player layer doesn’t collide with itself, so let’s create a new collision layer for our friend. First, add a new entry to the CollisionLayer enum for the layer.
C_Collider.hpp
enum class CollisionLayer
{
    Default = 1,    // bit 0
    Player = 2,     // bit 1
    Tile = 3,       // bit 2
    Projectile = 4,
    NPC = 5 // Add new entry for the skeleton
};
And then set the friends collider to that layer.
SceneGame.hpp
void SceneGame::CreateFriend()
{	
	…
    
	collider->SetLayer(CollisionLayer::NPC);
	
	…
}
When we create a new layer we need to define if it collides with other layers in S_Collidables constructor. At the moment we want the player layer to collide with the NPC layer.
SceneGame.hpp
S_Collidable::S_Collidable() : collisionTree(5, 5, 0, {0, 0, 4200, 1080}, nullptr)
{
	…
    
    Bitmask playerCollisions;
    playerCollisions.SetBit((int) CollisionLayer::Default);
    playerCollisions.SetBit((int) CollisionLayer::Tile);

	// Sets player layer to collide with npc layer
    playerCollisions.SetBit((int) CollisionLayer::NPC);

	…
}
Now the player layer should collide with the default, tile, and npc layers. We can test the collisions by running the game and walking into our skeleton friend.
Player colliding with friend.
Player colliding with his friend.
Over the next few weeks, we’ll work on creating interactions between the player and his new friend. We’ll write code for raycasting and displaying text on the screen so that our friend can say hi.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 39 – Removing Projectile on Collision

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.

Last week we wrote the code that will enable communication between objects when they collide. This communication can happen at three stages of a collision: when the collision occurs, every frame the collision is maintained, and when the collision ends. This week we are going to write a new component for our projectile that will remove it from the game when it collides with another object (so when collision first happens). It will be a simple component but will hopefully show you how to respond to collision events.
Create a new component called C_RemoveObjectOnCollisionEnter. It’s a wordy title but it explains exactly what this class will do.
C_RemoveObjectOnCollisionEnter.hpp
#ifndef C_RemoveObjectOnCollisionEnter_hpp
#define C_RemoveObjectOnCollisionEnter_hpp

#include "Component.hpp"
#include "C_Collidable.hpp"

class C_RemoveObjectOnCollisionEnter : public Component, public C_Collidable
{
public:
    C_RemoveObjectOnCollisionEnter(Object* owner);
    
    void OnCollisionEnter(std::shared_ptr<C_BoxCollider> other) override;
};

#endif /* C_RemoveObjectOnCollisionEnter_hpp */

The class inherits from Component but it also is derived from C_Collidable, which is the system we were working on last week. We override OnCollisionEnter as we’ll use that function to remove the projectile at the beginning of a collision.

For now, we’ll queue the object for removal as soon as it collides with another object. In future we could check the objects tag name so, for example, we only remove the object when it collides with other objects with the tag “Tile”. We will also want to use some form of object pooling but that’s a topic for a future tutorial.
C_RemoveObjectOnCollisionEnter.cpp
#include "C_RemoveObjectOnCollisionEnter.hpp"
#include "Object.hpp"

C_RemoveObjectOnCollisionEnter::C_RemoveObjectOnCollisionEnter(Object* owner) : Component(owner) {}

void C_RemoveObjectOnCollisionEnter::OnCollisionEnter(std::shared_ptr<C_BoxCollider> other)
{
	// Remove the projectile when it collides with any other object    
	owner->QueueForRemoval();
}
Attach the component to newly spawned projectiles in C_ProjectileAttack.
C_ProjectileAttack.hpp
#include "C_RemoveObjectOnCollisionEnter.hpp"
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
	…
    
    projectile->AddComponent<C_RemoveObjectOnCollisionEnter>();
    owner->context->objects->Add(projectile);
}
If you run the game and fire off a few projectiles you’ll quickly run into a problem. As soon as the projectiles collide with an object we get a “bad access” exception.
Bad Access Exception
Bad Access Exception.
You’ll receive this exception if you attempt to use a pointer that points to memory that has been deallocated. It’s not difficult to imagine what’s happening in our code. We’ve removed the projectile object from the game, and its memory has been automatically deallocated for us as we are using smart pointers; however something (in this case the collision system), still thinks it holds a valid pointer and is trying to call GetCollidable on the removed projectile.
Call Stack on Main Thread.
Call Stack on Main Thread.
To start debugging the issue we should start where the projectile is supposedly removed from the collision systems collection. We can check if the code to remove the projectile is being called by setting a breakpoint.
Setting a breakpoint for removing the projectile from the collision system.
Setting a breakpoint for removing the projectile from the collision system.
If we then set a breakpoint just after we clear the collision tree in the Update function or at the end of the ProcessRemovals function and inspect the contents of collidables you’ll notice something strange: it still contains the pointer to the projectile that we have supposedly removed.
Inspecting contents of collidables.
Inspecting contents of collidables.
If we look closely at the breakpoint navigator and step over the debug point we created (when we remove the drawable component) you’ll notice that the component is removed from the ‘layer’ (the name we gave each collidable layer as we iterate over them) however it is not removed from the collidables collection.
Projectile removed from layer but not collision collection.
Projectile removed from layer but not collision collection.
This is because as we iterate over the layers in collidables:

SceneGame.cpp
void SceneGame::OnCreate()
{
…
    //std::shared_ptr<Object> player = std::make_shared<Object>();
    std::shared_ptr<Object> player = std::make_shared<Object>(&context);

…
}

We are actually creating a copy of the layer and then removing the collidables from that copy. This is not the intended behaviour and luckily is easy to fix, all we need to do is retrieve a reference to the layer:
for (auto& layer : collidables) {…}
So the full function now looks like this:
S_Collidable.cpp
void S_Collidable::ProcessRemovals()
{
	//We access the layer by reference now.
    for (auto& layer : collidables)
    {
        auto itr = layer.second.begin();
        while (itr != layer.second.end())
        {
            if ((*itr)->owner->IsQueuedForRemoval())
            {
                itr = layer.second.erase(itr);
            }
            else
            {
                ++itr;
            }
        }
    }
}
And that fixes the bad access exception. Now objects are successfully removed from the collision system and we can fire projectiles as much as we want!
Projectiles are now removed on contact.
Now would be a good time to perform an audit on our other range-based for loops. As a guideline we want to use:
  • auto: when we want to make a copy of the original.
  • auto*: when we want to read and modify the original and be able to change what the pointer references.
  • const auto&: when we want to read and modify a property of the original but do not want to be able to change what the pointer references.
  • const shared_ptr&: when we want a read-only copy of the original.
It should also be noted that a pointer and the thing it points to are two separate objects. Either, none or both may be const and a const pointer simply means that you cannot change where the pointer points.
// Create pointer to const int
const int* constPointer = new int(1);
(*constPointer) = 5; // not allowed
constPointer = new int(2); // allowed
    
// Create const pointer to int
int* const constInt = new int(2);
(*constInt) = 5; // allowed
constInt = new int(2); // not allowed
    
// Create const pointer to const int
const int* const constPointerInt = new int(3);
(*constPointerInt) = 5; // not allowed
constPointerInt = new int(2); // not allowed
This doesn’t affect us much as we generally use references and smart pointers but its good to be aware of.
The first change we’ll make is to TilemapParser. As we loop over each layer in the map we are creating unnecessary copies.
TilemapParser.cpp
std::vector<std::shared_ptr<Object>> TileMapParser::Parse(const std::string& file, sf::Vector2i offset)
{
	…
    
    int tileSizeX = std::atoi(rootNode->first_attribute("tilewidth")->value());
    int tileSizeY = std::atoi(rootNode->first_attribute("tileheight")->value());
    
	// No longer need to retrieve map size as we don't currently use it. Remove these two lines:
    //int mapsizeX = std::atoi(rootNode->first_attribute("width")->value());
    //int mapsizeY = std::atoi(rootNode->first_attribute("height")->value());
    
    std::vector<std::shared_ptr<Object>> tileObjects;
    
    // We won’t have a negative layer count so make it unsigned:
    unsigned int layerCount = (unsigned int)map->size() - 1;
    
	// Retrieve layer as a reference.
    for (const auto& layer : *map)
    {
		// Retrieve tile as a reference.
        for (const auto& tile : layer.second->tiles) {...}
	}
	
	...
}
While we’re in this function I’ve also taken the liberty to make a couple of other changes: I’ve removed the references to map size as we didn’t use them and I’ve also changed the type of the layer count to an unsigned int as we don’t want/expect it to be a negative number.
The last change we make to the for loops (at least for now) is to change how we loop over the components in the Object class. We want to retrieve them all by const reference.
Object.cpp
void Object::Awake()
{
    for (const auto& component : components)
    {
        component->Awake();
    }
}

void Object::Start()
{
    for (const auto& component : components)
    {
        component->Start();
    }
}

void Object::Update(float timeDelta)
{
    for (const auto& component : components)
    {
        component->Update(timeDelta);
    }
}

void Object::LateUpdate(float timeDelta)
{
    for (const auto& component : components)
    {
        component->LateUpdate(timeDelta);
    }
}
There are a couple of small house tidying tasks I want to do before we finish for the week. First, in our collision system we add objects to the quadtree as they are added to a collision layer, however each frame in the collision systems Update function we remove all collidables from the tree and then re-add them (we do this to partition the objects based on their new position). So we don’t need to add objects individually to the tree.
void S_Collidable::Add(std::vector<std::shared_ptr<Object>>& objects)
{
    for (auto o : objects)
    {
        auto collider = o->GetComponent<C_BoxCollider>();
        if (collider)
        {
            CollisionLayer layer = collider->GetLayer();
            
            auto itr = collidables.find(layer);
            
            if (itr != collidables.end())
            {
                collidables[layer].push_back(collider);
            }
            else
            {
				// Refractored line below.
                collidables.insert(std::make_pair(layer,  std::vector<std::shared_ptr<C_BoxCollider>>{collider}));
            }
            
			// Remove line below.
           // collisionTree.Insert(collider);
        }
    }
}
I also refactored how we insert a new collidable pair. We no longer create a vector on a separate line but use an initialisation list.
In C_Projectile::SpawnProjectile we retrieve the current facing direction twice. So we’ll remove one of them:
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
    FacingDirection faceDir = direction->Get();
    
    std::shared_ptr<Object> projectile = std::make_shared<Object>(owner->context);
    
    projectile->transform->SetPosition(owner->transform->GetPosition() +
                                       offsetDirectionBindings.at(faceDir));
 
    // Remove line below.
    //FacingDirection currentDir = direction->Get();
    
 	…
}
And last change for the week, in TileMapParser::BuildLayer we retrieve the height value from the levels data file and don’t use it, so we’ll remove that line.
TilemapParser.cpp
std::pair<std::string, std::shared_ptr<Layer>> TileMapParser::BuildLayer(xml_node<>* layerNode, std::shared_ptr<TileSheets> tileSheets)
{
    TileSet tileSet;
    std::shared_ptr<Layer> layer = std::make_shared<Layer>();
    
    int width = std::atoi(layerNode->first_attribute("width")->value());
    
    // Remove line below.
    //int height = std::atoi(layerNode->first_attribute("height")->value());
    
	…
}
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 38 – Collision Communication

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
This week we are continuing where we left off by editing the collision system to call the OnCollisionEnter, OnCollisionStay, and OnCollisionExit functions that we wrote last week. This will lay the groundwork for many future features that require interaction between colliding objects.

First I want to make a quick change to the C_InstanceID class, whose job it is to provide a unique id number to every object. Currently we are storing the ids as int, however, I want to change this to unsigned int for reasons that will become apparent shortly.

C_InstanceID.hpp
#ifndef C_InstanceID_hpp
#define C_InstanceID_hpp

#include "Component.hpp"

class C_InstanceID : public Component
{
public:
    C_InstanceID(Object* owner);
    ~C_InstanceID();
    
    unsigned int Get() const;
    
private:
    static unsigned int count;
    unsigned int id;
};

#endif /* C_InstanceID_hpp */
C_InstanceID.cpp
#include "C_InstanceID.hpp"

unsigned int C_InstanceID::count = 0;

C_InstanceID::C_InstanceID(Object* owner) : Component(owner), id(count++){}

C_InstanceID::~C_InstanceID(){}

unsigned int C_InstanceID::Get() const
{
    return id;
}
Ok, with that done we’ll start work on the collision system. First, we need some way of determining when objects start, stop, and continue to collide. To accomplish this, I’ve decided to store colliding objects as a pair in an unordered_set. I’m using an unordered_set as it stores items uniquely, so it will prevent us from adding a pair that is already colliding and re-calling there collision functions. This way when we add a pair successfully we know they’ve started colliding so we can call OnCollisionStay; we can iterate over the pairs each frame and check if they’re still colliding, and if they are we call the OnCollisionStay function; and if they aren’t then we call OnCollisionExit.
The unordered_set stores elements in buckets based on their hash, so we need a quick hash function that can be used to determine if a colliding pair is unique. As an object will only have one C_BoxCollider, we can use the instance ID component of the owning object. I’ve chosen to use an altered version of Szudzik’s elegant pairing function.

“When x and y are non-negative integers, elegantPair(x, y) function outputs single non-negative integer that is uniquely associated with that pair.”

In this case x and y will be the instance ids of the first and second object in the pair. As we’ve changed the instances ids to use unsigned ints and they are unique to each object they’ll do nicely. I’ve changed the function slightly because currently it will provide a different hash depending on whether the object is first or second in the pair.
As an example, say we have two objects with the instance ids {120, 240}. We want to be able to insert them into the unordered_set as {120, 240} but then when we attempt to insert {240, 120} (because object 2 is also colliding with object 1) I want it to fail as the pair is already in the unordered_set.
It should be noted that this compares if two objects are unique as its based on the objects instance id. If in future we can add multiple colliders to a single object we’ll need some way to determine the uniqueness of a pair based on the components alone.
ComponentPairHash.hpp
#ifndef ComponentPairHash_hpp
#define ComponentPairHash_hpp

struct ComponentPairHash
{
    template <typename T>
    std::size_t operator()(T t) const
    {
        std::size_t x = t.first->owner->instanceID->Get();
        std::size_t y = t.second->owner->instanceID->Get();
        
		return (x >= y) ? (x * x + x + y) : (y * y + y + x);
    }
};

#endif /* ComponentPairHash_hpp */
With the hash function hashed out (sorry!), we can create the storage for the colliding objects in S_Collidable. As I said earlier this will be an unordered_set of collider pairs.
S_Collidable.hpp
#include "ComponentPairHash.hpp"

class S_Collidable
{
…
private:
…
    std::unordered_set<std::pair<std::shared_ptr<C_BoxCollider>, std::shared_ptr<C_BoxCollider>>, ComponentPairHash> objectsColliding;
    
};
Now when two objects collide we attempt to add them to this collection in the Resolve function.
S_Collidable.cpp
void S_Collidable::Resolve()
{
    for (auto maps = collidables.begin(); maps != collidables.end(); ++maps)
    {
		…
        
        for (auto collidable : maps->second)
        {
			…
            
            std::vector<std::shared_ptr<C_BoxCollider>> collisions = collisionTree.Search(collidable->GetCollidable());
            
            for (auto collision : collisions)
            {
				…
                    
                if(m.colliding)
				{
					auto collisionPair = objectsColliding.emplace(std::make_pair(collidable, collision));
                        
                   	if(collisionPair.second)
                 	{
               			collidable->owner->OnCollisionEnter(collision);
                     	collision->owner->OnCollisionEnter(collidable);
                  	}

    			}
            }
        }
    }
}
The call to emplace returns a pair and the second item in the pair is a bool indicating if the insertion was successful. If it returns true then we know that this is the first time they have collided and can call OnCollisionEnter.
With OnCollisionEnter sorted, create a new function called ProcessCollisingObjects. This will be responsible for calling OnCollisionStay and OnCollisionExit.
S_Collidable.hpp
class S_Collidable
{
…
    
private:
	// Remove below function definition.
    //void ProcessCollisions(std::vector<std::shared_ptr<Object>>& first, std::vector<std::shared_ptr<Object>>& second);
    
	// And add the definition for the new function:
	void ProcessCollidingObjects();
  …
};

I’ve also removed the definition for the ProcessCollisions function, which was something we added all the way back in week 18 and never used.

We iterate over the colliding objects and check if either of the objects has been queued for removal and if either of them has we then call OnCollisionExit on both objects and remove the pair from the set. If neither has been queued for removal then we need to check if they are both still colliding, if they aren’t we call OnCollisionExit and remove them from the set. Lastly, if neither of them has been queued for removal and they are still colliding then we call OnCollisionStay and keep them in the set to be tested next frame.
S_Collidable.cpp
void S_Collidable::ProcessCollidingObjects()
{
    auto itr = objectsColliding.begin();
    while (itr != objectsColliding.end())
    {
        auto pair = *itr;
        
        std::shared_ptr<C_BoxCollider> first = pair.first;
        std::shared_ptr<C_BoxCollider> second = pair.second;
        
        if(first->owner->IsQueuedForRemoval() || second->owner->IsQueuedForRemoval())
        {
            first->owner->OnCollisionExit(second);
            second->owner->OnCollisionExit(first);
            
            itr = objectsColliding.erase(itr);

        }
        else
        {
            Manifold m = first->Intersects(second);
            
            if (!m.colliding)
            {
                first->owner->OnCollisionExit(second);
                second->owner->OnCollisionExit(first);
                
                itr = objectsColliding.erase(itr);
            }
            else
            {
                first->owner->OnCollisionStay(second);
                second->owner->OnCollisionStay(first);
                
                ++itr;
            }
        }
    }
}
We’ll call this function in Update just before we update the quadtree.
S_Collidable.cpp
void S_Collidable::Update()
{
    collisionTree.DrawDebug();
    
    ProcessCollidingObjects();
    
    collisionTree.Clear();
    for (auto maps = collidables.begin(); maps != collidables.end(); ++maps)
    {
        for (auto collidable : maps->second)
        {
            collisionTree.Insert(collidable);
        }
    }
    
    Resolve();
}
Now if we’ve done it right we should have enabled communication between colliding objects! Next week we’ll write a component for our projectile that removes it from the game when it collides with the environment. Not super exciting but it will test our communication system.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 37 – Collidable Component

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
This week we will start work on a collision communication system. Currently, when two objects collide the only action they can take is to prevent the collision by moving apart, however what if we want more complicated behaviour? Such as causing damage to an enemy when the projectile collides with them or making the arrows disappear when they collide with a tile.

When we first discussed the collision system way back here, we summarised the expected functionality:

  1. ‘We need some way to let our engine know which objects we want to collide”. We accomplished this by creating the C_BoxCollider component, which we can attach to any object we want to collide.
  2. “The objects will be passed to a structure called a quadtree”. Yep done that as well.
  3. “Each frame our collision system will check if any objects with a collider component are colliding. If two are found to be colliding then the collision will need to be resolved so that they are no longer colliding”. Our collision system takes care of that.
  4. “We should have the ability to mark an object as static, which means that we will not resolve its collisions”. This has also been implemented. We use it to reduce the number of collision checks.
  5. “Every collidable will be on a specific layer. We will be able to set which layers collide”. We have our layer system setup. Another useful tool used to reduce the number of collision checks we need to perform.
  6. “When two objects collide we want to be able to inform any interested components”. This is yet to be completed and is what I want to work on this week.

So how are we going to accomplish this? As with any programming task, there are loads of ways we can go about it. However, I want to take advantage of our component system. First, we’ll create a new component that will be the base class for any component that wants to be notified of a collision change. This component will have three virtual functions: OnCollisionEnter, OnCollisionStay, and OnCollisionExit. The names of the functions may give you a clue on when the functions will be called: 

  • OnCollisionEnter will be called when two objects first collide.
  • OnCollisionStay will be called every frame that the objects stay colliding.
  • OnCollisionExit is called when two objects stop colliding by moving apart or when either of the objects has been queued for removal.

When a new component is added to an object, the object will check if the component derives from this base class and if it does it stores it in a separate collection. The object class will also have an OnCollisionEnter, Stay, and Exit functions. These will call the corresponding functions in any collidable components added to the object. We will call these functions on the objects rather than interacting with the components directly.

The enter, stay, and exit functions will accept a shared_ptr to a C_BoxCollider component. This is the collider component of the object that is being collided with. We can use this to do many things such as: check the object that collided with us is an arrow and if it is we can reduce its health, or from the arrows point of view we can check if the object we collided with is part of the environment and if it is the arrow can queue itself for removal. These are only two very arrow-specific examples but there are many different ways we can use the data as will become evident as the project progresses.
That’s a lot of theory but it will hopefully make sense as we write the code. This week we will create the base collidable component, add the data structure and method of storing collidable components in the Object class, and write the collision functions for the Object class. Next week we’ll write the code that calls these functions when two objects collide.
Start by creating a new class called C_Collidable. This will be the base component for any class that wants to be notified when a collision occurs.
C_Collidable.hpp
#ifndef C_Collidable_hpp
#define C_Collidable_hpp

#include <memory>

#include "C_BoxCollider.hpp"

class C_Collidable
{
public:
    virtual void OnCollisionEnter(std::shared_ptr<C_BoxCollider> other) {};
    virtual void OnCollisionStay(std::shared_ptr<C_BoxCollider> other) {};
    virtual void OnCollisionExit(std::shared_ptr<C_BoxCollider> other) {};
};

#endif /* C_Collidable_hpp */
The class has the three functions that I described earlier. This is a pure virtual class and will not be instantiated. As all the functions are virtual we do not need an implementation file.
Now we have this parent class we can modify our Object class to store these components in a separate collection. We do this so that when a collision does occur we can call the relevant components quickly.
Object.hpp
…
#include "C_Collidable.hpp"

class Object
{
public:
…
    
    template <typename T> std::shared_ptr<T> AddComponent()
    {
        static_assert(std::is_base_of<Component, T>::value, "T must derive from Component");
        
        // Check that we don't already have a component of this type.
        for (auto& exisitingComponent : components)
        {
            if (std::dynamic_pointer_cast<T>(exisitingComponent))
            {
                return std::dynamic_pointer_cast<T>(exisitingComponent);
            }
        }
        
        std::shared_ptr<T> newComponent = std::make_shared<T>(this);
        
        components.push_back(newComponent);
        
        if (std::dynamic_pointer_cast<C_Drawable>(newComponent))
        {
            drawable = std::dynamic_pointer_cast<C_Drawable>(newComponent);
        }
        
		// Check if the component inherits from C_Collidable. If it does store it in a separate vector.
        if (std::dynamic_pointer_cast<C_Collidable>(newComponent))
        {
            collidables.push_back(std::dynamic_pointer_cast<C_Collidable>(newComponent));
        }
        
        return newComponent;
    };
private:
…
    std::vector<std::shared_ptr<C_Collidable>> collidables;

};
Checking if a component inherits from C_Collidable is the same process as checking if it is a drawable component. If it does inherit from our collidable component, we add it to a new vector called collidables. Whenever there is a collision involving this object we will iterate over this vector and call the corresponding collision function. Let’s write the functions that will do that now.
Object.hpp
class Object
{
public:
	…
    
    void OnCollisionEnter(std::shared_ptr<C_BoxCollider> other);
    void OnCollisionStay(std::shared_ptr<C_BoxCollider> other);
    void OnCollisionExit(std::shared_ptr<C_BoxCollider> other);
    
	…
};
Object.hpp
void Object::OnCollisionEnter(std::shared_ptr<C_BoxCollider> other)
{
    for (const auto& component : collidables)
    {
        component->OnCollisionEnter(other);
    }
}

void Object::OnCollisionStay(std::shared_ptr<C_BoxCollider> other)
{
    for (const auto& component : collidables)
    {
        component->OnCollisionStay(other);
    }
}

void Object::OnCollisionExit(std::shared_ptr<C_BoxCollider> other)
{
    for (const auto& component : collidables)
    {
        component->OnCollisionExit(other);
    }
}
These functions will be called by our collision system, which we will modify next week. One last thing to do this week is to change how we store the layers and collidables in S_Collidable. Currently we are using a map, however, I want to swap to using unordered_maps as we don’t need them in any specific order.
S_Collidable.hpp
…
#include "EnumClassHash.hpp"

class S_Collidable
{
…
    
private:

    void 
…
    std::unordered_map<CollisionLayer, Bitmask, EnumClassHash> collisionLayers;
    std::unordered_map<CollisionLayer, std::vector<std::shared_ptr<C_BoxCollider>>, EnumClassHash> collidables;
    
…
};
That’s all for this week! Next week we’ll finish what we started here and edit the collision system to call the OnCollisionEnter, OnCollisionStay, and OnCollisionExit functions.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 36 – Spawning Projectiles as Animation Action

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.

We recently wrote a feature that enables us to call a function on a specific animation frame. This week we’ll take advantage of this system for our projectile attack. At the moment we are spawning projectiles as soon as we press the projectile attack key, which doesn’t look quite right. What we really want is for the projectile to spawn at the correct moment in the animation which is when the player’s character releases the bowstring.

Before we write the animation action I want to make one small change to C_Animation. As I was writing the code for this tutorial I noticed that I’ve used a map to store the animations. A map will store the entries in a specific order (sorted using caparison function ‘Compare’). As we don’t need the entries sorted we can swap to an unordered_map as they are generally faster for insertions and retrievals.

As unordered_maps store elements using a hash table we may need to provide a hash class as the third template parameter. Depending on the compiler you use this step may not be necessary. The easiest way to check if it is necessary for you is to change the definition in C_Animation from map to unordered_map like so:
C_Animation.hpp
class C_Animation : public Component
{
…
    
private:
    std::unordered_map<AnimationState, AnimationList> animations;
…
};
If you can compile without errors then you don’t need to follow the next steps and can jump directly to where we edit how we create the animations in SceneGame.
However, if you receive the compilation error: “Implicit instantiation of undefined template” then you need to provide your own hash for the enum class. We created one in a previous tutorial but it’s located in the C_ProjectileAttack header, as this class now requires it we’ll move it into its own file.
C_ProjectileAttack.hpp
#ifndef C_ProjectileAttack_hpp
#define C_ProjectileAttack_hpp

…

// Remove the EnumClassHash as we’ll be moving it to its own file.
/*
struct EnumClassHash
{
template 
std::size_t operator()(T t) const
{
return static_cast(t);
}
};
*/

…

#endif /* C_ProjectileAttack_hpp */
Create a new class called EnumClassHash and add the code we just removed from C_ProjectileAttack.
EnumClassHash.hpp
#ifndef EnumClassHash_hpp
#define EnumClassHash_hpp

struct EnumClassHash
{
template
std::size_t operator()(T t) const
{
return static_cast(t);
}
};

#endif /* EnumClassHash_hpp */

C_ProjectileAttack still needs access to this class so include our new file.

C_ProjectileAttack.hpp
#include “EnumClassHash.hpp"
…

Include the new file in C_Animation and then use it as the third parameter in the AnimationList (which I’ve also changed to use an unordered_map instead of a map) and the animations collection.

C_Animation.hpp
#include <unordered_map>
#include "EnumClassHash.hpp"

…

using AnimationList = std::unordered_map<FacingDirection, std::shared_ptr, EnumClassHash>;

class C_Animation : public Component
{
…

private:
std::unordered_map<AnimationState, AnimationList, EnumClassHash> animations;
…
};
As we’ve changed the definition of animations we also need to edit SceneGame where we actually create the animations. Change them to use an unordered_map and if necessary add the EnumClassHash as the third template parameter.
SceneGame.cpp
void SceneGame::OnCreate()
{
…

std::unordered_map<FacingDirection, std::shared_ptr, EnumClassHash> idleAnimations;
…

std::unordered_map<FacingDirection, std::shared_ptr, EnumClassHash> walkingAnimations;
…

std::unordered_map<FacingDirection, std::shared_ptr, EnumClassHash> projectileAnimations;
…
}

With that done we can start on the goal for this week: spawning the projectiles as an animation action. The Animation class already has the functionality for adding frame actions but we don’t interact directly with it, instead we’ll write a function in the animation component that will. This way any component can retrieve the animation component and add an action.

C_Animation.hpp
class C_Animation : public Component
{
public:
…

void AddAnimationAction(AnimationState state, FacingDirection dir, int frame, AnimationAction action);

…
};
To set an animation action we need to know the animation state (Idle, Walk, or Projectile), direction (Up, Down, Left, or Right), and the frame that we want the animation to run on. The frame is an integer between 0 and one less than the animations frame count. For example, the players projectile animation has 10 animation frames so we would expect the frame integer to be between 0 and 9 as the frame count starts at 0. The last argument is the action to be run on that frame.
The definition of the function looks like this:
C_Animation.hpp
void C_Animation::AddAnimationAction(AnimationState state, FacingDirection dir, int frame, AnimationAction action)
{
	auto animationList = animations.find(state);

	if(animationList != animations.end())
	{
		auto animation = animationList->second.find(dir);

		if(animation != animationList->second.end())
		{
			animation->second->AddFrameAction(frame, action);
		}
	}
}
We first attempt to find the state and then the direction for that state before we can call the AddFrameAction for that specific animation. If the entity does not have an animation for that state or direction then no action is added.
That should be it for setting up animation actions, we’ll test it by adding four actions to shoot a projectile in the four directions. Do this in the Start function of C_ProjectileAttack.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Start()
{
…
	animation->AddAnimationAction(
	AnimationState::Projectile, // 1
	FacingDirection::Up, // 2
	9, // 3
	std::bind(&C_ProjectileAttack::SpawnProjectile, this)); // 4

	animation->AddAnimationAction(AnimationState::Projectile, FacingDirection::Left, 9, std::bind(&C_ProjectileAttack::SpawnProjectile, this));
	animation->AddAnimationAction(AnimationState::Projectile, FacingDirection::Down, 9, std::bind(&C_ProjectileAttack::SpawnProjectile, this));
	animation->AddAnimationAction(AnimationState::Projectile, FacingDirection::Right, 9, std::bind(&C_ProjectileAttack::SpawnProjectile, this));
}

I’ve numbered the first call to the AddAnimationAction so we can discuss it in more detail.

  1. We want to add an action to the projectile animation for this entity so we pass that as the first argument.
  2. In this instance, we are adding an action for the Up direction (we add actions for the other 3 directions directly after).
  3. The animation has 10 frames and we want to want the action to be run on the last frame so, and remembering that the count starts at 0, we pass 9 in as the third parameter.
  4. The fourth argument is the AnimationAction. It’s the function we want to run on the specified frame. If we look at the definition of AnimationAction:
using AnimationAction = std::function<void(void)>;
We can see that it is a type alias for std::function. std::function is a function wrapper, it can store and invoke any callable object. In this case, the callable object is the function we want to be called on a specific frame. We use std::bind to create a callable object from the function. We pass the function as the first parameter and a list of the callable object’s parameters. A function that is non-static has an implicit first parameter, which is the object that owns the function (C_ProjectileAttack in this case), so we need to pass a reference to C_ProjectileAttack as the second parameter.
Because we spawn the projectiles during the animation we can remove the direct call to SpawnProjectile in the Update function of C_ProjectileAttack.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Update(float deltaTime)
{
	if(owner->context->input->IsKeyDown(Input::Key::E))
	{
		// We call SpawnProjectile during the animation
		// so no longer need to call it directly.
		//SpawnProjectile();
		animation->SetAnimationState(AnimationState::Projectile);
	}
	else if (owner->context->input->IsKeyUp(Input::Key::E))
	{
		animation->SetAnimationState(AnimationState::Idle);
	}
}
If you run the game and shoot an arrow using the ‘e’ key, a projectile should now be spawned at the end of the animation rather than the beginning. Make sure you test it in all four directions.
Shooting arrow in 4 directions using animation actions.
Arrows spawned on last frame of animation using action frames.
You may have noticed that the arrows disappear when they collide with the fence in the gif above. This is something we’ll add over the next few weeks as we improve and add to our collision system by implementing a way for objects to communicate when they collide.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 35 – Projectile Velocity and Collisions

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.

This week we’ll continue working on our projectile system. By the end of this tutorial we will have:

  1. Changed the arrows initial position so that it aligns with the player’s bow.
  2. Made the projectiles move through the environment.
  3. Made the projectiles collide with the environment.
It’s a lot to do so we’ll start straight away by implementing the first change: the relative position of the arrow. Depending on the direction we are facing we want the arrow to be spawned in different locations relative to the player’s bow; we want the arrow to always line up with the centre of the bow. There are a few ways of accomplishing this but one of the more straightforward is to add an offset to the arrow based on the player’s direction. This will use a similar solution to last week where we implemented an unordered_map to store the sprites texture rect based on the player’s direction. This time we’ll store the arrows offset as an sf::Vector2f with the FacingDirection once again as the key. Add the unordered_map, which I’ve called offsetDirectionBindings, to C_ProjectileAttack.
C_ProjectileAttack.hpp
class C_ProjectileAttack : public Component
{
…
    
private:
    static std::unordered_map<FacingDirection, sf::Vector2f, EnumClassHash> offsetDirectionBindings;
    
…
};

Similarly to the textureDirectionBindings I’ve made this map static for the same reasons I outlined in the previous tutorial: it reduces memory usage.

If you don’t recognise the EnumClassHash then you probably don’t need it to compile, see the previous tutorial  where I created and discussed the need for the hash class. If you didn’t need it last week then you don’t need it now and you can just remove it so the declaration looks like this:

C_ProjectileAttack.hpp
class C_ProjectileAttack : public Component
{
…
    
private:
    static std::unordered_map<FacingDirection, sf::Vector2f> offsetDirectionBindings;
    
…
};
You need to initialise the unordered_map in the source file. Remember to include/exclude the EnumClassHash as required.
C_ProjectileAttack.cpp
std::unordered_map<FacingDirection, sf::Vector2f, EnumClassHash> C_ProjectileAttack::offsetDirectionBindings = {};

We’ll insert the four offsets in the Start function. I’ve chosen the following offsets:

  • Up = 0, 0
  • Left = -8, 3
  • Down = -3, 15
  • Right = 8, 3
They are only rough estimates and can always be adjusted later.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Start()
{
…
    
    offsetDirectionBindings.emplace(FacingDirection::Up, 
									sf::Vector2f());
    offsetDirectionBindings.emplace(FacingDirection::Left, 
									sf::Vector2f(-8.f, 3.f));
    offsetDirectionBindings.emplace(FacingDirection::Down, 
									sf::Vector2f(-3.f, 15.f));
    offsetDirectionBindings.emplace(FacingDirection::Right, 
									sf::Vector2f(8.f, 3.f));
}
With the offsetDirectionBindings populated, add the offset directly after we spawn the projectile in the SpawnProjectile function.
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
    FacingDirection faceDir = direction->Get();
    
    std::shared_ptr<Object> projectile = std::make_shared<Object>(owner->context);
    
	// We add an offset depending on the direction the player is facing.
    projectile->transform->SetPosition(owner->transform->GetPosition() +
                                       offsetDirectionBindings.at(faceDir));
    
    auto projSprite = projectile->AddComponent<C_Sprite>();
    projSprite->Load(projectileTextureID);
    projSprite->SetDrawLayer(DrawLayer::Entities);
    projSprite->SetSortOrder(100);
    projSprite->SetTextureRect(textureDirectionBindings.at(faceDir));
 
    owner->context->objects->Add(projectile);
}
We set the projectiles position to equal that of the player’s position plus an offset that we retrieve from our unordered_map. If you run the game now you will notice that the projectiles spawn in different positions based on the player’s direction.
Now the arrows are being spawned in the correct(ish) position let’s move on to point number two: getting the projectiles moving. We’ll accomplish this by adding a previously written C_Velocity component (link) to the projectile object and providing the arrow with a velocity. However, the arrow’s velocity depends on the direction the player is facing. So once again we need a way of retrieving a value (the velocity) using the player’s direction. So as we have just done with the offset we will create an unordered_map called velocityDirectionBindings (last one I promise).
C_ProjectileAttack.hpp
class C_ProjectileAttack : public Component
{
…
    
private:
…
	static std::unordered_map<FacingDirection, sf::Vector2f, EnumClassHash> velocityDirectionBindings;
	float projectileVelocity;
};
It follows the same conventions as the last one we created: its static and you can remove the EnumClassHash if you are using a compiler that natively supports enums classes as a key. I’ve also added a projectileVelocity that will store the speed of the arrow regardless of the direction.
C_ProjectileAttack.cpp
std::unordered_map<FacingDirection, sf::Vector2f, EnumClassHash> 
C_ProjectileAttack::velocityDirectionBindings = {};

C_ProjectileAttack::C_ProjectileAttack(Object* owner) : 
Component(owner), 
projectileVelocity(400.f) // velocity defaults to 400
{}

We’ll populate the map in the Start function (again). Each direction will be stored along with the normalised velocity for that direction:

  • Up = 0, -1
  • Left = -1, 0
  • Down = 0, 1
  • Right = 1, 0
It will become clear how we will use these values shortly but for now add them to the map in the Start function.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Start()
{
…

velocityDirectionBindings.emplace(FacingDirection::Up, 
								  sf::Vector2f(0.f, -1.f));
    velocityDirectionBindings.emplace(FacingDirection::Left, 
									  sf::Vector2f(-1.f, 0.f));
    velocityDirectionBindings.emplace(FacingDirection::Down, 
									  sf::Vector2f(0.f, 1.f));
    velocityDirectionBindings.emplace(FacingDirection::Right, 
									  sf::Vector2f(1.f, 0.f));
}
Add the C_velocity component to the projectile and set it’s velocity directly before the projectile is added to the object collection.
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
    FacingDirection faceDir = direction->Get();
    
…
    
    auto velocity = projectile->AddComponent<C_Velocity>();
    velocity->Set(velocityDirectionBindings.at(faceDir) * projectileVelocity);
 
    owner->context->objects->Add(projectile);
}
By multiplying the normalised velocity retrieved from the unordered_map by the desired velocity our projectiles should go flying in the correct direction! You can test this for yourself by running the game and firing projectiles in different directions.
Projectile with Velocity!
Projectiles with velocity!

You’ll notice that the arrows do not collide with the environment and will fly off into the abyss. To fix this we’ll move onto task number three: enabling the projectiles to collide with the environment. Luckily we have already written a collision system that we can use by adding a C_BoxCollider component to each new arrow we spawn.

Before we add the component we need to add a new collision layer for the projectiles. Collision layers are used to control which objects collide. For example, we can add an object to the collision layer of ‘Projectile’ and set that to collide with another collision layer called ‘Tile’. Using layers we can minimise the number of collision checks we perform. For more information check out the previous tutorial where we wrote the layer system. To add a new collision layer we need to first add a new entry to the CollisionLayer enum in C_Collider.hpp

C_Collider.hpp
enum class CollisionLayer
{
    Default = 1,    // bit 0
    Player = 2,     // bit 1
    Tile = 3,       // bit 2
    Projectile = 4	// New collision layer
};

We assign the Projectile layer the number 4. For each layer, we create a bitmask (link) that sets bits at specific locations to determine whether a layer collides with another. For example, if we assign a layer a bitmask with the bits at position 1 and 3 set then we know that layer collides with the ‘Default’ and ‘Tile’ layer. For more information on why we assign the entries in the enum specific numbers have a look at the tutorial where we created the Collision system. We initialise the layer with it’s associated bitmask in the constructor of S_Collidable.

S_Collidable.cpp
S_Collidable::S_Collidable() : collisionTree(5, 5, 0, {0, 0, 4200, 1080}, nullptr)
{
…
    
    Bitmask projectileCollisions;
	// Projectiles will only collide with tiles for now.
    projectileCollisions.SetBit((int) CollisionLayer::Tile);
	collisionLayers.insert(std::make_pair(CollisionLayer::Projectile, projectileCollisions));
}
Setting the bit at the tile layer informs the collision system that the projectile layer should collide with the tile layer.
Now that the projectile layer has been initialised return to the C_ProjectileAttack component and add a collider to any projectiles spawned in SpawnProjectile.
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
…

    auto collider = projectile->AddComponent<C_BoxCollider>();
    collider->SetSize(32, 32);
    collider->SetLayer(CollisionLayer::Projectile);
 
    owner->context->objects->Add(projectile);
}

After the C_BoxCollider is added to the projectile, the colliders size is set to 32 * 32 pixels, which is larger than the arrow image but will help show the collision using the debug drawing system. As the name suggests a C_BoxCollider uses a box to check for collisions. We’re using a box collider for the arrow because it is relatively quick to check a collision between two squares (i.e. a tile and the box around the projectile).

If you run the game now, you can shoot arrows and they will collide with tiles that also have a collider component attached.
Arrows colliding with the environment
Projectiles colliding with the environment
That’s it for this week, we achieved a lot: we now spawn the arrows in a position relative to the player’s direction, the arrows now move through the environment (useful if we are going to hit anything), and talking about hitting things, the arrows now collide with the environment! Granted they stick around when they should probably disappear but that’s something we’ll change soon (not next week but the week after). Next week we’ll change when we spawn the projectiles. Currently, they are spawned as soon as the projectile attack button is pressed, but they should be spawned at the correct moment in the animation so that when the player releases the arrow during its animation, a projectile is spawned and released.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 34 – Direction Component

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
A couple of weeks ago we started work on spawning projectile and ended up with something like this:
The texture we use contains four sprites for each direction.
Whenever we spawn an arrow we draw the sprite for all four directions as we have no meaningful method of knowing which direction the player is facing. As the title of this tutorial suggests that is all about to change. This week we are going to create a component that we can add to any object with a velocity (i.e. any object that can move), which will calculate the direction the object is facing. We’ll call this component C_Direction.
C_Direction.hpp
#ifndef C_Direction_hpp
#define C_Direction_hpp

#include "Component.hpp"
#include "Animation.hpp"
#include "C_Velocity.hpp"

class C_Direction : public Component
{
public:
    C_Direction(Object* owner);
    
    void Awake() override;
    
    FacingDirection Get();
    
private:
    std::shared_ptr<C_Velocity> velocity;
    FacingDirection currentDir;
};

#endif /* C_Direction_hpp */

The component stores a reference to the objects velocity component, we’ll use this to calculate the movement direction. It also stores the current FacingDirection in a variable called currentDir, which, unsurprisingly, stores the entities current direction. We do this because if the entity is not moving that frame and we cannot calculate its direction based on its velocity, we can pass this as its last known direction (this will become more clear shortly).

The FacingDirection enum was created in a previous tutorial and is in the Animation header. I’ve included it below for reference.
enum class FacingDirection
{
    None,
    Left,
    Right,
    Up,
    Down
};
The constructor will initialise the default value for the currentDir. We set it to ‘FacingDirection::Down’ as that is the direction the player’s sprites are facing initially. This means that even if the player hasn’t moved but attempts to shoot a projectile the projectile is facing the correct way.

The component has an Awake function that we won’t be calling directly (for more information on for the Awake and Start functions work see here) but will be used to retrieve the velocity component from the object. So the only public function that we will call is ‘Get’ which returns a FacingDirection.

C_Direction.cpp
#include "C_Direction.hpp"
#include "Object.hpp"

C_Direction::C_Direction(Object* owner) : 
Component(owner), currentDir(FacingDirection::Down) { }

void C_Direction::Awake()
{
    velocity = owner->GetComponent<C_Velocity>();
}
For the Get function, we’re going to use very similar code to that found in C_MovementAnimation. We’ll retrieve the entities current velocity, determine which axis has the highest velocity, and then on the axis with the highest velocity we’ll check if its a positive or negative force; this will let us know which way the entity is currently moving. If the velocity is 0 on both axis then we return the currentDir, which will either be the default of ‘Down’ or will hold the previously calculated moment direction.
C_Direction.cpp
FacingDirection C_Direction::Get() 
{
    const sf::Vector2f& currentVel = velocity->Get();
    
    if(currentVel.x != 0.f || currentVel.y != 0.f)
    {
        float velXAbs = fabs(currentVel.x);
        float velYAbs = fabs(currentVel.y);
        
        if(velXAbs > velYAbs)
        {
            if(currentVel.x < 0)
            {
                currentDir = FacingDirection::Left;
            }
            else
            {
                currentDir = FacingDirection::Right;
            }
        }
        else
        {
            if(currentVel.y < 0)
            {
                currentDir = FacingDirection::Up;
            }
            else
            {
                currentDir = FacingDirection::Down;
            }
        }
    }
    
    return currentDir;
}
Now that we determine the entities movement direction in this new component we can remove the direction calculations from C_MovementAnimation::Update. The complete function now looks like this:
C_MovementAnimation.cpp
void C_MovementAnimation::Update(float deltaTime)
{
    if(animation->GetAnimationState() != AnimationState::Projectile)
    {
        const sf::Vector2f& currentVel = velocity->Get();
        
        if(currentVel.x != 0.f || currentVel.y != 0.f)
        {
            animation->SetAnimationState(AnimationState::Walk);
        }
        else
        {
            animation->SetAnimationState(AnimationState::Idle);
        }
    }
}
The movement animation component is now only concerned with setting the current animation based on the entities velocity.
With our new component complete we need to add it to our player object. As usual, we’ll do this in SceneGame::OnCreate.
SceneGame.hpp
#include “C_Direction.hpp"
…
SceneGame.cpp
void SceneGame::OnCreate()
{
…
    player->AddComponent<C_Direction>();
    
    objects.Add(player);
…
}
As we’ve removed the code that sets the direction for the player’s animation, if you were to run the game now, you will see that while the player does alternate between walking, idle, and shooting animations he is always facing the same direction. We need to find a new home for setting the animations direction. For now, we’ll set it directly in the animation component.
C_Animation.hpp
#include "C_Direction.hpp"

…

class C_Animation : public Component
{
…
private:
…
    std::shared_ptr<C_Direction> direction;
};
The animation component needs access to the direction component so it will store a reference and retrieve the component in the Awake function.
C_Animation.cpp
void C_Animation::Awake()
{
	…
    direction = owner->GetComponent<C_Direction>();
}
Set the animation direction in the Update function before we check for a new frame.
C_Animation.cpp
void C_Animation::Update(float deltaTime)
{
	// Set the animation direction before we update the animation frame.
    SetAnimationDirection(direction->Get());
    
    if(currentAnimation.first != AnimationState::None)
    {
        bool newFrame = currentAnimation.second->UpdateFrame(deltaTime);
        
        if(newFrame)
        {
            const FrameData* data = currentAnimation.second->GetCurrentFrame();
            sprite->Load(data->id);
            sprite->SetTextureRect(data->x, data->y, data->width, data->height);
        }
    }
}
Now when you run the game you will notice that once again the player’s sprite is updated based on the direction they are moving.
Back to the reason for the direction component: the projectile sprite. If you remember we had no way of selecting which sprite to use for the arrow.
The current sprite sheet we are using for the arrow consists of four images showing the arrow rotated to face different directions.
The sprite sheet for our projectiles contains the arrow for all four directions.
Now we have a method of retrieving our current direction we can use that to set which sprite in the sprite sheet we use. C_ProjectileAttack (our projectile attack component) needs to store a reference to the direction component.
C_ProjectileAttack.hpp
class C_ProjectileAttack : public Component
{
…
    
private:
…
    std::shared_ptr<C_Direction> direction;
};
As is typical we will retrieve the pointer to the component in the Awake function.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Awake()
{
	…
    direction = owner->GetComponent<C_Direction>();
}
Now we have the direction component we can retrieve the entities current moving direction when we spawn a projectile in the SpawnProjectile function.
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
    std::shared_ptr<Object> projectile = std::make_shared<Object>(owner->context);
    
    projectile->transform->SetPosition(owner->transform->GetPosition());
 	
	// Get the current facing direction.
    FacingDirection currentDir = direction->Get();
    
    auto projSprite = projectile->AddComponent<C_Sprite>();
    projSprite->Load(projectileTextureID);
    projSprite->SetDrawLayer(DrawLayer::Entities);
    projSprite->SetSortOrder(100);
 
    owner->context->objects->Add(projectile);
}
However, we still have no way of converting the direction to a texture rect for the arrow sprite. To do this we’ll create an unordered_map with the FacingDirection as the key and an IntRect representing the texture rect as the value. Add the unordered_map called textureDirectionBindings to C_ProjectileAttack.
C_ProjectileAttack.hpp
class C_ProjectileAttack : public Component
{
…
    
private:
     static std::unordered_map<FacingDirection, sf::IntRect> textureDirectionBindings;

…
};
C_ProjectileAttack.cpp
std::unordered_map<FacingDirection, sf::IntRect> C_ProjectileAttack::textureDirectionBindings = {};
I’ve made the map static because all the projectile attacks will use the same texture (as we only have the one texture at the moment), which means they will also use the same texture rects. As a result, there is no need to maintain separate maps for each projectile attack component. For example, if we have 100 entities that can shoot projectiles in a scene and they all had to maintain a separate map, with each maps size being 40 bytes, we are using 40 * 100 = 4000 bytes in memory. Not a huge memory footprint on today’s computers but it’s nice to be able to save memory when we can.
By the way, you can calculate the size of an object using the sizeof function.
size_t tBindings = sizeof(textureDirectionBindings); 
You may have trouble compiling your code now depending on which compiler you use. If you receive a compilation error saying something like: “Implicit instantiation of undefined template”, then you need to provide a hash as the third template parameter (with the enum being the first and sf::IntRect the second).
We’ll create the hash in the C_ProjectileAttack class for now. If we find we need it for anything else we’ll move it to its own separate class. Again if you can compile fine then you do not need to create this hash and you can jump further on to where we initialise the unordered_map in the Start function.
C_ProjectileAttack.hpp
struct EnumClassHash
{
    template <typename T>
    std::size_t operator()(T t) const
    {
        return static_cast<std::size_t>(t);
    }
};

The hash object is a functor object, which is an object that defines the () operator and can be treated as if they are a function. If we use them in the core code for the game I’ll go through it in more detail.

With the hash created we should set it as the third template parameter in our map.
C_ProjectileAttack.hpp
static std::unordered_map<FacingDirection, sf::IntRect, EnumClassHash> textureDirectionBindings;
C_ProjectileAttack.cpp
std::unordered_map<FacingDirection, sf::IntRect, EnumClassHash> C_ProjectileAttack::textureDirectionBindings = {};

With that complete, you should be able to compile without issues. For more information on why this was needed have a look at the question and its answers on stack overflow.

We’ll add four entries into the unordered_map, one for each direction. The IntRect accepts four parameters: left, top, width, height. We can work out what we need for each direction by examining the arrow sprite.
Arrows with rectangles

Using the image above we know that four directions and there corresponding texture locations are:

  • Up = 0, 0, 64, 64
  • Left = 64, 0, 64, 64
  • Down = 128, 0, 64, 64
  • Right = 192, 0, 64, 64
Add these entries in the Start function.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Start()
{
    projectileTextureID = owner->context->textureAllocator->Add(owner->context->workingDir->Get() + "LPC/Weapons/arrow.png");
    
    textureDirectionBindings.emplace(FacingDirection::Up, sf::IntRect(0, 0, 64, 64));
    textureDirectionBindings.emplace(FacingDirection::Left, sf::IntRect(64, 0, 64, 64));
    textureDirectionBindings.emplace(FacingDirection::Down, sf::IntRect(128, 0, 64, 64));
    textureDirectionBindings.emplace(FacingDirection::Right, sf::IntRect(192, 0, 64, 64));
}

And now we can use the rectangle returned from the unordered_map to specify which sprite we want to use. 

C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
    std::shared_ptr<Object> projectile = std::make_shared<Object>(owner->context);
    
    projectile->transform->SetPosition(owner->transform->GetPosition());
 
    FacingDirection currentDir = direction->Get();
    
    auto projSprite = projectile->AddComponent<C_Sprite>();
    projSprite->Load(projectileTextureID);
    projSprite->SetDrawLayer(DrawLayer::Entities);
    projSprite->SetSortOrder(100);
	// Use the direction to set the texture rect.
    projSprite->SetTextureRect(textureDirectionBindings.at(direction->Get()));
 
    owner->context->objects->Add(projectile);
}
Now when we run the game and fire a projectile (‘e’ key), the correct sprite is used based on the player’s direction. Nice!
Arrows sprite selected based on player's direction.
The arrow still doesn’t go anywhere but that will all change next week when we look at projectile movement and collisions.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 33 – Shared Context

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
This week we take a break from working on our projectile code to create a shared repository of useful classes, which we’ll call the shared context. The shared context is a class that contains references to other classes that we want our components to have access to. It all sounds very simple and luckily it is.

Creating a shared context involves a number of steps:

  1. Writing the shared context and adding references to the classes we want shared.
  2. Ensuring every object has access to the shared context.
  3. Removing any local references that components currently store and instead using the references in the shared context.
  4. Removing any setters/mutators from components for classes that are now included in the shared context.
At the moment the classes we want to include in the context are:
  • Input: we want any component to be able to query the keyboard state.
  • ObjectCollection: we want any component to be able to add other objects to the game, such as our projectile attack component creating new arrow objects.
  • WorkingDirectory: to retrieve anything from the disk we first need to retrieve the location of the asset.
  • TextureAllocator: some of our components (like our projectile attack) can add textures to objects.
  • Window: any component that acts as a camera or interacts with the player’s view will require access to the Window class.
This list will almost certainly expand in future as we create new components.

Now that we’ve decided which classes we want to include we can write the SharedContext class.

SharedContext.hpp
#ifndef SharedContext_hpp
#define SharedContext_hpp

#include "Input.hpp"
#include "WorkingDirectory.hpp"
#include "ResourceAllocator.hpp"
#include "Window.hpp"

class ObjectCollection;

struct SharedContext
{
    Input* input;
    ObjectCollection* objects;
    WorkingDirectory* workingDir;
    ResourceAllocator<sf::Texture>* textureAllocator;
    Window* window;
};

#endif /* SharedContext_hpp */
The class is simple especially as I’ve opted not to write mutators and accessors, although feel free to include them if you wish.
We want every object and it’s components to have access to the shared context. To achieve this change the Objects constructor so that it accepts a reference to the shared context.
Object.hpp
class Object
{
public:
    Object(SharedContext* context);

…
    
    SharedContext* context;
   
};
Object.cpp
Object::Object(SharedContext* context) : context(context), queuedForRemoval(false)
{
	…
}
Again you can create a getter/setter for the context if you wish.
Now when you create an object you need to pass it the shared context. Right now we’re only creating objects in our game scene so that’s where we’ll instantiate SharedContext. We may wish to change this in future so that we can share these classes between scenes.
SceneGame.hpp
class SceneGame : public Scene
{
…
    
private:
…
    SharedContext context;
};
Now SceneGame owns the context it will also be its job to initialise the pointers. We’ll do this first thing in the OnCreate function.
SceneGame.cpp
void SceneGame::OnCreate()
{
    context.input = &input;
    context.objects = &objects;
    context.workingDir = &workingDir;
    context.textureAllocator = &textureAllocator;
    context.window = &window;
…
}
This ensures that any object created does not have to worry about null pointers in the context.
As we’ve changed the objects default constructor we also need to change how we create new objects: we now have to pass the shared context. This includes the creation of our player object in the OnCreate function (just after initialising the pointers).

SceneGame.cpp
void SceneGame::OnCreate()
{
…
    //std::shared_ptr<Object> player = std::make_shared<Object>();
    std::shared_ptr<Object> player = std::make_shared<Object>(&context);

…
}

I kept the previous way of instantiating objects as a reminder of how we used to do it. The only change is that when creating the shared object we pass a reference to the context.
Our tilemap parser will also need a context to create each tile object. We’ll pass the context in the constructor and store a reference to be used when creating tiles.
TileMapParser.hpp
class TileMapParser
{
public:
    TileMapParser(ResourceAllocator<sf::Texture>& textureAllocator, 
				  SharedContext& context);
…
private:
    SharedContext& context;
…
};
TileMapParser.cpp
TileMapParser::TileMapParser(ResourceAllocator<sf::Texture>& textureAllocator, 
							 SharedContext& context) : 
textureAllocator(textureAllocator), context(context){}
Now when we create new tile objects in the Parse function we pass a pointer to the context.
TileMapParser.cpp
std::vector<std::shared_ptr<Object>> 
TileMapParser::Parse(const std::string& file, sf::Vector2i offset)
{
…
            
	std::shared_ptr<Object> tileObject = std::make_shared<Object>(&context);

…
}
We need to change the call to the TileMapParser constructor in SceneGame.
SceneGame.cpp
SceneGame::SceneGame(WorkingDirectory& workingDir, 
					 ResourceAllocator<sf::Texture>& textureAllocator, 
					 Window& window) : 
workingDir(workingDir), 
textureAllocator(textureAllocator), 
mapParser(textureAllocator, context), // Pass in the SharedContext
window(window) { }
The last object instantiation that needs to be changed is the creation of the projectile object in the C_ProjectileAttack component. As each object has a reference to the context and every component can access its own object we can use that reference.
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
	// Retrieve the shared context from the parent object.
    std::shared_ptr<Object> projectile = std::make_shared<Object>(owner->context);
    
…
}
This shows how we can access the context from any component.
Now that it’s possible to access the shared context we no longer need many of the mutator functions we created previously. We need to remove pointers and their corresponding set functions in the C_Sprite, C_KeyboardMovement, C_Camera, and our recently written C_ProjectileAttack classes. Let’s start with C_Sprite.
C_Sprite.hpp
class C_Sprite : public Component, public C_Drawable
{
public:
    C_Sprite(Object* owner);
    
	// Remove the below function
    //void SetTextureAllocator(ResourceAllocator<sf::Texture>* allocator);
    
private:
	// Remove the reference to the texture allocator.
    //ResourceAllocator<sf::Texture>* allocator;

};
Remove the function from the implementation file as well and change any references to the local texture allocator to use the one in our shared context instead.
C_Sprite.cpp
// Remove this function:
/*
void C_Sprite::SetTextureAllocator(ResourceAllocator<sf::Texture>* allocator)
{
    this->allocator = allocator;
}
*/

void C_Sprite::Load(int id)
{
    if(id >= 0 && id != currentTextureID)
    {
        currentTextureID = id;
		// Retrieve texture allocator from shared context.
        std::shared_ptr<sf::Texture> texture = owner->context->textureAllocator->Get(id);
        sprite.setTexture(*texture);
    }
}

void C_Sprite::Load(const std::string& filePath)
{
	// Retrieve texture allocator from shared context.
    int textureID = owner->context->textureAllocator->Add(filePath);
    
    if(textureID >= 0 && textureID != currentTextureID)
    {
        currentTextureID = textureID;

		// Also retrieve texture allocator from shared context.
        std::shared_ptr<sf::Texture> texture = owner->context->textureAllocator->Get(textureID);
        sprite.setTexture(*texture);
    }
}
Remove the references to the recently removed SetTextureAllocator function in TileMapParser::Parse, SceneGame::OnCreate and C_ProjectileAttack::SpawnProjectile.
TileMapParser.cpp
std::vector<std::shared_ptr<Object>> TileMapParser::Parse(const std::string& file, sf::Vector2i offset)
{
…
                auto sprite = tileObject->AddComponent<C_Sprite>();
				// Remove line below:
                // sprite->SetTextureAllocator(&textureAllocator);
                sprite->Load(tileInfo->textureID); 
                sprite->SetTextureRect(tileInfo->textureRect);
                sprite->SetScale(tileScale, tileScale);
                sprite->SetSortOrder(layerCount);
                sprite->SetDrawLayer(DrawLayer::Background);
…
}
SceneGame.cpp
void SceneGame::OnCreate()
{
    std::shared_ptr<Object> player = std::make_shared<Object>();
    
    player->transform->SetPosition(100, 700);
    
    auto sprite = player->AddComponent<C_Sprite>();
	
	// Remove line below.
    //sprite->SetTextureAllocator(&textureAllocator);
    sprite->SetDrawLayer(DrawLayer::Entities);
  …
}
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
    std::shared_ptr<Object> projectile = std::make_shared<Object>();
    
    projectile->transform->SetPosition(owner->transform->GetPosition());
    
    auto projSprite = projectile->AddComponent<C_Sprite>();
    // Remove line below.
	//projSprite->SetTextureAllocator(textureAllocator);
    projSprite->Load(projectileTextureID);
    projSprite->SetDrawLayer(DrawLayer::Entities);
    projSprite->SetSortOrder(100);
    
    objects->Add(projectile);
}
That’s it for C_Sprite, now we’ll do the same for C_KeyboardMovement by removing it’s SetInput function and its local reference to the Input class (which has now been moved to the shared context).
C_KeyboardMovement.hpp
class C_KeyboardMovement : public Component
{
public:
…
    // Remove below line:
    //void SetInput(Input* input);

…
    
private:
…
	// Remove below line:
    //Input* input;

};
Again we need to remove the function from the implementation file and replace any references to the Input class with that in the context.
C_KeyboardMovement.hpp
// Delete function below.
/*
void C_KeyboardMovement::SetInput(Input* input)
{
    this->input = input;
}
*/

void C_KeyboardMovement::Update(float deltaTime)
{
    //TODO: keyboardmovement should not interact with animation component.
    if(animation->GetAnimationState() == AnimationState::Projectile)
    {
        velocity->Set(0.f, 0.f);
        return;
    }
    
    float xMove = 0.f;

	// Use input class from shared context.
    if(owner->context->input->IsKeyPressed(Input::Key::Left))
    {
        xMove = -moveSpeed;
    }
	// Use input class from shared context.
    else if(owner->context->input->IsKeyPressed(Input::Key::Right))
    {
        xMove = moveSpeed;
    }
    
    float yMove = 0.f;
	// Use input class from shared context.
    if(owner->context->input->IsKeyPressed(Input::Key::Up))
    {
        yMove = -moveSpeed;
    }
	// Use input class from shared context.
    else if(owner->context->input->IsKeyPressed(Input::Key::Down))
    {
        yMove = moveSpeed;
    }
    
    velocity->Set(xMove, yMove);
}
Remove the call to SetInput from SceneGame::OnCreate.
SceneGame.cpp
void SceneGame::OnCreate()
{
    std::shared_ptr<Object> player = std::make_shared<Object>();
    
    player->transform->SetPosition(100, 700);
    
    auto sprite = player->AddComponent<C_Sprite>();
    sprite->SetTextureAllocator(&textureAllocator);
    sprite->SetDrawLayer(DrawLayer::Entities);
    
	// Replace these two lines:
    //auto movement = player->AddComponent<C_KeyboardMovement>();
    //movement->SetInput(&input);

	// With this one:
    player->AddComponent<C_KeyboardMovement>();
 …
}
We’re almost there, only the camera and projectile class to go. Let’s update the camera class next by removing its SetWindow function and the local pointer to the Window class.
C_Camera.hpp
class C_Camera : public Component
{
public:
    C_Camera(Object* owner);
    
    void LateUpdate(float deltaTime) override;
    
	// Remove line below.
    //void SetWindow(Window* gameWindow);
    
private:
	// Remove line below.
    //Window* window;
};
Again remember to remove the function from the implementation file and change the calls to the window class to retrieve it from the shared context instead.
C_Camera.cpp
// Delete this function:
/*
void C_Camera::SetWindow(Window* gameWindow)
{
    window = gameWindow;
}
*/

void C_Camera::LateUpdate(float deltaTime)
{
	// Use the Window class from shared context.
    sf::View view = owner->context->window->GetView();
    
    const sf::Vector2f& targetPos = owner->transform->GetPosition();
    view.setCenter(targetPos.x, targetPos.y);
    
	// Use the Window class from shared context.
    owner->context->window->SetView(view);
}
We once again need to edit SceneGame::OnCreate, this time to remove the call to SetWindow.
SceneGame.cpp
void SceneGame::OnCreate()
{
    std::shared_ptr<Object> player = std::make_shared<Object>();
    
    …
    
	// Replace these two lines:
    //auto camera = player->AddComponent<C_Camera>();
    //camera->SetWindow(&window);
	// With:
	player->AddComponent<C_Camera>();

    
   …
}
Last up is our new projectile attack component, we no longer need to set the input, object collection, working directory, or texture allocator manually.
C_ProjectileAttack.hpp
#include “ObjectCollection.hpp"
…

class C_ProjectileAttack : public Component
{
public:
    C_ProjectileAttack(Object* owner);
    
    void Awake() override;
    
    void Start() override;
    
    void Update(float deltaTime) override;
    
	// Remove lines below.
   // void SetInput(Input* input);
   // void SetObjectCollection(ObjectCollection* objects);
   // void SetWorkingDirectory(WorkingDirectory* workingDirectory);
   // void SetTextureAllocator(ResourceAllocator<sf::Texture>* textureAllocator);
    
private:
    void SpawnProjectile();
    
    std::shared_ptr<C_Animation> animation;

	// Remove lines below.
   // Input* input;
   // ObjectCollection* objects;
   // WorkingDirectory* workingDirectory;
   // ResourceAllocator<sf::Texture>* textureAllocator;
    int projectileTextureID;
};
Once again, and for the last time, we need to remove the functions from the implementation file and change how we reference the classes we’ve just removed.
C_ProjectileAttack.cpp
// Remove the mutator functions below:
/*
void C_ProjectileAttack::SetInput(Input* input)
{
    this->input = input;
}

void C_ProjectileAttack::SetObjectCollection(ObjectCollection* objects)
{
    this->objects = objects;
}

void C_ProjectileAttack::SetWorkingDirectory(WorkingDirectory* workingDirectory)
{
    this->workingDirectory = workingDirectory;
}

void C_ProjectileAttack::SetTextureAllocator(ResourceAllocator<sf::Texture>* textureAllocator)
{
    this->textureAllocator = textureAllocator;
}
*/

void C_ProjectileAttack::Start()
{
	// Use the texture allocator class from shared context.
    projectileTextureID = owner->context->textureAllocator->Add(
					owner->context->workingDir->Get() + "LPC/Weapons/arrow.png");
}

void C_ProjectileAttack::Update(float deltaTime)
{
	// Use the input class from shared context.
    if(owner->context->input->IsKeyDown(Input::Key::E))
    {
        SpawnProjectile();
        animation->SetAnimationState(AnimationState::Projectile);
    }
	// Use the input class from shared context.
    else if (owner->context->input->IsKeyUp(Input::Key::E))
    {
        animation->SetAnimationState(AnimationState::Idle);
    }
}



void C_ProjectileAttack::SpawnProjectile()
{
    std::shared_ptr<Object> projectile = std::make_shared<Object>(owner->context);
    
    projectile->transform->SetPosition(owner->transform->GetPosition());
    
    auto projSprite = projectile->AddComponent<C_Sprite>();
    projSprite->Load(projectileTextureID);
    projSprite->SetDrawLayer(DrawLayer::Entities);
    projSprite->SetSortOrder(100);
    
	// Use the object collection class from shared context.
    owner->context->objects->Add(projectile);
}
We’re nearly there I promise! The last step to implementing the shared context is to remove the calls to the mutator functions (the ones we’ve just deleted) from SceneGame::OnCreate.
SceneGame.cpp
void SceneGame::OnCreate()
{
    std::shared_ptr<Object> player = std::make_shared<Object>();
    
…
    	// Change the below lines:
/*
    auto projectileAttack = player->AddComponent<C_ProjectileAttack>();
    projectileAttack->SetInput(&input);
    projectileAttack->SetObjectCollection(&objects);
    projectileAttack->SetWorkingDirectory(&workingDir);
    projectileAttack->SetTextureAllocator(&textureAllocator);
*/
	// To:
	player->AddComponent<C_ProjectileAttack>();
	
…
}

Because we’ve made numerous small changes to the SceneGame::OnCreate function over the course of this tutorial you may want to refer to the implementation in the Github repo to make sure that your code matches mine.

And that’s it, when you run the game everything should still work in the same way (if not then let me know in the comments), but in future we no longer need to write mutator functions in our components for common classes, we can simply retrieve them from the shared repository that we created this week.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 32 – Spawning Projectiles

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
Last week we added the final touches to our projectile animation however there is still something missing: the ability to shoot projectiles. The player will expect at least one arrow to be released at a certain point in the animation. The arrow will be its own object separate from that of the player and it will need to collide and interact with other objects in different ways (hurting other characters but not a solid wall for example).

Before we can start coding we should find an image for our arrow. I adapted one from here but you can use any sprite you like. Once downloaded make sure you add it to your project.

I’ve created a sprite sheet consisting of four images. Each image shows an arrow pointing in a different direction. In future, we can use the one image and rotate it to suit our needs but this will do for now. The sprites I used are included in this week’s resources/LPC/Weapons folder in the Github repo.

The arrow sprite sheet.
We’ll use the previously written C_ProjectileAttack class to spawn the projectiles. To spawn the projectile as a new object we need access to the object collection (to add the projectile to), the working directory (to retrieve the directory for our new projectile sprite), and texture allocator (to load the new sprite and add it to the pool of existing textures to be used within the game). We’ll add references and setters for these classes.
C_ProjectileAttack.hpp
#include “ObjectCollection.hpp”
#include "WorkingDirectory.hpp"
…

class C_ProjectileAttack : public Component
{
public:
…
    void SetObjectCollection(ObjectCollection* objects);
    void SetWorkingDirectory(WorkingDirectory* workingDirectory);
    void SetTextureAllocator(ResourceAllocator<sf::Texture>* textureAllocator);

…
    
private:   
… 
    ObjectCollection* objects;
	WorkingDirectory* workingDirectory;
    ResourceAllocator<sf::Texture>* textureAllocator;
…
};
C_ProjectileAttack.hpp
void 
C_ProjectileAttack::SetObjectCollection(ObjectCollection* objects)
{
    this->objects = objects;
}

void 
C_ProjectileAttack::SetWorkingDirectory(WorkingDirectory* workingDirectory)
{
    this->workingDirectory = workingDirectory;
}

void
C_ProjectileAttack::SetTextureAllocator(ResourceAllocator<sf::Texture>* textureAllocator)
{
    this->textureAllocator = textureAllocator;
}
Having all of these separate set functions makes me think that we should spend some time creating a shared repository of useful classes. This repository would include the working directory, texture allocator, and object collection at the very least, and would be shared with every component at creation so we no longer need to worry about injecting a component’s dependencies. This is something we’ll look into next week.
The mutator functions need to be called when we add the component to our player in SceneGame.
SceneGame.cpp
void SceneGame::OnCreate()
{
    std::shared_ptr<Object> player = std::make_shared<Object>();

…
    
    auto projectileAttack = player->AddComponent<C_ProjectileAttack>();
	// Inject the necessary dependencies to create a new projectile.
    projectileAttack->SetInput(&input);
    projectileAttack->SetObjectCollection(&objects);
    projectileAttack->SetWorkingDirectory(&workingDir);
    projectileAttack->SetTextureAllocator(&textureAllocator);
…
}
Back to the projectile attack component. As we now have access to the texture allocator we’ll write a Start function, which is called at the beginning of the next frame, to add the texture to our resource allocator and store its id.
C_ProjectileAttack.hpp
public:
…
    
    void Start() override;
    
private:
…

    int projectileTextureID;
};
C_ProjectileAttack.cpp
void C_ProjectileAttack::Start()
{
    projectileTextureID = 
		textureAllocator->Add(workingDirectory->Get() 
							  + "LPC/Weapons/arrow.png");
}
We can use this texture id when spawning a projectile. Talking about spawning projectiles let’s create a new function to do just that.
C_ProjectileAttack.hpp
class C_ProjectileAttack : public Component
{
…
    
private:
    void SpawnProjectile();
    
…
};
C_ProjectileAttack.cpp
void C_ProjectileAttack::SpawnProjectile()
{
	// Create new object.
    std::shared_ptr<Object> projectile = std::make_shared<Object>();
    
	// Set objects position
    projectile->transform->SetPosition(owner->transform->GetPosition());
    
	// Add sprite component to object
    auto projSprite = projectile->AddComponent<C_Sprite>();
    projSprite->SetTextureAllocator(textureAllocator);
    projSprite->Load(projectileTextureID);
    projSprite->SetDrawLayer(DrawLayer::Entities);
    projSprite->SetSortOrder(100);
    
	// Add object to collection
    objects->Add(projectile);
}

The function:

  1. Creates a new object.
  2. Sets it position equal to the player’s position using the transform component that is automatically added to all new objects.
  3. Adds a sprite component and loads the arrow texture.
  4. Adds the newly created object to the object collection so it is updated and drawn the window.
Most of the objects in the game will follow these steps, with more or possibly even fewer components added depending on the objects complexity. As this object only has a sprite component (for now) it won’t move or collide with other objects yet.
When we create frame actions in a future tutorial we’ll want this function to be called during a specific animation frame (where the player is just about to release the bow string) but for now, we’ll call it whenever we press the attack button.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Update(float deltaTime)
{
    if(input->IsKeyDown(Input::Key::E))
    {
		// Call Spawn Projectile whenever we press the attack button.
        SpawnProjectile();
        animation->SetAnimationState(AnimationState::Projectile);
    }
    else if (input->IsKeyUp(Input::Key::E))
    {
        animation->SetAnimationState(AnimationState::Idle);
    }
}
Now if you run the game and press the projectile attack button (e), you’ll get something like this:
The texture we use contains four sprites for each direction.
The texture we use contains four sprites for each direction. We don’t want to show all four sprites every time we fire an arrow, so we need to be able to select a sprite from within the sprite sheet depending on which direction we are facing. We’ll look at accomplishing this over the next few weeks. However next week we’ll take a break from projectiles and look at creating a shared repository of useful classes that every component has access to.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂

C++ Game Engine Development – Part 31 – Finishing Projectile Animation

This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
Last week I said that we would spend this week adding a method of transitioning to different animations so that when we finish shooting a projectile we can play the idle animation, however, after some consideration I’ve come up with a better solution (at least for now). We’ll still want a transition system but we also want to add a data structure to handle the transitions which is a large project that will get in the way of what we’re trying to achieve at the moment (a working projectile system). I’m going to devote more time to update the animation system in a future tutorial.
At the moment In C_ProjectileAttack::Update we check if the ‘e’ key is pressed and if it is we tell the animation system to play the projectile animation. Which mostly works but we can make a small change here which will solve our transition problem.
C_ProjectileAttack.cpp
void C_ProjectileAttack::Update(float deltaTime)
{
    if(input->IsKeyDown(Input::Key::E))
    {
        animation->SetAnimationState(AnimationState::Projectile);
    }
    else if (input->IsKeyUp(Input::Key::E))
    {
        animation->SetAnimationState(AnimationState::Idle);
    }
}

Instead of just checking if the ‘e’ key is pressed we now perform two checks:

  1. When it is first pressed we start the projectile animation.
  2. When the key is released we transition to the idle state.
Simple but effective. If you run the game now you’ll see the effect for yourself.
We now want to loop the projectile animation so that while the projectile attack button is held we play the animation. Make this change this in SceneGame.
SceneGame.cpp
void SceneGame::OnCreate()
{
…

    /*************************
     * Projectile Animations *
     *************************/
    const bool projectileAnimationLooped = true;

…
}
Now holding down the attack button will play the projectile animation on a loop.
Projectile Animation Looped.
Projectile Animation Looped.
With that small change, we have resolved our transition issue. Although this should only be considered a temporary solution. As the number of animations grow and the interactions between them become ever more complex we will need something more robust.

You may have noticed that the player can still move while ‘shooting’ a projectile. Well, they can move while playing the projectile animation as we are yet to create the actual projectiles. For now, we want the player to be stationary while shooting, although this design decision may change as we playtest the game. To stop movement while shooting we’ll update C_KeyboardMovement by re-adding a reference to the animation component that we removed when we created the velocity component.

C_KeyboardMovement.hpp
#include "C_Animation.hpp"

class C_KeyboardMovement : public Component
{
…
    
private:
…
    std::shared_ptr<C_Animation> animation;
};
Then in the Update function we check if the animation state is projectile and if it is we set the players velocity to 0 to stop any existing movement and return from the function to prevent the player from moving.
C_KeyboardMovement.cpp
void C_KeyboardMovement::Update(float deltaTime)
{
    if(input == nullptr)
    {
        return;
    }
    
    //TODO: keyboardmovement should not interact with animation component.
    if(animation->GetAnimationState() == AnimationState::Projectile)
    {
        velocity->Set(0.f, 0.f);
        return;
    }
    
…
}

Don’t forget to assign the animation component in the Awake function. More information on what the Awake function is and when it is called can be found here.

C_KeyboardMovement.cpp
void C_KeyboardMovement::Awake()
{
	...
    animation = owner->GetComponent<C_Animation>();
}
When we create a method of inter-system communication such as an event system, we’ll once again remove the animation component from the keyboard movement as we really don’t want it there. Ideally, the movement and animation systems should be kept as separate as possible
That’s all for this week, next week we’ll start work on actually spawning projectiles and making them collide with the environment.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂