// ----------------------------------------------------------------
// From Game Programming in C++ by Sanjay Madhav
// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
//
// Released under the BSD License
// See LICENSE in root directory for full details.
// ----------------------------------------------------------------
#include "Game.h"
#include
#include "Renderer.h"
#include "Actor.h"
#include "SpriteComponent.h"
#include "MeshComponent.h"
#include "CameraActor.h"
#include "PlaneActor.h"
Game::Game()
:mRenderer(nullptr)
,mIsRunning(true)
,mUpdatingActors(false)
{
}
bool Game::Initialize()
{
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0)
{
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return false;
}
// Create the renderer
mRenderer = new Renderer(this);
if (!mRenderer->Initialize(1024.0f, 768.0f))
{
SDL_Log("Failed to initialize renderer");
delete mRenderer;
mRenderer = nullptr;
return false;
}
LoadData();
mTicksCount = SDL_GetTicks();
return true;
}
void Game::RunLoop()
{
while (mIsRunning)
{
ProcessInput();
UpdateGame();
GenerateOutput();
}
}
void Game::ProcessInput()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
mIsRunning = false;
break;
}
}
const Uint8* state = SDL_GetKeyboardState(NULL);
if (state[SDL_SCANCODE_ESCAPE])
{
mIsRunning = false;
}
for (auto actor : mActors)
{
actor->ProcessInput(state);
}
}
void Game::UpdateGame()
{
// Compute delta time
// Wait until 16ms has elapsed since last frame
while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16))
;
float deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
if (deltaTime > 0.05f)
{
deltaTime = 0.05f;
}
mTicksCount = SDL_GetTicks();
// Update all actors
mUpdatingActors = true;
for (auto actor : mActors)
{
actor->Update(deltaTime);
}
mUpdatingActors = false;
// Move any pending actors to mActors
for (auto pending : mPendingActors)
{
pending->ComputeWorldTransform();
mActors.emplace_back(pending);
}
mPendingActors.clear();
// Add any dead actors to a temp vector
std::vector deadActors;
for (auto actor : mActors)
{
if (actor->GetState() == Actor::EDead)
{
deadActors.emplace_back(actor);
}
}
// Delete dead actors (which removes them from mActors)
for (auto actor : deadActors)
{
delete actor;
}
}
void Game::GenerateOutput()
{
mRenderer->Draw();
}
void Game::LoadData()
{
// Create actors
Actor* a = new Actor(this);
a->SetPosition(Vector3(200.0f, 75.0f, 0.0f));
a->SetScale(100.0f);
Quaternion q(Vector3::UnitY, -Math::PiOver2);
q = Quaternion::Concatenate(q, Quaternion(Vector3::UnitZ, Math::Pi + Math::Pi / 4.0f));
a->SetRotation(q);
MeshComponent* mc = new MeshComponent(a);
mc->SetMesh(mRenderer->GetMesh("Assets/Cube.gpmesh"));
a = new Actor(this);
a->SetPosition(Vector3(200.0f, -75.0f, 0.0f));
a->SetScale(3.0f);
mc = new MeshComponent(a);
mc->SetMesh(mRenderer->GetMesh("Assets/Sphere.gpmesh"));
// Setup floor
const float start = -1250.0f;
const float size = 250.0f;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
a = new PlaneActor(this);
a->SetPosition(Vector3(start + i * size, start + j * size, -100.0f));
}
}
// Left/right walls
q = Quaternion(Vector3::UnitX, Math::PiOver2);
for (int i = 0; i < 10; i++)
{
a = new PlaneActor(this);
a->SetPosition(Vector3(start + i * size, start - size, 0.0f));
a->SetRotation(q);
a = new PlaneActor(this);
a->SetPosition(Vector3(start + i * size, -start + size, 0.0f));
a->SetRotation(q);
}
q = Quaternion::Concatenate(q, Quaternion(Vector3::UnitZ, Math::PiOver2));
// Forward/back walls
for (int i = 0; i < 10; i++)
{
a = new PlaneActor(this);
a->SetPosition(Vector3(start - size, start + i * size, 0.0f));
a->SetRotation(q);
a = new PlaneActor(this);
a->SetPosition(Vector3(-start + size, start + i * size, 0.0f));
a->SetRotation(q);
}
// Setup lights
mRenderer->SetAmbientLight(Vector3(0.2f, 0.2f, 0.2f));
DirectionalLight& dir = mRenderer->GetDirectionalLight();
dir.mDirection = Vector3(0.0f, -0.707f, -0.707f);
dir.mDiffuseColor = Vector3(0.78f, 0.88f, 1.0f);
dir.mSpecColor = Vector3(0.8f, 0.8f, 0.8f);
// Camera actor
mCameraActor = new CameraActor(this);
// UI elements
a = new Actor(this);
a->SetPosition(Vector3(-350.0f, -350.0f, 0.0f));
SpriteComponent* sc = new SpriteComponent(a);
sc->SetTexture(mRenderer->GetTexture("Assets/HealthBar.png"));
a = new Actor(this);
a->SetPosition(Vector3(375.0f, -275.0f, 0.0f));
a->SetScale(0.75f);
sc = new SpriteComponent(a);
sc->SetTexture(mRenderer->GetTexture("Assets/Radar.png"));
}
void Game::UnloadData()
{
// Delete actors
// Because ~Actor calls RemoveActor, have to use a different style loop
while (!mActors.empty())
{
delete mActors.back();
}
if (mRenderer)
{
mRenderer->UnloadData();
}
}
void Game::Shutdown()
{
UnloadData();
if (mRenderer)
{
mRenderer->Shutdown();
}
SDL_Quit();
}
void Game::AddActor(Actor* actor)
{
// If we're updating actors, need to add to pending
if (mUpdatingActors)
{
mPendingActors.emplace_back(actor);
}
else
{
mActors.emplace_back(actor);
}
}
void Game::RemoveActor(Actor* actor)
{
// Is it in pending actors?
auto iter = std::find(mPendingActors.begin(), mPendingActors.end(), actor);
if (iter != mPendingActors.end())
{
// Swap to end of vector and pop off (avoid erase copies)
std::iter_swap(iter, mPendingActors.end() - 1);
mPendingActors.pop_back();
}
// Is it in actors?
iter = std::find(mActors.begin(), mActors.end(), actor);
if (iter != mActors.end())
{
// Swap to end of vector and pop off (avoid erase copies)
std::iter_swap(iter, mActors.end() - 1);
mActors.pop_back();
}
}