LearnOpenGL

Translation in progress of learnopengl.com.
git clone https://git.mtkn.jp/LearnOpenGL
Log | Files | Refs

Render-text.html (29259B)


      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">Render text</h1>
    319 <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Render-text</h1>
    320 <p>
    321   In this chapter we'll be adding the final enhancements to the game by adding a life system, a win condition, and feedback in the form of rendered text. This chapter heavily builds upon the earlier introduced <a href="https://learnopengl.com/In-Practice/Text-Rendering" target="_blank">Text Rendering</a> chapter so it is highly advised to first work your way through that chapter if you haven't already.
    322 </p>
    323 
    324 <p>
    325   In Breakout all text rendering code is encapsulated within a class called <fun>TextRenderer</fun> that features the initialization of the FreeType library, render configuration, and the actual render code. You can find the code of the <fun>TextRenderer</fun> class here:
    326 </p>
    327 
    328 <ul>
    329   <li><strong>TextRenderer</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/text_renderer.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/text_renderer.cpp" target="_blank">code</a>.</li>
    330   <li><strong>Text shaders</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/shaders/text.vs" target="_blank">vertex</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/shaders/text.frag" target="_blank">fragment</a>.</li>
    331 </ul>
    332 
    333 <p>
    334   The content of the text renderer's functions is almost exactly the same as the code from the text rendering chapter. However, the code for rendering glyphs onto the screen is slightly different:
    335 </p>
    336 
    337 <pre><code>
    338 void TextRenderer::RenderText(std::string text, float x, float y, float scale, glm::vec3 color)
    339 {
    340     [...]
    341     for (c = text.begin(); c != text.end(); c++)
    342     {
    343         float xpos = x + ch.Bearing.x * scale;
    344         float ypos = y + (this-&gt;Characters['H'].Bearing.y - ch.Bearing.y) * scale;
    345 
    346         float w = ch.Size.x * scale;
    347         float h = ch.Size.y * scale;
    348         // update VBO for each character
    349         float vertices[6][4] = {
    350             { xpos,     ypos + h,   0.0f, 1.0f },
    351             { xpos + w, ypos,       1.0f, 0.0f },
    352             { xpos,     ypos,       0.0f, 0.0f },
    353 
    354             { xpos,     ypos + h,   0.0f, 1.0f },
    355             { xpos + w, ypos + h,   1.0f, 1.0f },
    356             { xpos + w, ypos,       1.0f, 0.0f }
    357         };
    358         [...]
    359     }
    360 }    
    361 </code></pre>
    362 
    363 <p>
    364   The reason for it being slightly different is that we use a different orthographic projection matrix from the one we've used in the text rendering chapter. In the text rendering chapter all <code>y</code> values ranged from bottom to top, while in the Breakout game all <code>y</code> values range from top to bottom with a <code>y</code> coordinate of <code>0.0</code> corresponding to the top edge of the screen. This means we have to slightly modify how we calculate the vertical offset.
    365 </p>
    366 
    367 <p>
    368   Since we now render downwards from <fun>RenderText</fun>'s <var>y</var> parameter, we calculate the vertical offset as the distance a glyph is pushed downwards from the top of the glyph space. Looking back at the glyph metrics image from FreeType, this is indicated by the red arrow:
    369 </p>
    370 
    371 <img src="/img/in-practice/breakout/glyph_offset.png" alt="Vertical offset of a FreeType glyph from the top of its glyph space for vertically inversed orthographic projection matrix in OpenGL"/>
    372   
    373 <p>
    374   To calculate this vertical offset we need to get the top of the glyph space (the length of the black vertical arrow from the origin). Unfortunately, FreeType has no such metric for us. What we do know is that that some glyphs always touch this top edge; characters like 'H', 'T' or 'X'. So what if we calculate the length of this red vector by subtracting <code>bearingY</code> from any of these <em>top-reaching</em> glyphs by <code>bearingY</code> of the glyph in question. This way, we push the glyph down based on how far its top point differs from the top edge.
    375 </p>
    376   
    377 <pre><code>
    378 float ypos = y + (this-&gt;Characters['H'].Bearing.y - ch.Bearing.y) * scale;  
    379 </code></pre>
    380   
    381 <p>
    382   In addition to updating the <code>ypos</code> calculation, we also switched the order of the vertices a bit to make sure all the vertices are still front facing when multiplied with the current orthographic projection matrix (as discussed in the <a href="https://learnopengl.com/Advanced-OpenGL/Face-culling" target="_blank">face culling</a> chapter).
    383 </p>
    384   
    385 <p>
    386   Adding the <fun>TextRenderer</fun> to the game is easy:
    387 </p>
    388   
    389 <pre><code>
    390 TextRenderer  *Text;
    391   
    392 void Game::Init()
    393 {
    394     [...]
    395     Text = new TextRenderer(this-&gt;Width, this-&gt;Height);
    396     Text->Load("fonts/ocraext.TTF", 24);
    397 }
    398 </code></pre>
    399 
    400 <p>
    401   The text renderer is initialized with a font called OCR A Extended that you can download from <a href="http://fontzone.net/font-details/ocr-a-extended" target="_blank">here</a>. If the font is not to your liking, feel free to use a different font.
    402 </p>
    403   
    404 <p>
    405   Now that we have a text renderer, let's finish the gameplay mechanics.
    406 </p>
    407   
    408 <h2>Player lives</h2>
    409 <p>
    410   Instead of immediately resetting the game as soon as the ball reaches the bottom edge, we'd like to give the player a few extra chances. We do this in the form of player lives, where the player begins with an initial number of lives (say <code>3</code>) and each time the ball touches the bottom edge, the player's life total is decreased by 1. Only when the player's life total becomes <code>0</code> we reset the game. This makes it easier for the player to finish a level while also building tension.
    411 </p>
    412   
    413 <p>
    414   We keep count of the lives of a player by adding it to the game class (initialized within the constructor to a value of <code>3</code>):
    415 </p>
    416   
    417 <pre><code>
    418 class Game
    419 {
    420     [...]
    421     public:  
    422         unsigned int Lives;
    423 }
    424 </code></pre>
    425 
    426 <p>
    427   We then modify the game's <fun>Update</fun> function to, instead of resetting the game, decrease the player's life total, and only reset the game once the life total reaches <code>0</code>:
    428 </p>
    429   
    430 <pre><code>
    431 void Game::Update(float dt)
    432 {
    433     [...]
    434     if (Ball-&gt;Position.y &gt;= this-&gt;Height) // did ball reach bottom edge?
    435     {
    436         --this-&gt;Lives;
    437         // did the player lose all his lives? : Game over
    438         if (this-&gt;Lives == 0)
    439         {
    440             this-&gt;ResetLevel();
    441             this-&gt;State = GAME_MENU;
    442         }
    443         this-&gt;ResetPlayer();
    444     }
    445 }
    446 </code></pre>
    447   
    448 <p>
    449   As soon as the player is game over (<var>lives</var> equals <code>0</code>), we reset the level and change the game state to <var>GAME_MENU</var> which we'll get to later. 
    450 </p>
    451   
    452 <p>
    453   Don't forget to reset the player's life total as soon as we reset the game/level:
    454 </p>
    455   
    456 <pre><code>
    457 void Game::ResetLevel()
    458 {
    459     [...]
    460     this-&gt;Lives = 3;
    461 }  
    462 </code></pre>
    463   
    464 <p>
    465   The player now has a working life total, but has no way of seeing how many lives he currently has while playing the game. That's where the text renderer comes in:
    466 </p>
    467  
    468 <pre><code>
    469 void Game::Render()
    470 {
    471     if (this-&gt;State == GAME_ACTIVE)
    472     {
    473         [...]
    474         std::stringstream ss; ss &lt;&lt; this-&gt;Lives;
    475         Text-&gt;RenderText("Lives:" + ss.str(), 5.0f, 5.0f, 1.0f);
    476     }
    477 }  
    478 </code></pre>
    479   
    480 <p>
    481   Here we convert the number of lives to a string, and display it at the top-left of the screen. It'll now look a bit like this:
    482 </p>
    483   
    484   <img src="/img/in-practice/breakout/render_text_lives.png" class="clean" alt="Rendered text with FreeType in OpenGL displaying the life total of the player"/>
    485     
    486 <p>
    487   As soon as the ball touches the bottom edge, the player's life total is decreased which is instantly visible at the top-left of the screen.
    488 </p>
    489   
    490 <h2>Level selection</h2>
    491 <p>
    492   Whenever the user is in the game state <var>GAME_MENU</var>, we'd like to give the player the control to select the level he'd like to play in. With either the 'w' or 's' key the player should be able to scroll through any of the levels we loaded. Whenever the player feels like the chosen level is indeed the level he'd like to play in, he can press the enter key to switch from the game's <var>GAME_MENU</var> state to the <var>GAME_ACTIVE</var> state.
    493 </p>
    494     
    495 <p>
    496   Allowing the player to choose a level is not too difficult. All we have to do is increase or decrease the game class's <var>Level</var> variable based on whether he pressed 'w' or 's' respectively:
    497 </p>
    498     
    499 <pre><code>
    500 if (this-&gt;State == GAME_MENU)
    501 {
    502     if (this-&gt;Keys[GLFW_KEY_ENTER])
    503         this-&gt;State = GAME_ACTIVE;
    504     if (this-&gt;Keys[GLFW_KEY_W])
    505         this-&gt;Level = (this-&gt;Level + 1) % 4;
    506     if (this-&gt;Keys[GLFW_KEY_S])
    507     {
    508         if (this-&gt;Level &gt; 0)
    509             --this-&gt;Level;
    510         else
    511             this-&gt;Level = 3;   
    512     }
    513 }  
    514 </code></pre>
    515     
    516 <p>
    517   We use the modulus operator (<code>%</code>) to make sure the <var>Level</var> variable remains within the acceptable level range (between <code>0</code> and <code>3</code>). 
    518 </p>
    519     
    520 <p>
    521   We also want to define what we want to render when we're in the menu state. We'd like to give the player some instructions in the form of text and also display the selected level in the background.
    522 </p>
    523     
    524 <pre><code>
    525 void Game::Render()
    526 {
    527     if (this-&gt;State == GAME_ACTIVE || this-&gt;State == GAME_MENU)
    528     {
    529         [...] // Game state's rendering code
    530     }
    531     if (this-&gt;State == GAME_MENU)
    532     {
    533         Text-&gt;RenderText("Press ENTER to start", 250.0f, Height / 2, 1.0f);
    534         Text-&gt;RenderText("Press W or S to select level", 245.0f, Height / 2 + 20.0f, 0.75f);
    535     }
    536 }  
    537 </code></pre>
    538     
    539 <p>
    540   Here we render the game whenever we're in either the <var>GAME_ACTIVE</var> state or the <var>GAME_MENU</var> state, and whenever we're in the <var>GAME_MENU</var> state we also render two lines of text to inform the player to select a level and/or accept his choice. Note that for this to work when launching the game you do have to set the game's state as <var>GAME_MENU</var> by default.  
    541 </p>    
    542     
    543 <img src="/img/in-practice/breakout/render_text_select.png" class="clean" alt="Selecting levels with FreeType rendered text in OpenGL"/>
    544       
    545 <p>
    546   It looks great, but once you try to run the code you'll probably notice that as soon as you press either the 'w' or the 's' key, the game rapidly scrolls through the levels making it difficult to select the level you want to play in. This happens because the game records the key press over  frames until we release the key. This causes the <fun>ProcessInput</fun> function to process the pressed key more than once. 
    547 </p>
    548   
    549 <p>
    550   We can solve this issue with a little trick commonly found within GUI systems. The trick is to, not only record the keys currently pressed, but also store the keys that have been processed once, until released again. We then check (before processing) whether the key has not yet been processed, and if so, process this key after which we store this key as being processed. Once we want to process the same key again without the key having been released, we do not process the key. This probably sounds somewhat confusing, but as soon as you see it in practice it (probably) starts to make sense.
    551 </p>
    552 
    553 <p>
    554   First we have to create another array of bool values to indicate which keys have been processed. We define this within the game class:
    555 </p>
    556 
    557 <pre><code>
    558 class Game
    559 {
    560     [...]
    561     public:  
    562         bool KeysProcessed[1024];
    563 } 
    564 </code></pre>
    565   
    566 <p>
    567   We then set the relevant key(s) to <code>true</code> as soon as they're processed and make sure to only process the key if it wasn't processed before (until released):
    568 </p>
    569 
    570 <pre><code>
    571 void Game::ProcessInput(float dt)
    572 {
    573     if (this-&gt;State == GAME_MENU)
    574     {
    575         if (this-&gt;Keys[GLFW_KEY_ENTER] && !this-&gt;KeysProcessed[GLFW_KEY_ENTER])
    576         {
    577             this-&gt;State = GAME_ACTIVE;
    578             this-&gt;KeysProcessed[GLFW_KEY_ENTER] = true;
    579         }
    580         if (this-&gt;Keys[GLFW_KEY_W] && !this-&gt;KeysProcessed[GLFW_KEY_W])
    581         {
    582             this-&gt;Level = (this-&gt;Level + 1) % 4;
    583             this-&gt;KeysProcessed[GLFW_KEY_W] = true;
    584         }
    585         if (this-&gt;Keys[GLFW_KEY_S] && !this-&gt;KeysProcessed[GLFW_KEY_S])
    586         {
    587             if (this-&gt;Level &gt; 0)
    588                 --this-&gt;Level;
    589             else
    590                 this-&gt;Level = 3;
    591             this-&gt;KeysProcessed[GLFW_KEY_S] = true;
    592         }
    593     }
    594     [...]
    595 }  
    596 </code></pre>
    597 
    598 <p>
    599   Now as soon as the key's value in the <var>KeysProcessed</var> array has not yet been set, we process the key and set its value to <code>true</code>. Next time we reach the <code>if</code> condition of the same key, it will have been processed so we'll pretend we never pressed the button until it's released again.
    600 </p>
    601   
    602 <p>
    603   Within GLFW's key callback function we then need to reset the key's processed value as soon as it's released so we can process it again the next time it's pressed:
    604 </p>
    605   
    606 <pre><code>
    607 void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
    608 {
    609     [...]
    610     if (key &gt;= 0 && key &lt; 1024)
    611     {
    612         if (action == GLFW_PRESS)
    613             Breakout.Keys[key] = true;
    614         else if (action == GLFW_RELEASE)
    615         {
    616             Breakout.Keys[key] = false;
    617             Breakout.KeysProcessed[key] = false;
    618         }
    619     }
    620 }  
    621 </code></pre>
    622   
    623 <p>
    624   Launching the game gives us a neat level select screen that now precisely selects a single level per key press, no matter how long we press he key. 
    625 </p>
    626   
    627 <h2>Winning</h2>
    628 <p>
    629   Currently the player is able to select levels, play the game, and fail in doing so to lose. It is kind of unfortunate if the player finds out after destroying all the bricks he cannot in any way win the game. So let's fix that.
    630 </p>
    631   
    632 <p>
    633   The player wins when all of the non-solid blocks have been destroyed. We already created a function to check for this condition in the <fun>GameLevel</fun> class:
    634 </p>
    635   
    636 <pre><code>
    637 bool GameLevel::IsCompleted()
    638 {
    639     for (GameObject &tile : this-&gt;Bricks)
    640         if (!tile.IsSolid && !tile.Destroyed)
    641             return false;
    642     return true;
    643 }  
    644 </code></pre>
    645   
    646 <p>
    647   We check all bricks in the game level and if a single non-solid brick isn't yet destroyed we return <code>false</code>. All we have to do is check for this condition in the game's <fun>Update</fun> function and as soon as it returns <code>true</code> we change the game state to <var>GAME_WIN</var>:
    648 </p>
    649   
    650 <pre><code>
    651 void Game::Update(float dt)
    652 {
    653     [...]
    654     if (this-&gt;State == GAME_ACTIVE && this-&gt;Levels[this-&gt;Level].IsCompleted())
    655     {
    656         this-&gt;ResetLevel();
    657         this-&gt;ResetPlayer();
    658         Effects-&gt;Chaos = true;
    659         this-&gt;State = GAME_WIN;
    660     }
    661 }
    662 </code></pre>
    663   
    664 <p>
    665   Whenever the level is completed while the game is active, we reset the game and display a small victory message in the <var>GAME_WIN</var> state. For fun we'll also enable the chaos effect while in the <var>GAME_WIN</var> screen. In the <fun>Render</fun> function we'll congratulate the player and ask him to either restart or quit the game:
    666 </p>
    667   
    668 <pre><code>
    669 void Game::Render()
    670 {
    671     [...]
    672     if (this-&gt;State == GAME_WIN)
    673     {
    674         Text-&gt;RenderText(
    675             "You WON!!!", 320.0, Height / 2 - 20.0, 1.0, glm::vec3(0.0, 1.0, 0.0)
    676         );
    677         Text-&gt;RenderText(
    678             "Press ENTER to retry or ESC to quit", 130.0, Height / 2, 1.0, glm::vec3(1.0, 1.0, 0.0)
    679         );
    680     }
    681 }  
    682 </code></pre>
    683 
    684 <p>
    685   Then we of course have to actually catch the mentioned keys:
    686 </p>
    687   
    688 <pre><code>
    689 void Game::ProcessInput(float dt)
    690 {
    691     [...]
    692     if (this-&gt;State == GAME_WIN)
    693     {
    694         if (this-&gt;Keys[GLFW_KEY_ENTER])
    695         {
    696             this-&gt;KeysProcessed[GLFW_KEY_ENTER] = true;
    697             Effects-&gt;Chaos = false;
    698             this-&gt;State = GAME_MENU;
    699         }
    700     }
    701 }  
    702 </code></pre>
    703   
    704 <p>
    705   If you're then good enough to actually win the game, you'd get the following image:
    706 </p>
    707   
    708   <img src="/img/in-practice/breakout/render_text_win.png" class="clean" alt="Image of winning in OpenGL Breakout with FreeType rendered text"/>
    709     
    710 <p>
    711   And that is it! The final piece of the puzzle of the Breakout game we've been actively working on. Try it out, customize it to your liking, and show it to all your family and friends! 
    712 </p>
    713     
    714 <p>
    715   You can find the final version of the game's code below:
    716 </p>
    717     
    718 <ul>
    719    <li><strong>Game</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game.cpp" target="_blank">code</a>.</li>
    720 </ul>
    721     
    722     <h2>Further reading</h2>
    723 <ul>
    724     <li><a href="https://www.websiteplanet.com/blog/best-free-fonts/" target="_blank">70+ Best Free Fonts for Designers</a>: summarized list of a large group of fonts to use in your project for personal or commercial use.</li>
    725 </ul>       
    726 
    727     </div>
    728     
    729     <div id="hover">
    730         HI
    731     </div>
    732    <!-- 728x90/320x50 sticky footer -->
    733 <div id="waldo-tag-6196"></div>
    734 
    735    <div id="disqus_thread"></div>
    736 
    737     
    738 
    739 
    740 </div> <!-- container div -->
    741 
    742 
    743 </div> <!-- super container div -->
    744 </body>
    745 </html>
    746 	</main>
    747 </body>
    748 </html>