Game.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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 "Ship.h"
  14. #include "Asteroid.h"
  15. #include "Random.h"
  16. Game::Game()
  17. :mWindow(nullptr)
  18. ,mRenderer(nullptr)
  19. ,mIsRunning(true)
  20. ,mUpdatingActors(false)
  21. {
  22. }
  23. bool Game::Initialize()
  24. {
  25. if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0)
  26. {
  27. SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
  28. return false;
  29. }
  30. mWindow = SDL_CreateWindow("Game Programming in C++ (Chapter 3)", 100, 100, 1024, 768, 0);
  31. if (!mWindow)
  32. {
  33. SDL_Log("Failed to create window: %s", SDL_GetError());
  34. return false;
  35. }
  36. mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
  37. if (!mRenderer)
  38. {
  39. SDL_Log("Failed to create renderer: %s", SDL_GetError());
  40. return false;
  41. }
  42. if (IMG_Init(IMG_INIT_PNG) == 0)
  43. {
  44. SDL_Log("Unable to initialize SDL_image: %s", SDL_GetError());
  45. return false;
  46. }
  47. Random::Init();
  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. mUpdatingActors = true;
  79. for (auto actor : mActors)
  80. {
  81. actor->ProcessInput(keyState);
  82. }
  83. mUpdatingActors = false;
  84. }
  85. void Game::UpdateGame()
  86. {
  87. // Compute delta time
  88. // Wait until 16ms has elapsed since last frame
  89. while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16))
  90. ;
  91. float deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
  92. if (deltaTime > 0.05f)
  93. {
  94. deltaTime = 0.05f;
  95. }
  96. mTicksCount = SDL_GetTicks();
  97. // Update all actors
  98. mUpdatingActors = true;
  99. for (auto actor : mActors)
  100. {
  101. actor->Update(deltaTime);
  102. }
  103. mUpdatingActors = false;
  104. // Move any pending actors to mActors
  105. for (auto pending : mPendingActors)
  106. {
  107. mActors.emplace_back(pending);
  108. }
  109. mPendingActors.clear();
  110. // Add any dead actors to a temp vector
  111. std::vector<Actor*> deadActors;
  112. for (auto actor : mActors)
  113. {
  114. if (actor->GetState() == Actor::EDead)
  115. {
  116. deadActors.emplace_back(actor);
  117. }
  118. }
  119. // Delete dead actors (which removes them from mActors)
  120. for (auto actor : deadActors)
  121. {
  122. delete actor;
  123. }
  124. }
  125. void Game::GenerateOutput()
  126. {
  127. SDL_SetRenderDrawColor(mRenderer, 220, 220, 220, 255);
  128. SDL_RenderClear(mRenderer);
  129. // Draw all sprite components
  130. for (auto sprite : mSprites)
  131. {
  132. sprite->Draw(mRenderer);
  133. }
  134. SDL_RenderPresent(mRenderer);
  135. }
  136. void Game::LoadData()
  137. {
  138. // Create player's ship
  139. mShip = new Ship(this);
  140. mShip->SetPosition(Vector2(512.0f, 384.0f));
  141. mShip->SetRotation(Math::PiOver2);
  142. // Create asteroids
  143. const int numAsteroids = 20;
  144. for (int i = 0; i < numAsteroids; i++)
  145. {
  146. new Asteroid(this);
  147. }
  148. }
  149. void Game::UnloadData()
  150. {
  151. // Delete actors
  152. // Because ~Actor calls RemoveActor, have to use a different style loop
  153. while (!mActors.empty())
  154. {
  155. delete mActors.back();
  156. }
  157. // Destroy textures
  158. for (auto i : mTextures)
  159. {
  160. SDL_DestroyTexture(i.second);
  161. }
  162. mTextures.clear();
  163. }
  164. SDL_Texture* Game::GetTexture(const std::string& fileName)
  165. {
  166. SDL_Texture* tex = nullptr;
  167. // Is the texture already in the map?
  168. auto iter = mTextures.find(fileName);
  169. if (iter != mTextures.end())
  170. {
  171. tex = iter->second;
  172. }
  173. else
  174. {
  175. // Load from file
  176. SDL_Surface* surf = IMG_Load(fileName.c_str());
  177. if (!surf)
  178. {
  179. SDL_Log("Failed to load texture file %s", fileName.c_str());
  180. return nullptr;
  181. }
  182. // Create texture from surface
  183. tex = SDL_CreateTextureFromSurface(mRenderer, surf);
  184. SDL_FreeSurface(surf);
  185. if (!tex)
  186. {
  187. SDL_Log("Failed to convert surface to texture for %s", fileName.c_str());
  188. return nullptr;
  189. }
  190. mTextures.emplace(fileName.c_str(), tex);
  191. }
  192. return tex;
  193. }
  194. void Game::AddAsteroid(Asteroid* ast)
  195. {
  196. mAsteroids.emplace_back(ast);
  197. }
  198. void Game::RemoveAsteroid(Asteroid* ast)
  199. {
  200. auto iter = std::find(mAsteroids.begin(),
  201. mAsteroids.end(), ast);
  202. if (iter != mAsteroids.end())
  203. {
  204. mAsteroids.erase(iter);
  205. }
  206. }
  207. void Game::Shutdown()
  208. {
  209. UnloadData();
  210. IMG_Quit();
  211. SDL_DestroyRenderer(mRenderer);
  212. SDL_DestroyWindow(mWindow);
  213. SDL_Quit();
  214. }
  215. void Game::AddActor(Actor* actor)
  216. {
  217. // If we're updating actors, need to add to pending
  218. if (mUpdatingActors)
  219. {
  220. mPendingActors.emplace_back(actor);
  221. }
  222. else
  223. {
  224. mActors.emplace_back(actor);
  225. }
  226. }
  227. void Game::RemoveActor(Actor* actor)
  228. {
  229. // Is it in pending actors?
  230. auto iter = std::find(mPendingActors.begin(), mPendingActors.end(), actor);
  231. if (iter != mPendingActors.end())
  232. {
  233. // Swap to end of vector and pop off (avoid erase copies)
  234. std::iter_swap(iter, mPendingActors.end() - 1);
  235. mPendingActors.pop_back();
  236. }
  237. // Is it in actors?
  238. iter = std::find(mActors.begin(), mActors.end(), actor);
  239. if (iter != mActors.end())
  240. {
  241. // Swap to end of vector and pop off (avoid erase copies)
  242. std::iter_swap(iter, mActors.end() - 1);
  243. mActors.pop_back();
  244. }
  245. }
  246. void Game::AddSprite(SpriteComponent* sprite)
  247. {
  248. // Find the insertion point in the sorted vector
  249. // (The first element with a higher draw order than me)
  250. int myDrawOrder = sprite->GetDrawOrder();
  251. auto iter = mSprites.begin();
  252. for ( ;
  253. iter != mSprites.end();
  254. ++iter)
  255. {
  256. if (myDrawOrder < (*iter)->GetDrawOrder())
  257. {
  258. break;
  259. }
  260. }
  261. // Inserts element before position of iterator
  262. mSprites.insert(iter, sprite);
  263. }
  264. void Game::RemoveSprite(SpriteComponent* sprite)
  265. {
  266. // (We can't swap because it ruins ordering)
  267. auto iter = std::find(mSprites.begin(), mSprites.end(), sprite);
  268. mSprites.erase(iter);
  269. }