Game.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. // ----------------------------------------------------------------
  2. // From Game Programming in C++ by Sanjay Madhav
  3. // Copyright (C) 2017 Sanjay Madhav. All rights reserved.
  4. //
  5. // Released under the BSD License
  6. // See LICENSE in root directory for full details.
  7. // ----------------------------------------------------------------
  8. #include "Game.h"
  9. #include "SDL/SDL_image.h"
  10. #include <algorithm>
  11. #include "Actor.h"
  12. #include "SpriteComponent.h"
  13. #include "Grid.h"
  14. #include "Enemy.h"
  15. #include "AIComponent.h"
  16. #include "AIState.h"
  17. Game::Game()
  18. :mWindow(nullptr)
  19. ,mRenderer(nullptr)
  20. ,mIsRunning(true)
  21. ,mUpdatingActors(false)
  22. {
  23. }
  24. bool Game::Initialize()
  25. {
  26. if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0)
  27. {
  28. SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
  29. return false;
  30. }
  31. mWindow = SDL_CreateWindow("Game Programming in C++ (Chapter 4)", 100, 100, 1024, 768, 0);
  32. if (!mWindow)
  33. {
  34. SDL_Log("Failed to create window: %s", SDL_GetError());
  35. return false;
  36. }
  37. mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
  38. if (!mRenderer)
  39. {
  40. SDL_Log("Failed to create renderer: %s", SDL_GetError());
  41. return false;
  42. }
  43. if (IMG_Init(IMG_INIT_PNG) == 0)
  44. {
  45. SDL_Log("Unable to initialize SDL_image: %s", SDL_GetError());
  46. return false;
  47. }
  48. LoadData();
  49. mTicksCount = SDL_GetTicks();
  50. return true;
  51. }
  52. void Game::RunLoop()
  53. {
  54. while (mIsRunning)
  55. {
  56. ProcessInput();
  57. UpdateGame();
  58. GenerateOutput();
  59. }
  60. }
  61. void Game::ProcessInput()
  62. {
  63. SDL_Event event;
  64. while (SDL_PollEvent(&event))
  65. {
  66. switch (event.type)
  67. {
  68. case SDL_QUIT:
  69. mIsRunning = false;
  70. break;
  71. }
  72. }
  73. const Uint8* keyState = SDL_GetKeyboardState(NULL);
  74. if (keyState[SDL_SCANCODE_ESCAPE])
  75. {
  76. mIsRunning = false;
  77. }
  78. if (keyState[SDL_SCANCODE_B])
  79. {
  80. mGrid->BuildTower();
  81. }
  82. // Process mouse
  83. int x, y;
  84. Uint32 buttons = SDL_GetMouseState(&x, &y);
  85. if (SDL_BUTTON(buttons) & SDL_BUTTON_LEFT)
  86. {
  87. mGrid->ProcessClick(x, y);
  88. }
  89. mUpdatingActors = true;
  90. for (auto actor : mActors)
  91. {
  92. actor->ProcessInput(keyState);
  93. }
  94. mUpdatingActors = false;
  95. }
  96. void Game::UpdateGame()
  97. {
  98. // Compute delta time
  99. // Wait until 16ms has elapsed since last frame
  100. while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16))
  101. ;
  102. float deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
  103. if (deltaTime > 0.05f)
  104. {
  105. deltaTime = 0.05f;
  106. }
  107. mTicksCount = SDL_GetTicks();
  108. // Update all actors
  109. mUpdatingActors = true;
  110. for (auto actor : mActors)
  111. {
  112. actor->Update(deltaTime);
  113. }
  114. mUpdatingActors = false;
  115. // Move any pending actors to mActors
  116. for (auto pending : mPendingActors)
  117. {
  118. mActors.emplace_back(pending);
  119. }
  120. mPendingActors.clear();
  121. // Add any dead actors to a temp vector
  122. std::vector<Actor*> deadActors;
  123. for (auto actor : mActors)
  124. {
  125. if (actor->GetState() == Actor::EDead)
  126. {
  127. deadActors.emplace_back(actor);
  128. }
  129. }
  130. // Delete dead actors (which removes them from mActors)
  131. for (auto actor : deadActors)
  132. {
  133. delete actor;
  134. }
  135. }
  136. void Game::GenerateOutput()
  137. {
  138. SDL_SetRenderDrawColor(mRenderer, 34, 139, 34, 255);
  139. SDL_RenderClear(mRenderer);
  140. // Draw all sprite components
  141. for (auto sprite : mSprites)
  142. {
  143. sprite->Draw(mRenderer);
  144. }
  145. SDL_RenderPresent(mRenderer);
  146. }
  147. void Game::LoadData()
  148. {
  149. mGrid = new Grid(this);
  150. // For testing AIComponent
  151. //Actor* a = new Actor(this);
  152. //AIComponent* aic = new AIComponent(a);
  153. //// Register states with AIComponent
  154. //aic->RegisterState(new AIPatrol(aic));
  155. //aic->RegisterState(new AIDeath(aic));
  156. //aic->RegisterState(new AIAttack(aic));
  157. //// Start in patrol state
  158. //aic->ChangeState("Patrol");
  159. }
  160. void Game::UnloadData()
  161. {
  162. // Delete actors
  163. // Because ~Actor calls RemoveActor, have to use a different style loop
  164. while (!mActors.empty())
  165. {
  166. delete mActors.back();
  167. }
  168. // Destroy textures
  169. for (auto i : mTextures)
  170. {
  171. SDL_DestroyTexture(i.second);
  172. }
  173. mTextures.clear();
  174. }
  175. SDL_Texture* Game::GetTexture(const std::string& fileName)
  176. {
  177. SDL_Texture* tex = nullptr;
  178. // Is the texture already in the map?
  179. auto iter = mTextures.find(fileName);
  180. if (iter != mTextures.end())
  181. {
  182. tex = iter->second;
  183. }
  184. else
  185. {
  186. // Load from file
  187. SDL_Surface* surf = IMG_Load(fileName.c_str());
  188. if (!surf)
  189. {
  190. SDL_Log("Failed to load texture file %s", fileName.c_str());
  191. return nullptr;
  192. }
  193. // Create texture from surface
  194. tex = SDL_CreateTextureFromSurface(mRenderer, surf);
  195. SDL_FreeSurface(surf);
  196. if (!tex)
  197. {
  198. SDL_Log("Failed to convert surface to texture for %s", fileName.c_str());
  199. return nullptr;
  200. }
  201. mTextures.emplace(fileName.c_str(), tex);
  202. }
  203. return tex;
  204. }
  205. void Game::Shutdown()
  206. {
  207. UnloadData();
  208. IMG_Quit();
  209. SDL_DestroyRenderer(mRenderer);
  210. SDL_DestroyWindow(mWindow);
  211. SDL_Quit();
  212. }
  213. void Game::AddActor(Actor* actor)
  214. {
  215. // If we're updating actors, need to add to pending
  216. if (mUpdatingActors)
  217. {
  218. mPendingActors.emplace_back(actor);
  219. }
  220. else
  221. {
  222. mActors.emplace_back(actor);
  223. }
  224. }
  225. void Game::RemoveActor(Actor* actor)
  226. {
  227. // Is it in pending actors?
  228. auto iter = std::find(mPendingActors.begin(), mPendingActors.end(), actor);
  229. if (iter != mPendingActors.end())
  230. {
  231. // Swap to end of vector and pop off (avoid erase copies)
  232. std::iter_swap(iter, mPendingActors.end() - 1);
  233. mPendingActors.pop_back();
  234. }
  235. // Is it in actors?
  236. iter = std::find(mActors.begin(), mActors.end(), actor);
  237. if (iter != mActors.end())
  238. {
  239. // Swap to end of vector and pop off (avoid erase copies)
  240. std::iter_swap(iter, mActors.end() - 1);
  241. mActors.pop_back();
  242. }
  243. }
  244. void Game::AddSprite(SpriteComponent* sprite)
  245. {
  246. // Find the insertion point in the sorted vector
  247. // (The first element with a higher draw order than me)
  248. int myDrawOrder = sprite->GetDrawOrder();
  249. auto iter = mSprites.begin();
  250. for ( ;
  251. iter != mSprites.end();
  252. ++iter)
  253. {
  254. if (myDrawOrder < (*iter)->GetDrawOrder())
  255. {
  256. break;
  257. }
  258. }
  259. // Inserts element before position of iterator
  260. mSprites.insert(iter, sprite);
  261. }
  262. void Game::RemoveSprite(SpriteComponent* sprite)
  263. {
  264. // (We can't swap because it ruins ordering)
  265. auto iter = std::find(mSprites.begin(), mSprites.end(), sprite);
  266. mSprites.erase(iter);
  267. }
  268. Enemy* Game::GetNearestEnemy(const Vector2& pos)
  269. {
  270. Enemy* best = nullptr;
  271. if (mEnemies.size() > 0)
  272. {
  273. best = mEnemies[0];
  274. // Save the distance squared of first enemy, and test if others are closer
  275. float bestDistSq = (pos - mEnemies[0]->GetPosition()).LengthSq();
  276. for (size_t i = 1; i < mEnemies.size(); i++)
  277. {
  278. float newDistSq = (pos - mEnemies[i]->GetPosition()).LengthSq();
  279. if (newDistSq < bestDistSq)
  280. {
  281. bestDistSq = newDistSq;
  282. best = mEnemies[i];
  283. }
  284. }
  285. }
  286. return best;
  287. }