Arkanoid
|
Time for another classic! This time we spent a week creating a basic version of Arkanoid. We’ve now gotten into the practice of separating interface from implementation via the use of header files and .cpp files, as well as through the use of an abstract class with pure virtual functions. Pure virtual functions are useful for declaring functions whose implementation is going to vary. They have no body when they are declared, and must be implemented in child classes. This way, we avoid writing multiple functions whose bodies may need to vary slightly, for example, for retrieving the x and y values of a variety of different game objects. Instead, we declare one pure virtual function, and then set any class whose objects need to use that function to inherit the abstract class where the pure virtual function is declared. We then instantiate the pure virtual function and define its body in the class’s .cpp file. This saves ram, frees up load caches, and allows you to buy a better keyboard etc. We’ve also made use of a vector (an array that changes in size) with templates which allow the vector to contain any datatype for our sprite manager. We add sprites to our vector when we load up the game, and delete from the vector them as the player destroys blocks. We also delete all sprites on game exit.
Here are some highlights from the code:
// Entity.h
#ifndef ENTITY_H_INCLUDED
#define ENTITY_H_INCLUDED
// forward declare
class Sprite;
class Entity // en helt abstract class, dvs en interface
{
public:
virtual ~Entity(){}
virtual void Update(float delatime) = 0; // pure virtual
virtual Sprite* GetSprite() = 0;
virtual float GetX() = 0;
virtual float GetY() = 0;
};
#endif // ENTITY_H_INCLUDED
// Paddle.h
#pragma once
#include "Entity.h";
// forward declare allows use of pointers for the declared class without needing to include Sprite.h
class Mouse;
class Paddle : public Entity // class Paddle inherits from the abstract class Entity
{
public:
Paddle(Mouse* mouse, Sprite* sprite, int width, int height);
void Update(float deltatime);
Sprite* GetSprite();
float GetX(); // since class Paddle inherits from class Entity, which is pure virutal, we MUST instantialize its members
float GetY();
void Reset();
private:
Mouse* m_mouse;
Sprite* m_sprite;
float m_x;
float m_y;
int m_width;
int m_height;
};
// SpriteManager.h
// håller reda på vilka texturer vi redan skapat i minnet, plus skapar sprites
#ifndef SPRITEMANAGER_H_INCLUDED
#define SPRITEMANAGER_H_INCLUDED
// forward declare
class Sprite;
class SpriteText;
class SpriteManager
{
friend class DrawManager;
public:
SpriteManager(SDL_Renderer* renderer);
~SpriteManager();
Sprite* CreateSprite(const std::string& filename, int x, int y, int w, int h); //tar in fem parametrar, filnamn och region
SpriteText* CreateSprite(const std::string& fontfilename, char size, const std::string&text, int colorR, int colorG, int colorB);
void DestroySprite(Sprite* sprite); //tar in pekare till objektet som parameter
private:
SDL_Renderer* m_renderer;
std::vector; m_sprites; //std::VEKTOR är en DYNAMISK ARRAY. <> är templates, innebär att vektorn kan innehålla vilka datatyper som helst
std::map; m_textures; //std::map skapar en ASSOCIATION mellan std::string och SDL_Texture
std::map; m_fonts; //vi vill spara undan fonts i en vector då vi kan återanvända dem
};
#endif // SPRITEMANAGER_H_INCLUDED
// SpriteManager.cpp
#include "stdafx.h";
#include "Sprite.h";
#include "SpriteText.h";
#include "SpriteManager.h";
SpriteManager::SpriteManager(SDL_Renderer* renderer)
{
m_renderer = renderer;
}
SpriteManager::~SpriteManager()
{
// delete all remanining sprites
auto it = m_sprites.begin();
while (it != m_sprites.end())
{
delete (*it);
++it;
}
m_sprites.clear();
// destroy all loaded textures
auto itr = m_textures.begin();
while (itr != m_textures.end())
{
SDL_DestroyTexture(itr->second);
++itr;
}
m_textures.clear();
// destroy all loaded fonts from the vector m_fonts
auto itf = m_fonts.begin();
while (itf != m_fonts.end())
{
TTF_CloseFont(itf->second);
++itf;
}
m_fonts.clear();
}
Sprite* SpriteManager::CreateSprite(const std::string& filename, int x, int y, int w, int h)
{
// first we search for if the texture is already loaded
auto it = m_textures.find(filename); // auto vet vad filename är för datatype. it står för iterate
if (it == m_textures.end()) // om iterator:n hittar end dvs slutet/inte hittar något. Vi vill bara läsa in bilderna en gång
{
// if not, we create a new one
SDL_Surface* surface = IMG_Load(filename.c_str()); // IMG_Load och FreeSurface är typ surface-versionen av New och Delete
SDL_Surface* optimizedSurface = nullptr;
if (surface != nullptr)
{
optimizedSurface = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ABGR4444, 0); // sista parametern är flags men i detta fall gör de inget vettigt, vi skickar in 0
SDL_FreeSurface(surface); // om texturen inte existerar optimerar vi genom att ta bort texturen när vi har använt klart den
}
SDL_Texture* texture = SDL_CreateTextureFromSurface(m_renderer, optimizedSurface);
// we save the texture in the map for later reuse
m_textures.insert(std::pairGetRegion()->x = x;
sprite->GetRegion()->y = y;
sprite->GetRegion()->w = w;
sprite->GetRegion()->h = h;
m_sprites.push_back(sprite);
// return the newly newed sprite
return sprite;
}
SpriteText* SpriteManager::CreateSprite(const std::string& fontfilename, char size, const std::string&text, int colorR, int colorG, int colorB)
{
auto it = m_fonts.find((fontfilename + "_" + size));
if (it == m_fonts.end())
{
TTF_Font* font = TTF_OpenFont(fontfilename.c_str(), size);
if (font != nullptr)
{
m_fonts.insert(std::pair((fontfilename + "_" + size), font));
}
else
{
const char* error = TTF_GetError();
}
it = m_fonts.find((fontfilename + "_" + size));
}
//then we create the SpriteText
SpriteText* spritetext = new SpriteText(m_renderer, it->second, text, colorR, colorG, colorB);
m_sprites.push_back(spritetext);
return spritetext;
}
void SpriteManager::DestroySprite(Sprite* sprite)
{
// we look through all sprites
auto it = m_sprites.begin();
while (it != m_sprites.end())
{
// if we find the right one
if ((*it) == sprite)
{
// we delete the sprite
delete sprite;
// and remove it from the vector
m_sprites.erase(it);
break;
}
++it;
}
}
This course quite intensive: the first three weeks were spent doing the equivalent of three years worth of basic-level programming. All I can say is there’s a lot of this happening to me:
|
