├── DiveIntoC++14 ├── 1_CPP14 │ ├── compile.sh │ ├── p3.cpp │ ├── p4.cpp │ ├── p2.cpp │ ├── p5.cpp │ └── p1.cpp ├── 2_ForArgs │ ├── compile.sh │ ├── tweet0.png │ ├── p1.cpp │ ├── p5.cpp │ ├── p2.cpp │ ├── p3.cpp │ ├── p4.cpp │ ├── p6.cpp │ └── p7.cpp └── 3_UniqueResource │ ├── compile.sh │ ├── p1.cpp │ └── p2.cpp ├── DiveIntoC++11 ├── 3_Memory │ ├── p1 │ ├── p2 │ ├── p3 │ ├── compile.sh │ ├── p2.cpp │ └── p1.cpp ├── 5_Entities │ ├── p3 │ ├── p5 │ ├── p6 │ ├── p7 │ ├── p8 │ ├── p9 │ ├── compile.sh │ ├── p2.cpp │ ├── p1.cpp │ ├── p4.cpp │ ├── p3.cpp │ ├── p6.cpp │ ├── p7.cpp │ ├── p8.cpp │ └── p5.cpp ├── 4_SmartPtrs │ ├── p1 │ ├── p2 │ ├── p3 │ ├── compile.sh │ ├── p2.cpp │ ├── p1.cpp │ └── p3.cpp ├── 1_Arkanoid │ ├── compile.sh │ ├── p1.cpp │ ├── p3.cpp │ ├── p2.cpp │ ├── p4.cpp │ ├── p5.cpp │ ├── p6.cpp │ ├── p7.cpp │ ├── p9.cpp │ └── p8.cpp ├── 2_Arkanoid │ ├── compile.sh │ ├── add1.cpp │ ├── add2.cpp │ ├── p1.cpp │ ├── p3.cpp │ ├── p2.cpp │ └── p5.cpp └── 6_Macros │ ├── compile.sh │ └── p1.cpp ├── Handles ├── compile.sh └── p1.cpp ├── .gitignore └── README.md /DiveIntoC++14/1_CPP14/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++14 "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++14 "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++14/3_UniqueResource/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++14 "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++11/3_Memory/p1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/3_Memory/p1 -------------------------------------------------------------------------------- /DiveIntoC++11/3_Memory/p2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/3_Memory/p2 -------------------------------------------------------------------------------- /DiveIntoC++11/3_Memory/p3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/3_Memory/p3 -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/5_Entities/p3 -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/5_Entities/p5 -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/5_Entities/p6 -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/5_Entities/p7 -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/5_Entities/p8 -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/5_Entities/p9 -------------------------------------------------------------------------------- /DiveIntoC++11/4_SmartPtrs/p1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/4_SmartPtrs/p1 -------------------------------------------------------------------------------- /DiveIntoC++11/4_SmartPtrs/p2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/4_SmartPtrs/p2 -------------------------------------------------------------------------------- /DiveIntoC++11/4_SmartPtrs/p3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++11/4_SmartPtrs/p3 -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/tweet0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/Tutorials/HEAD/DiveIntoC++14/2_ForArgs/tweet0.png -------------------------------------------------------------------------------- /Handles/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clang++ -std=c++1y -O3 -DNDEBUG -pthread "${@:2}" ./$1 -o /tmp/$1.temp && /tmp/$1.temp -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++11 -Wall -Wextra -Wpedantic -O3 -lsfml-system \ 3 | -lsfml-graphics -lsfml-window -lsfml-audio "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++11/2_Arkanoid/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++11 -Wall -Wextra -Wpedantic -O3 -lsfml-system \ 3 | -lsfml-graphics -lsfml-window -lsfml-audio "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++11/3_Memory/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++11 -Wall -Wextra -Wpedantic -O3 -lsfml-system \ 3 | -lsfml-graphics -lsfml-window -lsfml-audio "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++11/4_SmartPtrs/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++11 -Wall -Wextra -Wpedantic -O3 -lsfml-system \ 3 | -lsfml-graphics -lsfml-window -lsfml-audio "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++11 -Wall -Wextra -Wpedantic -O3 -lsfml-system \ 3 | -lsfml-graphics -lsfml-window -lsfml-audio "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++11/6_Macros/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -o $1 -std=c++11 -Wall -Wextra -Wpedantic -O3 -lsfml-system \ 3 | -lsfml-graphics -lsfml-window -lsfml-audio "./$1.cpp" && ./$1 -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace sf; 9 | 10 | // `constexpr` defines an immutable compile-time value. 11 | constexpr int windowWidth{800}, windowHeight{600}; 12 | 13 | int main() 14 | { 15 | // Creation of the game window. 16 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 1"}; 17 | window.setFramerateLimit(60); 18 | 19 | // Game loop. 20 | while(true) 21 | { 22 | // "Clear" the window from previously drawn graphics. 23 | window.clear(Color::Black); 24 | 25 | // If "Escape" is pressed, break out of the loop. 26 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 27 | 28 | // Show the window contents. 29 | window.display(); 30 | } 31 | 32 | return 0; 33 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | 15 | /WIP/*/p1 16 | /WIP/*/p2 17 | /WIP/*/p3 18 | /WIP/*/p4 19 | /WIP/*/p5 20 | /WIP/*/p6 21 | /WIP/*/p7 22 | /WIP/*/p8 23 | /WIP/*/p9 24 | /WIP/*/p10 25 | ./*/p1 26 | ./*/p2 27 | ./*/p3 28 | ./*/p4 29 | ./*/p5 30 | ./*/p6 31 | ./*/p7 32 | ./*/p8 33 | ./*/p9 34 | ./*/p10/log.txt 35 | /log.txt 36 | /3_Memory/p1 37 | /3_Memory/p2 38 | /3_Memory/p3 39 | /4_SmartPtrs/p1 40 | /4_SmartPtrs/p2 41 | /4_SmartPtrs/p3 42 | /5_Entities/log.txt 43 | /5_Entities/p3 44 | /5_Entities/TODO 45 | /5_Entities/p5 46 | /5_Entities/p6 47 | /5_Entities/p7 48 | /5_Entities/p9 49 | /5_Entities/p8 50 | /Handles/transcript.txt 51 | /DiveIntoC++14/2_ForArgs/p5 52 | /DiveIntoC++14/2_ForArgs/p6 53 | /DiveIntoC++14/2_ForArgs/p1 54 | /DiveIntoC++14/2_ForArgs/p2 55 | /DiveIntoC++14/2_ForArgs/p3 56 | /DiveIntoC++14/2_ForArgs/p4 57 | /DiveIntoC++14/2_ForArgs/p7 58 | /DiveIntoC++14/1_CPP14/p4 59 | /DiveIntoC++14/3_Resource/p1 60 | /DiveIntoC++14/3_Resource/p2 61 | /DiveIntoC++14/3_Resource/p3 62 | /DiveIntoC++14/3_UniqueResource/p1 63 | /DiveIntoC++14/3_UniqueResource/p2 64 | /DiveIntoC++14/3_UniqueResource/p3 65 | /DiveIntoC++14/3_UniqueResource/p4 66 | -------------------------------------------------------------------------------- /DiveIntoC++14/1_CPP14/p3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace CPP14LanguageFeatures 11 | { 12 | // Relaxed constexpr restrictions. 13 | // 14 | // * Allows `constexpr` functions to have multiple 15 | // instructions and common language constructs 16 | // such as branches and loops. 17 | // 18 | 19 | constexpr int computeSomething(int mX) 20 | { 21 | int result{mX}; 22 | 23 | for(int i{0}; i < 10; ++i) result += i; 24 | 25 | if(result > 5) result += 10; 26 | 27 | return result; 28 | } 29 | 30 | template 31 | struct Test 32 | { 33 | }; 34 | 35 | // Compiles! 36 | Test instance; 37 | 38 | 39 | 40 | // For more `constexpr` coolness, check out this 41 | // C++Now 2015 talk by Scott Schurr: 42 | 43 | // "constexpr: C++ At Compile Time" 44 | 45 | // All slides from the conference are available here: 46 | // https://github.com/boostcon/cppnow_presentations_2015/ 47 | } 48 | 49 | int main() { return 0; } 50 | -------------------------------------------------------------------------------- /DiveIntoC++11/2_Arkanoid/add1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | // The `constexpr` keyword can also be applied to functions 8 | 9 | // A big restriction (that will be lifted in C++14) is that 10 | // `constexpr` functions can only be made of a `return` 11 | // statement 12 | 13 | // `constexpr` functions will be resolved at compile-time 14 | // if the passed arguments are constant expressions, otherwise 15 | // they may get resolved at run-time 16 | 17 | constexpr int getSum(int mA, int mB, int mC) { return mA + mB + mC; } 18 | 19 | int main() 20 | { 21 | // getSum(1, 2, 3 + 5) will get resolved at compile-time 22 | // because `1`, `2`, `3 + 5` are `constexpr` expressions 23 | std::cout << getSum(1, 2, 3 + 5) << std::endl; 24 | 25 | int input; 26 | std::cin >> input; 27 | 28 | // getSum(input, 10) will get resolved at run-time 29 | // as `input` is not a `constexpr` and it cannot be 30 | // known at compile-time 31 | std::cout << getSum(input, 10, 1) << std::endl; 32 | 33 | // Since `constexpr` is resolved at compile-time, 34 | // we can use it, for example, to declare arrays 35 | int array[getSum(10, 10, 10)]; // int array[30]; 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /DiveIntoC++14/1_CPP14/p4.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace CPP14LanguageFeatures 11 | { 12 | // Variable templates. 13 | // 14 | // * C++14 allows the creation of templated variables. 15 | // 16 | // * Variable templates can also be specialized. 17 | // 18 | 19 | template 20 | T pi{3.14159265359}; 21 | 22 | void test0() 23 | { 24 | auto piFloat(pi); 25 | auto piDouble(pi); 26 | 27 | /* 28 | auto piInt(pi); 29 | 30 | // Will not compile because narrowing `double` to `int` 31 | // is not allowed using uniform initialization syntax. 32 | */ 33 | } 34 | 35 | 36 | 37 | template 38 | int typeID; 39 | template <> 40 | constexpr int typeID{0}; 41 | template <> 42 | constexpr int typeID{1}; 43 | template <> 44 | constexpr int typeID{2}; 45 | 46 | void test1() 47 | { 48 | static_assert(typeID == 0, ""); 49 | static_assert(typeID == 1, ""); 50 | static_assert(typeID == 2, ""); 51 | } 52 | } 53 | 54 | int main() { return 0; } 55 | -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | 8 | // Hmm... we have a lot of different game elements, but what 9 | // do they have in common? 10 | 11 | // Every game element has an `update` method and a `draw` 12 | // method. 13 | 14 | // Using C++'s inheritance and runtime polymorphism features, 15 | // we can create a `GameElement` class, that will be the base 16 | // of our game elements. 17 | 18 | // This will allow us to abstract away the "type" of the game 19 | // element, and store heterogeneous game elements in a single 20 | // container. 21 | 22 | namespace InheritanceArkanoid 23 | { 24 | // Before defining the game element classes, we need 25 | // to define the `GameElement` base class. 26 | 27 | // What are the common features of the game elements? 28 | // The `update` and `draw` methods. We will declare them 29 | // as `virtual`, so that C++ runtime polymorphism may take 30 | // place. 31 | 32 | struct GameElement 33 | { 34 | virtual void update(float mFT) {} 35 | virtual void draw() {} 36 | virtual ~GameElement() {} 37 | }; 38 | 39 | // The `virtual` keyword is fundamental. Without it, runtime 40 | // polymorphism won't take place. 41 | 42 | // ...what's this runtime polymorphism I've been talking about? 43 | // Let's see a quick example before moving on. 44 | 45 | // ... 46 | -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace sf; 9 | 10 | constexpr int windowWidth{800}, windowHeight{600}; 11 | 12 | // Let's create some constants for the Ball class. 13 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 14 | 15 | struct Ball 16 | { 17 | CircleShape shape; 18 | 19 | // 2D vector that stores the Ball's velocity. 20 | Vector2f velocity{-ballVelocity, -ballVelocity}; 21 | 22 | Ball(float mX, float mY) 23 | { 24 | shape.setPosition(mX, mY); 25 | shape.setRadius(ballRadius); 26 | shape.setFillColor(Color::Red); 27 | shape.setOrigin(ballRadius, ballRadius); 28 | } 29 | 30 | // Let's "update" the ball: move its shape 31 | // by the current velocity. 32 | void update() { shape.move(velocity); } 33 | }; 34 | 35 | int main() 36 | { 37 | Ball ball{windowWidth / 2, windowHeight / 2}; 38 | 39 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 3"}; 40 | window.setFramerateLimit(60); 41 | 42 | while(true) 43 | { 44 | window.clear(Color::Black); 45 | 46 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 47 | 48 | // Every loop iteration, we need to update the ball. 49 | ball.update(); 50 | 51 | window.draw(ball.shape); 52 | window.display(); 53 | } 54 | 55 | return 0; 56 | } -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace sf; 9 | 10 | constexpr int windowWidth{800}, windowHeight{600}; 11 | constexpr float ballRadius{10.f}; 12 | 13 | // Let's create a class for our Ball. 14 | // `struct` == `class` in C++. 15 | struct Ball 16 | { 17 | // CircleShape is an SFML class that 18 | // defines a renderable circle. 19 | CircleShape shape; 20 | 21 | // Let's create the Ball constructor. 22 | // (argument mX -> starting x coordinate) 23 | // (argument mY -> starting y coordinate) 24 | Ball(float mX, float mY) 25 | { 26 | // Apply position, radius, color and origin 27 | // to the CircleShape `shape`. 28 | shape.setPosition(mX, mY); 29 | shape.setRadius(ballRadius); 30 | shape.setFillColor(Color::Red); 31 | shape.setOrigin(ballRadius, ballRadius); 32 | } 33 | }; 34 | 35 | int main() 36 | { 37 | // Let's create an instance of `Ball` 38 | // positioned at the center of the window 39 | Ball ball{windowWidth / 2, windowHeight / 2}; 40 | 41 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 2"}; 42 | window.setFramerateLimit(60); 43 | 44 | while(true) 45 | { 46 | window.clear(Color::Black); 47 | 48 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 49 | 50 | // Let's render the Ball instance on the window 51 | window.draw(ball.shape); 52 | window.display(); 53 | } 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tutorials 2 | ========= 3 | 4 | > **Repository for my YouTube tutorials / code snippets.** 5 | 6 | [![license][badge.license]][license] 7 | [![gratipay][badge.gratipay]][gratipay] 8 | 9 | [badge.license]: http://img.shields.io/badge/license-afl%203.0-blue.svg?style=flat-square 10 | [badge.gratipay]: https://img.shields.io/gratipay/user/SuperV1234.svg?style=flat-square 11 | 12 | [license]: https://github.com/SuperV1234/Tutorials/blob/master/LICENSE 13 | [gratipay]: https://gratipay.com/~SuperV1234/ 14 | 15 | --- 16 | 17 | * [**Dive into C++11 - [1] - Arkanoid clone in 160~ lines of code (SFML 2.1)**](https://www.youtube.com/watch?v=_4K3tsKa1Uc) 18 | 19 | * [**Dive into C++11 - [2] - Frametime, FPS, constexpr, uniform initialization**](http://www.youtube.com/watch?v=tPbrWAbzyTE) 20 | 21 | * [**Dive into C++11 - [3] - Automatic lifetime, pointers, dynamic allocation**](http://www.youtube.com/watch?v=0TGp0o1KnG8) 22 | 23 | * [**Dive into C++11 - [4] - Smart pointers**](http://www.youtube.com/watch?v=zMdD-s5_BIY) 24 | 25 | * [**Dive into C++11 - [5] - Game entity management basics**](https://www.youtube.com/watch?v=QAmtgvwHInM) 26 | 27 | --- 28 | 29 | * [**Dive into C++14 - [1] - Introduction to C++14 core language features**](https://www.youtube.com/watch?v=WZYKzCsACiw) 30 | 31 | * [**Dive into C++14 - [2] - `for_each_argument` explained and expanded**](https://www.youtube.com/watch?v=Za92Tz_g0zQ) 32 | 33 | * [**Dive into C++14 - [3] - Generic "unique resource" wrapper**](https://www.youtube.com/watch?v=Ehdl6j3Ace4) 34 | 35 | --- 36 | 37 | * [**Handle-based entity management experiments**](https://www.youtube.com/watch?v=_-KSlhppzNE) 38 | 39 | --- 40 | 41 | 42 | *Recorded with:* http://www.maartenbaert.be/simplescreenrecorder/ 43 | -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | 8 | // Welcome to "Dive into C++14", part 2. 9 | // http://vittorioromeo.info 10 | 11 | // In this tutorial video we'll take a look at a very 12 | // interesting code snippet, originally posted on Twitter 13 | // by Sean Parent: . 14 | 15 | // We'll also cover a very useful C++14 standard library 16 | // addition: "compile-time integer sequences". 17 | 18 | // This tutorial video is a reviewed and improved version 19 | // of my C++Now 2015 lightning talk: 20 | 21 | // "`for_each_arg` explained and expanded" 22 | 23 | // The original files can be found here: 24 | // https://github.com/SuperV1234/cppnow2015 25 | 26 | // Also, slides from the conference are available here: 27 | // https://github.com/boostcon/cppnow_presentations_2015 28 | 29 | // ---------------------------------------------------------------- 30 | 31 | // So, what does `for_each_argument` do? 32 | // Well, the name is pretty self-explanatory... 33 | 34 | // It invokes a callable object on every passed argument. 35 | 36 | template 37 | void for_each_argument(F f, Args&&... args) 38 | { 39 | [](...) 40 | { 41 | }((f(std::forward(args)), 0)...); 42 | } 43 | 44 | int main() 45 | { 46 | // Prints "hello123". 47 | for_each_argument( 48 | [](const auto& x) 49 | { 50 | std::cout << x; 51 | }, 52 | 53 | "hello", 1, 2, 3); 54 | 55 | std::cout << "\n"; 56 | return 0; 57 | } 58 | 59 | // That is cool. How does it work? 60 | // Let's discover that in the next code segment. -------------------------------------------------------------------------------- /DiveIntoC++14/1_CPP14/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace CPP14LanguageFeatures 11 | { 12 | // `decltype(auto)` 13 | // 14 | // * `auto` will always deduce a non-reference type. 15 | // 16 | // * `auto&&` will always deduce a reference type. 17 | // 18 | // * `decltype(auto)` deduces a non-reference or 19 | // reference type depending upon the value category 20 | // and the nature of a particular expression. 21 | // 22 | 23 | auto func3() 24 | { 25 | static std::string test{"bye!"}; 26 | 27 | auto& result(test); 28 | return result; 29 | } 30 | 31 | static_assert(std::is_same(), ""); 32 | 33 | 34 | 35 | decltype(auto) func4() 36 | { 37 | static std::string test{"bye again!"}; 38 | 39 | auto& result(test); 40 | return result; 41 | } 42 | 43 | static_assert(std::is_same(), ""); 44 | 45 | 46 | 47 | decltype(auto) func5() 48 | { 49 | std::string test{"bye one more time!"}; 50 | return std::move(test); 51 | 52 | // By the way, do not `std::move` things out 53 | // of a function. 54 | // This actually returns a reference to a 55 | // local object, which is not right! 56 | // Rely on the compiler's RVO, instead. 57 | } 58 | 59 | static_assert(std::is_same(), ""); 60 | 61 | 62 | 63 | // If you want to know more about type deduction 64 | // in the latest standard, check out this 65 | // C++Now 2015 talk by David Stone: 66 | 67 | // "Type Deduction in C++14" 68 | 69 | // All slides from the conference are available here: 70 | // https://github.com/boostcon/cppnow_presentations_2015/ 71 | } 72 | 73 | int main() { return 0; } -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/p5.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | void forArgs(TF&& mFn, Ts&&... mArgs) 12 | { 13 | return (void)std::initializer_list{ 14 | (mFn(std::forward(mArgs)), 0)...}; 15 | } 16 | 17 | // The implementation I'm about to show you uses a new library 18 | // feature introduced in C++14: compile-time "integer sequences". 19 | 20 | // Before moving on, let's see what they are and what they can do. 21 | 22 | // Positive integer sequences using `std::size_t` as their underlying 23 | // index type are called "index sequences". 24 | // They can be generated using `std::make_index_sequence`. 25 | 26 | // `Seq0` is a compile-time index sequence. 27 | // It goes from `0` (inclusive) to `10` (non-inclusive). 28 | using Seq0 = std::make_index_sequence<10>; 29 | 30 | // To retrieve the numbers in the sequence, we must match it 31 | // using template specializations and expand it with `...`. 32 | 33 | // Let's forward-declare a `struct` that will print an index 34 | // sequence to the standard output. 35 | template 36 | struct SeqPrinter; 37 | 38 | // Let's now specialize it to match an index sequence: 39 | template 40 | struct SeqPrinter> 41 | { 42 | // And let's use our `forArgs` function to print 43 | // the indices: 44 | static void print() 45 | { 46 | forArgs( 47 | [](auto x) 48 | { 49 | std::cout << x << " "; 50 | }, 51 | 52 | // We can expand the matched indices here: 53 | TIs...); 54 | } 55 | }; 56 | 57 | int main() 58 | { 59 | // Let's try it out now. 60 | 61 | // Prints "0 1 2 3 4 5 6 7 8 9". 62 | SeqPrinter::print(); 63 | std::cout << "\n"; 64 | 65 | // Prints "0 1 2 3 4". 66 | SeqPrinter>::print(); 67 | std::cout << "\n"; 68 | 69 | return 0; 70 | } -------------------------------------------------------------------------------- /DiveIntoC++11/2_Arkanoid/add2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | // Some "uniform initialization syntax" thoughts. 8 | 9 | struct Example 10 | { 11 | Example(int mX) { std::cout << "Constructor!" << std::endl; } 12 | Example& operator=(int mX) 13 | { 14 | std::cout << "Operator=!" << std::endl; 15 | return *this; 16 | } 17 | }; 18 | 19 | int main() 20 | { 21 | // Even though this looks like an assignment, 22 | // it's actually a declaration, therefore it 23 | // calls Example::Example(int), 24 | // printing "Constructor!". 25 | Example example1 = 10; 26 | 27 | // This is an assignment, which prints "Operator=!", 28 | // calling Example::operator=(int). 29 | example1 = 5; 30 | 31 | 32 | 33 | // Using "uniform initialization syntax". 34 | 35 | // We are obviously calling the constructor here. 36 | Example example2{10}; 37 | 38 | // example2{10}; <- this will not compile. 39 | 40 | // We are obviously assigning here. 41 | example2 = 10; 42 | 43 | // Other benefits: 44 | // * It avoids the most vexing parse. 45 | // * It's cohesive: it uses the same syntax as aggregate 46 | // initialization and to deal with array initialization 47 | // and initializer-lists. 48 | // * Prevents data-loss implicit conversions. 49 | 50 | return 0; 51 | } 52 | 53 | // Another example: 54 | struct Vector2 55 | { 56 | float x, y; 57 | }; 58 | 59 | // These two functions are equivalent. 60 | // By using {...} syntax we can avoid repeating Vector2. 61 | Vector2 getMyVector1() { return Vector2(5.f, 5.f); } 62 | Vector2 getMyVector2() { return {5.f, 5.f}; } 63 | 64 | // IMPORTANT NOTE: 65 | // When using `auto` to let the compiler deduce the type, 66 | // never use `{...}` syntax, as it is always deduced to 67 | // be an `std::initializer_list`. When using `auto`, 68 | // always use the `(...)` initialization syntax. 69 | 70 | auto x = 5; // (fine, x is an `int`) 71 | auto x(5); // (fine, x is an `int`) 72 | auto x{5}; // (misleading, x is an `std::initializer_list`) 73 | -------------------------------------------------------------------------------- /DiveIntoC++14/1_CPP14/p5.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace CPP14LanguageFeatures 11 | { 12 | // Generic lambdas. 13 | // 14 | // * Lambda function parameters can now be `auto`. 15 | // 16 | // * This effectively makes the lambda behave like 17 | // a functor with a templated `operator()`. 18 | // 19 | 20 | auto l0([](auto x) 21 | { 22 | return x * 2; 23 | }); 24 | 25 | static_assert(std::is_same(), ""); 26 | 27 | static_assert(std::is_same(), ""); 28 | 29 | static_assert(std::is_same(), ""); 30 | 31 | 32 | 33 | // Lambdas can also take variadic parameter packs. 34 | auto l1([](auto... xs) 35 | { 36 | return sizeof...(xs); 37 | }); 38 | 39 | 40 | 41 | // This lambda... 42 | auto l2([](auto x, auto y) 43 | { 44 | return x * y; 45 | }); 46 | 47 | // ...gets converted to something like this during 48 | // compilation: 49 | struct CompilerGeneratedL2 50 | { 51 | template 52 | auto operator()(T1 x, T2 y) const 53 | { 54 | return x * y; 55 | } 56 | }; 57 | } 58 | 59 | int main() { return 0; } 60 | 61 | // The C++14 standard has more core language features and library 62 | // additions - these were just some of my favorites! 63 | 64 | // Here are some helpful links to explore what the new standard 65 | // has to offer: 66 | 67 | // * https://isocpp.org/wiki/faq/cpp14-language 68 | // * http://en.wikipedia.org/wiki/C%2B%2B14 69 | // * http://www.drdobbs.com/cpp/the-c14-standard-what-you-need-to-know 70 | 71 | // Thank you very much for watching this brief introduction to some 72 | // C++14 core language features. 73 | 74 | // Check out the next tutorial for a very interesting code snippet 75 | // implementation and explanation: `forArgs`. 76 | 77 | // You can fork/look at the full source code on my GitHub page: 78 | // http://github.com/SuperV1234/ 79 | 80 | // Check out my website for more tutorials and to personally 81 | // get in touch with me. 82 | 83 | // http://vittorioromeo.info -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p4.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace sf; 9 | 10 | constexpr int windowWidth{800}, windowHeight{600}; 11 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 12 | 13 | struct Ball 14 | { 15 | CircleShape shape; 16 | Vector2f velocity{-ballVelocity, -ballVelocity}; 17 | 18 | Ball(float mX, float mY) 19 | { 20 | shape.setPosition(mX, mY); 21 | shape.setRadius(ballRadius); 22 | shape.setFillColor(Color::Red); 23 | shape.setOrigin(ballRadius, ballRadius); 24 | } 25 | 26 | void update() 27 | { 28 | shape.move(velocity); 29 | 30 | // We need to keep the ball "inside the screen". 31 | 32 | // If it's leaving toward the left, we need to set 33 | // horizontal velocity to a positive value (towards the right). 34 | if(left() < 0) velocity.x = ballVelocity; 35 | 36 | // Otherwise, if it's leaving towards the right, we need to 37 | // set horizontal velocity to a negative value (towards the left). 38 | else if(right() > windowWidth) 39 | velocity.x = -ballVelocity; 40 | 41 | // The same idea can be applied for top/bottom collisions. 42 | if(top() < 0) 43 | velocity.y = ballVelocity; 44 | else if(bottom() > windowHeight) 45 | velocity.y = -ballVelocity; 46 | } 47 | 48 | // We can also create "property" methods to easily 49 | // get commonly used values. 50 | float x() { return shape.getPosition().x; } 51 | float y() { return shape.getPosition().y; } 52 | float left() { return x() - shape.getRadius(); } 53 | float right() { return x() + shape.getRadius(); } 54 | float top() { return y() - shape.getRadius(); } 55 | float bottom() { return y() + shape.getRadius(); } 56 | }; 57 | 58 | int main() 59 | { 60 | Ball ball{windowWidth / 2, windowHeight / 2}; 61 | 62 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 4"}; 63 | window.setFramerateLimit(60); 64 | 65 | while(true) 66 | { 67 | window.clear(Color::Black); 68 | 69 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 70 | 71 | ball.update(); 72 | 73 | window.draw(ball.shape); 74 | window.display(); 75 | } 76 | 77 | return 0; 78 | } -------------------------------------------------------------------------------- /DiveIntoC++11/6_Macros/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | 8 | // Welcome to "Dive into C++11", part 5. 9 | // http://vittorioromeo.info 10 | 11 | // In this tutorial we're gonna cover a common game-development 12 | // topic: entity management. 13 | 14 | // I highly recommend watching part 1 and part 4 15 | // before watching this video. 16 | 17 | // Let's dive in! 18 | 19 | // Let's say we're making an arkanoid/breakout clone. 20 | // There are various game elements that the player interacts with: 21 | // * Ball 22 | // * Bricks 23 | // * Powerups 24 | // * Paddle 25 | // * ... 26 | 27 | // A possible way of dealing with all these elements is creating 28 | // a class for every element, with an `update` method and a `draw` 29 | // method, then use `std::vectors` of `std::unique_ptr` to manage 30 | // their lifetime. Here's an example: 31 | 32 | namespace VeryNaiveArkanoid 33 | { 34 | // Here are the game element classes: 35 | 36 | struct Ball 37 | { 38 | void update(float mFT) { /* ... */} 39 | void draw() { /* ... */} 40 | }; 41 | 42 | struct Brick 43 | { 44 | void update(float mFT) { /* ... */} 45 | void draw() { /* ... */} 46 | }; 47 | 48 | struct Paddle 49 | { 50 | void update(float mFT) { /* ... */} 51 | void draw() { /* ... */} 52 | }; 53 | 54 | struct Powerup 55 | { 56 | void update(float mFT) { /* ... */} 57 | void draw() { /* ... */} 58 | }; 59 | 60 | // And there is the "game" class itself: 61 | 62 | struct Game 63 | { 64 | std::vector> balls; 65 | std::vector> bricks; 66 | std::vector> paddles; 67 | std::vector> powerups; 68 | 69 | void update(float mFT) 70 | { 71 | for(auto& b : balls) b->update(mFT); 72 | for(auto& b : bricks) b->update(mFT); 73 | for(auto& p : paddles) p->update(mFT); 74 | for(auto& p : powerups) p->update(mFT); 75 | } 76 | 77 | void draw() 78 | { 79 | for(auto& b : balls) b->draw(); 80 | for(auto& b : bricks) b->draw(); 81 | for(auto& p : paddles) p->draw(); 82 | for(auto& p : powerups) p->draw(); 83 | } 84 | }; 85 | 86 | // As you can probably see, this approach is very difficult 87 | // to maintain and expand. For `n` game element types, you need 88 | // `n` containers, and `n` function calls. 89 | 90 | // Adding another game element would require the developer 91 | // to modify the game class itself. 92 | 93 | // This approach is confusing, error-prone and not scalable. 94 | } 95 | 96 | // Let's move on, and check out a (possibly) better implementation. -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | 8 | // Let's rewrite the code snippet in a more organized 9 | // manner, using comments to make obvious what it is 10 | // actually happening. 11 | 12 | template 13 | void forArgs(TF&& mFn, Ts&&... mArgs) 14 | { 15 | // Instead of a generic variadic lambda, we can use 16 | // an `initializer_list` to create a context where 17 | // variadic parameter expansion can take place. 18 | 19 | // Any "brace-initializable" container works, such as 20 | // C-style arrays. (Examples: `bool[]`, `int[]`) 21 | 22 | // This guarantees that the arguments will be 23 | // evaluated in the correct order. 24 | 25 | return (void)std::initializer_list{ 26 | // Every element of the `initializer_list` is an 27 | // expression enclosed in round parenthesis. 28 | ( 29 | // In the parenthesis, the result of the `mFn` 30 | // function call is followed by the comma 31 | // operator and an integer (zero in this 32 | // case). 33 | 34 | // Thanks to the comma operator, the expression 35 | // evaluates to an (unused) integer, which is 36 | // accepted by the `initializer_list`. 37 | 38 | mFn( 39 | // As we're taking the variadic arguments by 40 | // "universal reference", it is important 41 | // to use `std::forward` to correctly forward 42 | // their reference types to `mFn`. 43 | std::forward(mArgs)), 44 | 45 | 0)...}; 46 | } 47 | 48 | // Still confused? Everything becomes clearer with an example 49 | // expansion of a `forArgs` call: 50 | 51 | /* 52 | 53 | // The following `forArgs` call... 54 | 55 | forArgs 56 | ( 57 | [](const auto& x){ std::cout << x << " "; }, 58 | 59 | "hello", 60 | 1, 61 | 2, 62 | 3 63 | ); 64 | 65 | // ..roughly expands to... 66 | 67 | (void) std::initializer_list 68 | { 69 | ([](const auto& x){ std::cout << x; }("hello"), 0), 70 | ([](const auto& x){ std::cout << x; }(1), 0), 71 | ([](const auto& x){ std::cout << x; }(2), 0), 72 | ([](const auto& x){ std::cout << x; }(3), 0) 73 | } 74 | 75 | // ...which is the same as writing... 76 | 77 | std::cout << "hello"; 78 | std::cout << 1; 79 | std::cout << 2; 80 | std::cout << 3; 81 | 82 | */ 83 | 84 | int main() 85 | { 86 | // Prints "hello123". 87 | forArgs( 88 | [](const auto& x) 89 | { 90 | std::cout << x; 91 | }, 92 | 93 | "hello", 1, 2, 3); 94 | 95 | std::cout << "\n"; 96 | return 0; 97 | } 98 | 99 | // Additional resources: 100 | // Here's an excellent article about universal references 101 | // and `std::forward` usage: 102 | 103 | // http://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c/ -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | 8 | // Welcome to "Dive into C++11", part 5. 9 | // http://vittorioromeo.info 10 | 11 | // In this tutorial we're gonna cover a common game-development 12 | // topic: entity management. 13 | 14 | // I highly recommend watching part 1 and part 4 15 | // before watching this video. 16 | 17 | // Let's dive in! 18 | 19 | // Let's say we're making an arkanoid/breakout clone. 20 | // There are various game elements that the player interacts with: 21 | // * Ball 22 | // * Bricks 23 | // * Powerups 24 | // * Paddle 25 | // * ... 26 | 27 | // A possible way of dealing with all these elements is creating 28 | // a class for every element, with an `update` method and a `draw` 29 | // method, then use `std::vectors` of `std::unique_ptr` to manage 30 | // their lifetime. Here's an example: 31 | 32 | namespace VeryNaiveArkanoid 33 | { 34 | // Here are the game element classes: 35 | 36 | struct Ball 37 | { 38 | void update(float mFT) { /* ... */} 39 | void draw() { /* ... */} 40 | }; 41 | 42 | struct Brick 43 | { 44 | void update(float mFT) { /* ... */} 45 | void draw() { /* ... */} 46 | }; 47 | 48 | struct Paddle 49 | { 50 | void update(float mFT) { /* ... */} 51 | void draw() { /* ... */} 52 | }; 53 | 54 | struct Powerup 55 | { 56 | void update(float mFT) { /* ... */} 57 | void draw() { /* ... */} 58 | }; 59 | 60 | // And there is the "game" class itself: 61 | 62 | struct Game 63 | { 64 | std::vector> balls; 65 | std::vector> bricks; 66 | std::vector> paddles; 67 | std::vector> powerups; 68 | 69 | // Note that we could also decide against using smart 70 | // pointers, and simply store game elements contiguously 71 | // in the vectors. This would improve performance, but 72 | // would make keeping track of specific instances of 73 | // these game elements very hard, as pointers to them 74 | // would get invalidated when the vectors are internally 75 | // resized. 76 | 77 | void update(float mFT) 78 | { 79 | for(auto& b : balls) b->update(mFT); 80 | for(auto& b : bricks) b->update(mFT); 81 | for(auto& p : paddles) p->update(mFT); 82 | for(auto& p : powerups) p->update(mFT); 83 | } 84 | 85 | void draw() 86 | { 87 | for(auto& b : balls) b->draw(); 88 | for(auto& b : bricks) b->draw(); 89 | for(auto& p : paddles) p->draw(); 90 | for(auto& p : powerups) p->draw(); 91 | } 92 | }; 93 | 94 | // As you can probably see, this approach is very difficult 95 | // to maintain and expand. For `N` game element types, you need 96 | // `N` containers, and `N` function calls. 97 | 98 | // Adding another game element would require the developer 99 | // to modify the game class itself. 100 | 101 | // This approach is confusing, error-prone and not scalable. 102 | 103 | // Something similar to this code segment is only appropriate 104 | // for small games with a small number of game element types. 105 | } 106 | 107 | // Let's move on, and check out a (possibly) better implementation. -------------------------------------------------------------------------------- /DiveIntoC++14/1_CPP14/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Welcome to "Dive into C++14", part 1. 12 | // http://vittorioromeo.info 13 | 14 | // C++11 is awesome, but C++14 is even better. 15 | 16 | // C++14-compliant compilers are becoming more widespread. 17 | // It is time to leave C++11 behind and move onto the 18 | // newest standard of the language. 19 | 20 | // I'm, therefore, introducing a new YouTube video series, 21 | // dedicated to C++14. 22 | 23 | // The videos will share the same format and topics as the 24 | // previous ones: 25 | // * Chronologically sequential commented code segments. 26 | // * Screencasting + live compilation and execution. 27 | // * Various topics: game development, language features, 28 | // interesting code snippets, best practices, etc... 29 | 30 | // Compiler support: 31 | // * "clang++ 3.5" has full C++14 support. 32 | // * "g++ 5.0" has full C++14 support. 33 | 34 | // Refer to the following links for more information: 35 | // * http://en.cppreference.com/w/cpp/compiler_support 36 | // * http://clang.llvm.org/cxx_status.html 37 | // * https://gcc.gnu.org/projects/cxx1y.html 38 | 39 | // Let's take a quick look at some of my favorite new 40 | // core language features of the C++14 standard: 41 | // 42 | // * Function return type deduction. 43 | // 44 | // * `decltype(auto)`. 45 | // 46 | // * Relaxed constexpr restrictions. 47 | // 48 | // * Variable templates. 49 | // 50 | // * Generic lambdas. 51 | // 52 | 53 | // If you are familiar with the features listed above, 54 | // feel free to skip to the next video in the series, 55 | // which shows a very interesting C++14 code snippet. 56 | 57 | namespace CPP14LanguageFeatures 58 | { 59 | // Function return type deduction. 60 | // 61 | // * This feature allows the use of the keyword `auto` 62 | // in place of a function's return type. 63 | // 64 | // * You can qualify the `auto` keyword with `const`, 65 | // `*`, `&`, etc... 66 | // 67 | // * It follows the same rules as `auto` variable 68 | // type deduction. 69 | // 70 | 71 | auto func0() { return 0; } 72 | 73 | static_assert(std::is_same(), ""); 74 | 75 | 76 | 77 | auto func1() 78 | { 79 | std::string test{"hello!"}; 80 | return test; 81 | } 82 | 83 | static_assert(std::is_same(), ""); 84 | 85 | 86 | 87 | const auto& func2() 88 | { 89 | static std::string test{"hello again!"}; 90 | return test; 91 | } 92 | 93 | static_assert(std::is_same(), ""); 94 | 95 | 96 | // Here are some additional examples: 97 | template 98 | struct SomeContainerWrapper 99 | { 100 | std::vector vec; 101 | 102 | typename std::vector::iterator beginCPP03() const 103 | { 104 | return std::begin(vec); 105 | } 106 | 107 | auto beginCPP11() const -> decltype(std::begin(vec)) 108 | { 109 | return std::begin(vec); 110 | } 111 | 112 | auto beginCPP14() const { return std::begin(vec); } 113 | }; 114 | 115 | 116 | 117 | template 118 | auto complicatedFuncCPP11(T1 x, T2 y, T3 z) -> decltype((x * y) - (y * z)) 119 | { 120 | return (x * y) - (y * z); 121 | } 122 | 123 | template 124 | auto complicatedFuncCPP14(T1 x, T2 y, T3 z) 125 | { 126 | return (x * y) - (y * z); 127 | } 128 | } 129 | 130 | int main() { return 0; } -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p5.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace sf; 9 | 10 | constexpr int windowWidth{800}, windowHeight{600}; 11 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 12 | 13 | // Let's create some constants for the paddle. 14 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{6.f}; 15 | 16 | struct Ball 17 | { 18 | CircleShape shape; 19 | Vector2f velocity{-ballVelocity, -ballVelocity}; 20 | 21 | Ball(float mX, float mY) 22 | { 23 | shape.setPosition(mX, mY); 24 | shape.setRadius(ballRadius); 25 | shape.setFillColor(Color::Red); 26 | shape.setOrigin(ballRadius, ballRadius); 27 | } 28 | 29 | void update() 30 | { 31 | shape.move(velocity); 32 | 33 | if(left() < 0) 34 | velocity.x = ballVelocity; 35 | else if(right() > windowWidth) 36 | velocity.x = -ballVelocity; 37 | 38 | if(top() < 0) 39 | velocity.y = ballVelocity; 40 | else if(bottom() > windowHeight) 41 | velocity.y = -ballVelocity; 42 | } 43 | 44 | float x() { return shape.getPosition().x; } 45 | float y() { return shape.getPosition().y; } 46 | float left() { return x() - shape.getRadius(); } 47 | float right() { return x() + shape.getRadius(); } 48 | float top() { return y() - shape.getRadius(); } 49 | float bottom() { return y() + shape.getRadius(); } 50 | }; 51 | 52 | // Let's create a `Paddle` class, similar to `Ball`. 53 | struct Paddle 54 | { 55 | // RectangleShape is an SFML class that defines 56 | // a renderable rectangular shape. 57 | RectangleShape shape; 58 | Vector2f velocity; 59 | 60 | // As with the ball, we construct the paddle with 61 | // arguments for initial position and pass the values 62 | // to the SFML `shape`. 63 | Paddle(float mX, float mY) 64 | { 65 | shape.setPosition(mX, mY); 66 | shape.setSize({paddleWidth, paddleHeight}); 67 | shape.setFillColor(Color::Red); 68 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 69 | } 70 | 71 | void update() 72 | { 73 | shape.move(velocity); 74 | 75 | // To move the paddle, we check if the user is pressing 76 | // the left or right arrow key: if so, we change the velocity. 77 | 78 | // To keep the paddle "inside the window", we change the velocity 79 | // only if its position is inside the window. 80 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 81 | velocity.x = -paddleVelocity; 82 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 83 | right() < windowWidth) 84 | velocity.x = paddleVelocity; 85 | 86 | // If the user isn't pressing anything, stop moving. 87 | else 88 | velocity.x = 0; 89 | } 90 | 91 | float x() { return shape.getPosition().x; } 92 | float y() { return shape.getPosition().y; } 93 | float left() { return x() - shape.getSize().x / 2.f; } 94 | float right() { return x() + shape.getSize().x / 2.f; } 95 | float top() { return y() - shape.getSize().y / 2.f; } 96 | float bottom() { return y() + shape.getSize().y / 2.f; } 97 | }; 98 | 99 | int main() 100 | { 101 | Ball ball{windowWidth / 2, windowHeight / 2}; 102 | 103 | // Let's create a `Paddle` instance 104 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 105 | 106 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 5"}; 107 | window.setFramerateLimit(60); 108 | 109 | while(true) 110 | { 111 | window.clear(Color::Black); 112 | 113 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 114 | 115 | ball.update(); 116 | 117 | // Let's update our `paddle`... 118 | paddle.update(); 119 | 120 | window.draw(ball.shape); 121 | 122 | // ...and draw its shape on the window. 123 | window.draw(paddle.shape); 124 | window.display(); 125 | } 126 | 127 | return 0; 128 | } -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p6.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace sf; 9 | 10 | constexpr int windowWidth{800}, windowHeight{600}; 11 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 12 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{6.f}; 13 | 14 | struct Ball 15 | { 16 | CircleShape shape; 17 | Vector2f velocity{-ballVelocity, -ballVelocity}; 18 | 19 | Ball(float mX, float mY) 20 | { 21 | shape.setPosition(mX, mY); 22 | shape.setRadius(ballRadius); 23 | shape.setFillColor(Color::Red); 24 | shape.setOrigin(ballRadius, ballRadius); 25 | } 26 | 27 | void update() 28 | { 29 | shape.move(velocity); 30 | 31 | if(left() < 0) 32 | velocity.x = ballVelocity; 33 | else if(right() > windowWidth) 34 | velocity.x = -ballVelocity; 35 | 36 | if(top() < 0) 37 | velocity.y = ballVelocity; 38 | else if(bottom() > windowHeight) 39 | velocity.y = -ballVelocity; 40 | } 41 | 42 | float x() { return shape.getPosition().x; } 43 | float y() { return shape.getPosition().y; } 44 | float left() { return x() - shape.getRadius(); } 45 | float right() { return x() + shape.getRadius(); } 46 | float top() { return y() - shape.getRadius(); } 47 | float bottom() { return y() + shape.getRadius(); } 48 | }; 49 | 50 | struct Paddle 51 | { 52 | RectangleShape shape; 53 | Vector2f velocity; 54 | 55 | Paddle(float mX, float mY) 56 | { 57 | shape.setPosition(mX, mY); 58 | shape.setSize({paddleWidth, paddleHeight}); 59 | shape.setFillColor(Color::Red); 60 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 61 | } 62 | 63 | void update() 64 | { 65 | shape.move(velocity); 66 | 67 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 68 | velocity.x = -paddleVelocity; 69 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 70 | right() < windowWidth) 71 | velocity.x = paddleVelocity; 72 | else 73 | velocity.x = 0; 74 | } 75 | 76 | float x() { return shape.getPosition().x; } 77 | float y() { return shape.getPosition().y; } 78 | float left() { return x() - shape.getSize().x / 2.f; } 79 | float right() { return x() + shape.getSize().x / 2.f; } 80 | float top() { return y() - shape.getSize().y / 2.f; } 81 | float bottom() { return y() + shape.getSize().y / 2.f; } 82 | }; 83 | 84 | // Dealing with collisions: let's define a generic function 85 | // to check if two shapes are intersecting (colliding). 86 | template 87 | bool isIntersecting(T1& mA, T2& mB) 88 | { 89 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 90 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 91 | } 92 | 93 | // Let's define a function that deals with paddle/ball collision. 94 | void testCollision(Paddle& mPaddle, Ball& mBall) 95 | { 96 | // If there's no intersection, get out of the function. 97 | if(!isIntersecting(mPaddle, mBall)) return; 98 | 99 | // Otherwise let's "push" the ball upwards. 100 | mBall.velocity.y = -ballVelocity; 101 | 102 | // And let's direct it dependently on the position where the 103 | // paddle was hit. 104 | if(mBall.x() < mPaddle.x()) 105 | mBall.velocity.x = -ballVelocity; 106 | else 107 | mBall.velocity.x = ballVelocity; 108 | } 109 | 110 | int main() 111 | { 112 | Ball ball{windowWidth / 2, windowHeight / 2}; 113 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 114 | 115 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 6"}; 116 | window.setFramerateLimit(60); 117 | 118 | while(true) 119 | { 120 | window.clear(Color::Black); 121 | 122 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 123 | 124 | ball.update(); 125 | paddle.update(); 126 | 127 | // Let's test the collision every game loop iteration. 128 | testCollision(paddle, ball); 129 | 130 | window.draw(ball.shape); 131 | window.draw(paddle.shape); 132 | window.display(); 133 | } 134 | 135 | return 0; 136 | } -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/p3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Here's our `forArgs` function without any comment. 11 | // Doesn't look so scary anymore, does it? 12 | 13 | template 14 | void forArgs(TF&& mFn, Ts&&... mArgs) 15 | { 16 | return (void)std::initializer_list{ 17 | (mFn(std::forward(mArgs)), 0)...}; 18 | } 19 | 20 | // So, what is `forArgs` useful for? 21 | // Let's see some potential use cases for it. 22 | 23 | // ---------------------------------------------------------------- 24 | 25 | // Example use case: `make_vector` function. 26 | 27 | // `make_vector` will take one or more arguments 28 | // and return an `std::vector` containing them. 29 | 30 | template 31 | auto make_vector(TArgs&&... mArgs) 32 | { 33 | // First problem: what type should we return? 34 | 35 | // We need to deduce a type suitable for all the 36 | // passed arguments. 37 | 38 | // `std::common_type_t` comes into play here. 39 | 40 | // Note: 41 | // 42 | // * C++14 added many versions of commonly used 43 | // type traits functions ending with "_t", that 44 | // do not require the user to write `typename` 45 | // at the beginning and `::type` at the end. 46 | // 47 | // * C++11: 48 | // using IntPtr = typename std::add_pointer::type; 49 | // 50 | // * C++14: 51 | // using IntPtr = std::add_pointer_t; 52 | // 53 | 54 | // `std::common_type_t` determines the common type among all 55 | // passed types - that is the type all passed types can be 56 | // implicitly converted to. 57 | 58 | // Computing the correct return type is now easy: 59 | using VectorItem = std::common_type_t; 60 | std::vector result; 61 | 62 | // We also know how many items we're going to put into 63 | // the vector - we can actually reserve the memory beforehand 64 | // as a small optimization. 65 | result.reserve(sizeof...(TArgs)); 66 | 67 | // Now we use `forArgs` to generate the code that emplaces 68 | // the items into our vector: 69 | forArgs( 70 | // Our lambda needs to capture the vector and use 71 | // an "universal reference" to correctly forward 72 | // the passed argument to `std::vector::emplace_back`. 73 | [&result](auto&& x) 74 | { 75 | // We do not know the type of `x` - but we can 76 | // retrieve it using `decltype(x)`. 77 | result.emplace_back(std::forward(x)); 78 | }, 79 | 80 | std::forward(mArgs)...); 81 | 82 | return result; 83 | } 84 | 85 | int main() 86 | { 87 | // Deduced as `std::vector`; 88 | auto v0(make_vector(1, 2, 3, 4, 5)); 89 | 90 | // This is roughly equivalent to writing: 91 | /* 92 | std::vector result; 93 | result.reserve(5); 94 | 95 | result.emplace_back(1); 96 | result.emplace_back(2); 97 | result.emplace_back(3); 98 | result.emplace_back(4); 99 | result.emplace_back(5); 100 | 101 | return result; 102 | */ 103 | 104 | static_assert(std::is_same>(), ""); 105 | 106 | // Prints "12345". 107 | for(const auto& x : v0) std::cout << x; 108 | std::cout << "\n"; 109 | 110 | 111 | 112 | // Deduced as `std::vector`; 113 | auto v1(make_vector("hello", " ", "everyone!")); 114 | 115 | static_assert(std::is_same>(), ""); 116 | 117 | // Prints "hello everyone!". 118 | for(const auto& x : v1) std::cout << x; 119 | std::cout << "\n"; 120 | 121 | 122 | 123 | // Deduced as `std::vector`. 124 | // This happens because `const char*` can be implicitly 125 | // converted to `std::string`, but not vice versa. 126 | auto v2(make_vector("hello", " ", std::string{"world"})); 127 | 128 | static_assert(std::is_same>(), ""); 129 | 130 | // Prints "hello world". 131 | for(const auto& x : v2) std::cout << x; 132 | std::cout << "\n"; 133 | 134 | return 0; 135 | } -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/p4.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | 9 | template 10 | void forArgs(TF&& mFn, Ts&&... mArgs) 11 | { 12 | return (void)std::initializer_list{ 13 | (mFn(std::forward(mArgs)), 0)...}; 14 | } 15 | 16 | // This code segments shows another interesting use case: 17 | // iteration over `std::tuple` elements. 18 | 19 | // Example use case: `forTuple` function. 20 | 21 | // We can use `forArgs` as a building block for an `std::tuple` 22 | // element iteration function. 23 | 24 | // To do so, we require an helper function that expands the 25 | // elements of the `std::tuple` into a function call. 26 | 27 | // The following helper function is taken from paper N3802, 28 | // proposed by Peter Sommerlad. 29 | 30 | // You can find the paper at the following address: 31 | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3802.pdf 32 | 33 | // Many similar implementations of the same function can be 34 | // easily found online. 35 | 36 | // ---------------------------------------------------------------- 37 | 38 | // We do not really need to know how this function works for the 39 | // scope of this video. 40 | 41 | template 42 | decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence) 43 | { 44 | return std::forward(f)(std::get(std::forward(t))...); 45 | } 46 | 47 | template 48 | decltype(auto) apply(F&& f, Tuple&& t) 49 | { 50 | using Indices = 51 | std::make_index_sequence>::value>; 52 | 53 | return apply_impl(std::forward(f), std::forward(t), Indices{}); 54 | } 55 | 56 | // ---------------------------------------------------------------- 57 | 58 | // `forTuple` is a function that takes a callable object and 59 | // and `std::tuple` as its parameters. 60 | 61 | // It then calls the passed function individually passing every 62 | // element of the tuple as its argument. 63 | 64 | template 65 | void forTuple(TFn&& mFn, TTpl&& mTpl) 66 | { 67 | // We basically expand the tuple into a function call to 68 | // a variadic polymorphic lambda with `apply`, which in 69 | // turn passes the expanded tuple elements to `forArgs`, 70 | // one by one... which in turn calls `mFn` with every 71 | // single tuple element individually. 72 | 73 | apply( 74 | // The callable object we will pass to `apply` is 75 | // a generic variadic lambda that forwards its 76 | // arguments to `forArgs`. 77 | [&mFn](auto&&... xs) 78 | { 79 | // The `xs...` parameter pack contains the 80 | // `mTpl` tuple elements, expanded thanks 81 | // to `apply`. 82 | 83 | // We will call the `mFn` unary function 84 | // for each expanded tuple element, thanks 85 | // to `forArgs`. 86 | forArgs(mFn, std::forward(xs)...); 87 | }, 88 | 89 | std::forward(mTpl)); 90 | } 91 | 92 | int main() 93 | { 94 | // Prints "10 hello 15 c". 95 | forTuple( 96 | [](const auto& x) 97 | { 98 | std::cout << x << " "; 99 | }, 100 | std::make_tuple(10, "hello", 15.f, 'c')); 101 | 102 | // This is roughly equivalent to writing: 103 | /* 104 | forArgs 105 | ( 106 | [](const auto& x){ std::cout << x << " "; }, 107 | 108 | 10, 109 | "hello", 110 | 15.f, 111 | 'c' 112 | ); 113 | 114 | // ...which, in turn, is roughly equivalent to: 115 | 116 | std::cout << 10 << " "; 117 | std::cout << "hello" << " "; 118 | std::cout << "15.f" << " "; 119 | std::cout << 'c' << " "; 120 | */ 121 | 122 | std::cout << "\n"; 123 | return 0; 124 | } 125 | 126 | // All of this is extremely cool and useful - but we're 127 | // limited to unary functions. 128 | 129 | // What if we want to take arguments two by two? 130 | // Or three by three? 131 | 132 | // It is actually possible to create a generic version 133 | // of `forArgs` that takes the arity of the passed callable 134 | // object as a template parameter. 135 | 136 | // Let's see an implementation of that... -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p4.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | 8 | // Now that we understand the basics of runtime polymorphism, 9 | // let's return to our arkanoid clone example. 10 | 11 | // We will define a base `GameElement` class, with a virtual 12 | // update method and a virtual draw method. 13 | 14 | // Game element types will inherit from this class and override 15 | // the virtual methods to define their own behavior. 16 | 17 | namespace InheritanceArkanoid 18 | { 19 | struct GameElement 20 | { 21 | virtual void update(float mFT) {} 22 | virtual void draw() {} 23 | virtual ~GameElement() {} 24 | }; 25 | 26 | // Now that we have defined a base class, we can define 27 | // the classes that inherit from this `GameElement`, 28 | // as we did in the animal example. 29 | 30 | struct Ball : GameElement 31 | { 32 | void update(float mFT) override { /* ... */} 33 | void draw() override { /* ... */} 34 | }; 35 | 36 | struct Brick : GameElement 37 | { 38 | void update(float mFT) override { /* ... */} 39 | void draw() override { /* ... */} 40 | }; 41 | 42 | struct Paddle : GameElement 43 | { 44 | void update(float mFT) override { /* ... */} 45 | void draw() override { /* ... */} 46 | }; 47 | 48 | struct Powerup : GameElement 49 | { 50 | void update(float mFT) override { /* ... */} 51 | void draw() override { /* ... */} 52 | }; 53 | 54 | // In the game class we can now store game elements in 55 | // the same container. Even if we'll be storing pointers 56 | // to `GameElement`, which is the base class, polymorphism 57 | // will make sure the correct overrides will be called for 58 | // each game element type. 59 | 60 | struct Game 61 | { 62 | std::vector> elements; 63 | 64 | void update(float mFT) 65 | { 66 | for(auto& e : elements) e->update(mFT); 67 | } 68 | 69 | void draw() 70 | { 71 | for(auto& e : elements) e->draw(); 72 | } 73 | }; 74 | 75 | // Much better! The game class doesn't care about the 76 | // game element types anymore. 77 | 78 | // It is now much easier to implement a new game element: 79 | // modifying the game class is not required. Also, managing 80 | // interactions between elements and deleting "dead" elements 81 | // will be much easier with a single container. 82 | } 83 | 84 | // This approach, however, has a big drawback: this kind 85 | // of design ("inheritance") does not allow "composition". 86 | 87 | // By composition I mean begin able to create game objects 88 | // by putting togheter several small components. 89 | 90 | // Using this method you will end up with a big inheritance 91 | // tree that makes sharing data and behavior between objects 92 | // very difficult. Code repetition will be an issue. 93 | // Here's an example: 94 | 95 | /* 96 | [ GameElement ] 97 | | 98 | [ EnemyNPC ]----------------[ FriendlyNPC ] 99 | | | 100 | [ EnemyArmoredNPC ] [ FriendlyArmoredNPC ] 101 | | | 102 | [ EnemyArmoredNPCWithGun ] [ FriendlyArmoredNPCWithGun ] 103 | */ 104 | 105 | // As you can see, even though both friendly and enemy NPCs 106 | // types may be armored or have a weapon, two separate 107 | // inheritance tree branches have to be constructed to allow 108 | // different combinations. 109 | 110 | // This becomes cumbersome very fast, especially when there 111 | // can be a lot of combinations. 112 | 113 | // Wouldn't it be better to separate common behaviors and data 114 | // in small components that objects could be made up of? 115 | 116 | // Here's an example of a better design: 117 | 118 | /* 119 | Components: 120 | [ NPC ] 121 | [ Enemy ] 122 | [ Friendly ] 123 | [ Armored ] 124 | [ WithGun ] 125 | ... 126 | 127 | Entities: 128 | [[ Skeleton ]] = Enemy + NPC 129 | [[ Paladin ]] = Friendly + NPC + Armored + WithGun 130 | [[ Player ]] = Friendly + Armored + WithGun 131 | [[ Sniper ]] = Enemy + NPC + WithGun 132 | ... 133 | */ 134 | 135 | // Let's check out a possible implementation in the next 136 | // code segment. -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | // Runtime polymorphism is a C++ feature that allows us to 10 | // define a hierarchy of classes sharing a common interface, 11 | // but different implementations. 12 | 13 | // Feel free to skip this code segment if you already know 14 | // how C++ runtime polymorphism works and its pitfalls. 15 | 16 | // A simple example will make polymorphism easy to understand: 17 | 18 | struct Animal 19 | { 20 | // Remember: the `virtual` keyword is essential for 21 | // runtime polymorphism! 22 | 23 | virtual void makeNoise() {} 24 | 25 | // As we'll be using this class polymorphically, it requires 26 | // a virtual destructor. Even if empty, the virtual destructor 27 | // will make sure the right amount of memory is freed when 28 | // a polymorphic instance is destroyed. 29 | virtual ~Animal() {} 30 | }; 31 | 32 | // The ` : ` syntax means that inherits from . 33 | struct Dog : Animal 34 | { 35 | // We use the `override` C++11 keyword to make sure 36 | // the method we're defining polymorphically overrides 37 | // the base class's method. 38 | 39 | // Overridden methods must have the same signature as 40 | // the base virtual method. 41 | 42 | void makeNoise() override { std::cout << "Bark!" << std::endl; } 43 | }; 44 | 45 | struct Cat : Animal 46 | { 47 | void makeNoise() override { std::cout << "Meow!" << std::endl; } 48 | }; 49 | 50 | int main() 51 | { 52 | // Derived classes may have different sizes from the base 53 | // class, so it is impossible to allocate them on the stack, since 54 | // the size of the object is not known at compile-time. 55 | // It is mandatory to access and interact with polymorphic objects 56 | // through pointers. 57 | 58 | /* 59 | // INCORRECT: 60 | 61 | Animal myDog{Dog{}}; 62 | Animal myCat{Cat{}}; 63 | 64 | // This causes a problem called "object slicing": 65 | // `sizeof(Animal)` could be different from `sizeof(Dog)` 66 | // or `sizeof(Cat)`. 67 | 68 | // Only enough memory for an object of type 69 | // `Animal` is allocated. 70 | 71 | // Polymorphism may not work as expected. 72 | */ 73 | 74 | { 75 | // OK: 76 | 77 | Dog myDog{}; 78 | Cat myCat{}; 79 | 80 | Animal* ptrAnimal; 81 | 82 | ptrAnimal = &myDog; 83 | ptrAnimal->makeNoise(); // Bark! 84 | 85 | ptrAnimal = &myCat; 86 | ptrAnimal->makeNoise(); // Meow! 87 | 88 | // `myDog` and `myCat` will not suffer from object slicing, 89 | // as they are allocated (on the stack) with their "real" 90 | // type. 91 | 92 | // Accessing them through a base `Animal*` pointer will 93 | // enable polymorphism. 94 | } 95 | 96 | { 97 | // Usually we use heap memory to deal with polymorphic objects: 98 | 99 | std::unique_ptr myDog{new Dog{}}; 100 | std::unique_ptr myCat{new Cat{}}; 101 | 102 | // We use `std::unique_ptr` to make sure the memory 103 | // allocated for the polymorphic object will be freed. 104 | 105 | // Let's call our polymorphic method. 106 | 107 | myDog->makeNoise(); // Prints "Bark!" 108 | myCat->makeNoise(); // Prints "Meow!" 109 | 110 | // As you can see, even if the types of `myDog` and 111 | // `myCat` are equal (`std::unique_ptr`), C++ 112 | // runtime polymorphism runs through the class hierarchy 113 | // and calls the correct method. 114 | 115 | // This allows us, for example, to store polymorphic objects 116 | // in the same container: 117 | 118 | std::vector> animals; 119 | 120 | std::cout << "Iterating...\n"; 121 | animals.emplace_back(new Dog{}); 122 | animals.emplace_back(new Dog{}); 123 | animals.emplace_back(new Cat{}); 124 | animals.emplace_back(new Dog{}); 125 | animals.emplace_back(new Cat{}); 126 | animals.emplace_back(new Cat{}); 127 | 128 | for(const auto& a : animals) a->makeNoise(); 129 | 130 | // Prints: 131 | // 132 | // "Bark!" 133 | // "Bark!" 134 | // "Meow!" 135 | // "Bark!" 136 | // "Meow!" 137 | // "Meow!" 138 | } 139 | 140 | return 0; 141 | } -------------------------------------------------------------------------------- /DiveIntoC++14/3_UniqueResource/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | 9 | // Welcome to "Dive into C++14", part 3. 10 | // http://vittorioromeo.info 11 | 12 | // In this tutorial we'll cover a very important topic: safe resource 13 | // management. 14 | 15 | // We're going to implement our own generic "unique resource" class, with 16 | // semantics equivalent to `std::unique_ptr`. 17 | 18 | // We're also going to implement a simple "scope guard". 19 | 20 | // So, what's a "resource" and why do we need to "manage" it? 21 | 22 | // From Wikipedia: 23 | // "A resource, or system resource, is any physical or virtual component of 24 | // limited availability within a computer system. Virtual system resources 25 | // include files, network connections, and memory areas." 26 | 27 | // Real-life examples include: files, sockets, heap-allocated memory locations, 28 | // game resources (models, sounds, textures, ...). 29 | 30 | // All resources have something in common: they need to be "acquired" and 31 | // "released". 32 | 33 | // Since resources are limited, we need to carefully think about their 34 | // acquisition and release, making sure we do not run out of resource types we 35 | // will need in the future and that we do not forget to release resources we do 36 | // not need anymore. 37 | 38 | // In other words, we need to think about the "ownership" and the "lifetime" of 39 | // a resource. 40 | 41 | // Fortunately, thanks to modern C++ generic programming, it is possible to 42 | // abstract common ownership/lifetime semantics for resources. 43 | 44 | // You've already seen, in previous "Dive into C++11/14" episodes, how 45 | // heap-allocated memory can be automatically and safely managed using 46 | // `std::unique_ptr` and `std::shared_ptr`. 47 | 48 | struct Resource 49 | { 50 | Resource() 51 | { 52 | std::cout << "Acquire.\n"; 53 | } 54 | ~Resource() 55 | { 56 | std::cout << "Release.\n"; 57 | } 58 | }; 59 | 60 | void unique_ptr_example() 61 | { 62 | // "Empty" resource handle. 63 | std::unique_ptr uptr0; 64 | 65 | // Acquire resource. 66 | uptr0 = std::make_unique(); 67 | 68 | // Transfer ownership. 69 | // (The explicit `std::move` is required.) 70 | std::unique_ptr uptr1{std::move(uptr0)}; 71 | 72 | // ... 73 | // Release resource. 74 | // (The resource is automatically released.) 75 | } 76 | 77 | void shared_ptr_example() 78 | { 79 | // "Empty" resource handle. 80 | std::shared_ptr external_sptr; 81 | 82 | { 83 | // Acquire resource. 84 | std::shared_ptr sptr0{std::make_shared()}; 85 | 86 | // Share ownership. 87 | std::shared_ptr sptr1{sptr0}; 88 | std::shared_ptr sptr2{sptr0}; 89 | external_sptr = sptr0; 90 | 91 | // ... 92 | // `sptr0`, `sptr1`, `sptr2` lose ownership. 93 | // `external_sptr` still has ownership. 94 | } 95 | 96 | // ... 97 | // `external_sptr` loses ownership. 98 | // Release resource. 99 | // (The resource is automatically released.) 100 | } 101 | 102 | // C++11 smart pointers are great and should always be used when dealing with 103 | // memory allocation. But, how do they work? How are they implemented? 104 | 105 | // And... could they be used for other resource types? 106 | // The answer is "yes". 107 | 108 | // It is possible to adapt non pointer-like handles to the `NullablePointer` 109 | // concept using simple wrapper classes. 110 | 111 | // Doing this allows the usage of standard smart pointer with any resource type. 112 | // More information: 113 | // * en.cppreference.com/w/cpp/concept/NullablePointer 114 | // * stackoverflow.com/a/11002936/598696 115 | // * goo.gl/W2x302 116 | 117 | // Nevertheless, for educational purposes and to understand the commonalities 118 | // between resource types and handle types, we're going to implement our own 119 | // `std::unique_ptr`-like generic "unique wrapper". 120 | 121 | // In a future tutorial, we'll expand upon this implementation, adding "shared" 122 | // generic resource wrappers that behave like `std::shared_ptr`. 123 | 124 | // We'll also see a generic way of wrapping any resource in a convenient modern 125 | // interface. 126 | 127 | int main() 128 | { 129 | unique_ptr_example(); 130 | std::cout << "\n"; 131 | // Prints: 132 | // "Acquire." 133 | // "Release." 134 | 135 | shared_ptr_example(); 136 | std::cout << "\n"; 137 | // Prints: 138 | // "Acquire." 139 | // "Release." 140 | 141 | return 0; 142 | } 143 | 144 | // Let's begin our journey in the next code segment! -------------------------------------------------------------------------------- /DiveIntoC++11/4_SmartPtrs/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Let's define an example class, `Resource`, that we'll use in 11 | // our examples. 12 | struct Resource 13 | { 14 | }; 15 | 16 | void passByValue(std::unique_ptr mResPtr) { /*...*/} 17 | void passByConstRef(const std::unique_ptr& mResPtr) { /*...*/} 18 | void passByRawPtr(Resource* mResPtr) { /*...*/} 19 | 20 | void whatShouldIPassByToMaintainOriginalOwnership() 21 | { 22 | // Example situation: after we acquire a resource, we need to 23 | // refer to its pointer, maintaining ownership. 24 | std::unique_ptr resPtr{new Resource}; 25 | 26 | 27 | 28 | // Compile-time error! `std::unique_ptr` cannot be copied! 29 | /* passByValue(resPtr); */ 30 | 31 | // `std::unique_ptr` cannot be copied because otherwise we would end 32 | // up in a situation where two `std::unique_ptr` instances "own" the 33 | // same free-store-allocated object. It makes no sense because, as 34 | // the class's name implies, `std::unique_ptr` is the "unique" owner, 35 | // there is no shared ownership. 36 | 37 | 38 | 39 | // The following code is fine, but it only works when the object's 40 | // lifetime is managed by `std::unique_ptr`. To make your code more 41 | // generic, the third solution is suggested. 42 | passByConstRef(resPtr); 43 | 44 | // We're not transferring ownership here, we're just referring to the 45 | // `std::unique_ptr` indirectly, and by doing so we can easily access 46 | // the smart pointer's contents. 47 | 48 | 49 | 50 | // This is the third solution, it works properly, and accepts objects 51 | // managed by any kind of smart pointers or allocated on the stack. 52 | passByRawPtr(resPtr.get()); 53 | 54 | // Herb Sutter actually suggests using T& and T* parameters instead of const 55 | // references to smart pointers. That way, your functions will work with any 56 | // T, 57 | // regardless of how its lifetime is managed. I strongly suggest to check 58 | // out 59 | // the article linked at the end of the code segment for more information. 60 | 61 | // Remember: `std::unique_ptr::get()` returns a raw pointer to the 62 | // smart pointer's contents. 63 | } 64 | 65 | void iHaveToTransferOwnershipWhatNow() 66 | { 67 | // Example situation: after we acquire a resource, we need to 68 | // transfer ownership from a smart pointer to another. 69 | std::unique_ptr resPtr{new Resource}; 70 | 71 | // `std::unique_ptr` cannot be copied, but it can be "moved". 72 | 73 | // "Move semantics" are a new feature introduced in C++11, 74 | // that basically allows library developers to write efficient 75 | // assignments/constructions that avoid expensive copies, or, 76 | // as with `std::unique_ptr`'s case, can have a different meaning 77 | // than traditional copying. Example: 78 | 79 | { 80 | std::string source{"hello!"}; 81 | std::string target{std::move(source)}; 82 | 83 | // After constructing target by "moving `source` into it", 84 | // we probably avoided a (possibly expensive) copy. 85 | 86 | // `source` is now in an undefined state - what we did was 87 | // basically rip `source`'s contents and put them into 88 | // `target`. Using `source` after moving it could result 89 | // in errors on unexpected results. 90 | } 91 | 92 | // `std::move()` helps us to transfer ownership between 93 | // smart pointers. It explictly expresses the intent of "moving" 94 | // ownership. 95 | 96 | std::unique_ptr newOwner{std::move(resPtr)}; 97 | 98 | // After moving `resPtr`, using `resPtr` may result in unexpected 99 | // results. `newOwner` is now the only owner of the resource, and 100 | // you should use it instead of `resPtr`. 101 | 102 | // Continuously transferring ownership can become confusing 103 | // and expensive. Only transfer ownership when it's really required, 104 | // otherwise simply refer to the smart pointer's contents either by 105 | // const reference or by "raw" pointer. 106 | 107 | // Notice that by using `std::move()`, we can now use the `passByValue` 108 | // function: 109 | 110 | passByValue(std::move(newOwner)); 111 | 112 | // After this call, the resource will be owned by the `passByValue` 113 | // parameter called `mPtr`, and after the function finishes the 114 | // `std::unique_ptr` will go out-of-scope and the memory previously 115 | // allocated for the resource will be freed. 116 | } 117 | 118 | int main() { return 0; } 119 | 120 | // Here's a very interesting and well-written article about smart pointers 121 | // parameters 122 | // by Herb Sutter. I strongly reccommend you to read it! 123 | // http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ 124 | 125 | // In the next code segment we'll take a look at shared ownership. 126 | -------------------------------------------------------------------------------- /DiveIntoC++11/3_Memory/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | // Before dealing with "dynamic storage", we need to learn 10 | // what pointers are. 11 | 12 | int main() 13 | { 14 | // In C and C++, we can think of variables as entities 15 | // having a "type", a "name", a "value" and an "address". 16 | 17 | int i{10}; 18 | float f{5.f}; 19 | int k; 20 | 21 | // | TYPE | NAME | VALUE | ADDRESS | 22 | // +-----------+-----------+-----------+---------------+ 23 | // | int | i | 10 | &i | 24 | // | float | f | 5.f | &f | 25 | // | int | k | ??? | &k | 26 | 27 | // What does it mean for variables to have an "address"? 28 | 29 | // The address of a variable is the location in memory 30 | // where the variable is allocated. 31 | 32 | // We can get the address of a variable with the `&` unary 33 | // operator. Getting the address of a variable returns a 34 | // pointer of the type of the variable. 35 | 36 | int* pointerToI{&i}; // `pointerToI` is a "pointer to int" (int*) 37 | float* pointerToF{&f}; // `pointerToF` is a "pointer to float" (float*) 38 | int* pointerToK{&k}; // `pointerToK` is a "pointer to int" (int*) 39 | 40 | // A pointer is basically a memory address. 41 | // We can access the value contained in that address 42 | // (or, "the value the pointer points to") by using the unary 43 | // operator `*` on the pointer variable. 44 | 45 | std::cout << *pointerToI << std::endl; // Prints "10". 46 | std::cout << *pointerToF << std::endl; // Prints "5". 47 | 48 | // We can also modify a pointer's value. 49 | // Doing so modifies the original variable. 50 | *pointerToK = 15; 51 | 52 | std::cout << k << std::endl; // Prints "15". 53 | std::cout << *pointerToK << std::endl; // Prints "15". 54 | 55 | // The opposite also applies: modifying the original 56 | // variable will also be reflected when accessing 57 | // a pointer's contents. 58 | 59 | k = 20; 60 | 61 | std::cout << k << std::endl; // Prints "20". 62 | std::cout << *pointerToK << std::endl; // Prints "20". 63 | 64 | // As you can see, since `pointerToK` points to `k`, 65 | // changing `k` or `*pointerToK` is the same thing. 66 | 67 | 68 | 69 | // Using pointers in this way does not alter in any way 70 | // the object's lifetime. Here's an example. 71 | 72 | int* pointerToNested; 73 | 74 | { 75 | int nestedNumber{42}; 76 | pointerToNested = &nestedNumber; 77 | 78 | // `nestedNumber` "dies" at the end of the block. 79 | } 80 | 81 | // Even if `pointerToNested` seems to point to 82 | // `nestedNumber`, the truth is that `nestedNumber` 83 | // got deallocated and destroyed at the end of its block, 84 | // and now `pointerToNested` points to an invalid memory 85 | // location! 86 | 87 | // Accessing the contents of `pointerToNested` is "undefined 88 | // behavior". It means that, basically, anything can happen. 89 | // Usually you get "garbage" values or what is left in memory. 90 | 91 | std::cout << *pointerToNested << std::endl; // Undefined behavior! 92 | 93 | // It "should" print "42" because it is the value that remained in 94 | // memory after destroying `nestedNumber`. Let's see a more explicit 95 | // example. 96 | 97 | std::vector* pointerToVec; 98 | 99 | { 100 | std::vector vec{1, 2, 3, 4}; 101 | pointerToVec = &vec; 102 | 103 | // The "arrow" operator (`->`) is basically syntactic 104 | // sugar for `(*ptr).member`. 105 | 106 | // `pointerToVec->size()` is exactly the same as 107 | // `(*pointerToVec).size()`. 108 | 109 | std::cout << vec.size() << std::endl; // Prints 4 110 | std::cout << pointerToVec->size() << std::endl; // Prints 4 111 | 112 | pointerToVec->push_back(1); 113 | 114 | std::cout << vec.size() << std::endl; // Prints 5 115 | std::cout << pointerToVec->size() << std::endl; // Prints 5 116 | 117 | // `vec` "dies" at the end of the block. 118 | } 119 | 120 | pointerToVec->push_back(1); // Undefined behavior! 121 | std::cout << pointerToVec->size() << std::endl; // Undefined behavior! 122 | 123 | pointerToVec->clear(); // Undefined behavior! 124 | std::cout << pointerToVec->size() << std::endl; // Undefined behavior! 125 | } 126 | 127 | // Notice: even if the "Undefined behavior!" code may seem to work, 128 | // do not be fooled! The code "working" is one possible outcome of 129 | // undefined behavior. Relying on undefined behavior is the worst 130 | // thing you could do - your code will not be portable, optimizations 131 | // may break it, and it may simply fail to work. 132 | 133 | // Now that we have a basic knowledge about pointers, let's move 134 | // to the next segment, where we deal with "dynamic object lifetime". 135 | 136 | // We will also create our own version of `std::vector`. -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p6.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Let's assume that an entity can only contain only one instance of a 12 | // certain component type. Let's also assume every component type has an 13 | // ID integer number. The first component type will have ID 0, and the 14 | // other component types will have ID 1, 2, 3, ..., N. 15 | 16 | // With these assumptions, we can create a sequence of bits that helps us 17 | // check whether or not an entity has a certain component type. 18 | 19 | /* Component bitset: 20 | [ 0 0 0 0 0 0 0 1 0 0 1 ] 21 | | | 22 | | \___ Component Type #0 23 | | 24 | \___ Component Type #3 25 | */ 26 | 27 | // With a simple and efficient "bitwise and", we can check if a certain 28 | // entity has a component. 29 | 30 | // Also, since we assign an ID number to every component type, we can 31 | // store components in a linear array so that we can efficiently get 32 | // a certain component type from an entity. 33 | 34 | /* Component bitset: 35 | [ 0 0 0 0 0 0 0 1 0 0 1 ] 36 | | | 37 | | \___ Component Type #0 38 | | 39 | \___ Component Type #3 40 | 41 | Component array: 42 | [0] = (Component Type #0)* 43 | [1] = nullptr 44 | [2] = nullptr 45 | [3] = (Component Type #3)* 46 | [4] = nullptr 47 | [5] = nullptr 48 | [6] = nullptr 49 | [7] = nullptr 50 | [8] = nullptr 51 | [9] = nullptr 52 | [10] = nullptr 53 | [11] = nullptr 54 | */ 55 | 56 | // Therefore, we can easily say: 57 | // 58 | // if(entity.hasComponent()) 59 | // entity.getComponent().doSomething(); 60 | 61 | // Let's implement everything we just talked about. 62 | 63 | // The first step is figuring out a way to automatically give 64 | // component types an unique ID. 65 | 66 | // We don't want to force our user to manually assign an unique ID 67 | // to every component. 68 | 69 | // We will use a very simple "template trick" that guarantees an 70 | // unique ID every time we call a function with a specific type. 71 | 72 | namespace CompositionArkanoid 73 | { 74 | // We define a typedef for the component ID type: 75 | using ComponentID = std::size_t; 76 | 77 | inline ComponentID getUniqueComponentID() noexcept 78 | { 79 | // We store a `static` lastID variable: static means 80 | // that every time we call this function it will refer 81 | // to the same `lastID` instance. 82 | 83 | // Basically, calling this function returns an unique ID 84 | // every time. 85 | 86 | static ComponentID lastID{0u}; 87 | return lastID++; 88 | } 89 | 90 | // Now, some "template magic" comes into play. 91 | // We create a function that returns an unique ComponentID based 92 | // upon the type passed. 93 | template 94 | inline ComponentID getComponentTypeID() noexcept 95 | { 96 | // Let's try to understand what happens here... 97 | 98 | // Every time we call this function with a specific type `T`, 99 | // we are actually calling an instantiation of this template, 100 | // with its own unique static `typeID` variable. 101 | 102 | // Upon calling this function for the first time with a specific 103 | // type `T1`, `typeID` will be initialized with an unique ID. 104 | // Subsequent calls with the same type `T1` will return the 105 | // same ID. 106 | 107 | static ComponentID typeID{getUniqueComponentID()}; 108 | return typeID; 109 | } 110 | 111 | // Before applying this code to our component-based entity 112 | // system, let's run some tests and see how it works. 113 | } 114 | 115 | // Let's define some random types: 116 | struct TypeA 117 | { 118 | }; 119 | struct TypeB 120 | { 121 | }; 122 | struct TypeC 123 | { 124 | }; 125 | 126 | int main() 127 | { 128 | using namespace CompositionArkanoid; 129 | 130 | std::cout << "TypeA: " << getComponentTypeID() << "\n" 131 | << "TypeB: " << getComponentTypeID() << "\n" 132 | << "TypeC: " << getComponentTypeID() << "\n" 133 | 134 | << "TypeA: " << getComponentTypeID() << "\n" 135 | << "TypeA: " << getComponentTypeID() << "\n" 136 | << "TypeB: " << getComponentTypeID() << "\n" 137 | << "TypeB: " << getComponentTypeID() << "\n" 138 | << "TypeC: " << getComponentTypeID() << "\n" 139 | << "TypeC: " << getComponentTypeID() << std::endl; 140 | 141 | return 0; 142 | } 143 | 144 | // Running this code will demonstrate that we always get the same ID 145 | // if we call `getComponentTypeID` with the same type `T`. 146 | 147 | // We can, therefore, use the return value of `getComponentTypeID` 148 | // to set a specific bit in our component bitset. 149 | 150 | // Let's implement everything into our component-based entity system. -------------------------------------------------------------------------------- /DiveIntoC++11/3_Memory/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | // Welcome to "Dive into C++11", part 3. 10 | // http://vittorioromeo.info 11 | 12 | // In this tutorial we're gonna take a look at pointers 13 | // and basic memory management. 14 | 15 | // We're gonna learn: 16 | // * The difference between objects allocated on the stack 17 | // and on the free-store (heap). 18 | // * What pointers are and how to use them. 19 | // * How to manage dynamic memory (objects on the free-store). 20 | 21 | // Let's begin with object lifetime (storage). 22 | 23 | int main() 24 | { 25 | // In C and C++, all variables are allocated with a "storage method". 26 | // The default method in both languages is "automatic storage". 27 | 28 | // "Automatic storage" and "automatic lifetime" can be used 29 | // interchangeably. 30 | 31 | // A variable with automatic lifetime is allocated 32 | // at the beginning of a code block and deallocated 33 | // at the end of the same block. 34 | 35 | // A code block is a "portion of code" between curly braces. 36 | 37 | { 38 | // Hello! I'm a code block. 39 | } 40 | 41 | 42 | 43 | // When a variable reaches the end of its block, it 44 | // is said to be "out-of-scope". 45 | 46 | { 47 | int var; 48 | var = 5; 49 | } 50 | 51 | // 'var' is now out-of-scope. 52 | // var = 5; <-- compile-time error. 53 | 54 | 55 | 56 | // Let's create a simple class with a constructor and a 57 | // destructor to make automatic storage easier to understand. 58 | 59 | // If you are unfamiliar with classes: roughly, a class is a "type" 60 | // that provides "structure" and "behavior". 61 | 62 | // In this case, the type `Example` provides: 63 | // * `id`: an integer member variable 64 | // * `Example(int mId)`: a constructor taking an int as an argument 65 | // * `~Example()`: a destructor 66 | 67 | // The constructor gets called on variable allocation. 68 | // The destructor gets called on variable deallocation. 69 | 70 | // Our constructor and our destructor both print the object's `id`. 71 | // This will be very useful to understand when an object gets 72 | // allocated or deallocated. 73 | 74 | struct Example 75 | { 76 | int id; 77 | Example(int mId) : id{mId} { std::cout << "CTOR " << id << std::endl; } 78 | ~Example() { std::cout << "DTOR " << id << std::endl; } 79 | }; 80 | 81 | 82 | 83 | // Let's now create some instances of `Example`, and analyze 84 | // when they get allocated/deallocated. 85 | 86 | { 87 | Example ex1{1}; 88 | Example ex2{2}; 89 | 90 | // `ex1` is allocated and constructed, "CTOR 1" will be printed. 91 | // `ex2` is allocated and constructed, "CTOR 2" will be printed. 92 | 93 | // ...we reach the end of the block. 94 | } 95 | 96 | // `ex2` and `ex1` are now out of scope. 97 | // `ex2` is deallocated and destroyed, "DTOR 2" will be printed. 98 | // `ex1` is deallocated and destroyed, "DTOR 1" will be printed. 99 | 100 | // As you can see, objects with automatic 101 | // lifetime are allocated/deallocated in a 102 | // LIFO order (last in, first out). 103 | 104 | // Let's see an example with nested blocks. 105 | 106 | { 107 | Example ex1{1}; 108 | 109 | // `ex1` is allocated and constructed, "CTOR 1" will be printed. 110 | 111 | { 112 | Example ex2{2}; 113 | 114 | // `ex2` is allocated and constructed, "CTOR 2" will be printed. 115 | 116 | // ...we reach the end of the block. 117 | } 118 | 119 | // `ex2` is now out of scope. 120 | // `ex2` is deallocated and destroyed, "DTOR 2" will be printed. 121 | 122 | 123 | 124 | Example ex3{3}; 125 | 126 | // `ex3` is allocated and constructed, "CTOR 3" will be printed. 127 | 128 | // ...we reach the end of the block. 129 | } 130 | 131 | // `ex1` and `ex3` are now out of scope. 132 | // `ex3` is deallocated and destroyed, "DTOR 3" will be printed. 133 | // `ex1` is deallocated and destroyed, "DTOR 1" will be printed. 134 | 135 | 136 | 137 | // As said previously, the default storage mode 138 | // in C and C++ is "automatic storage". 139 | 140 | // So, these variables have automatic storage. 141 | int intNumber{5}; 142 | std::string str{"I will die at the end of the block :("}; 143 | std::vector vec{1, 2, 3, 4}; 144 | 145 | // You can think about automatic storage as if it was 146 | // a stack. (LIFO order) 147 | 148 | // This is what happens when the above variables get 149 | // allocated and constrcuted: 150 | 151 | // 0(TOP): [intNumber ] [str ] [vec ] 152 | // 1: [ ] [intNumber ] [str ] 153 | // 2: [ ] [ ] [intNumber ] 154 | // 3: [ ] [ ] [ ] 155 | 156 | 157 | 158 | // And this is what happens when they get deallocated 159 | // and destroyed: 160 | 161 | // 0(TOP): [vec ] [str ] [intNumber ] 162 | // 1: [str ] [intNumber ] [ ] 163 | // 2: [intNumber ] [ ] [ ] 164 | // 3: [ ] [ ] [ ] 165 | 166 | 167 | 168 | // The simplicity of automatic storage make it incredibily 169 | // fast. You should always use automatic storage when possible, 170 | // to reduce code complexity and greatly improve performance. 171 | 172 | // Let's move on to the next code segment, where we'll deal 173 | // with pointers. 174 | } -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p7.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace sf; 9 | 10 | constexpr int windowWidth{800}, windowHeight{600}; 11 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 12 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{6.f}; 13 | 14 | // Let's define some constants for the bricks. 15 | constexpr float blockWidth{60.f}, blockHeight{20.f}; 16 | constexpr int countBlocksX{11}, countBlocksY{4}; 17 | 18 | struct Ball 19 | { 20 | CircleShape shape; 21 | Vector2f velocity{-ballVelocity, -ballVelocity}; 22 | 23 | Ball(float mX, float mY) 24 | { 25 | shape.setPosition(mX, mY); 26 | shape.setRadius(ballRadius); 27 | shape.setFillColor(Color::Red); 28 | shape.setOrigin(ballRadius, ballRadius); 29 | } 30 | 31 | void update() 32 | { 33 | shape.move(velocity); 34 | 35 | if(left() < 0) 36 | velocity.x = ballVelocity; 37 | else if(right() > windowWidth) 38 | velocity.x = -ballVelocity; 39 | 40 | if(top() < 0) 41 | velocity.y = ballVelocity; 42 | else if(bottom() > windowHeight) 43 | velocity.y = -ballVelocity; 44 | } 45 | 46 | float x() { return shape.getPosition().x; } 47 | float y() { return shape.getPosition().y; } 48 | float left() { return x() - shape.getRadius(); } 49 | float right() { return x() + shape.getRadius(); } 50 | float top() { return y() - shape.getRadius(); } 51 | float bottom() { return y() + shape.getRadius(); } 52 | }; 53 | 54 | struct Paddle 55 | { 56 | RectangleShape shape; 57 | Vector2f velocity; 58 | 59 | Paddle(float mX, float mY) 60 | { 61 | shape.setPosition(mX, mY); 62 | shape.setSize({paddleWidth, paddleHeight}); 63 | shape.setFillColor(Color::Red); 64 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 65 | } 66 | 67 | void update() 68 | { 69 | shape.move(velocity); 70 | 71 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 72 | velocity.x = -paddleVelocity; 73 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 74 | right() < windowWidth) 75 | velocity.x = paddleVelocity; 76 | else 77 | velocity.x = 0; 78 | } 79 | 80 | float x() { return shape.getPosition().x; } 81 | float y() { return shape.getPosition().y; } 82 | float left() { return x() - shape.getSize().x / 2.f; } 83 | float right() { return x() + shape.getSize().x / 2.f; } 84 | float top() { return y() - shape.getSize().y / 2.f; } 85 | float bottom() { return y() + shape.getSize().y / 2.f; } 86 | }; 87 | 88 | // Let's have a class `Brick` for the bricks. 89 | struct Brick 90 | { 91 | RectangleShape shape; 92 | 93 | // This boolean value will be used to check 94 | // whether a brick has been hit or not. 95 | bool destroyed{false}; 96 | 97 | // The constructor is almost identical to the `Paddle` one. 98 | Brick(float mX, float mY) 99 | { 100 | shape.setPosition(mX, mY); 101 | shape.setSize({blockWidth, blockHeight}); 102 | shape.setFillColor(Color::Yellow); 103 | shape.setOrigin(blockWidth / 2.f, blockHeight / 2.f); 104 | } 105 | 106 | float x() { return shape.getPosition().x; } 107 | float y() { return shape.getPosition().y; } 108 | float left() { return x() - shape.getSize().x / 2.f; } 109 | float right() { return x() + shape.getSize().x / 2.f; } 110 | float top() { return y() - shape.getSize().y / 2.f; } 111 | float bottom() { return y() + shape.getSize().y / 2.f; } 112 | }; 113 | 114 | template 115 | bool isIntersecting(T1& mA, T2& mB) 116 | { 117 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 118 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 119 | } 120 | 121 | void testCollision(Paddle& mPaddle, Ball& mBall) 122 | { 123 | if(!isIntersecting(mPaddle, mBall)) return; 124 | 125 | mBall.velocity.y = -ballVelocity; 126 | if(mBall.x() < mPaddle.x()) 127 | mBall.velocity.x = -ballVelocity; 128 | else 129 | mBall.velocity.x = ballVelocity; 130 | } 131 | 132 | int main() 133 | { 134 | Ball ball{windowWidth / 2, windowHeight / 2}; 135 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 136 | 137 | // We will use an `std::vector` to contain any number 138 | // of `Brick` instances. 139 | vector bricks; 140 | 141 | // We fill up our vector via a 2D for loop, creating 142 | // bricks in a grid-like pattern. 143 | for(int iX{0}; iX < countBlocksX; ++iX) 144 | for(int iY{0}; iY < countBlocksY; ++iY) 145 | bricks.emplace_back( 146 | (iX + 1) * (blockWidth + 3) + 22, (iY + 2) * (blockHeight + 3)); 147 | 148 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 7"}; 149 | window.setFramerateLimit(60); 150 | 151 | while(true) 152 | { 153 | window.clear(Color::Black); 154 | 155 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 156 | 157 | ball.update(); 158 | paddle.update(); 159 | testCollision(paddle, ball); 160 | 161 | window.draw(ball.shape); 162 | window.draw(paddle.shape); 163 | 164 | // We must draw every brick on the window! 165 | // Let's use a modern C++11 foreach loop, that allows 166 | // us to intuitively say: "draw every `brick` in `bricks`". 167 | for(auto& brick : bricks) window.draw(brick.shape); 168 | window.display(); 169 | } 170 | 171 | return 0; 172 | } -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p9.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | using namespace sf; 10 | 11 | constexpr int windowWidth{800}, windowHeight{600}; 12 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 13 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{6.f}; 14 | constexpr float blockWidth{60.f}, blockHeight{20.f}; 15 | constexpr int countBlocksX{11}, countBlocksY{4}; 16 | 17 | struct Ball 18 | { 19 | CircleShape shape; 20 | Vector2f velocity{-ballVelocity, -ballVelocity}; 21 | 22 | Ball(float mX, float mY) 23 | { 24 | shape.setPosition(mX, mY); 25 | shape.setRadius(ballRadius); 26 | shape.setFillColor(Color::Red); 27 | shape.setOrigin(ballRadius, ballRadius); 28 | } 29 | 30 | void update() 31 | { 32 | shape.move(velocity); 33 | 34 | if(left() < 0) 35 | velocity.x = ballVelocity; 36 | else if(right() > windowWidth) 37 | velocity.x = -ballVelocity; 38 | 39 | if(top() < 0) 40 | velocity.y = ballVelocity; 41 | else if(bottom() > windowHeight) 42 | velocity.y = -ballVelocity; 43 | } 44 | 45 | float x() { return shape.getPosition().x; } 46 | float y() { return shape.getPosition().y; } 47 | float left() { return x() - shape.getRadius(); } 48 | float right() { return x() + shape.getRadius(); } 49 | float top() { return y() - shape.getRadius(); } 50 | float bottom() { return y() + shape.getRadius(); } 51 | }; 52 | 53 | struct Rectangle 54 | { 55 | RectangleShape shape; 56 | float x() { return shape.getPosition().x; } 57 | float y() { return shape.getPosition().y; } 58 | float left() { return x() - shape.getSize().x / 2.f; } 59 | float right() { return x() + shape.getSize().x / 2.f; } 60 | float top() { return y() - shape.getSize().y / 2.f; } 61 | float bottom() { return y() + shape.getSize().y / 2.f; } 62 | }; 63 | 64 | struct Paddle : public Rectangle 65 | { 66 | Vector2f velocity; 67 | 68 | Paddle(float mX, float mY) 69 | { 70 | shape.setPosition(mX, mY); 71 | shape.setSize({paddleWidth, paddleHeight}); 72 | shape.setFillColor(Color::Red); 73 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 74 | } 75 | 76 | void update() 77 | { 78 | shape.move(velocity); 79 | 80 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 81 | velocity.x = -paddleVelocity; 82 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 83 | right() < windowWidth) 84 | velocity.x = paddleVelocity; 85 | else 86 | velocity.x = 0; 87 | } 88 | }; 89 | 90 | struct Brick : public Rectangle 91 | { 92 | bool destroyed{false}; 93 | 94 | Brick(float mX, float mY) 95 | { 96 | shape.setPosition(mX, mY); 97 | shape.setSize({blockWidth, blockHeight}); 98 | shape.setFillColor(Color::Yellow); 99 | shape.setOrigin(blockWidth / 2.f, blockHeight / 2.f); 100 | } 101 | }; 102 | 103 | template 104 | bool isIntersecting(T1& mA, T2& mB) 105 | { 106 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 107 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 108 | } 109 | 110 | void testCollision(Paddle& mPaddle, Ball& mBall) 111 | { 112 | if(!isIntersecting(mPaddle, mBall)) return; 113 | 114 | mBall.velocity.y = -ballVelocity; 115 | if(mBall.x() < mPaddle.x()) 116 | mBall.velocity.x = -ballVelocity; 117 | else 118 | mBall.velocity.x = ballVelocity; 119 | } 120 | 121 | void testCollision(Brick& mBrick, Ball& mBall) 122 | { 123 | if(!isIntersecting(mBrick, mBall)) return; 124 | mBrick.destroyed = true; 125 | 126 | float overlapLeft{mBall.right() - mBrick.left()}; 127 | float overlapRight{mBrick.right() - mBall.left()}; 128 | float overlapTop{mBall.bottom() - mBrick.top()}; 129 | float overlapBottom{mBrick.bottom() - mBall.top()}; 130 | 131 | bool ballFromLeft(abs(overlapLeft) < abs(overlapRight)); 132 | bool ballFromTop(abs(overlapTop) < abs(overlapBottom)); 133 | 134 | float minOverlapX{ballFromLeft ? overlapLeft : overlapRight}; 135 | float minOverlapY{ballFromTop ? overlapTop : overlapBottom}; 136 | 137 | if(abs(minOverlapX) < abs(minOverlapY)) 138 | mBall.velocity.x = ballFromLeft ? -ballVelocity : ballVelocity; 139 | else 140 | mBall.velocity.y = ballFromTop ? -ballVelocity : ballVelocity; 141 | } 142 | 143 | int main() 144 | { 145 | Ball ball{windowWidth / 2, windowHeight / 2}; 146 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 147 | vector bricks; 148 | 149 | for(int iX{0}; iX < countBlocksX; ++iX) 150 | for(int iY{0}; iY < countBlocksY; ++iY) 151 | bricks.emplace_back( 152 | (iX + 1) * (blockWidth + 3) + 22, (iY + 2) * (blockHeight + 3)); 153 | 154 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 9"}; 155 | window.setFramerateLimit(60); 156 | 157 | while(true) 158 | { 159 | window.clear(Color::Black); 160 | 161 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 162 | 163 | ball.update(); 164 | paddle.update(); 165 | testCollision(paddle, ball); 166 | for(auto& brick : bricks) testCollision(brick, ball); 167 | bricks.erase(remove_if(begin(bricks), end(bricks), 168 | [](const Brick& mBrick) 169 | { 170 | return mBrick.destroyed; 171 | }), 172 | end(bricks)); 173 | 174 | window.draw(ball.shape); 175 | window.draw(paddle.shape); 176 | for(auto& brick : bricks) window.draw(brick.shape); 177 | window.display(); 178 | } 179 | 180 | return 0; 181 | } -------------------------------------------------------------------------------- /Handles/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 3 | // License: Academic Free License ("AFL") v. 3.0 4 | // http://opensource.org/licenses/AFL-3.0 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Hello everyone! 12 | // Today we'll learn about entity storage and handles. 13 | 14 | // In games and many other applications it's common to 15 | // deal with "entity"-like classes. 16 | 17 | // What defines an "entity"? Personally, I think: 18 | // * Stores data and/or logic 19 | // * Tied to a specific concept (game object? UI element?) 20 | // * Object that we need to keep track of 21 | // * Can be either be alive or dead 22 | // * Rarely used on its own - we usually deal with many entities 23 | 24 | // For instance, all of our game objects can be "entities". 25 | // All of our UI widgets can be "entities". 26 | // In general: 27 | // 1. We need to be able to keep track of particular instances 28 | // of these objects. 29 | // 2. We need to iterate and perform actions on all of these 30 | // objects at once. 31 | 32 | // What is the easiest/best way to solve the first problem? 33 | // -------------------------------------------------------- 34 | // Allocate entities dynamically. Objects on the heap are very 35 | // easy to keep track of. Just pass a pointer around - it will 36 | // always point to the correct instance until the entity is 37 | // destroyed. 38 | // We can create an `std::vector` of dynamically allocated 39 | // entities and not worry about pointers being invalidated. 40 | 41 | // What is the easiest/best way to solve the second problem? 42 | // --------------------------------------------------------- 43 | // Store entities contiguously. Having entities laid out in memory 44 | // in a cache-friendly way greatly improves iteration efficiency. 45 | // Unfortunately, if we store entities contiguously, keeping track 46 | // of them becomes hard. 47 | 48 | // Let's see an implementation of the first approach. 49 | 50 | // Let's use C++11 pseudo-random generators to help us in 51 | // these examples. 52 | std::minstd_rand rndEngine; 53 | 54 | // This is our `Entity` class. It stores some data can perform 55 | // some actions. Its state can either be alive or dead. 56 | struct Entity 57 | { 58 | // The current state. 59 | bool alive{true}; 60 | 61 | // Example stored data. 62 | int health; 63 | 64 | // Let's construct the entity by giving it a random `health` 65 | // value. 66 | Entity() : health(10 + (rndEngine() % 50)) {} 67 | 68 | // Updates the entity. 69 | // Continuously decreases `health`. When `health` is 0, 70 | // alive is set to false. 71 | void update() 72 | { 73 | if(--health <= 0) alive = false; 74 | } 75 | }; 76 | 77 | // We will manage entities in a `Manager` class. 78 | // It will help us deal with entity creation, destruction, 79 | // and iteration. 80 | // The `Manager` will have two "main" user-called methods: 81 | // * `update`: iterates over all entities and updates them. 82 | // * `refresh`: destroys all "dead" entities and adds all "new" 83 | // entities to the main container. 84 | class Manager 85 | { 86 | public: 87 | // Typedef for the internal storage type. 88 | using Storage = std::vector>; 89 | 90 | // Typedef for handles that keep track of entities. 91 | using Handle = Entity*; 92 | 93 | private: 94 | // Main storage of all current entities. 95 | Storage entities; 96 | 97 | // Storage of entities waiting to be added to the main 98 | // storage on the next `refresh()` call. 99 | Storage toAdd; 100 | 101 | public: 102 | // Updates all entities. Entities can "die" during 103 | // the update. 104 | void update() 105 | { 106 | for(auto& e : entities) e->update(); 107 | } 108 | 109 | // Gets rid of all "dead" entities and adds the newly 110 | // created ones to the main storage. 111 | // We add entities in the `refresh()` step and not 112 | // instantly as the vector could be resized during 113 | // update, invalidating the iterators. 114 | void refresh() 115 | { 116 | // Destroy all "dead" entities. 117 | // Memory is automatically freed. 118 | entities.erase(std::remove_if(std::begin(entities), std::end(entities), 119 | [](const auto& mE) 120 | { 121 | return !mE->alive; 122 | }), 123 | std::end(entities)); 124 | 125 | // Add all new entities and clear `toAdd`. 126 | for(auto& e : toAdd) entities.emplace_back(std::move(e)); 127 | toAdd.clear(); 128 | } 129 | 130 | // Create a new entity and return an `Handle` pointing to it. 131 | Handle create() 132 | { 133 | toAdd.emplace_back(std::make_unique()); 134 | return toAdd.back().get(); 135 | } 136 | }; 137 | 138 | // Let's try out this implementation. 139 | 140 | int main() 141 | { 142 | Manager m; 143 | 144 | // Create two entities and get their handles. 145 | auto h1(m.create()); 146 | auto h2(m.create()); 147 | 148 | // Add the entities to the main storage. 149 | m.refresh(); 150 | 151 | while(h1 != nullptr || h2 != nullptr) 152 | { 153 | // Update... 154 | m.update(); 155 | 156 | // ...entities can now be marked as "dead". 157 | // If so, set their specific handles to `nullptr`. 158 | if(h1 != nullptr && !h1->alive) 159 | { 160 | std::cout << "h1 died\n"; 161 | 162 | // We need to invalidate the handles ourselves... 163 | h1 = nullptr; 164 | } 165 | if(h2 != nullptr && !h2->alive) 166 | { 167 | std::cout << "h2 died\n"; 168 | h2 = nullptr; 169 | } 170 | 171 | // Refresh (memory can be freed). 172 | m.refresh(); 173 | } 174 | } 175 | 176 | // This method allows us to iterate and keep track of entities. 177 | // However, it has some huge issues: 178 | // * It's inefficient. Entity iteration is cache-unfriendly. 179 | // * Handles point to deleted memory when the entity is "killed". 180 | // We have to invalidate handles ourselves. 181 | 182 | // Good news everyone! 183 | 184 | // We can solve these problems with the next approach. 185 | // Unfortunately, the implementation is not as straightforward. -------------------------------------------------------------------------------- /DiveIntoC++11/4_SmartPtrs/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Welcome to "Dive into C++11", part 4. 11 | // http://vittorioromeo.info 12 | 13 | // In this tutorial we're gonna take a look at one of the 14 | // best C++11 features: smart pointers. 15 | 16 | // We're gonna learn: 17 | // * What smart pointers are, and what problem they solve. 18 | // * How to deal with ownership transfer. 19 | // * The difference between `unique` and `shared` smart pointers. 20 | 21 | // I highly reccommend watching part 3 before this video. 22 | // Let's dive in! 23 | 24 | int main() 25 | { 26 | // As explained in part 3, we must be careful when dealing 27 | // with pointers that store addresses of objects allocated 28 | // on the free-store. Example: 29 | 30 | { 31 | int* correctUsage{new int{20}}; 32 | delete correctUsage; 33 | } 34 | 35 | { 36 | int* memoryLeak{new int{50}}; 37 | } 38 | 39 | // Whoops, we forgot to call `delete`. Great. 40 | // Now our program leaks memory. 41 | 42 | // Hmm... wonder if we could create a simple wrapper 43 | // class that automatically called `delete` for us 44 | // on destruction. 45 | 46 | class FreeStoreInt 47 | { 48 | // FreeStoreInt is a class that wraps an `int` 49 | // instance allocated on the free-store. 50 | 51 | private: 52 | int* pointer; // Points to our free-store-allocated `int`. 53 | 54 | public: 55 | // The constructor allocated the `int` on the free-store, 56 | // using the value passed by the user. 57 | FreeStoreInt(int* mInt) : pointer{mInt} {} 58 | 59 | // The destructor calls delete on `pointer`. 60 | // This ensures that the memory will be freed when an instance 61 | // of FreeStoreInt goes out-of-scope. 62 | ~FreeStoreInt() { delete pointer; } 63 | 64 | // And this is a simple "getter" function that allows us 65 | // to access the internal pointer. 66 | int* get() { return pointer; } 67 | }; 68 | 69 | // Great! Let's use our new class. 70 | 71 | { 72 | // Here `test` is constructed, with a value of 10. 73 | // Remember: `test.pointer`now points to a free-store-allocated `int`! 74 | FreeStoreInt test{new int{10}}; 75 | 76 | // We DO NOT HAVE to call `delete`! 77 | // When `test` goes out of scope, the destructor will be 78 | // automatically called, and `test.pointer` will be deleted 79 | // for us. 80 | } 81 | 82 | // Fantastic! We have no memory leaks. 83 | 84 | // Turns out our class has some problems, though: 85 | // * It is not generic. Can be easily fixed with templates 86 | // and C++11 variadic templates. 87 | // * What happens if we try to copy our class? What if 88 | // we try to move it? It would be a good idea to take a lot 89 | // of time to design and implement copy/move constructors, 90 | // assignment operator overloads, et cetera... 91 | // * What if we need to store `FreeStoreInt` instances in a 92 | // container? What are the implications? 93 | // * And much more... 94 | 95 | // Turns out our life-changing class is not that great... 96 | // Well, fortunately the C++11 Standard Library introduces 97 | // a new class, `std::unique_ptr`, which basically solves the 98 | // issue we were trying to solve with `FreeStoreInt`. 99 | 100 | // All smart pointers are in the `memory` header file. 101 | 102 | // Let's analyze the simplest (yet, usually most useful) type of 103 | // smart pointer: `std::unique_ptr`. 104 | 105 | { 106 | // `std::unique_ptr` is a clever well thought-out version 107 | // of our naive `FreeStoreInt`. On construction, it allocates 108 | // and constructs an object with the user specified parameters. 109 | std::unique_ptr test{new int{10}}; 110 | 111 | // And, like our `FreeStoreInt`, it will automatically free 112 | // memory on destruction (when it goes out-of-scope). 113 | 114 | // Also, it solves all the issues listed above! 115 | } 116 | 117 | { 118 | std::unique_ptr smartPointers{new double{20.3}}; 119 | std::unique_ptr are{new std::string{"extremely"}}; 120 | std::unique_ptr versatile{new char{'!'}}; 121 | 122 | // Do not worry! All memory will automatically be freed. 123 | // This is GUARANTEED, even in case of thrown exceptions. 124 | // `std::unique_ptr` and other smart pointers are extremely 125 | // safe and should always be used instead of "raw" pointers. 126 | } 127 | 128 | // <> 129 | // Nope. "Raw" pointers are still very useful. Here's what you should do: 130 | 131 | // You need to store something on the free-store -> USE A SMART POINTER. 132 | // (Basically, when your pointer needs to "own" memory, USE A SMART 133 | // POINTER.) 134 | 135 | // You need to refer to something, WITHOUT OWNING IT -> USE A RAW POINTER. 136 | 137 | // Examples: 138 | 139 | { 140 | std::size_t runtimeSize{10}; 141 | 142 | // We need to allocate an array with a runtime-known size. 143 | // We need the free-store. We need a pointer to "own" the array, 144 | // therefore we use a smart pointer. 145 | 146 | std::unique_ptr array{new int[runtimeSize]}; 147 | 148 | // Maybe we now need to simply refer to `array`, without representing 149 | // ownership. Maybe we need to refer to `array` in a function, or 150 | // maybe we just need an alias that changes its pointee during the 151 | // program execution. Therefore, we use a "raw" pointer. 152 | 153 | // `std::unique_ptr::get()` returns a "raw" pointer to the stored 154 | // object. 155 | // (Rember to use round parenthesis with `auto`!) 156 | auto pointerToArray(array.get()); 157 | 158 | // Careful: if `pointerToArray` goes out of scope, `array` won't 159 | // be deleted. Only the smart pointer frees memory on destruction! 160 | } 161 | 162 | { 163 | std::string testString{"this string is not on the free store"}; 164 | 165 | // Again, we may need to refer to `testString` without owning it. 166 | // A "raw" pointer is what we need here. 167 | 168 | std::string* pointerToString{&testString}; 169 | } 170 | 171 | // Also, `std::unique_ptr` should have zero overhead compared to "raw" 172 | // owning pointers. So feel free to use it without performance worries! 173 | 174 | // Let's move on to the next part, and take a look at possible ownership 175 | // issues. 176 | 177 | return 0; 178 | } 179 | -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p7.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace CompositionArkanoid 15 | { 16 | struct Component; 17 | class Entity; 18 | 19 | using ComponentID = std::size_t; 20 | 21 | // Let's hide implementation details into an "Internal" namespace: 22 | namespace Internal 23 | { 24 | inline ComponentID getUniqueComponentID() noexcept 25 | { 26 | static ComponentID lastID{0u}; 27 | return lastID++; 28 | } 29 | } 30 | 31 | template 32 | inline ComponentID getComponentTypeID() noexcept 33 | { 34 | // We an use a `static_assert` to make sure this function 35 | // is only called with types that inherit from `Component`. 36 | static_assert(std::is_base_of::value, 37 | "T must inherit from Component"); 38 | 39 | static ComponentID typeID{Internal::getUniqueComponentID()}; 40 | return typeID; 41 | } 42 | 43 | // Let's define a maximum number of components: 44 | constexpr std::size_t maxComponents{32}; 45 | 46 | // Let's typedef an `std::bitset` for our components: 47 | using ComponentBitset = std::bitset; 48 | 49 | // And let's also typedef an `std::array` for them: 50 | using ComponentArray = std::array; 51 | 52 | struct Component 53 | { 54 | Entity* entity; 55 | 56 | // Let's add a virtual `init` method to our Component 57 | // class that will be called after the component is 58 | // added to an entity. 59 | virtual void init() {} 60 | 61 | virtual void update(float mFT) {} 62 | virtual void draw() {} 63 | 64 | virtual ~Component() {} 65 | }; 66 | 67 | class Entity 68 | { 69 | private: 70 | bool alive{true}; 71 | std::vector> components; 72 | 73 | // Let's add an array to quickly get a component with 74 | // a specific ID, and a bitset to check the existance of 75 | // a component with a specific ID. 76 | ComponentArray componentArray; 77 | ComponentBitset componentBitset; 78 | 79 | public: 80 | void update(float mFT) 81 | { 82 | for(auto& c : components) c->update(mFT); 83 | } 84 | void draw() 85 | { 86 | for(auto& c : components) c->draw(); 87 | } 88 | 89 | bool isAlive() const { return alive; } 90 | void destroy() { alive = false; } 91 | 92 | // To check if this entity has a component, we simply 93 | // query the bitset. 94 | template 95 | bool hasComponent() const 96 | { 97 | return componentBitset[getComponentTypeID()]; 98 | } 99 | 100 | template 101 | T& addComponent(TArgs&&... mArgs) 102 | { 103 | // Before adding a component, we make sure it doesn't 104 | // already exist by using an assertion. 105 | assert(!hasComponent()); 106 | 107 | T* c(new T(std::forward(mArgs)...)); 108 | c->entity = this; 109 | std::unique_ptr uPtr{c}; 110 | components.emplace_back(std::move(uPtr)); 111 | 112 | // When we add a component of type `T`, we add it to 113 | // the bitset and to the array. 114 | componentArray[getComponentTypeID()] = c; 115 | componentBitset[getComponentTypeID()] = true; 116 | 117 | // We can now call `Component::init()`: 118 | c->init(); 119 | 120 | return *c; 121 | } 122 | 123 | template 124 | T& getComponent() const 125 | { 126 | // To retrieve a specific component, we get it from 127 | // the array. We'll also assert its existance. 128 | 129 | assert(hasComponent()); 130 | auto ptr(componentArray[getComponentTypeID()]); 131 | return *static_cast(ptr); 132 | } 133 | }; 134 | 135 | struct Manager 136 | { 137 | private: 138 | std::vector> entities; 139 | 140 | public: 141 | void update(float mFT) 142 | { 143 | for(auto& e : entities) e->update(mFT); 144 | } 145 | void draw() 146 | { 147 | for(auto& e : entities) e->draw(); 148 | } 149 | 150 | void refresh() 151 | { 152 | entities.erase( 153 | std::remove_if(std::begin(entities), std::end(entities), 154 | [](const std::unique_ptr& mEntity) 155 | { 156 | return !mEntity->isAlive(); 157 | }), 158 | std::end(entities)); 159 | } 160 | 161 | Entity& addEntity() 162 | { 163 | Entity* e{new Entity{}}; 164 | std::unique_ptr uPtr{e}; 165 | entities.emplace_back(std::move(uPtr)); 166 | return *e; 167 | } 168 | }; 169 | } 170 | 171 | using namespace CompositionArkanoid; 172 | 173 | struct CounterComponent : Component 174 | { 175 | float counter; 176 | void update(float mFT) override 177 | { 178 | counter += mFT; 179 | std::cout << counter << std::endl; 180 | } 181 | }; 182 | 183 | struct KillComponent : Component 184 | { 185 | CounterComponent* cCounter{nullptr}; 186 | 187 | // Instead of passing the entity as a parameter in the constructor, 188 | // we can now override the `init()` method and get the counter 189 | // component with our new methods. 190 | void init() override 191 | { 192 | cCounter = &entity->getComponent(); 193 | } 194 | 195 | void update(float mFT) override 196 | { 197 | if(cCounter->counter >= 100) entity->destroy(); 198 | } 199 | }; 200 | 201 | int main() 202 | { 203 | Manager manager; 204 | 205 | auto& entity(manager.addEntity()); 206 | 207 | // We can now avoid getting references to the components: 208 | entity.addComponent(); 209 | entity.addComponent(); 210 | 211 | for(auto i(0u); i < 1000; ++i) 212 | { 213 | manager.refresh(); 214 | manager.update(1.f); 215 | manager.draw(); 216 | } 217 | } 218 | 219 | // This approach, in my opinion, is cleaner and more efficient than 220 | // the previous one. 221 | 222 | // For the last code segment, I'll re-implement the arkanoid clone you 223 | // saw in the first episode of the series using this component-based 224 | // entity system. 225 | 226 | // Before that, however, we will add grouping to our entity manager. -------------------------------------------------------------------------------- /DiveIntoC++14/3_UniqueResource/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | 8 | // Let's take a look at possible resource types. We'll create "fake" versions of 9 | // commonly used resource handles and put them into a dedicated namespace for 10 | // readability. 11 | 12 | namespace legacy 13 | { 14 | // Example: free-store allocated pointers. 15 | // The most familiar and simple type of resource handles. 16 | 17 | // Acquiring a free-store allocated pointer requires no special 18 | // instructions. 19 | 20 | // The passed `ptr` argument will have to be either `nullptr` or be 21 | // allocated using the `new` keyword. 22 | 23 | template 24 | auto free_store_new(T* ptr) 25 | { 26 | std::cout << "free_store_new\n"; 27 | return ptr; 28 | } 29 | 30 | // Releasing is very simple too - we'll just use the `delete` keyword. 31 | 32 | template 33 | void free_store_delete(T* ptr) 34 | { 35 | if(ptr == nullptr) 36 | { 37 | // Do nothing. 38 | } 39 | else 40 | { 41 | std::cout << "free_store_delete\n"; 42 | } 43 | 44 | delete ptr; 45 | } 46 | 47 | 48 | 49 | // Example: OpenGL's "Vertex Buffer Objects". 50 | // "VBOs" are used to buffer and send vertex data to the GPU. 51 | 52 | // They need to be acquired and released using specific OpenGL global `void` 53 | // functions, which mutate the passed handle pointer. 54 | 55 | // OpenGL provides these and many more type aliases: 56 | using GLsizei = std::size_t; 57 | using GLuint = int; 58 | 59 | // The real OpenGL functions have the same name as signature as these fake 60 | // implementations: 61 | void glGenBuffers(GLsizei n, GLuint* ptr) 62 | { 63 | static GLuint next_id{1}; 64 | 65 | std::cout << "glGenBuffers(" << n << ", ptr) -> " << next_id << "\n"; 66 | 67 | *ptr = next_id++; 68 | } 69 | 70 | void glDeleteBuffers(GLsizei n, const GLuint* ptr) 71 | { 72 | if(*ptr == 0) 73 | { 74 | // Do nothing. 75 | } 76 | else 77 | { 78 | // Free buffer memory. 79 | std::cout << "glDeleteBuffers(" << n << ", " << *ptr << ")\n"; 80 | } 81 | } 82 | 83 | 84 | 85 | // Example: non-pointer file resource handle. 86 | // Some APIs may return a non-pointer object, such as an `int`, to track a 87 | // specific resource. 88 | 89 | // Let's create a fake file management API with these semantics. 90 | 91 | int open_file() 92 | { 93 | static int next_id(1); 94 | 95 | std::cout << "open_file() -> " << next_id << "\n"; 96 | 97 | return next_id++; 98 | } 99 | 100 | void close_file(int id) 101 | { 102 | if(id == -1) 103 | { 104 | // Do nothing. 105 | } 106 | else 107 | { 108 | // Close file. 109 | std::cout << "close_file(" << id << ")\n"; 110 | } 111 | } 112 | } 113 | 114 | // We can see that every resource API has a pattern: 115 | // * There's a function to "acquire". 116 | // * There's a function to "release". 117 | // * There's an "handle type" to track the resource. 118 | // * There's a "null handle" value that can be safely deleted multiple times. 119 | 120 | // Let's abstract these common features into types that will be used as template 121 | // parameters for our generic resource types. 122 | 123 | // We'll call these types "behaviors". 124 | 125 | namespace behavior 126 | { 127 | template 128 | struct free_store_b 129 | { 130 | using handle_type = T*; 131 | 132 | handle_type null_handle() 133 | { 134 | return nullptr; 135 | } 136 | 137 | // To avoid confusing name clashes with generic resource class 138 | // implementations: 139 | // * Behavior "acquiring" will be called `init`. 140 | // * Behavior "releasing" will be called `deinit`. 141 | 142 | handle_type init(T* ptr) 143 | { 144 | return legacy::free_store_new(ptr); 145 | } 146 | 147 | void deinit(const handle_type& handle) 148 | { 149 | legacy::free_store_delete(handle); 150 | } 151 | }; 152 | 153 | struct vbo_b 154 | { 155 | // Our VBO handle type will consist of both the id and the `n` 156 | // parameter. 157 | 158 | struct vbo_handle 159 | { 160 | legacy::GLuint _id; 161 | legacy::GLsizei _n; 162 | }; 163 | 164 | // The `_n` VBO parameter could also be a template parameter, if its 165 | // value is known at compile-time. 166 | 167 | using handle_type = vbo_handle; 168 | 169 | handle_type null_handle() 170 | { 171 | return {0, 0}; 172 | } 173 | 174 | handle_type init(std::size_t n) 175 | { 176 | handle_type result; 177 | 178 | legacy::glGenBuffers(n, &result._id); 179 | result._n = n; 180 | 181 | return result; 182 | } 183 | 184 | void deinit(const handle_type& handle) 185 | { 186 | legacy::glDeleteBuffers(handle._n, &handle._id); 187 | } 188 | }; 189 | 190 | struct file_b 191 | { 192 | using handle_type = int; 193 | 194 | handle_type null_handle() 195 | { 196 | return -1; 197 | } 198 | 199 | handle_type init() 200 | { 201 | return legacy::open_file(); 202 | } 203 | 204 | void deinit(const handle_type& handle) 205 | { 206 | legacy::close_file(handle); 207 | } 208 | }; 209 | } 210 | 211 | // Let's now simulate our desired "uniqueness semantics". 212 | void simulate_unique_ownership() 213 | { 214 | behavior::file_b b; 215 | 216 | // `h0` is the current unique owner. 217 | auto h0 = b.init(); 218 | 219 | // ... use `h0` ... 220 | 221 | // `h1` is the current unique owner. 222 | // (Ownership transfer.) 223 | auto h1 = h0; 224 | h0 = b.null_handle(); 225 | 226 | // ... use `h1` ... 227 | 228 | // OK - `h0` is a null handle. 229 | // (This will be done automatically.) 230 | b.deinit(h0); 231 | 232 | // ... use `h1` ... 233 | 234 | // Resource released. `h1` will point to a "null handle". 235 | // (This will be done automatically.) 236 | b.deinit(h1); 237 | h1 = b.null_handle(); 238 | } 239 | 240 | int main() 241 | { 242 | simulate_unique_ownership(); 243 | // Prints: 244 | // "open_file() -> 1" 245 | // "close_file(1)" 246 | 247 | return 0; 248 | } 249 | 250 | // In the next code segment, we'll implement an "unique" ownership resource 251 | // class, that will have the same behavior as our `simulate_unique_ownership` 252 | // test function. -------------------------------------------------------------------------------- /DiveIntoC++11/4_SmartPtrs/p3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // As in the previous segment, here we have our "expensive resource". 11 | struct Resource 12 | { 13 | }; 14 | 15 | // Let's say we need to create a "game effect" class that plays an 16 | // animation, a sound, and some particle effects. 17 | struct NaiveGameEffect 18 | { 19 | Resource animation; 20 | Resource backgroundTexture; 21 | Resource particleTexture; 22 | Resource sound; 23 | }; 24 | 25 | // If `Resource` is expensive to copy, copying `NaiveGameEffect` would 26 | // be madness! 27 | 28 | // There are many solutions: we could have a `ResourceManager` class 29 | // that internally stores expensive resources by using `std::unique_ptr` 30 | // and then gives out "raw" pointers or references to game effects. 31 | 32 | // Or we could use shared ownership, so that game effects own the resources 33 | // in a shared manner, and the resources are freed when there are no 34 | // game effects using them. This is where `std::shared_ptr` comes into 35 | // play. 36 | 37 | struct GameEffect 38 | { 39 | std::shared_ptr animation; 40 | std::shared_ptr backgroundTexture; 41 | std::shared_ptr particleTexture; 42 | std::shared_ptr sound; 43 | }; 44 | 45 | // This kind of design basically says: 46 | // * While there is at least a `GameEffect` alive, there will 47 | // at least be a `std::shared_ptr` alive, and memory for 48 | // the resource will be allocated. 49 | // * While there are more `GameEffect` instances alive, 50 | // `std::shared_ptr` keeps track of the number of alive 51 | // instances. As long as there are more than zero instances, 52 | // memory for the resource will still be allocated. 53 | // * If there are no more instances of `GameEffect` alive, 54 | // there will be no more instances of `std::shared_ptr` alive. 55 | // The "internal shared ownership counter" will then become 0, 56 | // and memory for the resource will be freed. 57 | // Let's see an example: 58 | 59 | int main() 60 | { 61 | struct TextureResource 62 | { 63 | TextureResource() { std::cout << "CTOR" << std::endl; } 64 | ~TextureResource() { std::cout << "DTOR" << std::endl; } 65 | 66 | // Non-copyable (using C++11's new `= delete` feature). 67 | TextureResource(const TextureResource&) = delete; 68 | TextureResource& operator=(const TextureResource&) = delete; 69 | }; 70 | 71 | // Let's acquire a texture resource: 72 | // "CTOR" will be printed, as we're constructing a resource. 73 | // Here the "internal shared ownership counter" becomes 1. 74 | std::shared_ptr source{new TextureResource}; 75 | 76 | 77 | // Here's an example of a class that uses shared ownership 78 | // to refer to a `TextureResource`. 79 | struct TexturedObject 80 | { 81 | std::shared_ptr texture; 82 | }; 83 | 84 | { 85 | // We instantiate a `TexturedObject`, and set its texture to 86 | // the shared pointer. Notice the lack of `std::move()`. 87 | 88 | // Here the "internal shared ownership counter" becomes 2. 89 | 90 | TexturedObject to1; 91 | to1.texture = source; 92 | } 93 | 94 | // What happened? We went in and out-of-scope, but neither "CTOR" 95 | // nor "DTOR" were printed. That's because instantiating a 96 | // `TexturedObject` simply increased the "internal shared ownership 97 | // counter" by one (it became 2), and after `to1` was destroyed it 98 | // went back to 1. 99 | 100 | 101 | 102 | // As with the previous situation, we aren't constructing/destroying 103 | // any resource here either: 104 | 105 | { 106 | TexturedObject to2; 107 | to2.texture = source; 108 | TexturedObject to3; 109 | to3.texture = source; 110 | TexturedObject to4; 111 | to4.texture = source; 112 | TexturedObject to5; 113 | to5.texture = source; 114 | TexturedObject to6; 115 | to6.texture = source; 116 | } 117 | 118 | // We can release ownership by using `std::shared_ptr::reset()`. 119 | std::shared_ptr newOwner{source}; 120 | source.reset(); 121 | 122 | // Now `source` doesn't own the texture resource anymore, 123 | // `newOwner` is currently the only owner. 124 | 125 | // We can also pass `std::shared_ptr` by value (if we want to 126 | // increase the "internal counter" by sharing ownership with 127 | // the function parameter), or by const reference, if we simply 128 | // want to refer to the smart pointer's contents. 129 | 130 | // We can also call `std::shared_ptr::get()` to get a "raw" 131 | // pointer to the smart pointer's contents. 132 | 133 | // Non-implemented examples: 134 | 135 | // Valid! "Ownership counter" will increase by 1 here... 136 | /* passByValue(newOwner); */ 137 | // ...and decrease by 1 here. (Assuming the function does nothing). 138 | 139 | // Valid! "Ownership counter" will stay the same. 140 | /* passByConstRef(newOwner); */ 141 | 142 | // As you can see, we've managed to instantiate `TexturedObject` 143 | // easily, avoid copying an expensive resource. 144 | 145 | // "CTOR" and "DTOR" will, in fact, be printed only once. 146 | 147 | return 0; 148 | 149 | // Caution: `std::shared_ptr`, unlike `std::unique_ptr`, can introduce 150 | // a significant runtime overhead. Use unique pointers if you don't 151 | // need the shared ownership! 152 | } 153 | 154 | // Smart pointers are incredibly powerful, and have much more to offer. 155 | // I suggest using the fantastic "cppreference.com" website to learn more 156 | // about them... 157 | 158 | // `std::unique_ptr` 159 | // http://en.cppreference.com/w/cpp/memory/unique_ptr 160 | 161 | // `std::shared_ptr` 162 | // http://en.cppreference.com/w/cpp/memory/shared_ptr 163 | 164 | // ...but don't forget that "raw" pointers and references are the best 165 | // solution when "ownership" isn't needed! 166 | 167 | 168 | 169 | // In this episode we've learned how C++11 solves the often tedious 170 | // and error-prone process of dynamic memory management. Smart pointers 171 | // should completely replace manual dynamic memory management: everywhere 172 | // you see the `new` and `delete` keywords you should use a smart pointer, 173 | // to avoid mistakes and improve the safety and readability of your program. 174 | 175 | // We've barely scratched the surface though. Again, I suggest reading 176 | // the "cppreference.com" pages for more information. 177 | 178 | // Let me know what you think in the comments. 179 | 180 | 181 | // Remember to check out: 182 | // http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ 183 | 184 | 185 | // Thank you very much for watching! 186 | // Hope you found the tutorial interesting. 187 | 188 | // You can fork/look at the full source code on my GitHub page: 189 | // http://github.com/SuperV1234/ 190 | 191 | // Check out my website for more tutorials and to personally 192 | // get in touch with me. 193 | 194 | // http://vittorioromeo.info -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/p6.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // In this code segment we're going to implement a generic 11 | // `forNArgs` function that passes its arguments to a callable 12 | // object in groups of `N` arguments, where `N` is divisible 13 | // by the number of total arguments. 14 | 15 | // The following implementation was written by Daniel Frey. 16 | 17 | // It's a very clever implementation that avoids compile-time 18 | // recursion (which can dramatically increase compilation time), 19 | // written as an answer to one of my questions on StackOverflow: 20 | // http://stackoverflow.com/questions/29900928 21 | 22 | // ---------------------------------------------------------------- 23 | 24 | // Forward declaration of the implementation function. 25 | // We will match two index sequences in `forNArgsImpl`. 26 | template 27 | struct forNArgsImpl; 28 | 29 | // Main `forNArgs` function - this will be called by the user 30 | // and generate the required index sequences. 31 | // `TArity` will be the number of parameters passed at once to a 32 | // single `mFn` function call. 33 | // `Ts...` is the parameter pack containing all passed arguments. 34 | template 35 | void forNArgs(TF&& mFn, Ts&&... mXs) 36 | { 37 | // The total number of arguments can be retrieved with 38 | // the `sizeof...` operator. 39 | constexpr auto numberOfArgs(sizeof...(Ts)); 40 | 41 | // The number of passed arguments must be divisible by `N`. 42 | static_assert(numberOfArgs % TArity == 0, "Invalid number of arguments"); 43 | 44 | // Call the implementation function with... 45 | forNArgsImpl< 46 | // ...a sequence from `0` to the number of `mFn` 47 | // calls that will be made. 48 | // (`numberOfArgs` divided by `TArity`) 49 | std::make_index_sequence, 50 | 51 | // ...a sequence from `0` to `TArity`. 52 | // ("size of a group of arguments") 53 | std::make_index_sequence>:: 54 | exec( 55 | // Pass the callable object to the implementation. 56 | mFn, 57 | 58 | // Forward the passed arguments as an `std::tuple`. 59 | std::forward_as_tuple(std::forward(mXs)...)); 60 | } 61 | 62 | // Specialization of the implementation function that matches 63 | // the generated index sequences into two `std::size_t` packs. 64 | template 65 | struct forNArgsImpl< 66 | // `TNCalls...` goes from `0` to the number of function calls. 67 | // (`numberOfArgs` divided by `TArity`) 68 | std::index_sequence, 69 | 70 | // `TNArity...` goes from `0` to the number of arguments per 71 | // function call (`TArity`). 72 | std::index_sequence> 73 | { 74 | template 75 | static void exec(TF&& mFn, const std::tuple& mXs) 76 | { 77 | // We can retrieve the arity again using the `sizeof...` 78 | // operator on the `TNArity` index sequence. 79 | constexpr auto arity(sizeof...(TNArity)); 80 | 81 | // `swallow` is a nice and readable way of creating a 82 | // context for parameter expansion, like `initializer_list`. 83 | using swallow = bool[]; 84 | 85 | // We'll roughly use the same idea behind `forArgs` here. 86 | 87 | (void)swallow{// `TNCalls...` is the sequence we are expanding here. 88 | 89 | // The code inside `swallow` gets expanded to 90 | // the number of function calls previously calculated. 91 | (execN(mFn, mXs), true)...}; 92 | 93 | // Example expansion of the above context for a binary 94 | // function called with 4 arguments: 95 | /* 96 | auto fn([](auto x, auto y){ return x + y; }); 97 | 98 | forNArgs<2> 99 | ( 100 | fn, 101 | 102 | 0, 103 | 10, 104 | 105 | 20, 106 | 30 107 | ); 108 | 109 | (void) swallow 110 | { 111 | (execN<0 * 2>(fn, TUPLE{0, 10, 20, 30}), true), 112 | (execN<1 * 2>(fn, TUPLE{0, 10, 20, 30}), true) 113 | }; 114 | */ 115 | } 116 | 117 | // `execN` simply calls the function getting the correct 118 | // elements from the tuple containing the forwarded arguments. 119 | template 120 | static void execN(TF&& mFn, const std::tuple& mXs) 121 | { 122 | // `TNBase` is the base index of the tuple elements 123 | // we're going to get. 124 | 125 | // `Cs...` gets expanded from 0 to the number of arguments 126 | // per function call (`N`). 127 | mFn(std::get(mXs)...); 128 | 129 | // Example expansion of `execN` for the previous 130 | // binary function example called with 4 arguments: 131 | /* 132 | auto fn([](auto x, auto y){ return x + y; }); 133 | 134 | forNArgs<2> 135 | ( 136 | fn, 137 | 138 | 0, 139 | 10, 140 | 141 | 20, 142 | 30 143 | ); 144 | 145 | (execN<0 * 2>(fn, TUPLE{0, 10, 20, 30}), true) 146 | // ...expands to... 147 | fn 148 | ( 149 | std::get<0 + 0>(TUPLE{0, 10, 20, 30}), 150 | std::get<0 + 1>(TUPLE{0, 10, 20, 30}) 151 | ); 152 | // ...expands to... 153 | fn 154 | ( 155 | 0, 156 | 10 157 | ); 158 | 159 | (execN<1 * 2>(fn, TUPLE{0, 10, 20, 30}), true) 160 | // ...expands to... 161 | fn 162 | ( 163 | std::get<2 + 0>(TUPLE{0, 10, 20, 30}), 164 | std::get<2 + 1>(TUPLE{0, 10, 20, 30}) 165 | ); 166 | // ...expands to... 167 | fn 168 | ( 169 | 20, 170 | 30 171 | ); 172 | */ 173 | } 174 | }; 175 | 176 | int main() 177 | { 178 | // Prints "2 4 6 8". 179 | forNArgs<2>( 180 | [](auto x, auto y) 181 | { 182 | std::cout << x * y << " "; 183 | }, 184 | 185 | // 2 * 1 = 2 186 | 2, 1, 187 | 188 | // 2 * 2 = 4 189 | 2, 2, 190 | 191 | // 2 * 3 = 6 192 | 2, 3, 193 | 194 | // 2 * 4 = 8 195 | 2, 4); 196 | 197 | std::cout << "\n"; 198 | 199 | // Prints "3 6 9 12". 200 | forNArgs<3>( 201 | [](auto x, auto y, auto z) 202 | { 203 | std::cout << x + y + z << " "; 204 | }, 205 | 206 | // 1 + 1 + 1 = 3 207 | 1, 1, 1, 208 | 209 | // 2 + 2 + 2 = 6 210 | 2, 2, 2, 211 | 212 | // 3 + 3 + 3 = 9 213 | 3, 3, 3, 214 | 215 | // 4 + 4 + 4 = 12 216 | 4, 4, 4); 217 | 218 | std::cout << "\n"; 219 | return 0; 220 | } 221 | 222 | // Interesting, isn't it? 223 | // Let's see a possible use case in the next code segment. -------------------------------------------------------------------------------- /DiveIntoC++11/1_Arkanoid/p8.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | using namespace sf; 10 | 11 | constexpr int windowWidth{800}, windowHeight{600}; 12 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 13 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{6.f}; 14 | constexpr float blockWidth{60.f}, blockHeight{20.f}; 15 | constexpr int countBlocksX{11}, countBlocksY{4}; 16 | 17 | struct Ball 18 | { 19 | CircleShape shape; 20 | Vector2f velocity{-ballVelocity, -ballVelocity}; 21 | 22 | Ball(float mX, float mY) 23 | { 24 | shape.setPosition(mX, mY); 25 | shape.setRadius(ballRadius); 26 | shape.setFillColor(Color::Red); 27 | shape.setOrigin(ballRadius, ballRadius); 28 | } 29 | 30 | void update() 31 | { 32 | shape.move(velocity); 33 | 34 | if(left() < 0) 35 | velocity.x = ballVelocity; 36 | else if(right() > windowWidth) 37 | velocity.x = -ballVelocity; 38 | 39 | if(top() < 0) 40 | velocity.y = ballVelocity; 41 | else if(bottom() > windowHeight) 42 | velocity.y = -ballVelocity; 43 | } 44 | 45 | float x() { return shape.getPosition().x; } 46 | float y() { return shape.getPosition().y; } 47 | float left() { return x() - shape.getRadius(); } 48 | float right() { return x() + shape.getRadius(); } 49 | float top() { return y() - shape.getRadius(); } 50 | float bottom() { return y() + shape.getRadius(); } 51 | }; 52 | 53 | struct Paddle 54 | { 55 | RectangleShape shape; 56 | Vector2f velocity; 57 | 58 | Paddle(float mX, float mY) 59 | { 60 | shape.setPosition(mX, mY); 61 | shape.setSize({paddleWidth, paddleHeight}); 62 | shape.setFillColor(Color::Red); 63 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 64 | } 65 | 66 | void update() 67 | { 68 | shape.move(velocity); 69 | 70 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 71 | velocity.x = -paddleVelocity; 72 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 73 | right() < windowWidth) 74 | velocity.x = paddleVelocity; 75 | else 76 | velocity.x = 0; 77 | } 78 | 79 | float x() { return shape.getPosition().x; } 80 | float y() { return shape.getPosition().y; } 81 | float left() { return x() - shape.getSize().x / 2.f; } 82 | float right() { return x() + shape.getSize().x / 2.f; } 83 | float top() { return y() - shape.getSize().y / 2.f; } 84 | float bottom() { return y() + shape.getSize().y / 2.f; } 85 | }; 86 | 87 | struct Brick 88 | { 89 | RectangleShape shape; 90 | bool destroyed{false}; 91 | 92 | Brick(float mX, float mY) 93 | { 94 | shape.setPosition(mX, mY); 95 | shape.setSize({blockWidth, blockHeight}); 96 | shape.setFillColor(Color::Yellow); 97 | shape.setOrigin(blockWidth / 2.f, blockHeight / 2.f); 98 | } 99 | 100 | float x() { return shape.getPosition().x; } 101 | float y() { return shape.getPosition().y; } 102 | float left() { return x() - shape.getSize().x / 2.f; } 103 | float right() { return x() + shape.getSize().x / 2.f; } 104 | float top() { return y() - shape.getSize().y / 2.f; } 105 | float bottom() { return y() + shape.getSize().y / 2.f; } 106 | }; 107 | 108 | template 109 | bool isIntersecting(T1& mA, T2& mB) 110 | { 111 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 112 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 113 | } 114 | 115 | void testCollision(Paddle& mPaddle, Ball& mBall) 116 | { 117 | if(!isIntersecting(mPaddle, mBall)) return; 118 | 119 | mBall.velocity.y = -ballVelocity; 120 | if(mBall.x() < mPaddle.x()) 121 | mBall.velocity.x = -ballVelocity; 122 | else 123 | mBall.velocity.x = ballVelocity; 124 | } 125 | 126 | // Here's the most complex part of our game: ball-brick collision. 127 | void testCollision(Brick& mBrick, Ball& mBall) 128 | { 129 | // If there's no intersection, get out of the function. 130 | if(!isIntersecting(mBrick, mBall)) return; 131 | 132 | // Otherwise, the brick has been hit! 133 | mBrick.destroyed = true; 134 | 135 | // Let's calculate how much the ball intersects the brick 136 | // in every direction. 137 | float overlapLeft{mBall.right() - mBrick.left()}; 138 | float overlapRight{mBrick.right() - mBall.left()}; 139 | float overlapTop{mBall.bottom() - mBrick.top()}; 140 | float overlapBottom{mBrick.bottom() - mBall.top()}; 141 | 142 | // If the magnitude of the left overlap is smaller than the 143 | // right one we can safely assume the ball hit the brick 144 | // from the left. 145 | bool ballFromLeft(abs(overlapLeft) < abs(overlapRight)); 146 | 147 | // We can apply the same idea for top/bottom collisions. 148 | bool ballFromTop(abs(overlapTop) < abs(overlapBottom)); 149 | 150 | // Let's store the minimum overlaps for the X and Y axes. 151 | float minOverlapX{ballFromLeft ? overlapLeft : overlapRight}; 152 | float minOverlapY{ballFromTop ? overlapTop : overlapBottom}; 153 | 154 | // If the magnitude of the X overlap is less than the magnitude 155 | // of the Y overlap, we can safely assume the ball hit the brick 156 | // horizontally - otherwise, the ball hit the brick vertically. 157 | 158 | // Then, upon our assumptions, we change either the X or Y velocity 159 | // of the ball, creating a "realistic" response for the collision. 160 | if(abs(minOverlapX) < abs(minOverlapY)) 161 | mBall.velocity.x = ballFromLeft ? -ballVelocity : ballVelocity; 162 | else 163 | mBall.velocity.y = ballFromTop ? -ballVelocity : ballVelocity; 164 | } 165 | 166 | int main() 167 | { 168 | Ball ball{windowWidth / 2, windowHeight / 2}; 169 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 170 | vector bricks; 171 | 172 | for(int iX{0}; iX < countBlocksX; ++iX) 173 | for(int iY{0}; iY < countBlocksY; ++iY) 174 | bricks.emplace_back( 175 | (iX + 1) * (blockWidth + 3) + 22, (iY + 2) * (blockHeight + 3)); 176 | 177 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 8"}; 178 | window.setFramerateLimit(60); 179 | 180 | while(true) 181 | { 182 | window.clear(Color::Black); 183 | 184 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 185 | 186 | ball.update(); 187 | paddle.update(); 188 | testCollision(paddle, ball); 189 | 190 | // We use another C++11 foreach loop to test collisions 191 | // between the ball and EVERY brick. 192 | for(auto& brick : bricks) testCollision(brick, ball); 193 | 194 | // And we use the "erase-remove idiom" to remove all `destroyed` 195 | // blocks from the block vector - using a cool C++11 lambda! 196 | bricks.erase(remove_if(begin(bricks), end(bricks), 197 | [](const Brick& mBrick) 198 | { 199 | return mBrick.destroyed; 200 | }), 201 | end(bricks)); 202 | 203 | window.draw(ball.shape); 204 | window.draw(paddle.shape); 205 | for(auto& brick : bricks) window.draw(brick.shape); 206 | window.display(); 207 | } 208 | 209 | return 0; 210 | } 211 | 212 | // More on the "erase-remove idiom": 213 | // * http://en.wikipedia.org/wiki/Erase-remove_idiom 214 | // * http://lazarenko.me/2013/01/14/erasing-vector-the-smart-way/ -------------------------------------------------------------------------------- /DiveIntoC++14/2_ForArgs/p7.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | // http://vittorioromeo.info | vittorio.romeo@outlook.com 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // We're going to implement a `make_unordered_map` function in 12 | // this code segment, similar to the previous `make_vector`. 13 | 14 | // As `std::unordered_map` is an associative container, we 15 | // will use `forNArgs<2>` to pass "key-value pairs". 16 | 17 | // ---------------------------------------------------------------- 18 | 19 | template 20 | struct forNArgsImpl; 21 | 22 | template 23 | void forNArgs(TF&& mFn, Ts&&... mXs) 24 | { 25 | constexpr auto numberOfArgs(sizeof...(Ts)); 26 | 27 | static_assert(numberOfArgs % TArity == 0, "Invalid number of arguments"); 28 | 29 | forNArgsImpl, 30 | std::make_index_sequence>::exec(mFn, 31 | std::forward_as_tuple(std::forward(mXs)...)); 32 | } 33 | 34 | template 35 | struct forNArgsImpl, 36 | std::index_sequence> 37 | { 38 | template 39 | static void exec(TF&& mFn, const std::tuple& mXs) 40 | { 41 | constexpr auto arity(sizeof...(TNArity)); 42 | using swallow = bool[]; 43 | 44 | (void)swallow{(execN(mFn, mXs), true)...}; 45 | } 46 | 47 | template 48 | static void execN(TF&& mFn, const std::tuple& mXs) 49 | { 50 | mFn(std::get(mXs)...); 51 | } 52 | }; 53 | 54 | // ---------------------------------------------------------------- 55 | 56 | // Example use case: `make_unordered_map` function. 57 | 58 | // `make_unordered_map` will take arguments in groups of two and 59 | // and return an `std::unordered_map` having the first arguments 60 | // of every group as keys and the second arguments of every group 61 | // as values. 62 | 63 | template 64 | auto make_unordered_map(TArgs&&... mArgs); 65 | 66 | // ---------------------------------------------------------------- 67 | 68 | // Our first job is defining an helper that will allow us 69 | // to deduce the common type for all keys and the common type 70 | // for all values of the `std::unordered_map`. 71 | 72 | // We're going to use C++11 index sequences to divide the 73 | // passed parameter pack types in two different packs: 74 | 75 | // (types) K V K V K V K V K V ... 76 | // (K seq) 0 2 4 6 8 ... 77 | // (V seq) 1 3 5 7 9 ... 78 | 79 | // Let's forward-declare an helper struct that will do 80 | // that for us. It's gonna take match an `std::index_sequence` 81 | // that goes from `0` to `sizeof...(Ts) / 2` and will also 82 | // take a variadic amount of types. 83 | template 84 | struct CommonKVHelper; 85 | 86 | // Our `CommonKVHelper` specialization will expand the index 87 | // sequence: 88 | template 89 | struct CommonKVHelper, Ts...> 90 | { 91 | // Let's make sure the number of variadic types is a 92 | // multiple of two. 93 | static_assert(sizeof...(Ts) % 2 == 0, ""); 94 | 95 | // We need a way to get the type at a specific index from 96 | // a variadic type list. Fortunately, we can make use of 97 | // `std::tuple_element_t` to do that. 98 | 99 | // `std::tuple_element_t` takes two template parameters: 100 | // an index and a tuple type. It then "returns" the type 101 | // of the tuple element at that specific index. 102 | 103 | // Our `TypeAt` type alias will return the type at index 104 | // `TI` from the variadic `Ts...` type pack. 105 | template 106 | using TypeAt = std::tuple_element_t>; 107 | 108 | // To get the common type of the keys, we'll expand our 109 | // index sequence multiplying every number by two. 110 | using KeyType = std::common_type_t...>; 111 | 112 | // To get the common type of the values, we'll expand our 113 | // index sequence multiplying every number by two, adding one. 114 | using ValueType = std::common_type_t...>; 115 | 116 | // Example expansion for 6 types: 117 | /* 118 | // (TIs...) = (0, 1, 2) 119 | 120 | using KeyType = std::common_type_t 121 | < 122 | TypeAt<0 * 2>, // TypeAt<0> 123 | TypeAt<1 * 2>, // TypeAt<2> 124 | TypeAt<2 * 2> // TypeAt<4> 125 | >; 126 | 127 | using ValueType = std::common_type_t 128 | < 129 | TypeAt<(0 * 2) + 1>, // TypeAt<1> 130 | TypeAt<(1 * 2) + 1>, // TypeAt<3> 131 | TypeAt<(2 * 2) + 1> // TypeAt<5> 132 | >; 133 | */ 134 | }; 135 | 136 | // We still need an additional helper type alias to generate the 137 | // `std::index_sequence` from `0` to "half the number of types": 138 | template 139 | using HelperFor = 140 | CommonKVHelper, Ts...>; 141 | 142 | // The last thing we need to do is define two additional 143 | // type aliases that will take our list of key and value types 144 | // as inputs: one will return the common key type, the other 145 | // one will return the common value type. 146 | 147 | template 148 | using CommonKeyType = typename HelperFor::KeyType; 149 | 150 | template 151 | using CommonValueType = typename HelperFor::ValueType; 152 | 153 | // Let's use `static_assert` to make sure everything works. 154 | 155 | static_assert(std::is_same, 156 | 157 | // Deduced key type: 158 | std::string>(), 159 | ""); 160 | 161 | static_assert(std::is_same, 162 | 163 | // Deduced value type: 164 | int>(), 165 | ""); 166 | 167 | static_assert( 168 | std::is_same, 171 | 172 | // Deduced key type: 173 | std::string>(), 174 | ""); 175 | 176 | static_assert( 177 | std::is_same, 180 | 181 | // Deduced value type: 182 | float>(), 183 | ""); 184 | 185 | // ---------------------------------------------------------------- 186 | 187 | // We can finally implement `make_unordered_map`: 188 | template 189 | auto make_unordered_map(TArgs&&... mArgs) 190 | { 191 | // Let's calculate and alias the common types: 192 | using KeyType = CommonKeyType; 193 | using ValueType = CommonValueType; 194 | 195 | // Let's instantiate an `std::unordered_map` with the correct 196 | // type and reserve memory for the passed elements: 197 | std::unordered_map result; 198 | result.reserve(sizeof...(TArgs) / 2); 199 | 200 | // We can now use `forNArgs<2>` to pass elements two by two 201 | // to a lambda function that will emplace them as key-value 202 | // pairs in the `std::unordered_map`. 203 | 204 | forNArgs<2>( 205 | [&result](auto&& k, auto&& v) 206 | { 207 | result.emplace( 208 | std::forward(k), std::forward(v)); 209 | }, 210 | 211 | std::forward(mArgs)...); 212 | 213 | return result; 214 | } 215 | 216 | int main() 217 | { 218 | using namespace std::literals; 219 | 220 | auto m = make_unordered_map("zero"s, 0, "one"s, 1, "two", 2.f); 221 | 222 | static_assert( 223 | std::is_same>(), 224 | ""); 225 | 226 | // Prints "012". 227 | std::cout << m["zero"] << m["one"] << m["two"]; 228 | 229 | std::cout << "\n"; 230 | return 0; 231 | } 232 | 233 | // Thank you for watching this video! 234 | // I hope you found the tutorial interesting. 235 | 236 | // You can fork/look at the full source code on my GitHub page: 237 | // http://github.com/SuperV1234/ 238 | 239 | // Check out my website for more tutorials and to personally 240 | // get in touch with me. 241 | 242 | // http://vittorioromeo.info -------------------------------------------------------------------------------- /DiveIntoC++11/2_Arkanoid/p1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | using namespace sf; 10 | 11 | constexpr int windowWidth{800}, windowHeight{600}; 12 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 13 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{6.f}; 14 | constexpr float blockWidth{60.f}, blockHeight{20.f}; 15 | constexpr int countBlocksX{11}, countBlocksY{4}; 16 | 17 | struct Ball 18 | { 19 | CircleShape shape; 20 | Vector2f velocity{-ballVelocity, -ballVelocity}; 21 | 22 | Ball(float mX, float mY) 23 | { 24 | shape.setPosition(mX, mY); 25 | shape.setRadius(ballRadius); 26 | shape.setFillColor(Color::Red); 27 | shape.setOrigin(ballRadius, ballRadius); 28 | } 29 | 30 | void update() 31 | { 32 | shape.move(velocity); 33 | 34 | if(left() < 0) 35 | velocity.x = ballVelocity; 36 | else if(right() > windowWidth) 37 | velocity.x = -ballVelocity; 38 | 39 | if(top() < 0) 40 | velocity.y = ballVelocity; 41 | else if(bottom() > windowHeight) 42 | velocity.y = -ballVelocity; 43 | } 44 | 45 | // Let's begin to dive even deeper into C++11... 46 | 47 | // A very important thing in C++ is const-correctness: 48 | // since "getter" functions are not modifying any 49 | // member of their caller, they should be marked as 50 | // const - this allows to call them on `const&` and 51 | // `*const` parents. 52 | 53 | // It also expresses the intent that the function isn't 54 | // going to modify any member of its parent! 55 | 56 | // And we should also mark the functions with `noexcept`, 57 | // a new C++11 keyword that allows the programmer to help 58 | // the compiler optimize the code by marking certain functions 59 | // that will never throw an exception. 60 | 61 | // In this case, we are 100% sure that SFML's getters and 62 | // additions/subtractions will not throw any exception. 63 | 64 | // In the unfortunate case where an exception will be thrown, 65 | // do not worry! The program will terminate. 66 | // In older standards of C++, incorrectly marking functions 67 | // could've caused run-time corruption and security issues, 68 | // but that's not the case in C++11! :) 69 | float x() const noexcept { return shape.getPosition().x; } 70 | float y() const noexcept { return shape.getPosition().y; } 71 | float left() const noexcept { return x() - shape.getRadius(); } 72 | float right() const noexcept { return x() + shape.getRadius(); } 73 | float top() const noexcept { return y() - shape.getRadius(); } 74 | float bottom() const noexcept { return y() + shape.getRadius(); } 75 | }; 76 | 77 | // We can now refactor our code by creating a Rectangle class 78 | // that encapsulates the common properties for `Brick` and `Paddle`. 79 | // There is no run-time overhead when we avoid using `virtual` methods 80 | // in hierarchies. 81 | struct Rectangle 82 | { 83 | RectangleShape shape; 84 | float x() const noexcept { return shape.getPosition().x; } 85 | float y() const noexcept { return shape.getPosition().y; } 86 | float left() const noexcept { return x() - shape.getSize().x / 2.f; } 87 | float right() const noexcept { return x() + shape.getSize().x / 2.f; } 88 | float top() const noexcept { return y() - shape.getSize().y / 2.f; } 89 | float bottom() const noexcept { return y() + shape.getSize().y / 2.f; } 90 | }; 91 | 92 | struct Paddle : public Rectangle 93 | { 94 | Vector2f velocity; 95 | 96 | Paddle(float mX, float mY) 97 | { 98 | shape.setPosition(mX, mY); 99 | shape.setSize({paddleWidth, paddleHeight}); 100 | shape.setFillColor(Color::Red); 101 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 102 | } 103 | 104 | void update() 105 | { 106 | shape.move(velocity); 107 | 108 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 109 | velocity.x = -paddleVelocity; 110 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 111 | right() < windowWidth) 112 | velocity.x = paddleVelocity; 113 | else 114 | velocity.x = 0; 115 | } 116 | }; 117 | 118 | struct Brick : public Rectangle 119 | { 120 | // I didn't talk about this earlier, but initializing 121 | // members in place as I did here with `destroyed` is 122 | // a new C++11 feature. It's an handy shortcut that 123 | // allows us to avoid writing constructor initialization 124 | // lists and helps remembering to initialize members. 125 | bool destroyed{false}; 126 | 127 | Brick(float mX, float mY) 128 | { 129 | shape.setPosition(mX, mY); 130 | shape.setSize({blockWidth, blockHeight}); 131 | shape.setFillColor(Color::Yellow); 132 | shape.setOrigin(blockWidth / 2.f, blockHeight / 2.f); 133 | } 134 | }; 135 | 136 | template 137 | bool isIntersecting(T1& mA, T2& mB) noexcept 138 | { 139 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 140 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 141 | } 142 | 143 | void testCollision(Paddle& mPaddle, Ball& mBall) noexcept 144 | { 145 | if(!isIntersecting(mPaddle, mBall)) return; 146 | 147 | mBall.velocity.y = -ballVelocity; 148 | if(mBall.x() < mPaddle.x()) 149 | mBall.velocity.x = -ballVelocity; 150 | else 151 | mBall.velocity.x = ballVelocity; 152 | } 153 | 154 | void testCollision(Brick& mBrick, Ball& mBall) noexcept 155 | { 156 | if(!isIntersecting(mBrick, mBall)) return; 157 | mBrick.destroyed = true; 158 | 159 | float overlapLeft{mBall.right() - mBrick.left()}; 160 | float overlapRight{mBrick.right() - mBall.left()}; 161 | float overlapTop{mBall.bottom() - mBrick.top()}; 162 | float overlapBottom{mBrick.bottom() - mBall.top()}; 163 | 164 | bool ballFromLeft(abs(overlapLeft) < abs(overlapRight)); 165 | bool ballFromTop(abs(overlapTop) < abs(overlapBottom)); 166 | 167 | float minOverlapX{ballFromLeft ? overlapLeft : overlapRight}; 168 | float minOverlapY{ballFromTop ? overlapTop : overlapBottom}; 169 | 170 | if(abs(minOverlapX) < abs(minOverlapY)) 171 | mBall.velocity.x = ballFromLeft ? -ballVelocity : ballVelocity; 172 | else 173 | mBall.velocity.y = ballFromTop ? -ballVelocity : ballVelocity; 174 | } 175 | 176 | int main() 177 | { 178 | Ball ball{windowWidth / 2, windowHeight / 2}; 179 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 180 | vector bricks; 181 | 182 | for(int iX{0}; iX < countBlocksX; ++iX) 183 | for(int iY{0}; iY < countBlocksY; ++iY) 184 | bricks.emplace_back( 185 | (iX + 1) * (blockWidth + 3) + 22, (iY + 2) * (blockHeight + 3)); 186 | 187 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 10"}; 188 | window.setFramerateLimit(60); 189 | 190 | while(true) 191 | { 192 | window.clear(Color::Black); 193 | 194 | // Event polling is a very important and useful SFML 195 | // feature that I forgot to talk about in the previous video. 196 | // 1. It is, on some operating systems, required! Not using 197 | // it may cause the window to freeze. 198 | // 2. It allows us to react to common events, such as the 199 | // window being closed, dragged, resized, etc... 200 | // 3. It also allows us to get input directly from the OS, 201 | // and immensely simplifies text entering (think about 202 | // entering a name in an high score). 203 | Event event; 204 | while(window.pollEvent(event)) 205 | { 206 | if(event.type == Event::Closed) 207 | { 208 | window.close(); 209 | break; 210 | } 211 | } 212 | 213 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 214 | 215 | ball.update(); 216 | paddle.update(); 217 | testCollision(paddle, ball); 218 | for(auto& brick : bricks) testCollision(brick, ball); 219 | bricks.erase(remove_if(begin(bricks), end(bricks), 220 | [](const Brick& mBrick) 221 | { 222 | return mBrick.destroyed; 223 | }), 224 | end(bricks)); 225 | 226 | window.draw(ball.shape); 227 | window.draw(paddle.shape); 228 | for(auto& brick : bricks) window.draw(brick.shape); 229 | window.display(); 230 | } 231 | 232 | return 0; 233 | } -------------------------------------------------------------------------------- /DiveIntoC++11/2_Arkanoid/p3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace sf; 11 | 12 | using FrameTime = float; 13 | 14 | // We will need to make the velocity constants a lot smaller 15 | // to deal with the new frametime values 16 | constexpr int windowWidth{800}, windowHeight{600}; 17 | constexpr float ballRadius{10.f}, ballVelocity{0.8f}; 18 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{0.6f}; 19 | constexpr float blockWidth{60.f}, blockHeight{20.f}; 20 | constexpr int countBlocksX{11}, countBlocksY{4}; 21 | 22 | struct Ball 23 | { 24 | CircleShape shape; 25 | Vector2f velocity{-ballVelocity, -ballVelocity}; 26 | 27 | Ball(float mX, float mY) 28 | { 29 | shape.setPosition(mX, mY); 30 | shape.setRadius(ballRadius); 31 | shape.setFillColor(Color::Red); 32 | shape.setOrigin(ballRadius, ballRadius); 33 | } 34 | 35 | // We somehow need to `scale` movements and actions 36 | // by the frametime - to do so, we must pass the 37 | // latest frametime as a parameter in the update methods 38 | void update(FrameTime mFT) 39 | { 40 | // Then we'll just scale our velocity by the frametime 41 | shape.move(velocity * mFT); 42 | 43 | if(left() < 0) 44 | velocity.x = ballVelocity; 45 | else if(right() > windowWidth) 46 | velocity.x = -ballVelocity; 47 | 48 | if(top() < 0) 49 | velocity.y = ballVelocity; 50 | else if(bottom() > windowHeight) 51 | velocity.y = -ballVelocity; 52 | } 53 | 54 | float x() const noexcept { return shape.getPosition().x; } 55 | float y() const noexcept { return shape.getPosition().y; } 56 | float left() const noexcept { return x() - shape.getRadius(); } 57 | float right() const noexcept { return x() + shape.getRadius(); } 58 | float top() const noexcept { return y() - shape.getRadius(); } 59 | float bottom() const noexcept { return y() + shape.getRadius(); } 60 | }; 61 | 62 | struct Rectangle 63 | { 64 | RectangleShape shape; 65 | float x() const noexcept { return shape.getPosition().x; } 66 | float y() const noexcept { return shape.getPosition().y; } 67 | float left() const noexcept { return x() - shape.getSize().x / 2.f; } 68 | float right() const noexcept { return x() + shape.getSize().x / 2.f; } 69 | float top() const noexcept { return y() - shape.getSize().y / 2.f; } 70 | float bottom() const noexcept { return y() + shape.getSize().y / 2.f; } 71 | }; 72 | 73 | struct Paddle : public Rectangle 74 | { 75 | Vector2f velocity; 76 | 77 | Paddle(float mX, float mY) 78 | { 79 | shape.setPosition(mX, mY); 80 | shape.setSize({paddleWidth, paddleHeight}); 81 | shape.setFillColor(Color::Red); 82 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 83 | } 84 | 85 | void update(FrameTime mFT) 86 | { 87 | shape.move(velocity * mFT); 88 | 89 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 90 | velocity.x = -paddleVelocity; 91 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 92 | right() < windowWidth) 93 | velocity.x = paddleVelocity; 94 | else 95 | velocity.x = 0; 96 | } 97 | }; 98 | 99 | struct Brick : public Rectangle 100 | { 101 | bool destroyed{false}; 102 | 103 | Brick(float mX, float mY) 104 | { 105 | shape.setPosition(mX, mY); 106 | shape.setSize({blockWidth, blockHeight}); 107 | shape.setFillColor(Color::Yellow); 108 | shape.setOrigin(blockWidth / 2.f, blockHeight / 2.f); 109 | } 110 | }; 111 | 112 | template 113 | bool isIntersecting(T1& mA, T2& mB) noexcept 114 | { 115 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 116 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 117 | } 118 | 119 | void testCollision(Paddle& mPaddle, Ball& mBall) noexcept 120 | { 121 | if(!isIntersecting(mPaddle, mBall)) return; 122 | 123 | mBall.velocity.y = -ballVelocity; 124 | if(mBall.x() < mPaddle.x()) 125 | mBall.velocity.x = -ballVelocity; 126 | else 127 | mBall.velocity.x = ballVelocity; 128 | } 129 | 130 | void testCollision(Brick& mBrick, Ball& mBall) noexcept 131 | { 132 | if(!isIntersecting(mBrick, mBall)) return; 133 | mBrick.destroyed = true; 134 | 135 | float overlapLeft{mBall.right() - mBrick.left()}; 136 | float overlapRight{mBrick.right() - mBall.left()}; 137 | float overlapTop{mBall.bottom() - mBrick.top()}; 138 | float overlapBottom{mBrick.bottom() - mBall.top()}; 139 | 140 | bool ballFromLeft(abs(overlapLeft) < abs(overlapRight)); 141 | bool ballFromTop(abs(overlapTop) < abs(overlapBottom)); 142 | 143 | float minOverlapX{ballFromLeft ? overlapLeft : overlapRight}; 144 | float minOverlapY{ballFromTop ? overlapTop : overlapBottom}; 145 | 146 | if(abs(minOverlapX) < abs(minOverlapY)) 147 | mBall.velocity.x = ballFromLeft ? -ballVelocity : ballVelocity; 148 | else 149 | mBall.velocity.y = ballFromTop ? -ballVelocity : ballVelocity; 150 | } 151 | 152 | int main() 153 | { 154 | Ball ball{windowWidth / 2, windowHeight / 2}; 155 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 156 | vector bricks; 157 | 158 | for(int iX{0}; iX < countBlocksX; ++iX) 159 | for(int iY{0}; iY < countBlocksY; ++iY) 160 | bricks.emplace_back( 161 | (iX + 1) * (blockWidth + 3) + 22, (iY + 2) * (blockHeight + 3)); 162 | 163 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 12"}; 164 | 165 | // Let's store the last frametime here 166 | FrameTime lastFt{0.f}; 167 | 168 | // By commenting/uncommenting these lines, we can check 169 | // if the game behaves the same way at every framerate 170 | window.setFramerateLimit(240); 171 | // window.setFramerateLimit(120); 172 | // window.setFramerateLimit(60); 173 | // window.setFramerateLimit(30); 174 | // window.setFramerateLimit(15); 175 | 176 | while(true) 177 | { 178 | auto timePoint1(chrono::high_resolution_clock::now()); 179 | 180 | window.clear(Color::Black); 181 | 182 | Event event; 183 | while(window.pollEvent(event)) 184 | { 185 | if(event.type == Event::Closed) 186 | { 187 | window.close(); 188 | break; 189 | } 190 | } 191 | 192 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 193 | 194 | // Now we need to pass the latest frametime in all 195 | // our `update` calls 196 | ball.update(lastFt); 197 | paddle.update(lastFt); 198 | 199 | testCollision(paddle, ball); 200 | for(auto& brick : bricks) testCollision(brick, ball); 201 | bricks.erase(remove_if(begin(bricks), end(bricks), 202 | [](const Brick& mBrick) 203 | { 204 | return mBrick.destroyed; 205 | }), 206 | end(bricks)); 207 | 208 | window.draw(ball.shape); 209 | window.draw(paddle.shape); 210 | for(auto& brick : bricks) window.draw(brick.shape); 211 | window.display(); 212 | 213 | auto timePoint2(chrono::high_resolution_clock::now()); 214 | auto elapsedTime(timePoint2 - timePoint1); 215 | FrameTime ft{ 216 | chrono::duration_cast>(elapsedTime) 217 | .count()}; 218 | 219 | // Let's assign the new frametime here 220 | lastFt = ft; 221 | 222 | auto ftSeconds(ft / 1000.f); 223 | auto fps(1.f / ftSeconds); 224 | 225 | window.setTitle("FT: " + to_string(ft) + "\tFPS: " + to_string(fps)); 226 | } 227 | 228 | return 0; 229 | } 230 | 231 | // You may have noticed that the game seems to behave in the same way 232 | // at any framerate - however, this is not the case. At very low 233 | // framerates something horrible can occur: velocities can be so high 234 | // that certain collision checks get skipped, because objects completely 235 | // jump over other objects! 236 | 237 | // Let's say our ballVelocity constant is `0.8f`. 238 | // If our FPS are 15, our frametime will be equal to `66`! 239 | 240 | // Our final "movement step" will then be `66 * 0.8f`, which 241 | // equals to `52.8f`. It is almost 3 times bigger than the 242 | // height of our bricks! Our ball can easily skip an entire 243 | // brick in a single movement step. Let's see how we can fix 244 | // this in the next code segment. -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p8.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // We will implement groups by giving a group bitset to every entity, 15 | // and storing entity pointers in the entity manager. 16 | 17 | namespace CompositionArkanoid 18 | { 19 | struct Component; 20 | class Entity; 21 | class Manager; 22 | 23 | using ComponentID = std::size_t; 24 | 25 | // Let's create a typedef for our group type... 26 | using Group = std::size_t; 27 | 28 | namespace Internal 29 | { 30 | inline ComponentID getUniqueComponentID() noexcept 31 | { 32 | static ComponentID lastID{0u}; 33 | return lastID++; 34 | } 35 | } 36 | 37 | template 38 | inline ComponentID getComponentTypeID() noexcept 39 | { 40 | static_assert(std::is_base_of::value, 41 | "T must inherit from Component"); 42 | 43 | static ComponentID typeID{Internal::getUniqueComponentID()}; 44 | return typeID; 45 | } 46 | 47 | constexpr std::size_t maxComponents{32}; 48 | using ComponentBitset = std::bitset; 49 | using ComponentArray = std::array; 50 | 51 | // ...and one for the bitset. 52 | constexpr std::size_t maxGroups{32}; 53 | using GroupBitset = std::bitset; 54 | 55 | struct Component 56 | { 57 | Entity* entity; 58 | 59 | virtual void init() {} 60 | virtual void update(float mFT) {} 61 | virtual void draw() {} 62 | 63 | virtual ~Component() {} 64 | }; 65 | 66 | class Entity 67 | { 68 | private: 69 | // The entity will need a reference to its manager now. 70 | Manager& manager; 71 | 72 | bool alive{true}; 73 | std::vector> components; 74 | ComponentArray componentArray; 75 | ComponentBitset componentBitset; 76 | 77 | // Let's add a bitset to our entities. 78 | GroupBitset groupBitset; 79 | 80 | public: 81 | Entity(Manager& mManager) : manager(mManager) {} 82 | 83 | void update(float mFT) 84 | { 85 | for(auto& c : components) c->update(mFT); 86 | } 87 | void draw() 88 | { 89 | for(auto& c : components) c->draw(); 90 | } 91 | 92 | bool isAlive() const { return alive; } 93 | void destroy() { alive = false; } 94 | 95 | template 96 | bool hasComponent() const 97 | { 98 | return componentBitset[getComponentTypeID()]; 99 | } 100 | 101 | // Groups will be handled at runtime, not compile-time: 102 | // therefore we will pass groups as a function argument. 103 | bool hasGroup(Group mGroup) const noexcept 104 | { 105 | return groupBitset[mGroup]; 106 | } 107 | 108 | // To add/remove group we define some methods that alter 109 | // the bitset and tell the manager what we're doing, 110 | // so that the manager can internally store this entity 111 | // in its grouped containers. 112 | // We'll need to define this method after the definition 113 | // of `Manager`, as we're gonna call `Manager::addtoGroup` 114 | // here. 115 | void addGroup(Group mGroup) noexcept; 116 | void delGroup(Group mGroup) noexcept 117 | { 118 | groupBitset[mGroup] = false; 119 | // We won't notify the manager that a group has been 120 | // removed here, as it will automatically remove 121 | // entities from the "wrong" group containers during 122 | // refresh. 123 | } 124 | 125 | template 126 | T& addComponent(TArgs&&... mArgs) 127 | { 128 | assert(!hasComponent()); 129 | 130 | T* c(new T(std::forward(mArgs)...)); 131 | c->entity = this; 132 | std::unique_ptr uPtr{c}; 133 | components.emplace_back(std::move(uPtr)); 134 | 135 | componentArray[getComponentTypeID()] = c; 136 | componentBitset[getComponentTypeID()] = true; 137 | 138 | c->init(); 139 | return *c; 140 | } 141 | 142 | template 143 | T& getComponent() const 144 | { 145 | assert(hasComponent()); 146 | auto ptr(componentArray[getComponentTypeID()]); 147 | return *reinterpret_cast(ptr); 148 | } 149 | }; 150 | 151 | struct Manager 152 | { 153 | private: 154 | std::vector> entities; 155 | 156 | // We store entities in groups by creating "group buckets" in an 157 | // array. `std::vector` could be also replaced for 158 | // `std::set`. 159 | std::array, maxGroups> groupedEntities; 160 | 161 | public: 162 | void update(float mFT) 163 | { 164 | for(auto& e : entities) e->update(mFT); 165 | } 166 | void draw() 167 | { 168 | for(auto& e : entities) e->draw(); 169 | } 170 | 171 | // When we add a group to an entity, we just add it to the 172 | // correct "group bucket". 173 | void addToGroup(Entity* mEntity, Group mGroup) 174 | { 175 | // It would be wise to either assert that the bucket doesn't 176 | // already contain `mEntity`, or use a set to prevent duplicates 177 | // in exchange for less efficient insertion/iteration. 178 | 179 | groupedEntities[mGroup].emplace_back(mEntity); 180 | } 181 | 182 | // To get entities that belong to a certain group, we can simply 183 | // get one of the "buckets" from the array. 184 | std::vector& getEntitiesByGroup(Group mGroup) 185 | { 186 | return groupedEntities[mGroup]; 187 | } 188 | 189 | void refresh() 190 | { 191 | // During refresh, we need to remove dead entities and entities 192 | // with incorrect groups from the buckets. 193 | for(auto i(0u); i < maxGroups; ++i) 194 | { 195 | auto& v(groupedEntities[i]); 196 | 197 | v.erase(std::remove_if(std::begin(v), std::end(v), 198 | [i](Entity* mEntity) 199 | { 200 | return !mEntity->isAlive() || 201 | !mEntity->hasGroup(i); 202 | }), 203 | std::end(v)); 204 | } 205 | 206 | entities.erase( 207 | std::remove_if(std::begin(entities), std::end(entities), 208 | [](const std::unique_ptr& mEntity) 209 | { 210 | return !mEntity->isAlive(); 211 | }), 212 | std::end(entities)); 213 | } 214 | 215 | Entity& addEntity() 216 | { 217 | Entity* e(new Entity(*this)); 218 | std::unique_ptr uPtr{e}; 219 | entities.emplace_back(std::move(uPtr)); 220 | return *e; 221 | } 222 | }; 223 | 224 | // Here's the definition of `Entity::addToGroup`: 225 | void Entity::addGroup(Group mGroup) noexcept 226 | { 227 | groupBitset[mGroup] = true; 228 | manager.addToGroup(this, mGroup); 229 | } 230 | } 231 | 232 | using namespace CompositionArkanoid; 233 | 234 | struct CounterComponent : Component 235 | { 236 | float counter; 237 | void update(float mFT) override 238 | { 239 | counter += mFT; 240 | std::cout << counter << std::endl; 241 | } 242 | }; 243 | 244 | struct KillComponent : Component 245 | { 246 | CounterComponent* cCounter{nullptr}; 247 | 248 | void init() override 249 | { 250 | cCounter = &entity->getComponent(); 251 | } 252 | 253 | void update(float mFT) override 254 | { 255 | if(cCounter->counter >= 100) entity->destroy(); 256 | } 257 | }; 258 | 259 | int main() 260 | { 261 | Manager manager; 262 | 263 | auto& entity(manager.addEntity()); 264 | entity.addComponent(); 265 | entity.addComponent(); 266 | 267 | for(auto i(0u); i < 1000; ++i) 268 | { 269 | manager.refresh(); 270 | manager.update(1.f); 271 | manager.draw(); 272 | } 273 | } -------------------------------------------------------------------------------- /DiveIntoC++11/2_Arkanoid/p2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace sf; 11 | 12 | // This is a modern C++11 `typedef` replacement: 13 | // we just defined an alias for `float`, called `FrameTime` 14 | using FrameTime = float; 15 | 16 | constexpr int windowWidth{800}, windowHeight{600}; 17 | constexpr float ballRadius{10.f}, ballVelocity{8.f}; 18 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{6.f}; 19 | constexpr float blockWidth{60.f}, blockHeight{20.f}; 20 | constexpr int countBlocksX{11}, countBlocksY{4}; 21 | 22 | struct Ball 23 | { 24 | CircleShape shape; 25 | Vector2f velocity{-ballVelocity, -ballVelocity}; 26 | 27 | Ball(float mX, float mY) 28 | { 29 | shape.setPosition(mX, mY); 30 | shape.setRadius(ballRadius); 31 | shape.setFillColor(Color::Red); 32 | shape.setOrigin(ballRadius, ballRadius); 33 | } 34 | 35 | void update() 36 | { 37 | shape.move(velocity); 38 | 39 | if(left() < 0) 40 | velocity.x = ballVelocity; 41 | else if(right() > windowWidth) 42 | velocity.x = -ballVelocity; 43 | 44 | if(top() < 0) 45 | velocity.y = ballVelocity; 46 | else if(bottom() > windowHeight) 47 | velocity.y = -ballVelocity; 48 | } 49 | 50 | float x() const noexcept { return shape.getPosition().x; } 51 | float y() const noexcept { return shape.getPosition().y; } 52 | float left() const noexcept { return x() - shape.getRadius(); } 53 | float right() const noexcept { return x() + shape.getRadius(); } 54 | float top() const noexcept { return y() - shape.getRadius(); } 55 | float bottom() const noexcept { return y() + shape.getRadius(); } 56 | }; 57 | 58 | struct Rectangle 59 | { 60 | RectangleShape shape; 61 | float x() const noexcept { return shape.getPosition().x; } 62 | float y() const noexcept { return shape.getPosition().y; } 63 | float left() const noexcept { return x() - shape.getSize().x / 2.f; } 64 | float right() const noexcept { return x() + shape.getSize().x / 2.f; } 65 | float top() const noexcept { return y() - shape.getSize().y / 2.f; } 66 | float bottom() const noexcept { return y() + shape.getSize().y / 2.f; } 67 | }; 68 | 69 | struct Paddle : public Rectangle 70 | { 71 | Vector2f velocity; 72 | 73 | Paddle(float mX, float mY) 74 | { 75 | shape.setPosition(mX, mY); 76 | shape.setSize({paddleWidth, paddleHeight}); 77 | shape.setFillColor(Color::Red); 78 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 79 | } 80 | 81 | void update() 82 | { 83 | shape.move(velocity); 84 | 85 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 86 | velocity.x = -paddleVelocity; 87 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 88 | right() < windowWidth) 89 | velocity.x = paddleVelocity; 90 | else 91 | velocity.x = 0; 92 | } 93 | }; 94 | 95 | struct Brick : public Rectangle 96 | { 97 | bool destroyed{false}; 98 | 99 | Brick(float mX, float mY) 100 | { 101 | shape.setPosition(mX, mY); 102 | shape.setSize({blockWidth, blockHeight}); 103 | shape.setFillColor(Color::Yellow); 104 | shape.setOrigin(blockWidth / 2.f, blockHeight / 2.f); 105 | } 106 | }; 107 | 108 | template 109 | bool isIntersecting(T1& mA, T2& mB) noexcept 110 | { 111 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 112 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 113 | } 114 | 115 | void testCollision(Paddle& mPaddle, Ball& mBall) noexcept 116 | { 117 | if(!isIntersecting(mPaddle, mBall)) return; 118 | 119 | mBall.velocity.y = -ballVelocity; 120 | if(mBall.x() < mPaddle.x()) 121 | mBall.velocity.x = -ballVelocity; 122 | else 123 | mBall.velocity.x = ballVelocity; 124 | } 125 | 126 | void testCollision(Brick& mBrick, Ball& mBall) noexcept 127 | { 128 | if(!isIntersecting(mBrick, mBall)) return; 129 | mBrick.destroyed = true; 130 | 131 | float overlapLeft{mBall.right() - mBrick.left()}; 132 | float overlapRight{mBrick.right() - mBall.left()}; 133 | float overlapTop{mBall.bottom() - mBrick.top()}; 134 | float overlapBottom{mBrick.bottom() - mBall.top()}; 135 | 136 | bool ballFromLeft(abs(overlapLeft) < abs(overlapRight)); 137 | bool ballFromTop(abs(overlapTop) < abs(overlapBottom)); 138 | 139 | float minOverlapX{ballFromLeft ? overlapLeft : overlapRight}; 140 | float minOverlapY{ballFromTop ? overlapTop : overlapBottom}; 141 | 142 | if(abs(minOverlapX) < abs(minOverlapY)) 143 | mBall.velocity.x = ballFromLeft ? -ballVelocity : ballVelocity; 144 | else 145 | mBall.velocity.y = ballFromTop ? -ballVelocity : ballVelocity; 146 | } 147 | 148 | // Our code has a critical issue: it is framerate dependent! 149 | // We set the framerate to 60 to prevent frame-dependent behavior, 150 | // but it's not a good solution at all. 151 | 152 | // We should, instead, pass the time the last frame took to update 153 | // in our `update()` methods, so that we can "scale" our movements 154 | // and actions with the frame time, achieving framerate indepedent 155 | // behavior. 156 | 157 | // Let's begin by getting the time a frame takes to update/draw, 158 | // using C++11's fantastic `std::chrono` library. 159 | 160 | int main() 161 | { 162 | Ball ball{windowWidth / 2, windowHeight / 2}; 163 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 164 | vector bricks; 165 | 166 | for(int iX{0}; iX < countBlocksX; ++iX) 167 | for(int iY{0}; iY < countBlocksY; ++iY) 168 | bricks.emplace_back( 169 | (iX + 1) * (blockWidth + 3) + 22, (iY + 2) * (blockHeight + 3)); 170 | 171 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 11"}; 172 | 173 | // Let's comment out the frame rate limit for now. 174 | // window.setFramerateLimit(60); 175 | 176 | while(true) 177 | { 178 | // Start of our time interval. 179 | auto timePoint1(chrono::high_resolution_clock::now()); 180 | 181 | window.clear(Color::Black); 182 | 183 | Event event; 184 | while(window.pollEvent(event)) 185 | { 186 | if(event.type == Event::Closed) 187 | { 188 | window.close(); 189 | break; 190 | } 191 | } 192 | 193 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) break; 194 | 195 | ball.update(); 196 | paddle.update(); 197 | testCollision(paddle, ball); 198 | for(auto& brick : bricks) testCollision(brick, ball); 199 | bricks.erase(remove_if(begin(bricks), end(bricks), 200 | [](const Brick& mBrick) 201 | { 202 | return mBrick.destroyed; 203 | }), 204 | end(bricks)); 205 | 206 | window.draw(ball.shape); 207 | window.draw(paddle.shape); 208 | for(auto& brick : bricks) window.draw(brick.shape); 209 | window.display(); 210 | 211 | // End of our time interval. 212 | auto timePoint2(chrono::high_resolution_clock::now()); 213 | 214 | // Let's calculate the frame time in milliseconds, 215 | // and "print it out" as the window's title. 216 | 217 | // Subtracting two std::chrono::time_point objects 218 | // returns an `std::chrono::duration` object, which 219 | // represents a time period. 220 | auto elapsedTime(timePoint2 - timePoint1); 221 | 222 | // We want to get the frametime in milliseconds, so we can 223 | // just use the safe `std::chrono::duration_cast` function. 224 | // To convert the duration to a `float`, we will use `.count()` 225 | // at the end. 226 | FrameTime ft{ 227 | chrono::duration_cast>(elapsedTime) 228 | .count()}; 229 | 230 | // We can approximate fps by dividing 1.f by the 231 | // elapsed seconds, calculated converting ft 232 | // to seconds (ms / 1000.f). 233 | auto ftSeconds(ft / 1000.f); 234 | auto fps(1.f / ftSeconds); 235 | 236 | // std::to_string is another very useful C++11 function, 237 | // that transforms many different types to an std::string. 238 | window.setTitle("FT: " + to_string(ft) + "\tFPS: " + to_string(fps)); 239 | } 240 | 241 | return 0; 242 | } 243 | 244 | // The game should now run insanely fast... let's see what 245 | // we can do about it in the next code segment. -------------------------------------------------------------------------------- /DiveIntoC++11/5_Entities/p5.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // To implement our component-based entity system, we need 11 | // a `Manager` class that helps us manage components and entities, 12 | // an `Entity` class that acts as a collection of components, 13 | // and a `Component` base class from which components will inherit. 14 | 15 | // We will implement a component-based entity system where 16 | // components contain not only data, but also logic. 17 | 18 | // This kind of system is easier to implement and it's a good 19 | // starting point to move away from inheritance-based game design. 20 | 21 | // In a future video, I may cover a more flexible and elegant 22 | // entity system where components do not contain logic, but only 23 | // data. 24 | 25 | // Here's a simple diagram of our design: 26 | 27 | /* 28 | 29 | [ Manager ] /-`update()` 30 | | /-[ Component ]---| 31 | \-[ Entity ]--| \-`draw()` 32 | | \-[ Component ] 33 | | 34 | | /-[ Component ] 35 | \-[ Entity ]--| 36 | \-[ Component ] 37 | */ 38 | 39 | namespace CompositionArkanoid 40 | { 41 | // Forward-declaration of the `Entity` class. 42 | class Entity; 43 | 44 | struct Component 45 | { 46 | // We begin by defining a base `Component` class. 47 | // Game components will inherit from this class. 48 | 49 | // We will use a pointer to store the parent entity. 50 | Entity* entity{nullptr}; 51 | 52 | // Usually a game component will have: 53 | // * Some data 54 | // * Update behavior 55 | // * Drawing behavior 56 | 57 | // Therefore we define two virtual methods that 58 | // will be overridden by game component types. 59 | virtual void update(float mFT) {} 60 | virtual void draw() {} 61 | 62 | // As we'll be using this class polymorphically, it requires 63 | // a virtual destructor. 64 | virtual ~Component() {} 65 | }; 66 | 67 | class Entity 68 | { 69 | // Next, we define an Entity class. 70 | // It will basically be an aggregate of components, 71 | // with some methods that help us update and draw 72 | // all of them. 73 | 74 | private: 75 | // We'll keep track of whether the entity is alive or dead 76 | // with a boolean and we'll store the components in a private 77 | // vector of `std::unique_ptr`, to allow polymorphism. 78 | bool alive{true}; 79 | std::vector> components; 80 | 81 | // Now we will define some public methods to update and 82 | // draw, to add components and to destroy the entity. 83 | 84 | public: 85 | // Updating and drawing simply consists in updating and drawing 86 | // all the components. 87 | void update(float mFT) 88 | { 89 | for(auto& c : components) c->update(mFT); 90 | } 91 | void draw() 92 | { 93 | for(auto& c : components) c->draw(); 94 | } 95 | 96 | // We will also define some methods to control the lifetime 97 | // of the entity. 98 | bool isAlive() const { return alive; } 99 | void destroy() { alive = false; } 100 | 101 | // Now, we'll define a method that allows us to add components 102 | // to our entity. 103 | // We'll take advantage of C++11 variadic templates and emplacement 104 | // to directly construct our components in place. 105 | // `T` is the component type. `TArgs` is a parameter pack of 106 | // types used to construct the component. 107 | template 108 | T& addComponent(TArgs&&... mArgs) 109 | { 110 | // We begin by allocating the component of type `T` 111 | // on the heap, by forwarding the passed arguments 112 | // to its constructor. 113 | T* c(new T(std::forward(mArgs)...)); 114 | 115 | // We set the component's entity to the current 116 | // instance. 117 | c->entity = this; 118 | 119 | // We will wrap the raw pointer into a smart one, 120 | // so that we can emplace it into our container and 121 | // to make sure we do not leak any memory. 122 | std::unique_ptr uPtr{c}; 123 | 124 | // Now we'll add the smart pointer to our container: 125 | // `std::move` is required, as `std::unique_ptr` cannot 126 | // be copied. 127 | components.emplace_back(std::move(uPtr)); 128 | 129 | // ...and we will return a reference to the newly added 130 | // component, in case the user wants to do something 131 | // with it. 132 | return *c; 133 | } 134 | }; 135 | 136 | // Even if the `Entity` class may seem complex, conceptually it is 137 | // very simple. Just think of an entity as a container for components, 138 | // with syntatic sugar methods to quicky add/update/draw components. 139 | 140 | // If `Entity` is an aggregate of components, `Manager` is an aggregate 141 | // of entities. Implementation is straightforward, and resembles the 142 | // previous one. 143 | 144 | struct Manager 145 | { 146 | private: 147 | std::vector> entities; 148 | 149 | public: 150 | void update(float mFT) 151 | { 152 | // We will start by cleaning up "dead" entities. 153 | 154 | entities.erase( 155 | std::remove_if(std::begin(entities), std::end(entities), 156 | [](const std::unique_ptr& mEntity) 157 | { 158 | return !mEntity->isAlive(); 159 | }), 160 | std::end(entities)); 161 | 162 | // This algorithm closely resembles the one we used in 163 | // the first episode of the series to delete "destroyed" 164 | // blocks. Basically, we're going through all entities and 165 | // erasing the "dead" ones. 166 | // This is known as the "erase-remove idiom". 167 | 168 | for(auto& e : entities) e->update(mFT); 169 | } 170 | 171 | void draw() 172 | { 173 | for(auto& e : entities) e->draw(); 174 | } 175 | 176 | Entity& addEntity() 177 | { 178 | Entity* e{new Entity{}}; 179 | std::unique_ptr uPtr{e}; 180 | entities.emplace_back(std::move(uPtr)); 181 | return *e; 182 | } 183 | }; 184 | 185 | // Now that we implemented our small (and naive) component-based 186 | // entity system, let's test it before going back to our arkanoid 187 | // example. 188 | } 189 | 190 | // The following example will demonstrate how an entity can be created 191 | // by putting togheter different components. In this case, we have 192 | // a `CounterComponent` component which increases an internal `counter` 193 | // float value every update, and a `KillComponent` that, after being 194 | // constructed with a reference to a `CounterComponent`, destroys the 195 | // parent entity when the `counter` float value reaches 100. 196 | 197 | using namespace CompositionArkanoid; 198 | 199 | struct CounterComponent : Component 200 | { 201 | float counter; 202 | void update(float mFT) override 203 | { 204 | counter += mFT; 205 | std::cout << counter << std::endl; 206 | } 207 | }; 208 | 209 | struct KillComponent : Component 210 | { 211 | CounterComponent& cCounter; 212 | 213 | KillComponent(CounterComponent& mCounterComponent) 214 | : cCounter(mCounterComponent) 215 | { 216 | } 217 | 218 | void update(float mFT) override 219 | { 220 | if(cCounter.counter >= 100) entity->destroy(); 221 | } 222 | }; 223 | 224 | int main() 225 | { 226 | Manager manager; 227 | 228 | // We create an entity and get a reference to it: 229 | auto& entity(manager.addEntity()); 230 | 231 | // We create components: 232 | auto& cCounter(entity.addComponent()); 233 | auto& cKill(entity.addComponent(cCounter)); 234 | 235 | // And here we simulate a game loop: 236 | for(auto i(0u); i < 1000; ++i) 237 | { 238 | manager.update(1.f); 239 | manager.draw(); 240 | } 241 | } 242 | 243 | // The above example works, but there is one major issue: 244 | // `CounterComponent` and `KillComponent` and tightly coupled. 245 | 246 | // We need to figure out an efficient way to check if a certain 247 | // entity has a certain component type, and, if so, retrieve 248 | // a reference. 249 | 250 | // In this way, we can avoid passing a reference in 251 | // `KillComponent`'s constructor, and also have a way of 252 | // getting/checking components at runtime. 253 | 254 | // Let's see what we can do in the next code segment. -------------------------------------------------------------------------------- /DiveIntoC++11/2_Arkanoid/p5.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 Vittorio Romeo 2 | // License: Academic Free License ("AFL") v. 3.0 3 | // AFL License page: http://opensource.org/licenses/AFL-3.0 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace sf; 11 | 12 | using FrameTime = float; 13 | 14 | constexpr int windowWidth{800}, windowHeight{600}; 15 | constexpr float ballRadius{10.f}, ballVelocity{0.8f}; 16 | constexpr float paddleWidth{60.f}, paddleHeight{20.f}, paddleVelocity{0.6f}; 17 | constexpr float blockWidth{60.f}, blockHeight{20.f}; 18 | constexpr int countBlocksX{11}, countBlocksY{4}; 19 | constexpr float ftStep{1.f}, ftSlice{1.f}; 20 | 21 | struct Ball 22 | { 23 | CircleShape shape; 24 | Vector2f velocity{-ballVelocity, -ballVelocity}; 25 | 26 | Ball(float mX, float mY) 27 | { 28 | shape.setPosition(mX, mY); 29 | shape.setRadius(ballRadius); 30 | shape.setFillColor(Color::Red); 31 | shape.setOrigin(ballRadius, ballRadius); 32 | } 33 | 34 | void update(FrameTime mFT) 35 | { 36 | shape.move(velocity * mFT); 37 | 38 | if(left() < 0) 39 | velocity.x = ballVelocity; 40 | else if(right() > windowWidth) 41 | velocity.x = -ballVelocity; 42 | 43 | if(top() < 0) 44 | velocity.y = ballVelocity; 45 | else if(bottom() > windowHeight) 46 | velocity.y = -ballVelocity; 47 | } 48 | 49 | float x() const noexcept { return shape.getPosition().x; } 50 | float y() const noexcept { return shape.getPosition().y; } 51 | float left() const noexcept { return x() - shape.getRadius(); } 52 | float right() const noexcept { return x() + shape.getRadius(); } 53 | float top() const noexcept { return y() - shape.getRadius(); } 54 | float bottom() const noexcept { return y() + shape.getRadius(); } 55 | }; 56 | 57 | struct Rectangle 58 | { 59 | RectangleShape shape; 60 | float x() const noexcept { return shape.getPosition().x; } 61 | float y() const noexcept { return shape.getPosition().y; } 62 | float left() const noexcept { return x() - shape.getSize().x / 2.f; } 63 | float right() const noexcept { return x() + shape.getSize().x / 2.f; } 64 | float top() const noexcept { return y() - shape.getSize().y / 2.f; } 65 | float bottom() const noexcept { return y() + shape.getSize().y / 2.f; } 66 | }; 67 | 68 | struct Paddle : public Rectangle 69 | { 70 | Vector2f velocity; 71 | 72 | Paddle(float mX, float mY) 73 | { 74 | shape.setPosition(mX, mY); 75 | shape.setSize({paddleWidth, paddleHeight}); 76 | shape.setFillColor(Color::Red); 77 | shape.setOrigin(paddleWidth / 2.f, paddleHeight / 2.f); 78 | } 79 | 80 | void update(FrameTime mFT) 81 | { 82 | shape.move(velocity * mFT); 83 | 84 | if(Keyboard::isKeyPressed(Keyboard::Key::Left) && left() > 0) 85 | velocity.x = -paddleVelocity; 86 | else if(Keyboard::isKeyPressed(Keyboard::Key::Right) && 87 | right() < windowWidth) 88 | velocity.x = paddleVelocity; 89 | else 90 | velocity.x = 0; 91 | } 92 | }; 93 | 94 | struct Brick : public Rectangle 95 | { 96 | bool destroyed{false}; 97 | 98 | Brick(float mX, float mY) 99 | { 100 | shape.setPosition(mX, mY); 101 | shape.setSize({blockWidth, blockHeight}); 102 | shape.setFillColor(Color::Yellow); 103 | shape.setOrigin(blockWidth / 2.f, blockHeight / 2.f); 104 | } 105 | }; 106 | 107 | template 108 | bool isIntersecting(T1& mA, T2& mB) noexcept 109 | { 110 | return mA.right() >= mB.left() && mA.left() <= mB.right() && 111 | mA.bottom() >= mB.top() && mA.top() <= mB.bottom(); 112 | } 113 | 114 | void testCollision(Paddle& mPaddle, Ball& mBall) noexcept 115 | { 116 | if(!isIntersecting(mPaddle, mBall)) return; 117 | 118 | mBall.velocity.y = -ballVelocity; 119 | if(mBall.x() < mPaddle.x()) 120 | mBall.velocity.x = -ballVelocity; 121 | else 122 | mBall.velocity.x = ballVelocity; 123 | } 124 | 125 | void testCollision(Brick& mBrick, Ball& mBall) noexcept 126 | { 127 | if(!isIntersecting(mBrick, mBall)) return; 128 | mBrick.destroyed = true; 129 | 130 | float overlapLeft{mBall.right() - mBrick.left()}; 131 | float overlapRight{mBrick.right() - mBall.left()}; 132 | float overlapTop{mBall.bottom() - mBrick.top()}; 133 | float overlapBottom{mBrick.bottom() - mBall.top()}; 134 | 135 | bool ballFromLeft(abs(overlapLeft) < abs(overlapRight)); 136 | bool ballFromTop(abs(overlapTop) < abs(overlapBottom)); 137 | 138 | float minOverlapX{ballFromLeft ? overlapLeft : overlapRight}; 139 | float minOverlapY{ballFromTop ? overlapTop : overlapBottom}; 140 | 141 | if(abs(minOverlapX) < abs(minOverlapY)) 142 | mBall.velocity.x = ballFromLeft ? -ballVelocity : ballVelocity; 143 | else 144 | mBall.velocity.y = ballFromTop ? -ballVelocity : ballVelocity; 145 | } 146 | 147 | // Let's create a class for our game. 148 | struct Game 149 | { 150 | // These members are related to the control of the game. 151 | RenderWindow window{{windowWidth, windowHeight}, "Arkanoid - 14"}; 152 | FrameTime lastFt{0.f}, currentSlice{0.f}; 153 | bool running{false}; 154 | 155 | // These members are game entities. 156 | Ball ball{windowWidth / 2, windowHeight / 2}; 157 | Paddle paddle{windowWidth / 2, windowHeight - 50}; 158 | vector bricks; 159 | 160 | Game() 161 | { 162 | // On construction, we initialize the window and create 163 | // the brick wall. On a more serious implementation, it 164 | // would be a good idea to have a `newGame()` method that 165 | // can be called at any time to restart the game. 166 | 167 | window.setFramerateLimit(240); 168 | 169 | for(int iX{0}; iX < countBlocksX; ++iX) 170 | for(int iY{0}; iY < countBlocksY; ++iY) 171 | bricks.emplace_back((iX + 1) * (blockWidth + 3) + 22, 172 | (iY + 2) * (blockHeight + 3)); 173 | } 174 | 175 | void run() 176 | { 177 | // The `run()` method is used to start the game and 178 | // contains the game loop. 179 | 180 | // Instead of using `break` to stop the game, we will 181 | // use a boolean variable, `running`. 182 | running = true; 183 | 184 | while(running) 185 | { 186 | auto timePoint1(chrono::high_resolution_clock::now()); 187 | 188 | window.clear(Color::Black); 189 | 190 | // It's not a bad idea to use methods to make the 191 | // code more organized. In this case, I've divided 192 | // the game loop in "input", "update" and "draw" 193 | // phases. It's one of many possible ways of tidying up 194 | // the code :) 195 | inputPhase(); 196 | updatePhase(); 197 | drawPhase(); 198 | 199 | auto timePoint2(chrono::high_resolution_clock::now()); 200 | auto elapsedTime(timePoint2 - timePoint1); 201 | FrameTime ft{chrono::duration_cast>( 202 | elapsedTime) 203 | .count()}; 204 | 205 | lastFt = ft; 206 | 207 | auto ftSeconds(ft / 1000.f); 208 | auto fps(1.f / ftSeconds); 209 | 210 | window.setTitle( 211 | "FT: " + to_string(ft) + "\tFPS: " + to_string(fps)); 212 | } 213 | } 214 | 215 | void inputPhase() 216 | { 217 | Event event; 218 | while(window.pollEvent(event)) 219 | { 220 | if(event.type == Event::Closed) 221 | { 222 | window.close(); 223 | break; 224 | } 225 | } 226 | 227 | if(Keyboard::isKeyPressed(Keyboard::Key::Escape)) running = false; 228 | } 229 | 230 | void updatePhase() 231 | { 232 | currentSlice += lastFt; 233 | for(; currentSlice >= ftSlice; currentSlice -= ftSlice) 234 | { 235 | ball.update(ftStep); 236 | paddle.update(ftStep); 237 | 238 | testCollision(paddle, ball); 239 | for(auto& brick : bricks) testCollision(brick, ball); 240 | bricks.erase(remove_if(begin(bricks), end(bricks), 241 | [](const Brick& mBrick) 242 | { 243 | return mBrick.destroyed; 244 | }), 245 | end(bricks)); 246 | } 247 | } 248 | 249 | void drawPhase() 250 | { 251 | window.draw(ball.shape); 252 | window.draw(paddle.shape); 253 | for(auto& brick : bricks) window.draw(brick.shape); 254 | window.display(); 255 | } 256 | }; 257 | 258 | int main() 259 | { 260 | // Let's create a temporary variable of type `Game` and 261 | // run it, to start our game. 262 | Game{}.run(); 263 | return 0; 264 | } 265 | 266 | // Thanks for watching! 267 | 268 | // You can fork/look at the full source code on my GitHub page: 269 | // http://github.com/SuperV1234/ 270 | 271 | // Check out my website for more tutorials and to personally 272 | // get in touch with me. 273 | 274 | // http://vittorioromeo.info --------------------------------------------------------------------------------