Levels.html (27862B)
1 <!DOCTYPE html> 2 <html lang="ja"> 3 <head> 4 <meta charset="utf-8"/> 5 <title>LearnOpenGL</title> 6 <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> 7 <link rel="stylesheet" href="../static/style.css" /> 8 <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> 9 <script src="/static/functions.js"></script> 10 </head> 11 <body> 12 <nav> 13 <ol> 14 <li id="Introduction"> 15 <a href="https://learnopengl.com/Introduction">はじめに</a> 16 </li> 17 <li id="Getting-started"> 18 <span class="closed">入門</span> 19 <ol> 20 <li id="Getting-started/OpenGL"> 21 <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> 22 </li> 23 <li id="Getting-started/Creating-a-window"> 24 <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> 25 </li> 26 <li id="Getting-started/Hello-Window"> 27 <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> 28 </li> 29 <li id="Getting-started/Hello-Triangle"> 30 <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> 31 </li> 32 <li id="Getting-started/Shaders"> 33 <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> 34 </li> 35 <li id="Getting-started/Textures"> 36 <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> 37 </li> 38 <li id="Getting-started/Transformations"> 39 <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> 40 </li> 41 <li id="Getting-started/Coordinate-Systems"> 42 <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> 43 </li> 44 <li id="Getting-started/Camera"> 45 <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> 46 </li> 47 <li id="Getting-started/Review"> 48 <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> 49 </li> 50 </ol> 51 </li> 52 <li id="Lighting"> 53 <span class="closed">Lighting </span> 54 <ol> 55 <li id="Lighting/Colors"> 56 <a href="https://learnopengl.com/Lighting/Colors">Colors </a> 57 </li> 58 <li id="Lighting/Basic-Lighting"> 59 <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> 60 </li> 61 <li id="Lighting/Materials"> 62 <a href="https://learnopengl.com/Lighting/Materials">Materials </a> 63 </li> 64 <li id="Lighting/Lighting-maps"> 65 <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> 66 </li> 67 <li id="Lighting/Light-casters"> 68 <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> 69 </li> 70 <li id="Lighting/Multiple-lights"> 71 <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> 72 </li> 73 <li id="Lighting/Review"> 74 <a href="https://learnopengl.com/Lighting/Review">Review </a> 75 </li> 76 </ol> 77 </li> 78 <li id="Model-Loading"> 79 <span class="closed">Model Loading </span> 80 <ol> 81 <li id="Model-Loading/Assimp"> 82 <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> 83 </li> 84 <li id="Model-Loading/Mesh"> 85 <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> 86 </li> 87 <li id="Model-Loading/Model"> 88 <a href="https://learnopengl.com/Model-Loading/Model">Model </a> 89 </li> 90 </ol> 91 </li> 92 <li id="Advanced-OpenGL"> 93 <span class="closed">Advanced OpenGL </span> 94 <ol> 95 <li id="Advanced-OpenGL/Depth-testing"> 96 <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> 97 </li> 98 <li id="Advanced-OpenGL/Stencil-testing"> 99 <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> 100 </li> 101 <li id="Advanced-OpenGL/Blending"> 102 <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> 103 </li> 104 <li id="Advanced-OpenGL/Face-culling"> 105 <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> 106 </li> 107 <li id="Advanced-OpenGL/Framebuffers"> 108 <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> 109 </li> 110 <li id="Advanced-OpenGL/Cubemaps"> 111 <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> 112 </li> 113 <li id="Advanced-OpenGL/Advanced-Data"> 114 <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> 115 </li> 116 <li id="Advanced-OpenGL/Advanced-GLSL"> 117 <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> 118 </li> 119 <li id="Advanced-OpenGL/Geometry-Shader"> 120 <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> 121 </li> 122 <li id="Advanced-OpenGL/Instancing"> 123 <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> 124 </li> 125 <li id="Advanced-OpenGL/Anti-Aliasing"> 126 <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> 127 </li> 128 </ol> 129 </li> 130 <li id="Advanced-Lighting"> 131 <span class="closed">Advanced Lighting </span> 132 <ol> 133 <li id="Advanced-Lighting/Advanced-Lighting"> 134 <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> 135 </li> 136 <li id="Advanced-Lighting/Gamma-Correction"> 137 <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> 138 </li> 139 <li id="Advanced-Lighting/Shadows"> 140 <span class="closed">Shadows </span> 141 <ol> 142 <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> 143 <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> 144 </li> 145 <li id="Advanced-Lighting/Shadows/Point-Shadows"> 146 <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> 147 </li> 148 </ol> 149 </li> 150 <li id="Advanced-Lighting/Normal-Mapping"> 151 <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> 152 </li> 153 <li id="Advanced-Lighting/Parallax-Mapping"> 154 <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> 155 </li> 156 <li id="Advanced-Lighting/HDR"> 157 <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> 158 </li> 159 <li id="Advanced-Lighting/Bloom"> 160 <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> 161 </li> 162 <li id="Advanced-Lighting/Deferred-Shading"> 163 <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> 164 </li> 165 <li id="Advanced-Lighting/SSAO"> 166 <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> 167 </li> 168 </ol> 169 </li> 170 <li id="PBR"> 171 <span class="closed">PBR </span> 172 <ol> 173 <li id="PBR/Theory"> 174 <a href="https://learnopengl.com/PBR/Theory">Theory </a> 175 </li> 176 <li id="PBR/Lighting"> 177 <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> 178 </li> 179 <li id="PBR/IBL"> 180 <span class="closed">IBL </span> 181 <ol> 182 <li id="PBR/IBL/Diffuse-irradiance"> 183 <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> 184 </li> 185 <li id="PBR/IBL/Specular-IBL"> 186 <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> 187 </li> 188 </ol> 189 </li> 190 </ol> 191 </li> 192 <li id="In-Practice"> 193 <span class="closed">In Practice </span> 194 <ol> 195 <li id="In-Practice/Debugging"> 196 <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> 197 </li> 198 <li id="In-Practice/Text-Rendering"> 199 <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> 200 </li> 201 <li id="In-Practice/2D-Game"> 202 <span class="closed">2D Game </span> 203 <ol> 204 <li id="In-Practice/2D-Game/Breakout"> 205 <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> 206 </li> 207 <li id="In-Practice/2D-Game/Setting-up"> 208 <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> 209 </li> 210 <li id="In-Practice/2D-Game/Rendering-Sprites"> 211 <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> 212 </li> 213 <li id="In-Practice/2D-Game/Levels"> 214 <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> 215 </li> 216 <li id="In-Practice/2D-Game/Collisions"> 217 <span class="closed">Collisions </span> 218 <ol> 219 <li id="In-Practice/2D-Game/Collisions/Ball"> 220 <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> 221 </li> 222 <li id="In-Practice/2D-Game/Collisions/Collision-detection"> 223 <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> 224 </li> 225 <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> 226 <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> 227 </li> 228 </ol> 229 </li> 230 <li id="In-Practice/2D-Game/Particles"> 231 <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> 232 </li> 233 <li id="In-Practice/2D-Game/Postprocessing"> 234 <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> 235 </li> 236 <li id="In-Practice/2D-Game/Powerups"> 237 <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> 238 </li> 239 <li id="In-Practice/2D-Game/Audio"> 240 <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> 241 </li> 242 <li id="In-Practice/2D-Game/Render-text"> 243 <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> 244 </li> 245 <li id="In-Practice/2D-Game/Final-thoughts"> 246 <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> 247 </li> 248 </ol> 249 </li> 250 </ol> 251 </li> 252 <li id="Guest-Articles"> 253 <span class="closed">Guest Articles </span> 254 <ol> 255 <li id="Guest-Articles/How-to-publish"> 256 <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> 257 </li> 258 <li id="Guest-Articles/2020"> 259 <span class="closed">2020 </span> 260 <ol> 261 <li id="Guest-Articles/2020/OIT"> 262 <span class="closed">OIT </span> 263 <ol> 264 <li id="Guest-Articles/2020/OIT/Introduction"> 265 <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> 266 </li> 267 <li id="Guest-Articles/2020/OIT/Weighted-Blended"> 268 <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> 269 </li> 270 </ol> 271 </li> 272 <li id="Guest-Articles/2020/Skeletal-Animation"> 273 <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> 274 </li> 275 </ol> 276 </li> 277 <li id="Guest-Articles/2021"> 278 <span class="closed">2021 </span> 279 <ol> 280 <li id="Guest-Articles/2021/CSM"> 281 <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> 282 </li> 283 <li id="Guest-Articles/2021/Scene"> 284 <span class="closed">Scene </span> 285 <ol> 286 <li id="Guest-Articles/2021/Scene/Scene-Graph"> 287 <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> 288 </li> 289 <li id="Guest-Articles/2021/Scene/Frustum-Culling"> 290 <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> 291 </li> 292 </ol> 293 </li> 294 <li id="Guest-Articles/2021/Tessellation"> 295 <span class="closed">Tessellation </span> 296 <ol> 297 <li id="Guest-Articles/2021/Tessellation/Height-map"> 298 <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> 299 </li> 300 </ol> 301 </li> 302 </ol> 303 </li> 304 </ol> 305 </li> 306 <li id="Code-repository"> 307 <a href="https://learnopengl.com/Code-repository">Code repository </a> 308 </li> 309 <li id="Translations"> 310 <a href="https://learnopengl.com/Translations">Translations </a> 311 </li> 312 <li id="About"> 313 <a href="https://learnopengl.com/About">About </a> 314 </li> 315 </ol> 316 </nav> 317 <main> 318 <h1 id="content-title">Levels</h1> 319 <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Levels</h1> 320 <p> 321 Breakout is unfortunately not just about a single happy green face, but contains complete levels with a lot of playfully colored bricks. We want these levels to be configurable such that they can support any number of rows and/or columns, we want the levels to have solid bricks (that cannot be destroyed), we want the levels to support multiple brick colors, and we want them to be stored externally in (text) files. 322 </p> 323 324 <p> 325 In this chapter we'll briefly walk through the code of a game level object that is used to manage a large amount of bricks. We first have to define what an actual <def>brick</def> is though. 326 </p> 327 328 <p> 329 We create a component called a <def>game object</def> that acts as the base representation of an object inside the game. Such a game object holds state data like its position, size, and velocity. It holds a color, a rotation component, whether it is solid and/or destroyed, and it also stores a <fun>Texture2D</fun> variable as its sprite. 330 </p> 331 332 <p> 333 Each object in the game is represented as a <fun>GameObject</fun> or a derivative of this class. You can find the code of the <fun>GameObject</fun> class below: 334 </p> 335 336 <ul> 337 <li><strong>GameObject</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_object.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_object.cpp" target="_blank">code</a> </li> 338 </ul> 339 340 <p> 341 A level in Breakout consists entirely of bricks so we can represent a level by exactly that: a collection of bricks. Because a brick requires the same state as a game object, we're going to represent each brick of the level as a <fun>GameObject</fun>. The declaration of the <fun>GameLevel</fun> class then looks as follows: 342 </p> 343 344 <pre><code> 345 class GameLevel 346 { 347 public: 348 // level state 349 std::vector<GameObject> Bricks; 350 // constructor 351 GameLevel() { } 352 // loads level from file 353 void Load(const char *file, unsigned int levelWidth, unsigned int levelHeight); 354 // render level 355 void Draw(SpriteRenderer &renderer); 356 // check if the level is completed (all non-solid tiles are destroyed) 357 bool IsCompleted(); 358 private: 359 // initialize level from tile data 360 void init(std::vector<std::vector<unsigned int>> tileData, 361 unsigned int levelWidth, unsigned int levelHeight); 362 }; 363 </code></pre> 364 365 <p> 366 Since a level is loaded from an external (text) file, we need to propose some kind of level structure. Here is an example of what a game level may look like in a text file: 367 </p> 368 369 <pre><code> 370 1 1 1 1 1 1 371 2 2 0 0 2 2 372 3 3 4 4 3 3 373 </code></pre> 374 375 <p> 376 A level is stored in a matrix-like structure where each number represents a type of brick, each one separated by a space. Within the level code we can then assign what each number represents. We have chosen the following representation: 377 </p> 378 379 <ul> 380 <li>A number of 0: no brick, an empty space within the level.</li> 381 <li>A number of 1: a solid brick, a brick that cannot be destroyed.</li> 382 <li>A number higher than 1: a destroyable brick; each subsequent number only differs in color.</li> 383 </ul> 384 385 <p> 386 The example level listed above would, after being processed by <fun>GameLevel</fun>, look like this: 387 </p> 388 389 <img src="/img/in-practice/breakout/levels-example.png" class="clean" alt="Example of a level using the Breakout GameLevel class"/> 390 391 <p> 392 The <fun>GameLevel</fun> class uses two functions to generate a level from file. It first loads all the numbers in a two-dimensional vector within its <fun>Load</fun> function that then processes these numbers (to create all game objects) in its <fun>init</fun> function. 393 </p> 394 395 396 <pre><code> 397 void GameLevel::Load(const char *file, unsigned int levelWidth, unsigned int levelHeight) 398 { 399 // clear old data 400 this->Bricks.clear(); 401 // load from file 402 unsigned int tileCode; 403 GameLevel level; 404 std::string line; 405 std::ifstream fstream(file); 406 std::vector<std::vector<unsigned int>> tileData; 407 if (fstream) 408 { 409 while (std::getline(fstream, line)) // read each line from level file 410 { 411 std::istringstream sstream(line); 412 std::vector<unsigned int> row; 413 while (sstream >> tileCode) // read each word separated by spaces 414 row.push_back(tileCode); 415 tileData.push_back(row); 416 } 417 if (tileData.size() > 0) 418 this->init(tileData, levelWidth, levelHeight); 419 } 420 } 421 </code></pre> 422 423 <p> 424 The loaded <var>tileData</var> is then passed to the game level's <fun>init</fun> function: 425 </p> 426 427 <pre><code> 428 void GameLevel::init(std::vector<std::vector<unsigned int>> tileData, 429 unsigned int lvlWidth, unsigned int lvlHeight) 430 { 431 // calculate dimensions 432 unsigned int height = tileData.size(); 433 unsigned int width = tileData[0].size(); 434 float unit_width = lvlWidth / static_cast<float>(width); 435 float unit_height = lvlHeight / height; 436 // initialize level tiles based on tileData 437 for (unsigned int y = 0; y < height; ++y) 438 { 439 for (unsigned int x = 0; x < width; ++x) 440 { 441 // check block type from level data (2D level array) 442 if (tileData[y][x] == 1) // solid 443 { 444 glm::vec2 pos(unit_width * x, unit_height * y); 445 glm::vec2 size(unit_width, unit_height); 446 GameObject obj(pos, size, 447 ResourceManager::GetTexture("block_solid"), 448 glm::vec3(0.8f, 0.8f, 0.7f) 449 ); 450 obj.IsSolid = true; 451 this->Bricks.push_back(obj); 452 } 453 else if (tileData[y][x] > 1) 454 { 455 glm::vec3 color = glm::vec3(1.0f); // original: white 456 if (tileData[y][x] == 2) 457 color = glm::vec3(0.2f, 0.6f, 1.0f); 458 else if (tileData[y][x] == 3) 459 color = glm::vec3(0.0f, 0.7f, 0.0f); 460 else if (tileData[y][x] == 4) 461 color = glm::vec3(0.8f, 0.8f, 0.4f); 462 else if (tileData[y][x] == 5) 463 color = glm::vec3(1.0f, 0.5f, 0.0f); 464 465 glm::vec2 pos(unit_width * x, unit_height * y); 466 glm::vec2 size(unit_width, unit_height); 467 this->Bricks.push_back( 468 GameObject(pos, size, ResourceManager::GetTexture("block"), color) 469 ); 470 } 471 } 472 } 473 } 474 </code></pre> 475 476 <p> 477 The <fun>init</fun> function iterates through each of the loaded numbers and adds a <fun>GameObject</fun> to the level's <var>Bricks</var> vector based on the processed number. The size of each brick is automatically calculated (<var>unit_width</var> and <var>unit_height</var>) based on the total number of bricks so that each brick perfectly fits within the screen bounds. 478 </p> 479 480 <p> 481 Here we load the game objects with two new textures, a <a href="/img/in-practice/breakout/textures/block.png" target="_blank">block</a> texture and a <a href="/img/in-practice/breakout/textures/block_solid.png" target="_blank">solid block</a> texture. 482 </p> 483 484 <img src="/img/in-practice/breakout/block-textures.png" alt="Image of two types of block textures"/> 485 486 <p> 487 A nice little trick here is that these textures are completely in gray-scale. The effect is that we can neatly manipulate their colors within the game-code by multiplying their grayscale colors with a defined color vector; exactly as we did within the <fun>SpriteRenderer</fun>. This way, customizing the appearance of their colors doesn't look too weird or unbalanced. 488 </p> 489 490 <p> 491 The <fun>GameLevel</fun> class also houses a few other functions, like rendering all non-destroyed bricks, or validating if all non-solid bricks are destroyed. You can find the source code of the <fun>GameLevel</fun> class below: 492 </p> 493 494 <ul> 495 <li><strong>GameLevel</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_level.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_level.cpp" target="_blank">code</a> </li> 496 </ul> 497 498 <p> 499 The game level class gives us a lot of flexibility since any amount of rows and columns are supported and a user could easily create his/her own levels by modifying the level files. 500 </p> 501 502 <h2>Within the game</h2> 503 <p> 504 We would like to support multiple levels in the Breakout game so we'll have to extend the game class a little by adding a vector that holds variables of type <fun>GameLevel</fun>. We'll also store the currently active level while we're at it: 505 </p> 506 507 <pre><code> 508 class Game 509 { 510 [...] 511 std::vector<GameLevel> Levels; 512 unsigned int Level; 513 [...] 514 }; 515 </code></pre> 516 517 <p> 518 This series' version of the Breakout game features a total of 4 levels: 519 </p> 520 521 <ul> 522 <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/one.lvl" target="_blank">Standard</a></li> 523 <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/two.lvl" target="_blank">A few small gaps</a></li> 524 <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/three.lvl" target="_blank">Space invader</a></li> 525 <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/four.lvl" target="_blank">Bounce galore</a></li> 526 </ul> 527 528 <p> 529 Each of the textures and levels are then initialized within the game class's <fun>Init</fun> function: 530 </p> 531 532 <pre><code> 533 void Game::Init() 534 { 535 [...] 536 // load textures 537 ResourceManager::LoadTexture("textures/background.jpg", false, "background"); 538 ResourceManager::LoadTexture("textures/awesomeface.png", true, "face"); 539 ResourceManager::LoadTexture("textures/block.png", false, "block"); 540 ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid"); 541 // load levels 542 GameLevel one; one.Load("levels/one.lvl", this->Width, this->Height / 2); 543 GameLevel two; two.Load("levels/two.lvl", this->Width, this->Height / 2); 544 GameLevel three; three.Load("levels/three.lvl", this->Width, this->Height / 2); 545 GameLevel four; four.Load("levels/four.lvl", this->Width, this->Height / 2); 546 this->Levels.push_back(one); 547 this->Levels.push_back(two); 548 this->Levels.push_back(three); 549 this->Levels.push_back(four); 550 this->Level = 0; 551 } 552 </code></pre> 553 554 <p> 555 Now all that is left to do, is actually render the level. We accomplish this by calling the currently active level's <fun>Draw</fun> function that in turn calls each <fun>GameObject</fun>'s <fun>Draw</fun> function using the given sprite renderer. Next to the level, we'll also render the scene with a nice <a href="/img/in-practice/breakout/textures/background.jpg" target="_blank">background image</a> (courtesy of Tenha): 556 </p> 557 558 <pre><code> 559 void Game::Render() 560 { 561 if(this->State == GAME_ACTIVE) 562 { 563 // draw background 564 Renderer->DrawSprite(ResourceManager::GetTexture("background"), 565 glm::vec2(0.0f, 0.0f), glm::vec2(this->Width, this->Height), 0.0f 566 ); 567 // draw level 568 this->Levels[this->Level].Draw(*Renderer); 569 } 570 } 571 </code></pre> 572 573 <p> 574 The result is then a nicely rendered level that really starts to make the game feel more alive: 575 </p> 576 577 <img src="/img/in-practice/breakout/levels.png" class="clean" alt="Level in OpenGL breakout"/> 578 579 <h3>The player paddle</h3> 580 <p> 581 While we're at it, we may just as well introduce a paddle at the bottom of the scene that is controlled by the player. The paddle only allows for horizontal movement and whenever it touches any of the scene's edges, its movement should halt. For the player paddle we're going to use the <a href="/img/in-practice/breakout/textures/paddle.png" target="_blank">following</a> texture: 582 </p> 583 584 <img src="/img/in-practice/breakout/textures/paddle.png" class="clean" style="width:256px;height:auto;" alt="Texture image if a paddle in OpenGL breakout"/> 585 586 <p> 587 A paddle object will have a position, a size, and a sprite texture, so it makes sense to define the paddle as a <fun>GameObject</fun> as well: 588 </p> 589 590 <pre><code> 591 // Initial size of the player paddle 592 const glm::vec2 PLAYER_SIZE(100.0f, 20.0f); 593 // Initial velocity of the player paddle 594 const float PLAYER_VELOCITY(500.0f); 595 596 GameObject *Player; 597 598 void Game::Init() 599 { 600 [...] 601 ResourceManager::LoadTexture("textures/paddle.png", true, "paddle"); 602 [...] 603 glm::vec2 playerPos = glm::vec2( 604 this->Width / 2.0f - PLAYER_SIZE.x / 2.0f, 605 this->Height - PLAYER_SIZE.y 606 ); 607 Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle")); 608 } 609 </code></pre> 610 611 <p> 612 Here we defined several constant values that define the paddle's size and speed. Within the Game's <fun>Init</fun> function we calculate the starting position of the paddle within the scene. We make sure the player paddle's center is aligned with the horizontal center of the scene. 613 </p> 614 615 <p> 616 With the player paddle initialized, we also need to add a statement to the Game's <fun>Render</fun> function: 617 </p> 618 619 <pre><code> 620 Player->Draw(*Renderer); 621 </code></pre> 622 623 <p> 624 If you'd start the game now, you would not only see the level, but also a fancy player paddle aligned to the bottom edge of the scene. As of now, it doesn't really do anything so we're going to delve into the Game's <fun>ProcessInput</fun> function to horizontally move the paddle whenever the user presses the <var>A</var> or <var>D</var> key: 625 </p> 626 627 <pre><code> 628 void Game::ProcessInput(float dt) 629 { 630 if (this->State == GAME_ACTIVE) 631 { 632 float velocity = PLAYER_VELOCITY * dt; 633 // move playerboard 634 if (this->Keys[GLFW_KEY_A]) 635 { 636 if (Player->Position.x >= 0.0f) 637 Player->Position.x -= velocity; 638 } 639 if (this->Keys[GLFW_KEY_D]) 640 { 641 if (Player->Position.x <= this->Width - Player->Size.x) 642 Player->Position.x += velocity; 643 } 644 } 645 } 646 </code></pre> 647 648 <p> 649 Here we move the player paddle either in the left or right direction based on which key the user pressed (note how we multiply the velocity with the <def>deltatime</def> variable). If the paddle's <code>x</code> value would be less than <code>0</code> it would've moved outside the left edge, so we only move the paddle to the left if the paddle's <code>x</code> value is higher than the left edge's <code>x</code> position (<code>0.0</code>). We do the same for when the paddle breaches the right edge, but we have to compare the right edge's position with the right edge of the paddle (subtract the paddle's width from the right edge's <code>x</code> position). 650 </p> 651 652 <p> 653 Now running the game gives us a player paddle that we can move all across the bottom edge: 654 </p> 655 656 <img src="/img/in-practice/breakout/levels-player.png" class="clean" alt="Image of OpenGL breakout now with player paddle"/> 657 658 <p> 659 You can find the updated code of the Game class here: 660 </p> 661 662 <ul> 663 <li><strong>Game</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/4.game.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/4.game.cpp" target="_blank">code</a> </li> 664 </ul> 665 666 </div> 667 668 <div id="hover"> 669 HI 670 </div> 671 <!-- 728x90/320x50 sticky footer --> 672 <div id="waldo-tag-6196"></div> 673 674 <div id="disqus_thread"></div> 675 676 677 678 679 </div> <!-- container div --> 680 681 682 </div> <!-- super container div --> 683 </body> 684 </html> 685 </main> 686 </body> 687 </html>