⭐ Do not forget to give a star on GitHub!
This is a project I have made for an university assignment using C++ and SFML library - https://www.sfml-dev.org/.
It allows you to play all 33 Arkanoid's original levels with an additional Boss fight in the end!!!
Arkanoid is an arcade game from 1986, created and published by Taito.
- Install C++ in Visual Studio 2022.
- Clone GitHub repository
git clone https://github.com/szejkerek/ArkanoidClone.git
- Open project in Visual Studio 2022
- Compile and run project
If you have any feedback, please reach out to me at bartekk.gordon@gmail.com
// Ball.cpp – lines 74–103
sf::Vector3f CalculateCorrectionVector(sf::FloatRect& overlap, sf::Vector2f& collisionVector)
{
sf::Vector3f correctionVector(0, 0, 0); // x,y - from where ball collide // z - depth of penetration
if (overlap.width < overlap.height)
{
correctionVector.x = (collisionVector.x < 0) ? 1.f : -1.f;
correctionVector.z = overlap.width;
}
else
{
correctionVector.y = (collisionVector.y < 0) ? 1.f : -1.f;
correctionVector.z = overlap.height;
}
return correctionVector;
}
inline sf::Vector2f ReflectedVector(sf::Vector2f& currentDirection, sf::Vector2f& normal)
{
return -2.f * DotProduct(currentDirection, normal) * normal + currentDirection;
}
void Ball::UpdateBrickCollision(IBrick* brick, sf::FloatRect& _overlap)
{
sf::Vector2f collisionVector = brick->GetCenterPoint() - GetPosition();
sf::Vector3f correctionVector = CalculateCorrectionVector(_overlap, collisionVector);
sf::Vector2f normal(correctionVector.x, correctionVector.y);
Move(normal * correctionVector.z); //Move ball outside brick
ChangeDirection(ReflectedVector(direction, normal));
}The collision system derives the surface normal from the AABB penetration overlap: whichever axis has the smaller overlap depth is the axis of collision, and the vector from ball to brick center determines which side was hit. The ball is then displaced by the penetration depth along the normal to prevent tunneling, and the new direction is computed with the standard specular reflection formula r = d - 2(d·n)n. The same helper functions are reused for Vaus paddle collisions, keeping the physics logic DRY.
// Utility/Resources.h – lines 9–91
template <class ResourceType>
class Resource
{
private:
std::string resourcePath;
std::string resourceExtension;
std::unordered_map<std::string, std::shared_ptr<ResourceType>> resources;
public:
Resource(const std::string& path, const std::string& extension) : resourcePath(path), resourceExtension(extension){}
~Resource()
{
for (auto i = resources.begin(); i != resources.end();)
i = resources.erase(i);
}
ResourceType* GetResource(const std::string& name)
{
std::filesystem::path current = std::filesystem::current_path().append(resourcePath);
const auto i = resources.find(name);
if (i != resources.end())
{
return i->second.get();
}
else
{
std::shared_ptr<ResourceType> tempResource = std::make_shared<ResourceType>();
for (std::filesystem::directory_entry entry : std::filesystem::recursive_directory_iterator(current))
{
if (entry.path().stem() == name)
{
tempResource->loadFromFile(entry.path().string());
resources.insert({ name , tempResource });
return tempResource.get();
}
}
return nullptr;
}
}
};
class ResourceManager
{
#pragma region Singleton
ResourceManager()
:textures("Resources/Textures", ".png"),
fonts("Resources/Fonts", ".ttf"),
soundBuffers("Resources/Audio", ".wav")
{}
public:
static ResourceManager& Get()
{
static ResourceManager INSTANCE;
return INSTANCE;
}
ResourceManager(ResourceManager const&) = delete;
ResourceManager& operator=(ResourceManager const&) = delete;
#pragma endregion
private:
Resource<sf::Font> fonts;
Resource<sf::Texture> textures;
Resource<sf::SoundBuffer> soundBuffers;
public:
sf::Font* GetFont(const std::string& name)
{
return fonts.GetResource(name);
}
sf::Texture* GetTexture(const std::string& name)
{
return textures.GetResource(name);
}
sf::SoundBuffer* GetAudio(const std::string& name)
{
return soundBuffers.GetResource(name);
}
};A single Resource<T> template handles all three SFML asset types (textures, fonts, sound buffers) with lazy loading and shared_ptr caching. On first request it walks the asset directory recursively via std::filesystem::recursive_directory_iterator, matching by file stem rather than requiring callers to know exact paths. Subsequent requests hit the unordered_map cache directly. The ResourceManager singleton composes three typed instances, exposing a clean named API without duplicating any loading logic.





