| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625 |
- // ----------------------------------------------------------------
- // 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 <algorithm>
- #include "Renderer.h"
- #include "AudioSystem.h"
- #include "PhysWorld.h"
- #include "Actor.h"
- #include "UIScreen.h"
- #include "HUD.h"
- #include "MeshComponent.h"
- #include "FollowActor.h"
- #include "PlaneActor.h"
- #include "TargetActor.h"
- #include "BallActor.h"
- #include "PauseMenu.h"
- #include <SDL/SDL.h>
- #include <SDL/SDL_ttf.h>
- #include "Font.h"
- #include <fstream>
- #include <sstream>
- #include <rapidjson/document.h>
- #include "Skeleton.h"
- #include "Animation.h"
- #include "PointLightComponent.h"
- Game::Game()
- :mRenderer(nullptr)
- ,mAudioSystem(nullptr)
- ,mPhysWorld(nullptr)
- ,mGameState(EGameplay)
- ,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;
- }
- // Create the audio system
- mAudioSystem = new AudioSystem(this);
- if (!mAudioSystem->Initialize())
- {
- SDL_Log("Failed to initialize audio system");
- mAudioSystem->Shutdown();
- delete mAudioSystem;
- mAudioSystem = nullptr;
- return false;
- }
- // Create the physics world
- mPhysWorld = new PhysWorld(this);
-
- // Initialize SDL_ttf
- if (TTF_Init() != 0)
- {
- SDL_Log("Failed to initialize SDL_ttf");
- return false;
- }
- LoadData();
- mTicksCount = SDL_GetTicks();
-
- return true;
- }
- void Game::RunLoop()
- {
- while (mGameState != EQuit)
- {
- ProcessInput();
- UpdateGame();
- GenerateOutput();
- }
- }
- void Game::AddPlane(PlaneActor* plane)
- {
- mPlanes.emplace_back(plane);
- }
- void Game::RemovePlane(PlaneActor* plane)
- {
- auto iter = std::find(mPlanes.begin(), mPlanes.end(), plane);
- mPlanes.erase(iter);
- }
- void Game::ProcessInput()
- {
- SDL_Event event;
- while (SDL_PollEvent(&event))
- {
- switch (event.type)
- {
- case SDL_QUIT:
- mGameState = EQuit;
- break;
- // This fires when a key's initially pressed
- case SDL_KEYDOWN:
- if (!event.key.repeat)
- {
- if (mGameState == EGameplay)
- {
- HandleKeyPress(event.key.keysym.sym);
- }
- else if (!mUIStack.empty())
- {
- mUIStack.back()->
- HandleKeyPress(event.key.keysym.sym);
- }
- }
- break;
- case SDL_MOUSEBUTTONDOWN:
- if (mGameState == EGameplay)
- {
- HandleKeyPress(event.button.button);
- }
- else if (!mUIStack.empty())
- {
- mUIStack.back()->
- HandleKeyPress(event.button.button);
- }
- break;
- default:
- break;
- }
- }
-
- const Uint8* state = SDL_GetKeyboardState(NULL);
- if (mGameState == EGameplay)
- {
- for (auto actor : mActors)
- {
- if (actor->GetState() == Actor::EActive)
- {
- actor->ProcessInput(state);
- }
- }
- }
- else if (!mUIStack.empty())
- {
- mUIStack.back()->ProcessInput(state);
- }
- }
- void Game::HandleKeyPress(int key)
- {
- switch (key)
- {
- case SDLK_ESCAPE:
- // Create pause menu
- new PauseMenu(this);
- break;
- case '-':
- {
- // Reduce master volume
- float volume = mAudioSystem->GetBusVolume("bus:/");
- volume = Math::Max(0.0f, volume - 0.1f);
- mAudioSystem->SetBusVolume("bus:/", volume);
- break;
- }
- case '=':
- {
- // Increase master volume
- float volume = mAudioSystem->GetBusVolume("bus:/");
- volume = Math::Min(1.0f, volume + 0.1f);
- mAudioSystem->SetBusVolume("bus:/", volume);
- break;
- }
- case '1':
- {
- // Load English text
- LoadText("Assets/English.gptext");
- break;
- }
- case '2':
- {
- // Load Russian text
- LoadText("Assets/Russian.gptext");
- break;
- }
- case SDL_BUTTON_LEFT:
- {
- break;
- }
- default:
- break;
- }
- }
- 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();
- if (mGameState == EGameplay)
- {
- // 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<Actor*> 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;
- }
- }
- // Update audio system
- mAudioSystem->Update(deltaTime);
-
- // Update UI screens
- for (auto ui : mUIStack)
- {
- if (ui->GetState() == UIScreen::EActive)
- {
- ui->Update(deltaTime);
- }
- }
- // Delete any UIScreens that are closed
- auto iter = mUIStack.begin();
- while (iter != mUIStack.end())
- {
- if ((*iter)->GetState() == UIScreen::EClosing)
- {
- delete *iter;
- iter = mUIStack.erase(iter);
- }
- else
- {
- ++iter;
- }
- }
- }
- void Game::GenerateOutput()
- {
- mRenderer->Draw();
- }
- void Game::LoadData()
- {
- // Load English text
- LoadText("Assets/English.gptext");
- // Create actors
- Actor* a = nullptr;
- Quaternion q;
- //MeshComponent* mc = nullptr;
- // 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);
- Vector3 pos = Vector3(start + i * size, start + j * size, -100.0f);
- a->SetPosition(pos);
- // Create some point lights
- a = new Actor(this);
- pos.z += 100.0f;
- a->SetPosition(pos);
- PointLightComponent* p = new PointLightComponent(a);
- Vector3 color;
- switch ((i + j) % 5)
- {
- case 0:
- color = Color::Green;
- break;
- case 1:
- color = Color::Blue;
- break;
- case 2:
- color = Color::Red;
- break;
- case 3:
- color = Color::Yellow;
- break;
- case 4:
- color = Color::LightPink;
- break;
- }
- p->mDiffuseColor = color;
- p->mInnerRadius = 100.0f;
- p->mOuterRadius = 200.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.4f, 0.4f, 0.4f));
- 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);
- // UI elements
- mHUD = new HUD(this);
-
- // Start music
- mMusicEvent = mAudioSystem->PlayEvent("event:/Music");
- // Enable relative mouse mode for camera look
- SDL_SetRelativeMouseMode(SDL_TRUE);
- // Make an initial call to get relative to clear out
- SDL_GetRelativeMouseState(nullptr, nullptr);
- // Different camera actors
- mFollowActor = new FollowActor(this);
- // Create target actors
- a = new TargetActor(this);
- a->SetPosition(Vector3(1450.0f, 0.0f, 100.0f));
- a = new TargetActor(this);
- a->SetPosition(Vector3(1450.0f, 0.0f, 400.0f));
- a = new TargetActor(this);
- a->SetPosition(Vector3(1450.0f, -500.0f, 200.0f));
- a = new TargetActor(this);
- a->SetPosition(Vector3(1450.0f, 500.0f, 200.0f));
- a = new TargetActor(this);
- a->SetPosition(Vector3(0.0f, -1450.0f, 200.0f));
- a->SetRotation(Quaternion(Vector3::UnitZ, Math::PiOver2));
- a = new TargetActor(this);
- a->SetPosition(Vector3(0.0f, 1450.0f, 200.0f));
- a->SetRotation(Quaternion(Vector3::UnitZ, -Math::PiOver2));
- }
- void Game::UnloadData()
- {
- // Delete actors
- // Because ~Actor calls RemoveActor, have to use a different style loop
- while (!mActors.empty())
- {
- delete mActors.back();
- }
- // Clear the UI stack
- while (!mUIStack.empty())
- {
- delete mUIStack.back();
- mUIStack.pop_back();
- }
- if (mRenderer)
- {
- mRenderer->UnloadData();
- }
- // Unload fonts
- for (auto f : mFonts)
- {
- f.second->Unload();
- delete f.second;
- }
- // Unload skeletons
- for (auto s : mSkeletons)
- {
- delete s.second;
- }
- // Unload animations
- for (auto a : mAnims)
- {
- delete a.second;
- }
- }
- void Game::Shutdown()
- {
- UnloadData();
- TTF_Quit();
- delete mPhysWorld;
- if (mRenderer)
- {
- mRenderer->Shutdown();
- }
- if (mAudioSystem)
- {
- mAudioSystem->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();
- }
- }
- void Game::PushUI(UIScreen* screen)
- {
- mUIStack.emplace_back(screen);
- }
- Font* Game::GetFont(const std::string& fileName)
- {
- auto iter = mFonts.find(fileName);
- if (iter != mFonts.end())
- {
- return iter->second;
- }
- else
- {
- Font* font = new Font(this);
- if (font->Load(fileName))
- {
- mFonts.emplace(fileName, font);
- }
- else
- {
- font->Unload();
- delete font;
- font = nullptr;
- }
- return font;
- }
- }
- void Game::LoadText(const std::string& fileName)
- {
- // Clear the existing map, if already loaded
- mText.clear();
- // Try to open the file
- std::ifstream file(fileName);
- if (!file.is_open())
- {
- SDL_Log("Text file %s not found", fileName.c_str());
- return;
- }
- // Read the entire file to a string stream
- std::stringstream fileStream;
- fileStream << file.rdbuf();
- std::string contents = fileStream.str();
- // Open this file in rapidJSON
- rapidjson::StringStream jsonStr(contents.c_str());
- rapidjson::Document doc;
- doc.ParseStream(jsonStr);
- if (!doc.IsObject())
- {
- SDL_Log("Text file %s is not valid JSON", fileName.c_str());
- return;
- }
- // Parse the text map
- const rapidjson::Value& actions = doc["TextMap"];
- for (rapidjson::Value::ConstMemberIterator itr = actions.MemberBegin();
- itr != actions.MemberEnd(); ++itr)
- {
- if (itr->name.IsString() && itr->value.IsString())
- {
- mText.emplace(itr->name.GetString(),
- itr->value.GetString());
- }
- }
- }
- const std::string& Game::GetText(const std::string& key)
- {
- static std::string errorMsg("**KEY NOT FOUND**");
- // Find this text in the map, if it exists
- auto iter = mText.find(key);
- if (iter != mText.end())
- {
- return iter->second;
- }
- else
- {
- return errorMsg;
- }
- }
- Skeleton* Game::GetSkeleton(const std::string& fileName)
- {
- auto iter = mSkeletons.find(fileName);
- if (iter != mSkeletons.end())
- {
- return iter->second;
- }
- else
- {
- Skeleton* sk = new Skeleton();
- if (sk->Load(fileName))
- {
- mSkeletons.emplace(fileName, sk);
- }
- else
- {
- delete sk;
- sk = nullptr;
- }
- return sk;
- }
- }
- Animation* Game::GetAnimation(const std::string& fileName)
- {
- auto iter = mAnims.find(fileName);
- if (iter != mAnims.end())
- {
- return iter->second;
- }
- else
- {
- Animation* anim = new Animation();
- if (anim->Load(fileName))
- {
- mAnims.emplace(fileName, anim);
- }
- else
- {
- delete anim;
- anim = nullptr;
- }
- return anim;
- }
- }
|