I coded a (more or less) simple Tetris game as an example for some of the other trainees in my company. Since I am used to my coding style I would like to have some other people to look over it and maybe give me some advices to improve the readability of the code.
Some of the class names in the code (such as actor) are chosen to give some more general ideas about the parts in a video game (not that I am a pro in that area) so if I had in mind to just develop a tetris game maybe I would have chosen different names so please keep that in mind.
Yes, I am planning to split the code in header/cpp files, and yes I left out the header guards.
typedefs.h:
//The pixelUtility isn't included in the uploaded code since that is what i provide others as a "blackbox" library to work with, as well as the mangolib.h
//Please focus at the game code and not the naming of the mango:: or the graphic:: classes/structs, they will be explained to the people who use them pretty extensive
//The mango library is used since it implements some c++11 features I want to use and our IDE does not provide (shared_ptr and lambda for instance and later threading)
//btw. feel free to use this code as you please, as general example or if necessary (I hope it's not that bad ;) ) as negativ example, but I won't provide
//a code sample without my library, but im pretty sure it won't be much work to change the code to regular c++11 for whoever wants to use it.
#define NO_THREAD_ALLOC //disables thread_safety in my customallocator for better performance since this project is single threaded and locks are not needed
#include <Gaming/pixelutility.h> //header which contains graphic::Character, the GraphicController the KatCoord struct etc, includes <mangolib.h> --> mango::
namespace engine
{
typedef typename graphic::Character actor; //a simple graphic class with a position, animations and collision
typedef typename mango::shared_ptr<graphic::Character> actor_handle;
typedef typename mango::vector<mango::shared_ptr<graphic::Character> > actor_list;
typedef typename graphic::KatCoord point; //simple point class p(int32 x, int32 y)
typedef typename mango::vector<graphic::KatCoord> point_list;
typedef mango::int32 layerID;
typedef mango::int32 counter;
}
stone.h:
#include "typedefs.h"
#define Rot0 0
#define Rot90 1
#define Rot180 2
#define Rot270 3
namespace engine
{
namespace tetris
{
namespace shapes
{
enum shape
{
RLShape,
LLShape,
NTShape,
NIShape,
RZShape,
LZShape,
SQShape
};
}
//Shapes
//
// RL: #
// #
// # #
//
// LL: #
// #
// # #
//
// NT: # # #
// #
//
// NI: #
// #
// #
// #
//
// RZ: #
// # #
// #
//
// LZ: #
// # #
// #
//
// SQ: # #
// # #
//
//Shapes
typedef typename shapes::shape shape;
class RotationProcessor //this class is meant to make the Stone class smaller so I outsourced the rotation to this class
{
public:
void Rotate(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, shape sh, bool counterClockwise)
{
switch (sh)
{
case shapes::RLShape:
RotateRL(segments, state, counterClockwise);
break;
case shapes::LLShape:
RotateLL(segments, state, counterClockwise);
break;
case shapes::NTShape:
RotateNT(segments, state, counterClockwise);
break;
case shapes::NIShape:
RotateNI(segments, state, counterClockwise);
break;
case shapes::RZShape:
RotateRZ(segments, state, counterClockwise);
break;
case shapes::LZShape:
RotateLZ(segments, state, counterClockwise);
break;
default:
break;
}
}
void RotateNI(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
{
if (counterClockwise)
{
RotateNI(segments, state, false);
RotateNI(segments, state, false);
}
if (!state)
{
segments->at(0)->Move( 0, 20);
segments->at(1)->Move( 10, 10);
segments->at(2)->Move( 20, 0);
segments->at(3)->Move( 30, -10);
state = !state;
return;
}
segments->at(0)->Move( 0, -20);
segments->at(1)->Move(-10, -10);
segments->at(2)->Move(-20, 0);
segments->at(3)->Move(-30, 10);
state = !state;
return;
}
void RotateRZ(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
{
if (counterClockwise)
{
RotateRZ(segments, state, false);
RotateRZ(segments, state, false);
}
if(!state)
{
segments->at(0)->Move( 20, 10);
segments->at(1)->Move( 10, 0);
segments->at(2)->Move( 0 , 10);
segments->at(3)->Move(-10, 0);
state = !state;
return;
}
segments->at(0)->Move(-20, -10);
segments->at(1)->Move(-10, 0);
segments->at(2)->Move( 0, -10);
segments->at(3)->Move( 10, 0);
state = !state;
return;
}
void RotateLZ(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
{
if (counterClockwise)
{
RotateLZ(segments, state, false);
RotateLZ(segments, state, false);
}
if (!state)
{
segments->at(0)->Move(-10, 10);
segments->at(1)->Move( 0, 0);
segments->at(2)->Move( 10, 10);
segments->at(3)->Move( 20, 0);
state = !state;
return;
}
segments->at(0)->Move( 10, -10);
segments->at(1)->Move( 0, 0);
segments->at(2)->Move(-10, -10);
segments->at(3)->Move(-20, 0);
state = !state;
return;
}
void RotateLL(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
{
if (counterClockwise)
{
RotateLL(segments, state, false);
RotateLL(segments, state, false);
}
if (Rot0 == state)
{
segments->at(0)->Move( 10, 20);
segments->at(1)->Move( 0, 10);
segments->at(2)->Move(-10, 0);
segments->at(3)->Move( 0, -10);
++state;
return;
}
if (Rot90 == state)
{
segments->at(0)->Move(-20, 0);
segments->at(1)->Move(-10, -10);
segments->at(2)->Move( 0, -20);
segments->at(3)->Move( 10, -10);
++state;
return;
}
if (Rot180 == state)
{
segments->at(0)->Move( 0, -10);
segments->at(1)->Move( 10, 0);
segments->at(2)->Move( 20, 10);
segments->at(3)->Move( 10, 20);
++state;
return;
}
if (Rot270 == state)
{
segments->at(0)->Move( 10, -10);
segments->at(1)->Move( 0, 0);
segments->at(2)->Move(-10, 10);
segments->at(3)->Move(-20, 0);
state = Rot0;
return;
}
return;
}
void RotateRL(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise) //GOTO:
{
if (counterClockwise)
{
RotateRL(segments, state, false);
RotateRL(segments, state, false);
}
if (Rot0 == state)
{
segments->at(0)->Move( 20, 10);
segments->at(1)->Move( 10, 0);
segments->at(2)->Move( 0, -10);
segments->at(3)->Move(-10, 0);
++state;
return;
}
if (Rot90 == state)
{
segments->at(0)->Move(-10, 10);
segments->at(1)->Move( 0, 0);
segments->at(2)->Move( 10, -10);
segments->at(3)->Move( 0, -20);
++state;
return;
}
if (Rot180 == state)
{
segments->at(0)->Move(-10, 0);
segments->at(1)->Move( 0, 10);
segments->at(2)->Move( 10, 20);
segments->at(3)->Move( 20, 10);
++state;
return;
}
if (Rot270 == state)
{
segments->at(0)->Move( 0, -20);
segments->at(1)->Move(-10, -10);
segments->at(2)->Move(-20, 0);
segments->at(3)->Move(-10, 10);
state = Rot0;
return;
}
return;
}
void RotateNT(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
{
if (counterClockwise)
{
RotateNT(segments, state, false);
RotateNT(segments, state, false);
}
if (Rot0 == state)
{
segments->at(0)->Move( 0, 0);
segments->at(1)->Move(-10, -10);
segments->at(2)->Move( 0, -20);
segments->at(3)->Move(-20, 0);
++state;
return;
}
if (Rot90 == state)
{
segments->at(0)->Move( 0, 10);
segments->at(1)->Move( 10, 0);
segments->at(2)->Move( 20, 10);
segments->at(3)->Move( 0, -10);
++state;
return;
}
if (Rot180 == state)
{
segments->at(0)->Move(-10, -10);
segments->at(1)->Move( 0, 0);
segments->at(2)->Move(-10, 10);
segments->at(3)->Move( 10, -10);
++state;
return;
}
if (Rot270 == state)
{
segments->at(0)->Move( 10, 0);
segments->at(1)->Move( 0, 10);
segments->at(2)->Move(-10, 0);
segments->at(3)->Move( 10, 20);
state = Rot0;
return;
}
return;
}
};
class Stone
{
public:
Stone(mango::shared_ptr<engine::actor_list> segments, mango::shared_ptr<engine::point_list> collisionPoints, engine::point pos, shape sh)
: m_Segments(segments), m_CollisionPoints(collisionPoints), m_Position(pos), m_Shape(sh), m_State(0)
{}
void Draw(Gdiplus::Graphics* graphic, mango::mutex& lock)
{
lambda((engine::actor_handle actor, Gdiplus::Graphics* graphic, mango::mutex& lock) { actor->Draw(graphic, lock); return 0; }, 1);
mango::for_each(m_Segments->begin(), m_Segments->end(), exp1, graphic, lock);
}
void Rotate(bool counterClockwise)
{
RotationProcessor re;
re.Rotate(m_Segments, m_State, m_Shape, counterClockwise);
mango::shared_ptr<engine::point_list> tmpCollisionPoints = GetPoints(m_Segments);
m_CollisionPoints->swap(*tmpCollisionPoints);
}
void Move(mango::int32 x, mango::int32 y)
{
m_Position.X += x;
m_Position.Y += y;
lambda((engine::actor_handle actor, mango::int32 x, mango::int32 y) { actor->Move(x, y); return 0; }, 1);
mango::for_each(m_Segments->begin(), m_Segments->end(), exp1, x, y);
m_CollisionPoints = GetPoints(m_Segments);
}
bool Contains(mango::shared_ptr<Stone> actor)
{
mango::int32 result = 0;
mango::int32* resultHandle = &result;
lambda((engine::actor_handle actor, const engine::point_list& points, mango::int32* result)
{
for (mango::int32 current = 0; points.size() > current; ++current)
{
*result += actor->Contains(points.at(current));
}
return 0;
}, 1);
mango::for_each(m_Segments->begin(), m_Segments->end(), exp1, *actor->m_CollisionPoints, resultHandle);
return result != 0;
}
bool ContainedBy(mango::shared_ptr<engine::actor> actor)
{
mango::int32 result = 0;
for (mango::int32 current = 0; m_CollisionPoints->size() > current; ++current)
{
result += actor->Contains(m_CollisionPoints->at(current));
}
return result != 0;
}
mango::shared_ptr<engine::actor_list> GetSegments()
{
mango::shared_ptr<engine::actor_list> tmp = m_Segments;
m_Segments.reset();
return tmp;
}
bool GetVisibility()
{
return true;
}
static mango::shared_ptr<engine::point_list> GetPoints(mango::shared_ptr<engine::actor_list> actors)
{
mango::shared_ptr<engine::point_list> tmpCP = new engine::point_list();
for (mango::int32 current = 0; actors->size() > current; ++current)
{
engine::point pos = actors->at(current)->GetPosition();
tmpCP->push_back(engine::point(pos.X + 1, pos.Y + 1));
tmpCP->push_back(engine::point(pos.X + 9, pos.Y + 1));
tmpCP->push_back(engine::point(pos.X + 9, pos.Y + 9));
tmpCP->push_back(engine::point(pos.X + 1, pos.Y + 9));
tmpCP->push_back(engine::point(pos.X + 3, pos.Y + 3));
tmpCP->push_back(engine::point(pos.X + 7, pos.Y + 7));
tmpCP->push_back(engine::point(pos.X + 7, pos.Y + 3));
tmpCP->push_back(engine::point(pos.X + 3, pos.Y + 7));
}
return tmpCP;
}
private:
mango::shared_ptr<engine::actor_list> m_Segments;
mango::shared_ptr<engine::point_list> m_CollisionPoints;
engine::point m_Position;
shape m_Shape;
mango::int32 m_State;
};
typedef typename Stone t_actor;
typedef typename mango::shared_ptr<Stone> t_actor_handle;
typedef typename mango::vector<mango::shared_ptr<Stone> > t_actor_list;
//not optimal naming ---> t_ => tetris_ to make it easier to spot if you have an engine::actor or a engine::tetris::actor
class StoneFactory
{
public:
StoneFactory(mango::int32 stoneSize)
: m_StoneSize(stoneSize)
{}
t_actor_handle BuildStone(shape stoneShape, engine::point pos)
{
switch (stoneShape)
{
case shapes::RLShape:
return BuildRLStone(pos, stoneShape);
break;
case shapes::LLShape:
return BuildLLStone(pos, stoneShape);
break;
case shapes::NTShape:
return BuildNTStone(pos, stoneShape);
break;
case shapes::NIShape:
return BuildNIStone(pos, stoneShape);
break;
case shapes::RZShape:
return BuildRZStone(pos, stoneShape);
break;
case shapes::LZShape:
return BuildLZStone(pos, stoneShape);
break;
case shapes::SQShape:
return BuildSQStone(pos, stoneShape);
break;
default:
return nullptr;
break;
}
return nullptr;
}
private:
t_actor_handle BuildSQStone(engine::point pos, shape sh)
{
engine::point pos1 = pos;
engine::point pos2 = pos;
engine::point pos3 = pos;
engine::point pos4 = pos;
pos1.Y += 10;
pos2.Y += 20;
pos3.Y += 10;
pos4.Y += 20;
pos3.X += 10;
pos4.X += 10;
mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();
engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_vm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_vm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_vm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_vm", 1);
actors->push_back(tmp);
t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);
return tmpActor;
}
t_actor_handle BuildRLStone(engine::point pos, shape sh)
{
engine::point pos1 = pos;
engine::point pos2 = pos;
engine::point pos3 = pos;
engine::point pos4 = pos;
pos2.Y += 10;
pos3.Y += 20;
pos4.X += 10;
pos4.Y += 20;
mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();
engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_bm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_bm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_bm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_bm", 1);
actors->push_back(tmp);
t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);
return tmpActor;
}
t_actor_handle BuildLLStone(engine::point pos, shape sh)
{
engine::point pos1 = pos;
engine::point pos2 = pos;
engine::point pos3 = pos;
engine::point pos4 = pos;
pos1.X += 10;
pos2.Y += 10;
pos2.X += 10;
pos3.Y += 20;
pos3.X += 10;
pos4.Y += 20;
mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();
engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_rm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_rm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_rm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_rm", 1);
actors->push_back(tmp);
t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);
return tmpActor;
}
t_actor_handle BuildNTStone(engine::point pos, shape sh)
{
engine::point pos1 = pos;
engine::point pos2 = pos;
engine::point pos3 = pos;
engine::point pos4 = pos;
pos1.X += 10;
pos1.Y += 10;
pos2.X += 10;
pos2.Y += 20;
pos3.Y += 20;
pos4.X += 20;
pos4.Y += 20;
mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();
engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_gm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_gm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_gm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_gm", 1);
actors->push_back(tmp);
t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);
return tmpActor;
}
t_actor_handle BuildNIStone(engine::point pos, shape sh)
{
engine::point pos1 = pos;
engine::point pos2 = pos;
engine::point pos3 = pos;
engine::point pos4 = pos;
pos2.Y += 10;
pos3.Y += 20;
pos4.Y += 30;
mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();
engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_ym", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_ym", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_ym", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_ym", 1);
actors->push_back(tmp);
t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);
return tmpActor;
}
t_actor_handle BuildRZStone(engine::point pos, shape sh)
{
engine::point pos1 = pos;
engine::point pos2 = pos;
engine::point pos3 = pos;
engine::point pos4 = pos;
pos2.Y += 10;
pos3.X += 10;
pos3.Y += 10;
pos4.X += 10;
pos4.Y += 20;
mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();
engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_pm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_pm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_pm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_pm", 1);
actors->push_back(tmp);
t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);
return tmpActor;
}
t_actor_handle BuildLZStone(engine::point pos, shape sh)
{
engine::point pos1 = pos;
engine::point pos2 = pos;
engine::point pos3 = pos;
engine::point pos4 = pos;
pos1.X += 10;
pos2.X += 10;
pos2.Y += 10;
pos3.Y += 10;
pos4.Y += 20;
mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();
engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_tm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_tm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_tm", 1);
actors->push_back(tmp);
tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
tmp->LoadAnimation(0, "square_tm", 1);
actors->push_back(tmp);
t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);
return tmpActor;
}
mango::int32 m_StoneSize;
};
void rotatePoint(engine::point& p, const engine::point& center)
{
mango::int32 tmpX = p.X - center.X;
mango::int32 tmpY = p.Y - center.Y;
p.X = center.X + tmpY;
p.Y = center.Y - tmpX;
}
}
}
engine.h:
#include "typedefs.h"
#include "stone.h"
namespace engine
{
class Engine
{
public:
Engine()
: m_Graphic(new graphic::GraphicController()), m_Factory(10), m_UpdateInterval(0.5f), m_SoundDuration(83.0f)
{
srand((mango::uns32)time(nullptr));
CreateNewPlayerActor();
//the magic numbers will be wrapped in const values when I split the files in header/cpp
m_Ground = new engine::actor(engine::point(0, 290), 640, 50, 0, false); //engine::actor(point position, int32 width, int32 height, int32 renderLayer, bool visibility);
m_Ground->LoadAnimation(0, "default", 1); //LoadAnimation(int32 animationID, string fileName, int32 numberOfFrames); //"default" is an empty transparent sprite
m_Background = new engine::actor(engine::point(8, -8), 204, 291, 0, true);
m_Background->LoadAnimation(0, "Background", 1);
m_LeftBorder = new engine::actor(engine::point(0, 0), 10, 290, 0, false);
m_LeftBorder->LoadAnimation(0, "default", 1);
m_RightBorder = new engine::actor(engine::point(210, 0), 10, 290, 0, false);
m_RightBorder->LoadAnimation(0, "default", 1);
for (mango::int32 current = 0; 290 > current * 10; ++current)
{
m_Layers.push_back(mango::key_pair<layerID, counter>(current, 0));
}
}
void Play()
{
bool isRunning = true;
m_Timer.start();
m_SpeedTimer.start();
m_SoundTimer.start();
audio::playSound("Sounds\\sound.wav");
while (isRunning && nullptr != m_PlayerActor)
{
Audio();
Move();
Controls(isRunning);
Draw();
Present();
}
audio::stopSound();
}
private:
void Audio()
{
m_SoundTimer.tick();
if (m_SoundDuration <= m_SoundTimer.totalTime())
{
audio::stopSound();
audio::playSound("Sounds\\sound.wav");
ResetTimer(m_SoundTimer);
}
}
void CreateNewPlayerActor()
{
while (nullptr == m_PlayerActor)
{
mango::int32 shape = rand() % 40; //I chose a higher value to get more different shapes spawned
m_PlayerActor = m_Factory.BuildStone(*reinterpret_cast<engine::tetris::shape*>(&shape), engine::point(150, 1));
}
}
void RemoveLayer(mango::int32 layer)
{
for (mango::int32 current = 0; m_Actors.size() > current; ++current) //erase actors in layer
{
if ((mango::int32)(m_Actors.at(current)->GetPosition().Y / 10) == layer)
{
m_Actors.erase(current);
--current;
}
}
for (mango::int32 current = 0; m_Actors.size() > current; ++current) //move down actors in higher layers
{
if ((mango::int32)(m_Actors.at(current)->GetPosition().Y / 10) < layer)
{
m_Actors.at(current)->Move(0, 10);
}
}
for (mango::int32 current = layer; 0 < current; --current)
{
m_Layers.at(current).a = m_Layers.at(current - 1).a; // mango::key_pair<class KEY, class A> x; ---> x.a; to access the value;
}
m_Layers.front().a = 0;
}
void Collision()
{
if (0 < m_Layers.at(4).a) //Layer 4 is the highest layer to reach until game over
{
m_Actors.clear();
m_PlayerActor.reset();
m_Background = new engine::actor(engine::point(10, 100), 600, 100);
m_Background->LoadAnimation(0, "game_over", 1);
return;
}
//lambda((parameters){function}, int32 ID); --> exp + ID is used to call the expression the id has to be unique in its scope
mango::int32 result = 0;
lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
{
if (playerActor->ContainedBy(actor))
{
++result;
}
return 0;
}, 2);
if (0 != m_Actors.size())
{
mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
}
if (m_PlayerActor->ContainedBy(m_Ground) || 0 != result)
{
m_PlayerActor->Move(0, -10);
mango::shared_ptr<engine::actor_list> actors = m_PlayerActor->GetSegments();
for (mango::int32 current = 0; actors->size() > current; ++current) //transfer segments to global actor pipeline
{
m_Actors.push_back(actors->at(current));
++m_Layers.at((actors->at(current)->GetPosition().Y) / 10).a;
}
m_PlayerActor.reset();
CreateNewPlayerActor();
const mango::int32 layerWidth = 20;
for (mango::int32 current = m_Layers.size() - 1; 0 <= current; --current) //Remove Filled layers
{
if (layerWidth == m_Layers.at(current).a)
{
RemoveLayer(current);
current = m_Layers.size() - 1;
}
}
}
}
void ResetTimer(mango::delta_timer& timer)
{
timer.stop();
timer.reset();
timer.start();
}
void ChangeSpeed()
{
m_SpeedTimer.tick();
if (30.0f <= m_SpeedTimer.totalTime() && m_UpdateInterval > 0.1f)
{
ResetTimer(m_SpeedTimer);
m_UpdateInterval -= 0.05f; //increase game speed over time
}
}
void Move()
{
m_Timer.tick();
if (m_UpdateInterval <= m_Timer.totalTime())
{
ResetTimer(m_Timer);
ChangeSpeed();
m_PlayerActor->Move(0, 10); //move player down
Collision();
}
}
void TryRotationLeft()
{
m_PlayerActor->Rotate(true);
mango::int32 result = 0;
lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
{
if (playerActor->ContainedBy(actor))
{
++result;
}
return 0;
}, 2);
if (0 != m_Actors.size())
{
mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
}
if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
{
m_PlayerActor->Rotate(false); //undo rotation if collision detected
}
}
void TryRotationRight()
{
m_PlayerActor->Rotate(false);
mango::int32 result = 0;
lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
{
if (playerActor->ContainedBy(actor))
{
++result;
}
return 0;
}, 2);
if (0 != m_Actors.size())
{
mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
}
if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
{
m_PlayerActor->Rotate(true); //undo rotation if collision detected
}
}
void TryMoveLeft()
{
m_PlayerActor->Move(-10, 0);
mango::int32 result = 0;
lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
{
if (playerActor->ContainedBy(actor))
{
++result;
}
return 0;
}, 2);
if (0 != m_Actors.size())
{
mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
}
if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
{
m_PlayerActor->Move(10, 0);
}
}
void TryMoveRight()
{
m_PlayerActor->Move(10, 0);
mango::int32 result = 0;
lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
{
if (playerActor->ContainedBy(actor))
{
++result;
}
return 0;
}, 2);
if (0 != m_Actors.size())
{
mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
}
if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
{
m_PlayerActor->Move(-10, 0);
}
}
void TryMoveDown()
{
m_PlayerActor->Move(0, 10);
Collision();
}
void Controls(bool& isRunning)
{
mango::Byte input = ' ';
if (_kbhit())
{
input = mango::archive::mgetch();
if ('q' == input)
{
TryRotationLeft();
}
if ('e' == input)
{
TryRotationRight();
}
if ('a' == input)
{
TryMoveLeft();
}
if ('d' == input)
{
TryMoveRight();
}
if ('s' == input)
{
TryMoveDown();
}
if ('X' == input)
{
isRunning = false;
}
}
}
void Draw()
{
m_Graphic->ClearBuffer();
m_Graphic->Draw(m_Background.get()); //Background is drawn first
lambda((engine::actor_handle actor, graphic::GraphicController* gc) { gc->Draw(actor.get()); return 0; }, 1);
if (0 != m_Actors.size())
{
mango::for_each(m_Actors.begin(), m_Actors.end(), exp1, m_Graphic);
}
if (nullptr != m_PlayerActor) //Player is drawn last
{
m_Graphic->Draw(m_PlayerActor.get());
}
}
void Present()
{
m_Graphic->Present();
}
mango::vector<mango::key_pair<mango::int32, mango::int32> > m_Layers;
engine::actor_handle m_RightBorder; //Special basic Actor
engine::actor_handle m_LeftBorder; //Special basic Actor
engine::actor_handle m_Background; //Special basic Actor
engine::actor_handle m_Ground; //Special basic Actor
engine::tetris::t_actor_handle m_PlayerActor; //Special tetris Actor (t_actor --> tetris_actor)
mango::shared_ptr<graphic::GraphicController> m_Graphic; //Graphic controller which contains "swap_chain" buffers and draw capabilities
engine::tetris::StoneFactory m_Factory; //Creates stones (t_actors)
engine::actor_list m_Actors; //All other Actors
mango::delta_timer m_Timer; //Core game timer to specify update rate
mango::delta_timer m_SpeedTimer; //Timer which indicates the increase game speed over time
mango::delta_timer::seconds m_UpdateInterval; //Interval witch controls the update rate --> higher update right faster game
mango::delta_timer m_SoundTimer; //Timer which tracks the duration of the background sound
mango::delta_timer::seconds m_SoundDuration; //duration of the background sound file
};
}
main.cpp:
#include "engine.h"
mango::int32 main()
{
mango::shared_ptr<engine::Engine> game(new engine::Engine);
game->Play();
mango::archive::mgetch(); //Wrapper for _getch()
return 0;
}
And here is a picture of the running game:
As I already mentioned, feel free to reuse the code.
namespace std
intonamespace mango
? Or are you using custom versions ofshared_ptr
, etc? \$\endgroup\$