├── .clang-format ├── .editorconfig ├── README.md ├── assignment ├── README.md ├── images │ ├── behaviour_tree_example.png │ ├── ccotm_hud_bars.png │ ├── ccotm_items.png │ ├── ccotm_kickboots_banner.png │ ├── ccotm_map.jpg │ ├── ccotm_nathen_sprite_map.png │ ├── ccotm_pause_menu.png │ ├── game_loop.png │ ├── pokemon_map.png │ ├── sprite_map.png │ └── unity_inspector.png ├── metroidvania_specification.md └── rpg_specification.md ├── cmake-example ├── .gitignore ├── CMakeLists.txt ├── apps │ └── myapp.cpp ├── cmake │ └── compile_flags.cmake ├── include │ └── mylib │ │ └── foo.hpp ├── src │ └── foo.cpp ├── tests │ ├── foo_test.cpp │ └── test_runner.cpp └── vendor │ └── Catch2 │ ├── CMakeLists.txt │ └── catch.hpp ├── exercises ├── .gitignore ├── README.md ├── images │ ├── task03_dependencies.dot │ └── task03_dependencies.png ├── task01 │ ├── hello.cpp │ └── hello_boost.cpp ├── task02 │ └── vec.cpp ├── task03 │ ├── app │ │ └── app.cpp │ ├── libBar │ │ ├── bar.cpp │ │ └── bar.hpp │ ├── libBaz │ │ ├── baz.cpp │ │ └── baz.hpp │ └── libFoo │ │ ├── foo.cpp │ │ └── foo.hpp ├── task04 │ └── iterations.cpp ├── task05 │ └── strange.cpp ├── task16 │ └── plugin.hpp └── task22 │ ├── CMakeLists.txt │ ├── executable.cpp │ └── libFoo │ ├── foo.cpp │ └── foo.hpp ├── images └── banner.gif └── topic ├── game_ai ├── images │ ├── behavior_tree_example.png │ └── ooda_loop.png └── notes.md └── little_features ├── .gitignore ├── CMakeLists.txt ├── conversion_operator.cpp ├── empty_struct.cpp ├── execution_policy.cpp ├── object_slicing.cpp ├── placement_new.cpp └── pointer_to_member.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | ColumnLimit: 120 4 | IndentWidth: 4 5 | TabWidth: 4 6 | UseTab: ForIndentation 7 | AlignEscapedNewlines: DontAlign 8 | AllowShortFunctionsOnASingleLine: Inline 9 | AlwaysBreakTemplateDeclarations: true 10 | BreakBeforeBinaryOperators: NonAssignment 11 | BreakBeforeBraces: Custom 12 | BraceWrapping: 13 | AfterFunction: true 14 | ... 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = tab 9 | indent_size = 8 10 | 11 | [*.md] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.{hpp,cpp,h,c}] 16 | indent_style = tab 17 | indent_size = 4 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced C++ Programming - Practical Part 2 | 3 | This repository contains practical exercises to accompany [the lecture](https://github.com/PeterTh/uibk_cpp). 4 | Based on Material by Alex Hirsch. 5 | 6 | ## Grading 7 | 8 | Grading for the entire module is based on 9 | 10 | 1. An exam covering the lecture. 11 | 2. Completion of [the assignment](assignment). 12 | 3. Weekly participation in the presentation of assignment progress, and its discussion. 13 | 14 | ## Exercises 15 | 16 | These exercises support the lecture and explore certain auxiliary topics relevant to C++. 17 | You are encouraged to solve (or at least think about) these exercises. 18 | You will not submit a solution as these exercises are optional, but feel free to ask questions when running into problems. 19 | 20 | ## C++ Related Questions 21 | 22 | If you want to ask a specific question, please break it down to a minimal example. 23 | State your question and observations as comments in the code. 24 | You can use [Compiler Explorer](https://compiler-explorer.com/) and its short link functionality under *Share*. 25 | 26 | ## Essential References 27 | 28 | - [C++ 23 Standard (Draft)](https://timsong-cpp.github.io/cppwp/n4950/) 29 | - [C++ Core Guidelines](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) 30 | - [cppreference.com](http://en.cppreference.com) 31 | 32 | ## Useful References / Tools 33 | 34 | - [Awesome Modern C++](https://github.com/rigtorp/awesome-modern-cpp) 35 | - [Boost](http://www.boost.org) 36 | - [C++ 20 Standard (Draft)](https://isocpp.org/files/papers/N4860.pdf) 37 | - [Clang Sanitizers](https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation) 38 | - [Clang Tidy](http://clang.llvm.org/extra/clang-tidy/) 39 | - [CMake](https://cmake.org) 40 | - [Curated List of Awesome C/C++ Stuff](https://github.com/fffaraz/awesome-cpp) 41 | - [DevDocs](https://devdocs.io/) 42 | - [Doxygen](http://www.stack.nl/~dimitri/doxygen) 43 | - [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) 44 | - [Catch2](https://github.com/catchorg/Catch2) 45 | - [Modern C++ Features](https://github.com/AnthonyCalandra/modern-cpp-features) 46 | - [Modern CMake](https://cliutils.gitlab.io/modern-cmake/) 47 | - [Online C++ Demangler](https://demangler.com) 48 | - [STL Containers](http://en.cppreference.com/w/cpp/container) 49 | -------------------------------------------------------------------------------- /assignment/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 2 | 3 | *due on February 13th, 2025* 4 | 5 | For this assignment you can either pick one of the two provided specifications or come up with your own. 6 | Yes, you are free to come up with a topic for this assignment. 7 | However, doing so requires you to put together a specification similar to the ones provided and get my approval. 8 | The specification doesn't have to be bulletproof. 9 | But it must contain a bullet list of goals (with points to score) at the bottom which can be checked / evaluated. 10 | You can also adjust one of the provided specifications. 11 | 12 | You are allowed to work in teams, and the team size has to correspond to the amount of work (features and their complexity) of the topic. 13 | For the provided specifications the recommended team size is 3. 14 | 15 | You are allowed to use: 16 | - C++ standard library (C++17 standard) 17 | - C standard library (as fallback) 18 | - [Boost](https://www.boost.org/) 19 | - [SDL](https://www.libsdl.org/) 20 | - [GLFW](https://www.glfw.org/) 21 | - [GLM](https://glm.g-truc.net/) 22 | - [SFML](https://www.sfml-dev.org/) 23 | - [Vulkan](https://www.khronos.org/vulkan/) / [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp) 24 | - [Qt](https://www.qt.io/) 25 | - [ImGui](https://github.com/ocornut/imgui) 26 | - [ncurses](https://invisible-island.net/ncurses/) 27 | - [OpenAL](https://openal.org/) 28 | - [nlohmann/json](https://github.com/nlohmann/json) / [RapidJSON](https://rapidjson.org/) 29 | - [RapidYAML](https://github.com/biojppm/rapidyaml) 30 | - [Assimp](https://www.assimp.org/) 31 | - [stb-image](https://github.com/nothings/stb/blob/master/stb_image.h) 32 | - [Ogg](https://xiph.org/ogg/) / [Vorbis](https://xiph.org/vorbis/) / [Opus](https://opus-codec.org/) 33 | - [Tiled](https://www.mapeditor.org/) / [tileson](https://github.com/SSBMTonberry/tileson) 34 | - [OpenSSL](https://www.openssl.org/) 35 | - [SQLite](https://www.sqlite.org/) 36 | - [Catch2](https://github.com/DigitalInBlue/Celero) 37 | - [Google Test](https://github.com/google/googletest) 38 | - [Google Benchmark](https://github.com/google/benchmark) 39 | - [spdlog](https://github.com/gabime/spdlog) / [plog](https://github.com/SergiusTheBest/plog) 40 | - [fmt](https://github.com/fmtlib/fmt) 41 | - [AngelScript](https://www.angelcode.com/angelscript/) 42 | - [Lua](http://www.lua.org/) / [sol2](https://github.com/ThePhD/sol2) 43 | - [cereal](https://github.com/USCiLab/cereal) 44 | - [protobuf](https://github.com/protocolbuffers/protobuf) 45 | - [Font Chef](https://github.com/mobius3/font-chef) 46 | 47 | Feel free to ask me about other libraries / tools. 48 | 49 | Your application should work either on Linux (64-Bit) or Windows (64-Bit), preferably both unless there is a specific reason why it cannot be cross-platform. 50 | For Linux, assume a recent version of Ubuntu Desktop and that the required dependencies are installed via the system's package manager. 51 | Use the corresponding CMake `find_package` mechanism to find them. 52 | Prefer [`pkgconf`](https://cmake.org/cmake/help/latest/module/FindPkgConfig.html) over custom *FindPackage* scripts. 53 | For Windows you can simply ship pre-built libraries that are picked up by CMake automatically. 54 | 55 | Some fixed constraints (you **must** adhere to these): 56 | * Use git for version control. 57 | * Use [CMake](https://cmake.org/) as build system. 58 | * Use [ClangFormat](https://clgitang.llvm.org/docs/ClangFormat.html) to automatically format your code using the provided [`.clang-format`](../.clang-format) configuration. 59 | 60 | ## Team Composition + Specification 61 | 62 | Send me an email with your team composition and your specification as early as possible. 63 | Use the following link: 64 | 65 | 📧 [send email](mailto:peter.thoman@uibk.ac.at?subject=703333%20-%20Assignment%20Team%20Composition) 66 | 67 | ## Submission 68 | 69 | ### Packaging 70 | 71 | Please use the `git archive` command to package your project. 72 | Use the following command, replacing `XX` with your team number (with leading zero, e.g. `02`). 73 | 74 | git archive --prefix=team_XX_assignment/ --format=zip HEAD > team_XX_assignment.zip 75 | 76 | If your archive is too large to send by email, you can host it somewhere and link it, but you have to 77 | ensure that the link is accessible and persistent. 78 | 79 | ### Build Test Submission 80 | 81 | Submit a non-final version of your project around 2 weeks before the final deadline. 82 | I will verify that your project builds on my test system(s) and let you know if I run into any issues. 83 | Use the following link, again replacing `XX` with your team number. 84 | 85 | 📧 [send email](mailto:peter.thoman@uibk.ac.at?subject=703333%20-%20Team%20XX%20Assignment%20Build%20Test) 86 | 87 | 88 | ### Final Submission 89 | 90 | Verify that the packaged version is working. 91 | Use the following link, again replacing `XX` with your team number. 92 | 93 | Include your specification in the package, even if it was initially provided with the course material. 94 | 95 | 📧 [send email](mailto:peter.thoman@uibk.ac.at?subject=703333%20-%20Team%20XX%20Assignment%20Final) 96 | -------------------------------------------------------------------------------- /assignment/images/behaviour_tree_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/behaviour_tree_example.png -------------------------------------------------------------------------------- /assignment/images/ccotm_hud_bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/ccotm_hud_bars.png -------------------------------------------------------------------------------- /assignment/images/ccotm_items.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/ccotm_items.png -------------------------------------------------------------------------------- /assignment/images/ccotm_kickboots_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/ccotm_kickboots_banner.png -------------------------------------------------------------------------------- /assignment/images/ccotm_map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/ccotm_map.jpg -------------------------------------------------------------------------------- /assignment/images/ccotm_nathen_sprite_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/ccotm_nathen_sprite_map.png -------------------------------------------------------------------------------- /assignment/images/ccotm_pause_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/ccotm_pause_menu.png -------------------------------------------------------------------------------- /assignment/images/game_loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/game_loop.png -------------------------------------------------------------------------------- /assignment/images/pokemon_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/pokemon_map.png -------------------------------------------------------------------------------- /assignment/images/sprite_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/sprite_map.png -------------------------------------------------------------------------------- /assignment/images/unity_inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/assignment/images/unity_inspector.png -------------------------------------------------------------------------------- /assignment/metroidvania_specification.md: -------------------------------------------------------------------------------- 1 | # Metroidvania Specification 2 | 3 | As the title already suggests, the ultimate goal is to create a small, *[Metroidvania](https://en.wikipedia.org/wiki/Metroidvania)-like* side-scroller. 4 | If you are not familiar with games falling into the Metroidvania genre – or side-scrollers in general – I recommend having a look at these titles: 5 | 6 | - [Castlevania: Symphony of the Night](https://en.wikipedia.org/wiki/Castlevania:_Symphony_of_the_Night) 7 | - [Castlevania: Circle of the Moon](https://en.wikipedia.org/wiki/Castlevania:_Circle_of_the_Moon) 8 | - [Super Metroid](https://en.wikipedia.org/wiki/Super_Metroid) 9 | - [Ori and the Blind Forest](https://www.gog.com/game/ori_and_the_blind_forest_definitive_edition) 10 | - [Dust: An Elysian Tail](https://www.gog.com/game/dust_an_elysian_tail) 11 | - [Salt and Sanctuary](https://store.steampowered.com/app/283640/Salt_and_Sanctuary/) 12 | - [Owlboy](https://www.gog.com/game/owlboy) 13 | - [Guacamelee](https://www.gog.com/game/guacamelee_gold_edition) 14 | - [Hollow Knight](https://www.gog.com/game/hollow_knight) 15 | - [Iconoclasts](https://www.gog.com/game/iconoclasts) 16 | - [Dead Cells](https://www.gog.com/game/dead_cells) 17 | - [**Timespinner**](https://store.steampowered.com/app/368620/Timespinner/) 18 | 19 | After playing some gam-… ehh… doing some research, you should be familiar with the core game concepts and mechanics of the genre. 20 | 21 | Before moving on, I want you to make up your minds about the setting your game takes place in. 22 | While a medieval fantasy world with demons and vampires sounds straight forward, don't overlook other possibilities like cyberpunk, steampunk, post apocalypse and so on. 23 | Determine upfront whether you are going for a more serious or a more humorous tone – whether things should be realistic or over the top with crazy anime effects everywhere. 24 | 25 | Now that a certain setting has been established, continue the read and imagine what the equivalences to my examples would be in your game. 26 | These equivalences can often be derived directly from the chosen setting. 27 | However, you might want to throw in a twist here and there to surprise the player and keep her engaged. 28 | 29 | Most of the examples presented in the following sections are taken from *Castlevania: Circle of the Moon*. 30 | This way I can stick to a common setting throughout. 31 | 32 | ### Assets 33 | 34 | That's nice and stuff, but what about all the assets (eg sprites, music, sound effects) that are required for such a game? 35 | 36 | Well, you don't have to look far. 37 | As this is still about learning C++, I don't expect you to spend your precious time on creating all of them by hand. 38 | There are megatons of assets for free- / non-commercial- / educational-use available online. 39 | I am pretty sure you'll find something among them which suits your setting. 40 | Maybe some slight modifications are needed here and there, but nothing that should distract you from learning C++. 41 | 42 | For various types of assets – sound effects especially – there are also generators available which can be tweaked for unique results. 43 | Pick your poison! 44 | 45 | ## Building the World 46 | 47 | Metroidvania style games are typically semi-open world. 48 | While the player can explore the game world freely, certain areas are gated off at the start. 49 | 50 | Let's take a look at this map. 51 | 52 | ![Castlevania: Circle of the Moon Map](images/ccotm_map.jpg) 53 | 54 | *source: http://castlevania.wikia.com/* 55 | 56 | It displays the whole layout of the castle – each wing (aka section or area) coloured differently. 57 | Each of the polygons represents a room, which in turn is connected to other rooms, and is associated with one of the wings – here indicated by its colour. 58 | The small, red rooms are save points; while the small, white rooms are teleporters. 59 | 60 | Each wing is unique in the architecture of the rooms, the enemies encountered, the background music, the items obtained, and so on and so forth. 61 | 62 | The player needs to face off the vicious boss of a wing in order to progress. 63 | After defeating such a boss, a new item or ability is acquired which grants her passage to other, previously inaccessible, wings of the castle. 64 | 65 | Here is an example: 66 | 67 | ![Castlevania: Circle of the Moon Kickboots Banner](images/ccotm_kickboots_banner.png) 68 | 69 | *source: http://castlevania.wikia.com/* 70 | 71 | The final boss of the *Machine Tower* is the *Iron Golem* (left). 72 | From it, the player obtains the *Kick Boots* (centre) which enables jumping off walls to gain more height (right). 73 | With them, the *Chapel Tower* can now be accessed. 74 | 75 | ### Implementation 76 | 77 | A room essentially consists of a 2D array of tiles. 78 | Together, the tiles build the floor, ceiling, and walls of the room. 79 | Platforms are added to enable vertical traversal. 80 | For additional depth, an image is rendered behind the layer of tiles, which is sometimes panned as the camera moves – similar to a skybox in 3D games. 81 | 82 | Game entities (eg items, enemies, doors) can be placed via XY coordinates. 83 | The doors are particularly important as they are responsible for interconnecting rooms. 84 | 85 | The layout of each room in your game should be fully determined by a single file – preferably human-readable. 86 | It *could* look like this: 87 | 88 | ``` 89 | MAP Room01 90 | BACKGROUND assets/images/background/library.png 91 | MUSIC assets/music/ancient_tomb.ogg 92 | TILES 93 | . = none 94 | w = Wall01 95 | c = Ceiling01 96 | f = Floor01 97 | p = Platform01 98 | LAYOUT 99 | wcccccccccccccccccw 100 | w.................w 101 | w.................w 102 | w..........ppp....w 103 | w.................w 104 | w.................w 105 | fffffffffffffffffff 106 | ENTITIES 107 | ( 2, 5) Player() 108 | ( 8, 5) Enemy_BookTossingRedneck() 109 | (12, 2) SmallHealthPotion() 110 | (18, 5) Door01_right(Room01_exit, Room02_entry) 111 | ``` 112 | 113 | Tiles and game entities are defined elsewhere, yet some of them require arguments for their instantiation. 114 | In this example, the door gets a designation (`Room01_exit`) and is connected to some other door (`Room02_entry`). 115 | 116 | ## Hearing the World 117 | 118 | Commonly, the music playing is either determined by the room or wing. 119 | The selected tune is repeated until a room / wing with different background music is entered. 120 | 121 | A more advanced technique is to use different variations of the same background music and blend them together depending on the situation. 122 | 123 | For instance: 124 | the player enters a room, no enemy in sight; 125 | a *softer* version of the tune is playing. 126 | As the player progresses through the room, enemies awake and engage the player; 127 | the tune becomes more dramatic. 128 | One of the enemies lands a devastating blow, depleting the player's health bar almost entirely; 129 | a *high tension* version fades in – additionally the player's low health causes an annoying alarm to be played. 130 | 131 | ### Implementation 132 | 133 | SDL_mixer is probably all you need for this. 134 | It provides separate sound channels, each with its own volume setting. 135 | 136 | One simple way to blend variations of the same track is by playing all of them on separate channels and adjusting the volume accordingly. 137 | 138 | Obviously, sound effects are played on different channels than background music so they do not interfere. 139 | 140 | ## Entities of the Game 141 | 142 | Having rooms and wings would be a bit pointless without anything that interacts with it, wouldn't it? 143 | 144 | All of the objects in your game are referred to as entities. 145 | Entities interact with the game world as well as with each other. 146 | Their complexity varies greatly, from a simple key that can be picked up to unlock doors, to AI controlled enemies. 147 | 148 | Most of them are visible (rendered) and have collision so they can be interacted with. 149 | Others may be almost unnoticeable by the player – like a point in 2D space which just emits a screeching noise. 150 | 151 | Typically, entities have a position, rotation, scale, and a type; the latter may come with additional state and ways of interaction. 152 | A more complex alternative for typing entities is by using an entity component system (see below). 153 | 154 | ### Rendering 155 | 156 | Displaying (static) entities is straight forward. 157 | 158 | ![Castlevania: Circle of the Moon Items](images/ccotm_items.png) 159 | 160 | *source: https://strategywiki.org/wiki/Castlevania:_Circle_of_the_Moon* 161 | 162 | [Sprites](https://en.wikipedia.org/wiki/Sprite_(computer_graphics)) are essentially bitmap images with transparent background. 163 | The position, rotation, and scale for rendering the sprite is determined by the position, rotation, and scale of the entity respectively. 164 | Offsets may be added, but apart from that, that's it. 165 | 166 | ### Animation 167 | 168 | More dynamic entities, like the player character (PC) or enemies, often feature animations. 169 | These start with simple idle and walking animations, but can also turn into breathtaking combat and dodging choreographies. 170 | 171 | ![Castlevania: Circle of the Moon Nathen Sprite Map](images/ccotm_nathen_sprite_map.png) 172 | 173 | *modified: removed middle part for brevity* 174 | 175 | The key idea is to use multiple sprites instead of just one. 176 | Each animation frame is a new sprite. 177 | When an animation is played, the related sprites are displayed one after another to simulate motion. 178 | 179 | Multiple sprites are combined and put into the same image, which is then called a *sprite map*. 180 | The rendering API allows us to select which part of the image to use for rendering. 181 | A simple rectangle selection is used to pick the sprite for the current frame. 182 | 183 | #### Implementation 184 | 185 | Composing a sprite map is trivial, yet we are still missing some information. 186 | Namely, where exactly the sprites are located in the sprite map, which animation they belong to, and how long each sprite should be displayed before it is swapped out. 187 | 188 | This can be solved via a simple text file. 189 | Like with the room layout, determine the format yourself, preferably human-readable. 190 | A hypothetical example is provided: 191 | 192 | ``` 193 | ANIMATION Enemy_BookTossingRedneck 194 | SPRITES assets/images/sprites/enemies/book_tossing_redneck.png 195 | ITEMS 196 | idle 0ms ( 0, 0) (16, 16) 197 | drinking 0ms (16, 0) (32, 16) 198 | walking 500ms ( 0, 16) (16, 32) 199 | (16, 16) (32, 32) 200 | (32, 16) (48, 32) 201 | (48, 16) (64, 32) 202 | ``` 203 | 204 | First we state to which entity the animation file belongs and where the corresponding sprite map can be found. 205 | It consists of 6 sprites, the first two are displayed when the entity is idling or drinking respectively. 206 | As both only consist of a single sprite, the timing is irrelevant and therefore set to zero. 207 | For the walking animation, we use 4 sprites and state that one period takes 500 ms, hence each sprite is displayed 125 ms before getting swapped out. 208 | After the forth sprite has been displayed, the animation loops, starting over with the first sprite. 209 | 210 | ### Collision 211 | 212 | So far we can display the game world and entities – even with animations – yet we still lack basic interaction. 213 | This is where collision comes in. 214 | Whenever one entity collides with the room layout (walls, floors, platforms etc) or another entity, this needs to be recognised so the associated logic can be triggered. 215 | 216 | If the player runs into a wall, the wall should of course stop the player and prevent her from running out of bounds. 217 | Similarly, an enemy projectile hitting the player should cause a hit animation to be played along with a sound effect and decreasing the player's health. 218 | 219 | #### Implementation 220 | 221 | There are various different ways one can implement collision for 2D side-scrollers. 222 | All of them come with their own benefits and drawbacks. 223 | I'll leave things for you to decide. 224 | 225 | ## Stats 226 | 227 | Sometimes Metroidvania-like games implement mechanics originating from role-playing games (RPGs). 228 | 229 | A prime example is the introduction of stats. 230 | Rather than having fixed values for weapon damage, health, damage resistance etc; the actual values are computed by taking a base-value and scaling it. 231 | This scale factor is determined by the entity's stats (aka attributes). 232 | 233 | These stats change over the course of the game. 234 | Typically, the player character's stats are improved by earning experience points (XP) and levelling up. 235 | Experience is often earned by slaying enemies, completing quests, and alike. 236 | 237 | Even further, entity stats can be influenced by items (eg *speed boost elixir*), gear (eg *boots of strength*), spells (eg *improved fire resistance*), conditions (eg *cursed*), and the list goes on and on and on. 238 | 239 | ## AI 240 | 241 | Enemies just standing around, doin' nothing ain't particularly interesting. 242 | 243 | Even very simple and deterministic AI can be sufficient. 244 | Like: 245 | 246 | ```cpp 247 | void do_something() 248 | { 249 | static bool moved_forward = true; 250 | 251 | if (player_in_range()) { 252 | attack_player(); 253 | return; 254 | } 255 | 256 | if (moved_forward) { 257 | move_backward(); 258 | } else { 259 | move_forward(); 260 | } 261 | 262 | moved_forward = !moved_forward; 263 | } 264 | ``` 265 | 266 | Additionally, one can add randomness to make enemies less predictable. 267 | For instance: one out of ten times, use a different attack which is faster and does more damage. 268 | 269 | Leverage randomness and other factors to create interesting boss AIs. 270 | The distance to the player could determine which kind of attack the boss uses. 271 | The player's health could dictate how aggressive the boss becomes. 272 | The number of possibilities is endless. 273 | 274 | ### Implementation 275 | 276 | For simplicity, the AI can be hardcoded in the logic of an enemy. 277 | 278 | Alternatively, you may want to look into [behaviour trees](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)). 279 | 280 | ![Behaviour Tree Example](images/behaviour_tree_example.png) 281 | 282 | ## HUD and Menus 283 | 284 | The head-up display (HUD) is a crucial component as it conveys important information to the player. 285 | In Castlevania, the health- and mana-bar can be found in the top-left corner of the screen, along with the secondary weapon, and ammo. 286 | 287 | ![Castlevania Circle of the Moon Hud Bars](images/ccotm_hud_bars.png) 288 | 289 | *source: http://castlevania.wikia.com* 290 | 291 | Not all aspects of the game can be done (reasonably) by moving the player character around. 292 | Certain tasks require a menu system. 293 | This starts at the main menu of the game, along with the pause and option menus. 294 | 295 | ![Castlevania Circle of the Moon Pause Menu](images/ccotm_pause_menu.png) 296 | 297 | *source: https://strategywiki.org/wiki/Castlevania:_Circle_of_the_Moon* 298 | 299 | The pause menu displays the players stats (health, attributes, experience etc) and provides access to various submenus – inventory management among them. 300 | 301 | ### Implementation 302 | 303 | SDL provides basic shape drawing functionality. 304 | With it, you can easily build an abstraction layer for creating rudimentary user interface (UI) components. 305 | Alternatively, you can simply use images. 306 | 307 | SDL_ttf allows you to use fonts for printing text. 308 | Please supply used fonts along with all assets. 309 | 310 | ## Entity Component System 311 | 312 | Many modern game engines use an architecture known as [entity component system (ECS)](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system). 313 | While this paradigm is probably overkill for a small project like this, you may scavenge some of its ideas. 314 | 315 | In ECS, entities are just containers for components. 316 | Components, however, determine how the associated entity behaves, how it can be interacted with, and so on. 317 | 318 | Take the player entity for a moment, in terms of the ECS architecture, it would have: 319 | 320 | - a component for rendering sprites 321 | - a component which deals with animations 322 | - a component which emits sound 323 | - a physics collider or bounding box 324 | - a component which models health 325 | - … 326 | 327 | You basically create a huge toolbox of little things (components) that can be mixed and matched to compose a full-blown entity. 328 | 329 | ![Unity Inspector](images/unity_inspector.png) 330 | 331 | The figure above shows the inspector window of the [Unity](https://unity3d.com/) editor. 332 | The inspector displays the components attached to the selected entity. 333 | Here, the entity named *Cube* consists of a *Mesh Filter*, *Box Collider*, and *Mash Renderer* component. 334 | The *Transform* is part of the entity itself and gives it its position, rotation, and scale. 335 | As can be seen from the screen-shot, each component possesses properties that can be tweaked, like, the material used for mesh rendering. 336 | 337 | This leaves us with the *system* part. 338 | Each system (audio system, physics system, rendering, *whatever you can think of*) interacts with the associated components possessed by entities. 339 | 340 | ## Main Game Loop 341 | 342 | Often, in addition to ECS, games use the *main game loop* paradigm. 343 | In combination with ECS this is commonly presented as a straightforward sequence: 344 | 345 | 1. Take the current game state. 346 | 2. Invoke all systems. 347 | Each system generates events based on the game state. 348 | For instance, one event indicates that the jump button is pressed, another that two entities collide, and so on. 349 | 3. Gather emitted events in a queue. 350 | 4. Process each event one after another, mutating the game state. 351 | Upon exhausting the queue, the next game state is reached. 352 | 5. Move on to rendering. 353 | 6. `goto 1` 354 | 355 | See also this figure, taken from *[How to Think Like a Computer Scientist](http://openbookproject.net/thinkcs/python/english3e/index.html)*: 356 | 357 | ![Main Game Loop Example](images/game_loop.png) 358 | 359 | However, what looks easy on paper can be rather challenging in reality. 360 | First, certain events may stay in conflict with each other. 361 | Like, while the player character should move upwards because of the jump button being pressed, a projectile may hit her in the exact same frame which causes knock-back. 362 | Now, do you move the player up, or away from the projectile due to knock-back? 363 | Or both? 364 | And what if a wall is there preventing the player character from being pushed in this specific direction. 365 | 366 | Even in this tiny example, the order in which events are processed matters. 367 | They may even build circular dependencies with each other. 368 | 369 | As it turns out, such an *invoke everything first, ask questions later* approach comes with a lot of problems. 370 | One way to improve upon this is by doing stuff in phases, plus ensuring that the modified game state is always valid. 371 | 372 | The whole topic of game engine architecture is huge and most decisions are trade-offs. 373 | I recommend that you try not to over-think this. 374 | Keep your cool and experiment until you find something that kinda works. 375 | 376 | ## Debugging 377 | 378 | Develop debugging utilities as early as possible. 379 | Consider the following: 380 | 381 | - log output 382 | - show bounding boxes, game entity positions, damage numbers etc; on screen 383 | - *noclip* (disable collision + player can fly) 384 | - *god mode* (player doesn't take any damage) 385 | - infinite ammo / mana / resources 386 | - spawning entities in front of the player 387 | - directly load a specific map 388 | - goto x y 389 | - … 390 | 391 | Another helpful technique is the use of fallback assets. 392 | Let's say, a certain sprite cannot be loaded for whatever reason. 393 | Instead of crashing the application with a cryptic error code, log a warning and use an *error sprite* instead. 394 | 395 | - - - 396 | 397 | ## Goals (11 Points) 398 | 399 | - (2) two consecutive areas, each: 400 | - with its own tone (architecture, sprites, music, enemies etc) 401 | - with a handful of rooms each 402 | - a boss guarding an item / ability 403 | - (2) basic movement 404 | - running left / right 405 | - jumping 406 | - interacting with game entities (doors, chests etc) 407 | - (1) save / load with dedicated save points (rooms) 408 | - (1) one or more advanced movement mechanics 409 | - eg double jump, wall jump, sliding 410 | - needs to be unlocked 411 | - (1) main combat 412 | - melee and/or ranged 413 | - hitting enemies 414 | - getting hit by enemies 415 | - (2) enemies 416 | - attack the player character as it gets in range 417 | - variants with melee attacks 418 | - variants with ranged attacks 419 | - bosses are capable of using different attacks 420 | - (1) map 421 | - shows discovered rooms and their doorways 422 | - shows discovered save points 423 | - shows where the player is currently located 424 | - (1) menus 425 | - main menu 426 | - new game 427 | - load game 428 | - exit 429 | - pause menu 430 | - shows player stats 431 | - inventory management 432 | - continue 433 | - go to main menu 434 | - game over 435 | - player dies 436 | - player defeats final boss 437 | -------------------------------------------------------------------------------- /assignment/rpg_specification.md: -------------------------------------------------------------------------------- 1 | # RPG Specification 2 | 3 | We describe a simple 2D RPG with two layers. 4 | 5 | The first layer is an interactive *overworld* which is explored by the player. 6 | There, the player can talk to other characters and manage their resources (items, equipment, …). 7 | 8 | The second layer is a turn-based combat system as typically found in JRPGs. 9 | 10 | For references, have a look at these titles / series: 11 | 12 | - Octopath Traveler 13 | - Final Fantasy 14 | - Chrono Trigger 15 | - Pokémon 16 | - Persona 17 | - Trails of Cold Steel 18 | 19 | After playing some gam-… ehh… doing some research, you should be familiar with the core game concepts and mechanics of the genre. 20 | 21 | Before moving on, I want you to make up your mind about the setting your game takes place in. 22 | While a fantasy world with magic and monsters sounds straight forward, don't overlook other possibilities like cyberpunk, steampunk, post apocalypse and so on. 23 | Determine upfront whether you are going for a more serious or a more humorous tone — whether things should be realistic or over the top with crazy anime effects everywhere. 24 | 25 | ### Assets 26 | 27 | But, what about all the assets (sprites, music, sound effects, …) that are required for such a game? 28 | 29 | Well, you don't have to look far. 30 | As this is still about learning C++, I don't expect you to spend your precious time on creating all of them by hand. 31 | There are megatons of assets for free- / non-commercial- / educational-use available online. 32 | I am pretty sure you'll find something among them which suits your setting. 33 | Maybe some slight modifications are needed here and there, but nothing that should distract you from learning C++. 34 | 35 | For various types of assets — sound effects especially — there are also generators available which can be tweaked for unique results. 36 | Pick your poison! 37 | 38 | ## Building the World 39 | 40 | As already mentioned, the first layer of your game is an overworld where the player interacts with other characters and so on. 41 | This overworld is typically composed of safe-zones (like villages or small settlements) and wilderness where enemies are encountered. 42 | Often, certain sections of the overworld remain gated off until the player has advanced far enough into the story. 43 | 44 | Here is the overworld of Pokémon Red and Blue (1996) 45 | 46 | ![Pokémon Red and Blue Map](images/pokemon_map.png) 47 | 48 | ### Overworld Implementation 49 | 50 | A 2D array of tiles can be used to describe the map itself. 51 | Entities (characters, chests, enemies, …) can be added by simply stating their coordinates. 52 | 53 | I recommend to put the world definition into a dedicated file, following a human-readable, easy-to-parse file-format. 54 | Alternatively, just put the definition in your code. 55 | 56 | ## Viewing the World 57 | 58 | [Sprites](https://en.wikipedia.org/wiki/Sprite_(computer_graphics)) are essentially bitmap images with transparent background. 59 | They are used for everything that needs to be displayed. 60 | 61 | The *transform* (position, rotation, scale) of an entity determines where and how the corresponding sprite is rendered. 62 | 63 | ### Animations 64 | 65 | Dynamic entities, like the player character (PC) or enemies, often feature animations. 66 | These start with simple idle and walking animations, but can also turn into breath-taking combat and dodging choreographies. 67 | 68 | ![Sprite Map](images/sprite_map.png) 69 | 70 | The key idea is to use multiple sprites instead of just one. 71 | Each animation frame is a new sprite. 72 | When an animation is played, the related sprites are displayed one after another to simulate motion. 73 | 74 | Multiple sprites are combined and put into the same image, which is then called a *sprite map*. 75 | The rendering API allows us to select which part of the image to use for rendering. 76 | A simple rectangle selection is used to pick the sprite for the current frame. 77 | 78 | ## Hearing the World 79 | 80 | Commonly, the background music is either determined by the section of the overworld or enemy encounter (regular battle theme, boss battle theme, …). 81 | The selected tune is repeated until the player moves to a new section or finishes the encounter. 82 | 83 | A more advanced technique would be to use different variations of the same background music and blend them together depending on the situation. Say, switch to a more tense version of your regular battle theme as soon as the player's combatants are low on health. 84 | 85 | ### Audio Implementation 86 | 87 | SDL_mixer is probably all you need for this. 88 | It provides separate sound channels, each with its own volume setting. 89 | Sound effects are played on different channels than background music so they do not interfere. 90 | 91 | If you wanna go for something fancy, with more features, have a look at [OpenAL]. 92 | 93 | ## Interacting with the World 94 | 95 | Most interaction between the player and the game world is done through message boxes and menu systems. 96 | For this, you need to implement some basic UI system. 97 | You already know how to render sprites, font support is available via [SDL_ttf]. 98 | 99 | ### Interaction Implementation 100 | 101 | Often, a scripting engine is added to the game engine for scripting the interactions between characters, combat, and so on. 102 | However, this goes beyond the scope of this project. 103 | 104 | My recommendation is to simply hard-code the logic and script of your game in C++. 105 | Think about creating your own little embedded domain-specific language (EDSL) on top of C++. 106 | Consider trying [Boost Coroutine](https://theboostcpplibraries.com/boost.coroutine) for adding asynchronous programming paradigms. 107 | 108 | ## Story 109 | 110 | Again, the focus of the side project lies on learning C++. 111 | Your RPG should feature a short story with a twist near the end. 112 | Nothing fancy, just something to keep the player interested while exploring the overworld. 113 | 114 | ## Combat AI 115 | 116 | Similar to your other game logic components, the combat AI may be hard-coded in C++. 117 | [Behaviour trees](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)) are often utilised for creating game AI. 118 | Maybe give them a try. 119 | Consider adding randomness to your decision making progress to make enemies less predictable. 120 | 121 | ## Debugging 122 | 123 | Develop debugging utilities as early as possible. 124 | Consider the following: 125 | 126 | - Log output 127 | - Show details (positions, damage numbers, decision making process, …) on screen 128 | - *God mode* (player's combatants can't die) 129 | - Infinite resources (items, spells, …) 130 | - … 131 | 132 | Another helpful technique is the use of fall-back assets. 133 | Let's say, a certain sprite cannot be loaded for whatever reason. 134 | Instead of crashing the application with a cryptic error code, log a warning and use an *error sprite* instead. 135 | 136 | --- 137 | 138 | ## Goals (11 Points) 139 | 140 | - (2) Overworld with 2 different sections: 141 | - Each with its own tone (architecture, sprites, music, enemies, …) 142 | - The second is only accessible after some story progress 143 | - Player can roam the world and interact with other entities 144 | - (1) Characters 145 | - Player can talk to other characters 146 | - (1) Resources 147 | - Player can manage acquired resources through dedicated menus 148 | - (1) Stats 149 | - Player's combatants have stats that influence the combat 150 | - Player's combatants get experience from combat, increasing their stats 151 | - Stats are influenced by equipment 152 | - (3) Combat 153 | - Turn-based 154 | - Player selects which attacks, spells, items, etc. to use on which target 155 | - Enemies use attacks, spells, items, etc. to combat the player 156 | - *Game Over* when all of player's combatants are dead 157 | - (1) Save points 158 | - Player can save her progress at specific points in the game 159 | - Saved progress is persistent across play sessions (application termination) 160 | - (1) Audio 161 | - Background music 162 | - Sound effects 163 | - (1) Main menu 164 | - New game 165 | - Load game 166 | - Exit 167 | -------------------------------------------------------------------------------- /cmake-example/.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | -------------------------------------------------------------------------------- /cmake-example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Ideally you want to require the lowest CMake version possible for your 2 | # project. But you have to test that manually, so just go with what you are 3 | # currently using and adjust as you go. 4 | cmake_minimum_required(VERSION 3.16) 5 | 6 | # The name of the core library is used as project name. 7 | project(mylib 8 | VERSION 0.1.0 9 | DESCRIPTION "An example CMake project" 10 | LANGUAGES CXX) 11 | 12 | # This allows us to include CMake modules located in `./cmake`. 13 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) 14 | 15 | # Note that we do not set *any* global properties here! 16 | 17 | # A dedicated function is used for setting common compile flags and properties. 18 | include(compile_flags) 19 | 20 | # Catch2 is used for testing. We bring along the single header version. 21 | add_subdirectory(vendor/Catch2) 22 | 23 | # We start off by declaring our main library. 24 | add_library(mylib src/foo.cpp) 25 | mylib_cxx_flags(mylib) 26 | target_include_directories(mylib PUBLIC include) 27 | 28 | # Followed by the executable using the library. Targets get prefixed with the 29 | # project name in order to avoid conflicts. We can still adjust the actual 30 | # output name for the executable. 31 | add_executable(mylib_myapp apps/myapp.cpp) 32 | mylib_cxx_flags(mylib_myapp) 33 | set_target_properties(mylib_myapp PROPERTIES OUTPUT_NAME myapp) 34 | target_link_libraries(mylib_myapp PRIVATE mylib) 35 | 36 | # We also declare a test runner, again targets are prefixed with the project 37 | # name. 38 | enable_testing() 39 | add_executable(mylib_test_runner tests/test_runner.cpp tests/foo_test.cpp) 40 | mylib_cxx_flags(mylib_test_runner) 41 | target_link_libraries(mylib_test_runner PRIVATE mylib mylib_Catch2) 42 | add_test(NAME mylib_test_runner COMMAND $) 43 | -------------------------------------------------------------------------------- /cmake-example/apps/myapp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mylib/foo.hpp" 5 | 6 | int main() 7 | { 8 | std::cout << mylib::foo() << "\n"; 9 | return EXIT_SUCCESS; 10 | } 11 | -------------------------------------------------------------------------------- /cmake-example/cmake/compile_flags.cmake: -------------------------------------------------------------------------------- 1 | # Note that functions are also prefixed with the project name to prevent 2 | # collisions. 3 | function(mylib_cxx_flags target) 4 | # Instead of setting a specific standard directly, we use the *compile 5 | # features* to require (at least) C++17 standard. 6 | target_compile_features(${target} PRIVATE cxx_std_17) 7 | 8 | # Furthermore, we turn off compiler extensions. 9 | set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) 10 | 11 | # We enable some warnings, although only if we know the compiler actually 12 | # supports these flags. The `$<...>` syntax is a *generator expression*. 13 | target_compile_options(${target} PRIVATE 14 | $<$:-Wall -Wextra -pedantic>) 15 | endfunction() 16 | -------------------------------------------------------------------------------- /cmake-example/include/mylib/foo.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MYLIB_FOO_HPP 2 | #define MYLIB_FOO_HPP 3 | 4 | namespace mylib { 5 | 6 | int foo(); 7 | 8 | } // end namespace mylib 9 | 10 | #endif // MYLIB_FOO_HPP 11 | -------------------------------------------------------------------------------- /cmake-example/src/foo.cpp: -------------------------------------------------------------------------------- 1 | #include "mylib/foo.hpp" 2 | 3 | namespace mylib { 4 | 5 | int foo() 6 | { 7 | return 42; 8 | } 9 | 10 | } // end namespace mylib 11 | -------------------------------------------------------------------------------- /cmake-example/tests/foo_test.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "mylib/foo.hpp" 4 | 5 | TEST_CASE("Foo Test", "[foo]") 6 | { 7 | REQUIRE(mylib::foo() == 42); 8 | } 9 | -------------------------------------------------------------------------------- /cmake-example/tests/test_runner.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | -------------------------------------------------------------------------------- /cmake-example/vendor/Catch2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Since we bring along our own version of Catch2, we prefix the target with the 2 | # project name. Alternatively we could check whether Catch2::Catch2 is already 3 | # available and use that one instead of our own via `add_library(mylib_Catch2 4 | # ALIAS Catch2::Catch2)`. 5 | 6 | add_library(mylib_Catch2 INTERFACE) 7 | target_include_directories(mylib_Catch2 INTERFACE .) 8 | -------------------------------------------------------------------------------- /exercises/.gitignore: -------------------------------------------------------------------------------- 1 | ARCHIVE.md 2 | INFO.md 3 | -------------------------------------------------------------------------------- /exercises/README.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | ## Task 01 4 | 5 | Install G++ and Clang, then compile the provided file [`hello.cpp`](task01/hello.cpp). 6 | Use the following flags when compiling: 7 | 8 | -std=c++17 -Wall -Wextra -O2 9 | 10 | Next, set up [Boost](http://www.boost.org/) on your system and compile the provided file [`hello_boost.cpp`](task01/hello_boost.cpp). 11 | Boost is quite common and provides you a set of useful C++ libraries. 12 | Some of its content is even promoted into the C++ standard library. 13 | 14 | ## Task 02 15 | 16 | Run Clang on the provided file [`vec.cpp`](task02/vec.cpp) using the following command: 17 | 18 | clang -std=c++17 -Xclang -ast-dump -fsyntax-only -Wno-vexing-parse vec.cpp 19 | 20 | Clang will parse the input file and display its abstract syntax tree (AST). 21 | In the bottom half of the output you'll find the function declaration of `main` followed by its `CompoundStmt`. 22 | Take a close look at its children and compare the resulting AST with the input code. 23 | Notice any oddities — something that looks counter intuitive? 24 | 25 | As you can see, there are multiple different ways of initialisation in C++. 26 | Check out the [corresponding section at cppreference](https://en.cppreference.com/w/cpp/language/initialization). 27 | 28 | ## Task 03 29 | 30 | The directory [`task03`](task03) hosts four subdirectories, `libFoo`, `libBar`, `libBaz`, and `app`. 31 | 32 | Each folder prefixed with `lib` represents a library and contains a header plus a source file. 33 | Furthermore, the library `libBaz.so` depends on `libBar.so`. 34 | 35 | `app` contains a single source file providing a `main` function. 36 | It depends on all three libraries. 37 | 38 | ![Dependency Graph](images/task03_dependencies.png) 39 | 40 | - Model this project structure using [CMake](https://cmake.org/) 41 | - Be sure to set the C++ standard to C++17 and enable warnings (`-Wall -Wextra`) 42 | - The default build type should be *Release* 43 | 44 | CMake itself is a build system generator. 45 | You can choose from a variety of target build systems. 46 | 47 | ## Task 04 48 | 49 | Examine the program [`iterations.cpp`](task04/iterations.cpp) and think about the expected output. 50 | Compile the program and run it. 51 | What do you notice? 52 | Did you expect this behaviour? 53 | Did you get any compiler warnings? 54 | Investigate what is actually happening (consider using `valgrind` or a debugger). 55 | 56 | How can such errors be prevented? 57 | Look for tools (e.g. static code analysers) which help discovering such faulty code. 58 | 59 | **Note:** If you run the executable and everything seems normal, try changing the initial content of `xs`, using different optimisation flags, or a different compiler. 60 | The actual behaviour of this executable depends on various factors. 61 | 62 | See [Iterator Invalidation](https://en.cppreference.com/w/cpp/container#Iterator_invalidation). 63 | 64 | ## Task 05 65 | 66 | You are given the program [`strange.cpp`](task05/strange.cpp). 67 | Compile it with different compilers and optimisation flags. 68 | What do you notice? 69 | What is really happening here? 70 | 71 | See [Undefined Behaviour](https://en.cppreference.com/w/cpp/language/ub) and [Defining the undefinedness of C](https://dl.acm.org/citation.cfm?id=2737979). 72 | 73 | ## Task 06 74 | 75 | This task focuses on the correct implementation of RAII as well as copy and move semantics. 76 | You are asked to implement the concept of `unique_ptr` and `shared_ptr`. 77 | Since we won't concern ourselves with templates for the moment your implementation will *own* an instance of the following `struct`. 78 | 79 | ```cpp 80 | struct Vec2 { 81 | float x, y; 82 | }; 83 | ``` 84 | 85 | - Read the documentation regarding *smart pointers*, `unique_ptr`, and `shared_ptr` 86 | - Implement your version of `unique_ptr_to_vec2` and `shared_ptr_to_vec2` fulfilling these requirements: 87 | - *Dynamically* allocate an instance of `Vec2` in your constructor 88 | - De-allocate the `Vec2` instance in your destructor 89 | - Implement correct copy semantics (copy constructor / copy assignment) 90 | - Implement correct move semantics (move constructor / move assignment) 91 | - Enable access to `Vec2` via the operators `*` and `->` 92 | - Thread-safety for `shared_ptr_to_vec2`'s reference counter is not required 93 | - Pay attention to corner-cases like self-assignment (`v = v`) 94 | - Prepare a few interesting test cases 95 | - Check your implementation for memory leaks and memory corruptions using `valgrind` and sanitizers 96 | 97 | See [Rule of Three](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)). 98 | 99 | ## Task 07 100 | 101 | Read [this blog post](https://www.gamedev.net/blogs/entry/2265481-oop-is-dead-long-live-oop). 102 | 103 | - Pay attention to *implementation vs. interface inheritance* 104 | - Pay attention to the use of templates (assuming you've already covered them) 105 | - Think about the benefits and drawbacks of the used patterns 106 | 107 | ## Task 08 108 | 109 | You are given the following definition of a person: 110 | 111 | ```cpp 112 | struct Person { 113 | std::string firstname; 114 | std::string lastname; 115 | int age; 116 | }; 117 | ``` 118 | 119 | - Implement relational operators (`<`, `<=`, `>`, `>=`) 120 | - Implement comparison operators (`==`, `!=`) 121 | 122 | Next, create 5 different instances and put all of them 123 | - in an `std::vector`; 124 | - in an `std::set`; and 125 | - in an `std::map` as key (we don't care about the value type of the map). 126 | 127 | Use algorithms from the standard library, like `std::find` and `std::partition` on these containers and examine which operators are used. 128 | 129 | **Hint:** You may want to have a look at `std::tie`. 130 | 131 | ## Task 09 132 | 133 | Reuse `Person` from Task 08 and implement the necessary parts for inserting it into an `std::unordered_set`. 134 | 135 | Compare the performance of: 136 | 137 | - `std::vector` 138 | - `std::list` 139 | - `std::set` 140 | - `std::unordered_set` 141 | 142 | ## Task 10 143 | 144 | Have a look at [this](https://bollu.github.io/mathemagic/declarative/index.html). 145 | Now, do that in C++! 146 | 147 | Utilize lambdas, `std::function`, and/or structs with call operators. 148 | Critically think about ownership and minimize the amount of heap allocations. 149 | 150 | ## Task 11 151 | 152 | Take a look at [Boost's chat server example](https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/examples/cpp11_examples.html#boost_asio.examples.cpp11_examples.chat). 153 | Try to understand how the session's lifetime is managed by the server. 154 | Focus on `std::enable_shared_from_this` in combination with lambda captures. 155 | 156 | ## Task 12 157 | 158 | Reuse `Person` from Task 08 and take the following, incomplete definition of a room: 159 | 160 | ```cpp 161 | class Room { 162 | public: 163 | Room(int id, size_t limit) : id(id), limit(limit) {} 164 | 165 | // Returns true iff the person successfully entered. 166 | bool enter(/* Person */) {} 167 | 168 | void exit(/* Person */) {} 169 | 170 | private: 171 | const int id; 172 | const size_t limit; 173 | std::vector peopleInside; 174 | }; 175 | ``` 176 | 177 | `Room` contains a list of people currently located inside. 178 | People can enter and exit the room via the respective member functions. 179 | However, at most only `limit` people may be inside at any given time (invariant). 180 | 181 | - Add the missing pieces, paying special attention to the types 182 | 183 | The following use cases need to be covered next: 184 | - Asking a `Room` how many people are currently located inside 185 | - Asking a `Room` whether a specific person is currently located inside 186 | - Iterating over all people currently located in a `Room` 187 | 188 | Implement whatever is necessary to support these use cases, making sure the invariant remains intact. 189 | 190 | ## Task 13 191 | 192 | Reuse `Person` from Task 08. 193 | 194 | Create an `std::vector>` with at least 3 different elements. 195 | Create a function which takes a `const std::vector>&` as input and returns an `std::vector`. 196 | Each element in the result vector corresponds to the respective element in the input vector. 197 | 198 | For the functional programming nerds, the definition of this function would be something like `fmap std::shared_ptr::get`. 199 | 200 | Write your function in different ways and compare the readability: 201 | - use a range-based for loop 202 | - use `std::transform` 203 | - use a lambda expression 204 | - use `std::mem_fn` 205 | 206 | Think about taking the argument by value instead of taking it by const reference. 207 | 208 | ## Task 14 209 | 210 | Implement your own version of `std::vector` without using any of the provided containers — use *regular arrays* (`new[]` / `delete[]`) to house your elements. 211 | The focus of this task lies on the use of templates and implementation of iterators. 212 | You do not have to concern yourself with custom allocators. 213 | 214 | Test your implementation with different types (`int`, `double`, and a custom struct). 215 | 216 | Take your vector from task 1 and implement iterators. 217 | You might want to read through the respective documentation. 218 | 219 | Write some tests utilising algorithms provided by the standard library to check if your iterators behave correctly. 220 | 221 | ## Task 15 222 | 223 | Take your vector implementation from Task 14 and instantiate it with a big number of unique types. 224 | 225 | Inspect the relationship between the number of unique instantiates and compile time. 226 | Furthermore, look at the compiled object file using `nm`. 227 | 228 | ## Task 16 229 | 230 | In this task you have to create a rudimentary plugin system. 231 | 232 | You are given `plugin.hpp` which contains an interface for your plugins, as well as the function name of the constructor function and its type. 233 | Note that the constructor function returns an `std::unique_ptr`. 234 | 235 | - create an executable which *dynamically* loads plugins and executes their `run` member function 236 | - create two different plugins (`foo` and `bar`) showing off the plugin system 237 | 238 | It could look like this: 239 | 240 | $ ./main ./libFoo.so 241 | Creation of first plugin 242 | Running the first plugin 243 | Destruction of first plugin 244 | 245 | $ ./main ./libFoo.so ./libBar.so 246 | Creation of first plugin 247 | Running the first plugin 248 | Destruction of first plugin 249 | Creation of second plugin 250 | Running the second plugin 251 | Destruction of second plugin 252 | 253 | **Hint:** Have a look at the related man-pages *dlopen(3)* and *dlsym(3)*. 254 | 255 | ## Task 17 256 | 257 | Take your vector from Task 14 and implement component-wise addition via `operator+` on your vector. 258 | Support implicit type conversions: `MyVector{} + MyVector{}` yields a `MyVector`. 259 | 260 | **Hint:** Look into `decltype` and `std::declval`. 261 | 262 | ## Task 18 263 | 264 | You are given the following code snippet of a mathematical vector. 265 | 266 | ```cpp 267 | template 268 | class Vector { 269 | public: 270 | /* ... */ 271 | 272 | private: 273 | std::array data; 274 | }; 275 | ``` 276 | 277 | Find an elegant way to provide the following interface: 278 | 279 | - On default construction (no arguments), all elements are initialized to zero. 280 | - Besides copy / move semantics, there is only one additional constructor which takes *exactly* `N` `double`s to initialize `data`. 281 | - Accessing elements via the subscript operator `operator[]`. 282 | - Members `.x`, `.y`, `.z` access `data[0]`, `data[1]`, `data[2]` respectively: 283 | - With `N == 1` there should be only `.x` available. 284 | - With `N == 2` there should be `.x` and `.y` available. 285 | - With `N == 3` there should be `.x`, `.y`, and `.z` available. 286 | 287 | Add a few tests to ensure correct behavior using the following aliases: 288 | 289 | ```cpp 290 | using Vec1 = Vector<1>; 291 | using Vec2 = Vector<2>; 292 | using Vec3 = Vector<3>; 293 | ``` 294 | 295 | **Note:** You are allowed to modify the given snippet as necessary. 296 | 297 | ## Task 19 298 | 299 | Revisit the meta programming example from the lecture regarding `std::tuple`. 300 | 301 | Given the following class template: 302 | 303 | ```cpp 304 | template 305 | class type_set {}; 306 | ``` 307 | 308 | `type_set` should behave like a set of types. 309 | The empty set would therefore be `type_set<>`, while the set containing the type `int` would be `type_set`, so on and so forth. 310 | 311 | - Create a meta function `type_set_contains_v` which checks if a given `type_set` contains a given type. 312 | - Create a meta function `type_set_is_subset_v` which checks if a given `type_set` is a subset of another given `type_set`. 313 | - Create a meta function `type_set_is_same_v` which checks if a given `type_set` is equal to another given `type_set`. 314 | - Create a meta function `type_set_size_v` which tells the size of a given `type_set`. 315 | For `type_set` it should return 2. 316 | 317 | Try not to use any of the utilities provided by the standard library (like the example provided in the lecture). 318 | 319 | **Hint:** If you are struggling with this exercise you might want to have a look at how *fold* (i.e. *reduce*) is used in functional programming languages. 320 | 321 | ## Task 20 322 | 323 | Revisit the *Advanced Template* slides. 324 | 325 | Go through the `has_print_to` example from the slides step by step. 326 | Explain all parts like it's done in the lecture. 327 | 328 | ## Task 21 329 | 330 | Take a look at [Boost Operators](https://www.boost.org/doc/libs/1_74_0/libs/utility/operators.htm). 331 | Try to understand *why* the *curiously recurring template pattern (CRTP)* is used. 332 | 333 | ## Task 22 334 | 335 | You are given the code and build instructions for a shared library and an executable which uses the shared library. 336 | The shared library features two functions `random_number` and `just_a_print` inside the `foo` namespace. 337 | 338 | Your task is to create an *interceptor* library: 339 | 340 | - `random_number` should be replaced with a function that always returns `4` for improved determinism 341 | - `just_a_print` should be wrapped so that some text is printed before and after the original function is executed 342 | 343 | Running the executable with and without the interceptor library could look like this: 344 | 345 | ``` 346 | $ ./executable 347 | Random Number: 880806932 348 | 349 | Just a print to stdout, nothing else 350 | 351 | $ LD_PRELOAD=$PWD/interceptor.so ./executable 352 | Random Number: 4 353 | 354 | some text before 355 | Just a print to stdout, nothing else 356 | some text after 357 | ``` 358 | 359 | **Hint:** For Linux, have a look at the related man-pages, like *ld-linux(8)*. 360 | 361 | ## Task 23 362 | 363 | Implement a simple calculator using either [imgui](https://github.com/ocornut/imgui), [Qt Widgets](https://doc.qt.io/qt-5/qtwidgets-index.html), [Qt Quick](https://doc.qt.io/qt-5/qtquick-index.html), or any other cross-platform GUI toolkit. 364 | 365 | For Qt Quick, the actual calculation needs to be implemented in C++ as we want to investigate the interaction between the toolkit and the C++ programming language. 366 | 367 | ## Task 24 368 | 369 | Read through [*The 7 Tasks*](https://eugenkiss.github.io/7guis/tasks/) and implement the described tasks using one cross-platform GUI toolkit. 370 | -------------------------------------------------------------------------------- /exercises/images/task03_dependencies.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | node [shape=box style=filled fillcolor=lightblue fontname="Roboto Mono" fontsize=12 ]; 3 | edge [style=dashed arrowhead=onormal]; 4 | 5 | app; 6 | foo [label="libFoo.so"]; 7 | bar [label="libBar.so"]; 8 | baz [label="libBaz.so"]; 9 | 10 | app -> foo; 11 | app -> bar; 12 | app -> baz; 13 | baz -> bar; 14 | } 15 | -------------------------------------------------------------------------------- /exercises/images/task03_dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/exercises/images/task03_dependencies.png -------------------------------------------------------------------------------- /exercises/task01/hello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | std::cout << "Hello World" << std::endl; 6 | } 7 | -------------------------------------------------------------------------------- /exercises/task01/hello_boost.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | std::string s = "foo bar"; 8 | boost::replace_all(s, "foo", "Hello"); 9 | boost::replace_all(s, "bar", "World"); 10 | std::cout << s << std::endl; 11 | } 12 | -------------------------------------------------------------------------------- /exercises/task02/vec.cpp: -------------------------------------------------------------------------------- 1 | class Vec3 { 2 | public: 3 | Vec3() = default; 4 | Vec3(int x, int y, int z) : x(x), y(y), z(z) {} 5 | 6 | private: 7 | int x = 0; 8 | int y = 0; 9 | int z = 0; 10 | }; 11 | 12 | int main() 13 | { 14 | Vec3 v0; 15 | Vec3 v1(); 16 | Vec3 v2(1, 2, 3); 17 | Vec3 v3{1, 2, 3}; 18 | Vec3 v4 = {1, 2, 3}; 19 | 20 | auto v5 = Vec3{}; 21 | auto v6 = Vec3(1, 2, 3); 22 | auto v7 = Vec3{1, 2, 3}; 23 | } 24 | -------------------------------------------------------------------------------- /exercises/task03/app/app.cpp: -------------------------------------------------------------------------------- 1 | #include "bar.hpp" 2 | #include "baz.hpp" 3 | #include "foo.hpp" 4 | 5 | int main() 6 | { 7 | foo(); 8 | bar(); 9 | baz(); 10 | } 11 | -------------------------------------------------------------------------------- /exercises/task03/libBar/bar.cpp: -------------------------------------------------------------------------------- 1 | #include "bar.hpp" 2 | 3 | #include 4 | 5 | void bar() 6 | { 7 | std::cout << "Function bar called" << std::endl; 8 | } 9 | -------------------------------------------------------------------------------- /exercises/task03/libBar/bar.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UIBK_703333_EX01_TASK3_BAR_HPP 2 | #define UIBK_703333_EX01_TASK3_BAR_HPP 3 | 4 | void bar(); 5 | 6 | #endif // UIBK_703333_EX01_TASK3_BAR_HPP 7 | -------------------------------------------------------------------------------- /exercises/task03/libBaz/baz.cpp: -------------------------------------------------------------------------------- 1 | #include "baz.hpp" 2 | 3 | #include 4 | 5 | #include "bar.hpp" 6 | 7 | void baz() 8 | { 9 | bar(); 10 | std::cout << "Function baz called" << std::endl; 11 | } 12 | -------------------------------------------------------------------------------- /exercises/task03/libBaz/baz.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UIBK_703333_EX01_TASK3_BAZ_HPP 2 | #define UIBK_703333_EX01_TASK3_BAZ_HPP 3 | 4 | void baz(); 5 | 6 | #endif // UIBK_703333_EX01_TASK3_BAZ_HPP 7 | -------------------------------------------------------------------------------- /exercises/task03/libFoo/foo.cpp: -------------------------------------------------------------------------------- 1 | #include "foo.hpp" 2 | 3 | #include 4 | 5 | void foo() 6 | { 7 | std::cout << "Function foo called" << std::endl; 8 | } 9 | -------------------------------------------------------------------------------- /exercises/task03/libFoo/foo.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UIBK_703333_EX01_TASK3_FOO_HPP 2 | #define UIBK_703333_EX01_TASK3_FOO_HPP 3 | 4 | void foo(); 5 | 6 | #endif // UIBK_703333_EX01_TASK3_FOO_HPP 7 | -------------------------------------------------------------------------------- /exercises/task04/iterations.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | std::vector xs{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 7 | 8 | for (auto it = xs.begin(); it != xs.end(); ++it) { 9 | for (int i = 0; i < *it; i++) { 10 | xs.push_back(*it); 11 | } 12 | } 13 | 14 | std::cout << "Vector: "; 15 | for (const auto &x : xs) { 16 | std::cout << x << " "; 17 | } 18 | std::cout << "\n"; 19 | } 20 | -------------------------------------------------------------------------------- /exercises/task05/strange.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int array[5] = {}; 4 | 5 | bool contains(int v) 6 | { 7 | for (int i = 0; i <= 5; ++i) { 8 | if (array[i] == v) { 9 | return true; 10 | } 11 | } 12 | return false; 13 | } 14 | 15 | int main() 16 | { 17 | for (int i = 0; i < 10; ++i) { 18 | auto contains_text = contains(i) ? "contains " : "does not contain "; 19 | std::cout << "array " << contains_text << i << std::endl; 20 | } 21 | 22 | std::cout << "done" << std::endl; 23 | } 24 | -------------------------------------------------------------------------------- /exercises/task16/plugin.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PLUGIN_HPP 2 | #define PLUGIN_HPP 3 | 4 | #include 5 | 6 | /// Interface for plugins. 7 | class Plugin { 8 | public: 9 | virtual void run() = 0; 10 | virtual ~Plugin() {} 11 | }; 12 | 13 | /// Symbol of the plugin constructor function. 14 | const auto PLUGIN_CONSTRUCTOR = "create_plugin"; 15 | 16 | /// Type of the plugin constructor function. 17 | using PLUGIN_CONSTRUCTOR_T = std::unique_ptr (*)(); 18 | 19 | #endif // PLUGIN_HPP 20 | -------------------------------------------------------------------------------- /exercises/task22/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(example LANGUAGES CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_EXTENSIONS OFF) 7 | 8 | add_library(foo SHARED libFoo/foo.cpp) 9 | target_include_directories(foo PUBLIC libFoo) 10 | 11 | add_executable(executable executable.cpp) 12 | target_link_libraries(executable foo) 13 | -------------------------------------------------------------------------------- /exercises/task22/executable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "foo.hpp" 4 | 5 | int main() 6 | { 7 | std::cout << "Random Number: " << foo::random_number() << "\n\n"; 8 | foo::just_a_print(); 9 | } 10 | -------------------------------------------------------------------------------- /exercises/task22/libFoo/foo.cpp: -------------------------------------------------------------------------------- 1 | #include "foo.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace foo { 8 | 9 | int random_number() 10 | { 11 | static std::random_device device; 12 | static std::mt19937 rng(device()); 13 | static std::uniform_int_distribution dist(0, std::numeric_limits::max()); 14 | 15 | return dist(rng); 16 | } 17 | 18 | void just_a_print() 19 | { 20 | std::cout << "Just a print to stdout, nothing else\n"; 21 | } 22 | 23 | } // end namespace foo 24 | -------------------------------------------------------------------------------- /exercises/task22/libFoo/foo.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FOO_HPP 2 | #define FOO_HPP 3 | 4 | namespace foo { 5 | 6 | int random_number(); 7 | 8 | void just_a_print(); 9 | 10 | } // end namespace foo 11 | 12 | #endif // FOO_HPP 13 | -------------------------------------------------------------------------------- /images/banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/images/banner.gif -------------------------------------------------------------------------------- /topic/game_ai/images/behavior_tree_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/topic/game_ai/images/behavior_tree_example.png -------------------------------------------------------------------------------- /topic/game_ai/images/ooda_loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PeterTh/uibk_cpp_lab/a98e42e4c5b26cac565e5e6722084ebbeadd506e/topic/game_ai/images/ooda_loop.png -------------------------------------------------------------------------------- /topic/game_ai/notes.md: -------------------------------------------------------------------------------- 1 | # Video Game AI 2 | 3 | A very coarse, practical introduction. 4 | 5 | ## Disclaimer 6 | 7 | The term artificial intelligence (AI) is used to describe a hand-crafted (i.e. scripted) opponent in video games. 8 | In this context, the term is not related to machine learning. 9 | 10 | While machine learning can be leveraged to realize video game AI, we have not seen the use of such in commercial products. 11 | 12 | ## Terminology 13 | 14 | - Agent: typically an AI controlled *character / actor* 15 | 16 | ## Goal 17 | 18 | - Entertain the player! 19 | - Playing the game, adhering to the rules, using the (underlying) mechanics 20 | - Act natural 21 | 22 | ## Simple AI 23 | 24 | > Castlevania demo 25 | 26 | - Can be enough for simple games 27 | - Often requires *design workarounds* to make challenging 28 | - Not very engaging 29 | 30 | ## Big Picture 31 | 32 | > Glass of water 33 | 34 | - Emulate human thinking / behavior 35 | - Layers 36 | - Algorithms at the bottom 37 | - Strategy at the top 38 | - Usually 2 layers sufficient 39 | 40 | ## Bottom Layer (1st) 41 | 42 | - Contains very basic building blocks 43 | - Go to position 44 | - Interact with object 45 | - … 46 | - Leverage algorithms 47 | 48 | > [Bug Algorithm](https://www.cs.cmu.edu/~motionplanning/lecture/Chap2-Bug-Alg_howie.pdf) 49 | 50 | > Dynamite Person Demo 51 | 52 | > Over Engineered Demo 53 | 54 | > [Overwatch Navmesh](https://youtu.be/W3aieHjyNvw?t=2946) 55 | 56 | > [Doom 2016 AI](https://youtu.be/3lO1q8mQrrg?t=1455) 57 | 58 | > Project Daredevil Demo 1 59 | 60 | > [Project Daredevil Demo 2](https://www.youtube.com/watch?v=pfWpMPXzBj8&list=PL7GgcoHpNOrAZ9LQBF9Rzukov59Qy7eyK&index=8) 61 | 62 | ## Top Layer 63 | 64 | AI needs to: 65 | - be aware of game mechanics; 66 | - interact with game mechanics; and 67 | - use the *meta* of the game. 68 | 69 | Meta: The highest level of strategy. 70 | 71 | > Diabotical Demo 72 | 73 | - Explain general arena FPS concept 74 | - Non-trivial map → positioning 75 | - Different weapons → situational choice 76 | - Pickups → resources! 77 | - Pickups respawn → item timing 78 | 79 | → more about resource management and decision making then just simply shooting each other. 80 | 81 | Key concept: **control** 82 | - In-control vs. out-of-control 83 | - Contested control 84 | 85 | ## Tools 86 | 87 | - Bottom layer 88 | - Graphs 89 | - Control Theory 90 | 91 | - Top layer 92 | - Behavior trees 93 | - Decision table 94 | - Finite state machine 95 | - Async programming (co-routines) 96 | 97 | ![Behavior tree example](images/behavior_tree_example.png) 98 | > https://docs.unrealengine.com/en-US/InteractiveExperiences/ArtificialIntelligence/BehaviorTrees/BehaviorTreesOverview/index.html 99 | 100 | ## Technique 101 | 102 | [OODA Loop](https://en.wikipedia.org/wiki/OODA_loop): 103 | - Observe 104 | - Orient 105 | - Decide 106 | - Act 107 | 108 | ![OODA Loop](images/ooda_loop.png) 109 | 110 | - [In-depth explanation](https://taylorpearson.me/ooda-loop/) 111 | 112 | ## Testing 113 | 114 | - Handcrafted scenarios with well defined outcome 115 | - AI vs. AI 116 | - Relate to human vs. human 117 | 118 | ## Take Home 119 | 120 | **Make enemies smarter, not stronger.** 121 | 122 | - Good AI keeps the player entertained 123 | - Can be forgiving (difficulty settings) 124 | - Good AI plays the game similar to how a human plays the game. 125 | - Think in layers → concern yourself with behavior rather than algorithms 126 | - Leverage the OODA loop 127 | 128 | - [🎥 AI and Games](https://www.youtube.com/channel/UCov_51F0betb6hJ6Gumxg3Q) 129 | -------------------------------------------------------------------------------- /topic/little_features/.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | -------------------------------------------------------------------------------- /topic/little_features/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(little_features CXX) 3 | 4 | file(GLOB srcs *.cpp) 5 | foreach(src IN LISTS srcs) 6 | get_filename_component(exe ${src} NAME_WE) 7 | add_executable(${exe} ${src}) 8 | target_compile_features(${exe} PRIVATE cxx_std_17) 9 | set_target_properties(${exe} PROPERTIES CXX_EXTENSIONS OFF) 10 | target_compile_options(${exe} PRIVATE 11 | $<$:-Wall -Wextra -pedantic>) 12 | target_link_libraries(${exe} -ltbb) 13 | endforeach() 14 | -------------------------------------------------------------------------------- /topic/little_features/conversion_operator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Vec3 { 4 | float x, y, z; 5 | }; 6 | 7 | struct Vec2 { 8 | float x, y; 9 | operator Vec3() const { return Vec3{x, y, 0}; } 10 | }; 11 | 12 | int main() 13 | { 14 | Vec2 v2{1, 2}; 15 | 16 | Vec3 v3 = v2; 17 | 18 | assert(v3.x == 1 && v3.y == 2 && v3.z == 0); 19 | } 20 | -------------------------------------------------------------------------------- /topic/little_features/empty_struct.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Emptry structs are valid in C++, not so in C. 4 | struct Empty { 5 | }; 6 | 7 | int main() 8 | { 9 | std::cout << sizeof(Empty) << "\n"; 10 | } 11 | -------------------------------------------------------------------------------- /topic/little_features/execution_policy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | std::vector numbers; 9 | for (auto i = 0; i < 1000; i++) { 10 | numbers.push_back(i); 11 | } 12 | 13 | // Output should be malformed since there is no synchronization in place. 14 | std::for_each(std::execution::par, numbers.begin(), numbers.end(), [](int i) { std::cout << i << "\n"; }); 15 | } 16 | -------------------------------------------------------------------------------- /topic/little_features/object_slicing.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Shape { 5 | virtual float area() const 6 | { 7 | std::cout << "Shape::area\n"; 8 | return 0; 9 | } 10 | 11 | virtual ~Shape() {} 12 | }; 13 | 14 | struct Rect : public Shape { 15 | Rect(float width, float height) : width(width), height(height) {} 16 | 17 | float area() const override 18 | { 19 | std::cout << "Rect::area\n"; 20 | return width * height; 21 | } 22 | 23 | float width, height; 24 | }; 25 | 26 | int main() 27 | { 28 | std::cout << "size of Shape: " << sizeof(Shape) << "\n" 29 | << "size of Rect: " << sizeof(Rect) << "\n"; 30 | 31 | Rect rect{2.0f, 3.0f}; 32 | { 33 | const auto area = rect.area(); 34 | std::cout << "Area of rect: " << area << "\n"; 35 | } 36 | 37 | Shape shape = rect; // rect gets sliced to fit into shape. 38 | { 39 | const auto area = shape.area(); 40 | std::cout << "Area of shape: " << area << "\n"; 41 | } 42 | 43 | Shape *shape_ptr = ▭ 44 | { 45 | const auto area = shape_ptr->area(); 46 | std::cout << "Area of shape_ptr: " << area << "\n"; 47 | } 48 | 49 | std::shared_ptr shape_sptr = std::make_shared(3.0f, 4.0f); 50 | { 51 | const auto area = shape_sptr->area(); 52 | std::cout << "Area of shape_sptr: " << area << "\n"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /topic/little_features/placement_new.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Point { 5 | Point(float x, float y) : x(x), y(y) { std::cout << "Constructed at address: " << this << "\n"; } 6 | ~Point() { std::cout << "Deconstructed at address: " << this << "\n"; } 7 | float x, y; 8 | }; 9 | 10 | int main() 11 | { 12 | auto memory = malloc(sizeof(Point)); 13 | 14 | // Construct a point at a specific location. 15 | Point *p = new (memory) Point(1.0f, 2.0f); 16 | 17 | std::cout << "x: " << p->x << "\n"; 18 | 19 | // Need to manually invoke the destructor. 20 | p->~Point(); 21 | 22 | free(memory); 23 | } 24 | -------------------------------------------------------------------------------- /topic/little_features/pointer_to_member.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Point { 4 | float x, y; 5 | }; 6 | 7 | struct Rect { 8 | Point tl, br; 9 | float area() const { return (br.x - tl.x) * (br.y - tl.y); } 10 | }; 11 | 12 | class IShape { 13 | public: 14 | virtual float area() const { assert(false); }; 15 | virtual ~IShape() {} 16 | }; 17 | 18 | class Square : public IShape { 19 | public: 20 | Square(float extend) : m_extend(extend) {} 21 | float area() const override { return m_extend * m_extend; } 22 | 23 | private: 24 | float m_extend; 25 | }; 26 | 27 | int main() 28 | { 29 | Rect r1{{1, 1}, {4, 4}}; 30 | assert(r1.area() == 9.0f); 31 | 32 | Rect *r1_ptr = &r1; 33 | assert(r1_ptr->area() == 9.0f); 34 | 35 | // pointer to member variable 36 | auto tl_mem_ptr = &Rect::tl; // Point Rect::*tl_mem_ptr 37 | 38 | assert((r1.*tl_mem_ptr).x == 1.0f); 39 | 40 | assert((r1_ptr->*tl_mem_ptr).x == 1.0f); 41 | 42 | // pointer to member function 43 | auto area_memfn_ptr = &Rect::area; // float (Rect::*area_memfn_ptr)() const 44 | 45 | assert((r1.*area_memfn_ptr)() == 9.0f); 46 | 47 | assert((r1_ptr->*area_memfn_ptr)() == 9.0f); 48 | 49 | // Virtual 50 | Square square{2.0f}; 51 | IShape *shape_ptr = □ 52 | 53 | // virtual member function pointer 54 | auto virt_area_memfn_ptr = &IShape::area; // float (IShape::*virt_area_memfn_ptr)() const 55 | 56 | assert((square.*virt_area_memfn_ptr)() == 4.0f); 57 | 58 | assert((shape_ptr->*virt_area_memfn_ptr)() == 4.0f); 59 | } 60 | --------------------------------------------------------------------------------