C++ game engine
Uses SDL for rendering 2D games. Dig Dug was recreated inside the engine.
The engine uses components, commands, observers, a service locator, and the state pattern. The renderer and managers come from the service locator. There are two state machines: player and AI. The AI state machine runs with multithreading. Commands come from a command factory. Components they affect are passed with dependency injection.
void dae::AiComponent::Update(float dt)
{
if (firstFrame)
{
firstFrame = false;
result = std::future < std::pair<bool, BaseAiState *>>(std::async(&BaseAiState::Update, m_CurrentState.get(), dt));
return;
}
auto newstate = result.get();
if (newstate.first)
m_CurrentState.reset(newstate.second);
result = std::future < std::pair<bool, BaseAiState *>>(std::async(&BaseAiState::Update, m_CurrentState.get(), dt));
}
std::pair<bool, dae::BaseAiState*> dae::IdleAiState::Update(float dt)
{
m_ElapsedTime += dt;
if (m_ElapsedTime > 0.4 && m_Owner.m_AnimationComponent.m_ActiveAnimationId <= 2)
{
m_ElapsedTime = 0;
if (m_Owner.m_EnemyType == AiTypes::Fygar)
{
if (rand() % (int)(1 / m_ChanceToFire) == 0)
{
m_Owner.m_FireBreathComponent->BreathFire();
return { true, new FireBreathAiState(m_Owner) };
}
}
if (rand() % (int)(1 / m_ChanceToGhost) == 0)
{
const auto Commander = ServiceLocator::GetCommandFactory();
Commander->AiGhost() (&m_Owner.m_AnimationComponent);
Commander->ToggleCollision()(false, &m_Owner.m_CollisionComponent);
return { true, new GhostAiState(m_Owner) };
}
if (rand() % (int)(1 / m_ChanceToMove) == 0)
return { true, new MovingAiState(m_Owner) };
}
return { false, nullptr };
};
Each game object is a vector of components: position, movement, input, physics, and so on. The collision component uses the physics manager to test overlaps.
inline bool CheckBoxesIntersect(Box a, Box b) const noexcept { return (abs((a.x ) - b.x) * 2 < (a.width + b.width)) && (abs(a.y - b.y) * 2 < (a.height + b.height));}
namespace dae {
struct Box {
float x;
float y;
int width;
int height;
Box(float xpos, float ypos, int w = g_blocksize, int h = g_blocksize) :
x(xpos ), y(ypos ), width(w), height(h)
{}
};
class PhysicsManager {
public:
PhysicsManager() ;
~PhysicsManager() = default ;
void InitActiveComponents();
CollisionFlags CheckPlayerCollision(dae::Vec2);
std::pair<bool, dae::CollisionComponent *> CheckCollision(dae::Vec2 pos);
std::pair<dae::CollisionFlags, dae::GameObject* > CheckHoseCollision(dae::Vec2 pos, dae::Vec2 size);
std::pair<dae::CollisionFlags, dae::GameObject*> CheckFireCollision(Box b);
inline bool CheckBoxesIntersect(Box a, Box b) const noexcept { return (abs((a.x ) - b.x) * 2 < (a.width + b.width)) && (abs(a.y - b.y) * 2 < (a.height + b.height));}
std::vector<std::pair<PositionComponent *,GameObject*>> m_Pair_PosComp_GameObj;
};
}
void dae::PhysicsManager::InitActiveComponents()
{
m_Pair_PosComp_GameObj.clear();
auto sceneMngr = ServiceLocator::GetSceneManager();
auto scene = sceneMngr->GetActiveScene();
auto sObjects = scene->GetSceneObjects();
for (auto sObj : sObjects)
{
GameObject * gObj = static_cast<GameObject *> (sObj.get());
if (gObj != nullptr)
{
auto cComp = gObj->GetComponent<CollisionComponent>();
if (cComp == nullptr)
continue;;
auto pComp = (gObj->GetComponent<PositionComponent>());
m_Pair_PosComp_GameObj.push_back({ pComp,gObj });
}
}
}
dae::CollisionFlags dae::PhysicsManager::CheckPlayerCollision(dae::Vec2 pos)
{
auto pos1 = pos;
auto Box1 = Box((float)pos1.x + 16, (float)pos1.y + 16);
for (auto pair : m_Pair_PosComp_GameObj)
{
auto posComp2 = pair.first;
if (posComp2->GetPosition().x == pos1.x && posComp2->GetPosition().y == pos1.y)
continue;
auto pos2 = posComp2->GetPosition();
auto Box2 = Box((float)pos2.x + 16, (float)pos2.y + 16);
Box2.height = 16; Box2.width = 16;
if (CheckBoxesIntersect(Box1, Box2))
{ //could optimize this by changing pair into tuple with collisioncomp
auto colComp = pair.second->GetComponent<CollisionComponent>();
if (colComp->m_CanCollide)
return (colComp->m_CollisionCategoryFlags);
}
}
return NoCollision;
}
bool dae::CollisionComponent::CheckCollision(dae::Vec2 pos)
{
if (m_CanCollide && m_CollisionCategoryFlags == CollisionFlags::Player)
{
auto pair = m_PhysicsManager->CheckCollision(pos);
if (pair.first && pair.second->m_CanCollide)
{
switch (pair.second->m_CollisionCategoryFlags)
{
case Enemy:
case FallingRock:
m_CommandComponent.Death();
case Static:
return true;
break;
case Player:
default:
return false;
break;
};
}
}
}