C++ Game engine

Uses SDL for rendering 2D games. Dig Dug was recreated inside the engine.


The engine makes use of components, commands, observers, a service locator and the state pattern. The renderer and different managers are provided with the service locator. There are 2 different state machines for the player and AI. The AI state machine is implmenented with multithreading. Commands are provided through the command factory. The components on which they have an effect are passed with dependency injection. Here you can see the AiComponent.cpp and AiStates.cpp

AiComponent.cpp


            void dae::AiComponent::Update(float dt)
            {
              if (firstFrame)
              {
                firstFrame = false; 
                result = std::future < std::pair>(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>(std::async(&BaseAiState::Update, m_CurrentState.get(), dt));
            }
            

AiStates.cpp


            std::pair 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 gameobject is a vector of components. These components handle things such as position, movement, input, physics,... The collision component uses the physicsmanager to check for collisions. Below you can find a part of the CollisionComponent.cpp, PhysicsManager.h and PhysicsManager.cpp files.

PhysicsManager.h


            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 CheckCollision(dae::Vec2 pos);

                  std::pair CheckHoseCollision(dae::Vec2 pos, dae::Vec2 size);

                  std::pair 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> m_Pair_PosComp_GameObj;
                };
              }
            

PhysicsManager.cpp


            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 (sObj.get()); 
                if (gObj != nullptr)
                {
                  auto cComp = gObj->GetComponent();
                  if (cComp == nullptr)
                    continue;;
                  auto pComp = (gObj->GetComponent());
                  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();
                  if (colComp->m_CanCollide)
                    return (colComp->m_CollisionCategoryFlags); 
                }
                  
              

              }
              return NoCollision;
            }
            

CollisionComponent.cpp


            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;
                  };
                }
                
              }
            }