Add assignment 2
39
assignment2/README.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Assignment 2
|
||||||
|
|
||||||
|
*due on 19 February 2021*
|
||||||
|
|
||||||
|
For the second assignment you can either pick one of the two provided specifications or come up with your own.
|
||||||
|
Yes, you are free to come up with a topic for the second assignment.
|
||||||
|
However, doing so requires you to put together a specification similar to the ones provided and get my approval.
|
||||||
|
The specification doesn't have to be bulletproof.
|
||||||
|
But it must contain a bullet list of goals (with points to score) at the bottom which can be checked / evaluated.
|
||||||
|
You can also adjust one of the provided specifications.
|
||||||
|
|
||||||
|
You are allowed to work in teams, yet the team size has to correspond to the amount of work (features) of the topic.
|
||||||
|
For the provided specifications the recommended team size is 3.
|
||||||
|
|
||||||
|
You are allowed to use:
|
||||||
|
- C++ standard library (C++17 standard)
|
||||||
|
- C standard library (as fallback)
|
||||||
|
- [Boost](https://www.boost.org/)
|
||||||
|
- [SDL](https://www.libsdl.org/)
|
||||||
|
- [GLFW](https://www.glfw.org/)
|
||||||
|
- [GLM](https://glm.g-truc.net/)
|
||||||
|
- [Vulkan](https://www.khronos.org/vulkan/) / [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp)
|
||||||
|
- [Qt](https://www.qt.io/)
|
||||||
|
- [ImGui](https://github.com/ocornut/imgui)
|
||||||
|
- [OpenAL](https://openal.org/)
|
||||||
|
- [RapidJSON](https://rapidjson.org/)
|
||||||
|
- [Tiled](https://www.mapeditor.org/)
|
||||||
|
|
||||||
|
Feel free to ask me about other libraries / tools.
|
||||||
|
|
||||||
|
Your application should work on Linux and Windows unless there is a specific reason why it cannot be cross-platform.
|
||||||
|
For Linux, assume a recent version of Ubuntu Desktop and that the required dependencies are installed via the system's package manager.
|
||||||
|
Use the corresponding CMake `find_package` mechanism to find them, it is recommended to leverage [`pkgconf`](https://cmake.org/cmake/help/latest/module/FindPkgConfig.html).
|
||||||
|
For Windows you can simply ship pre-built libraries that are picked up by CMake automatically.
|
||||||
|
|
||||||
|
You must use [CMake](https://cmake.org/) as build system.
|
||||||
|
|
||||||
|
Use [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) to automatically format your code using the provided [`.clang-format`](../.clang-format) configuration.
|
||||||
|
|
BIN
assignment2/images/behaviour_tree_example.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
assignment2/images/ccotm_hud_bars.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
assignment2/images/ccotm_items.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
assignment2/images/ccotm_kickboots_banner.png
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
assignment2/images/ccotm_map.jpg
Normal file
After Width: | Height: | Size: 172 KiB |
BIN
assignment2/images/ccotm_nathen_sprite_map.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assignment2/images/ccotm_pause_menu.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assignment2/images/game_loop.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
assignment2/images/pokemon_map.png
Normal file
After Width: | Height: | Size: 170 KiB |
BIN
assignment2/images/sprite_map.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assignment2/images/unity_inspector.png
Normal file
After Width: | Height: | Size: 14 KiB |
436
assignment2/metroidvania_specification.md
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
# Metroidvania Specification
|
||||||
|
|
||||||
|
As the title already suggests, the ultimate goal is to create a small, *[Metroidvania](https://en.wikipedia.org/wiki/Metroidvania)-like* side-scroller.
|
||||||
|
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:
|
||||||
|
|
||||||
|
- [Castlevania: Symphony of the Night](https://en.wikipedia.org/wiki/Castlevania:_Symphony_of_the_Night)
|
||||||
|
- [Castlevania: Circle of the Moon](https://en.wikipedia.org/wiki/Castlevania:_Circle_of_the_Moon)
|
||||||
|
- [Super Metroid](https://en.wikipedia.org/wiki/Super_Metroid)
|
||||||
|
- [Ori and the Blind Forest](https://www.gog.com/game/ori_and_the_blind_forest_definitive_edition)
|
||||||
|
- [Dust: An Elysian Tail](https://www.gog.com/game/dust_an_elysian_tail)
|
||||||
|
- [Salt and Sanctuary](https://store.steampowered.com/app/283640/Salt_and_Sanctuary/)
|
||||||
|
- [Owlboy](https://www.gog.com/game/owlboy)
|
||||||
|
- [Guacamelee](https://www.gog.com/game/guacamelee_gold_edition)
|
||||||
|
- [Hollow Knight](https://www.gog.com/game/hollow_knight)
|
||||||
|
- [Iconoclasts](https://www.gog.com/game/iconoclasts)
|
||||||
|
- [Dead Cells](https://www.gog.com/game/dead_cells)
|
||||||
|
- [**Timespinner**](https://store.steampowered.com/app/368620/Timespinner/)
|
||||||
|
|
||||||
|
After playing some gam-… ehh… doing some research, you should be familiar with the core game concepts and mechanics of the genre.
|
||||||
|
|
||||||
|
Before moving on, I want you to make up your minds about the setting your game takes place in.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
|
||||||
|
Now that a certain setting has been established, continue the read and imagine what the equivalences to my examples would be in your game.
|
||||||
|
These equivalences can often be derived directly from the chosen setting.
|
||||||
|
However, you might want to throw in a twist here and there to surprise the player and keep her engaged.
|
||||||
|
|
||||||
|
Most of the examples presented in the following sections are taken from *Castlevania: Circle of the Moon*.
|
||||||
|
This way I can stick to a common setting throughout.
|
||||||
|
|
||||||
|
### Assets
|
||||||
|
|
||||||
|
That's nice and stuff, but what about all the assets (eg sprites, music, sound effects) that are required for such a game?
|
||||||
|
|
||||||
|
Well, you don't have to look far.
|
||||||
|
As this is still about learning C++, I don't expect you to spend your precious time on creating all of them by hand.
|
||||||
|
There are megatons of assets for free- / non-commercial- / educational-use available online.
|
||||||
|
I am pretty sure you'll find something among them which suits your setting.
|
||||||
|
Maybe some slight modifications are needed here and there, but nothing that should distract you from learning C++.
|
||||||
|
|
||||||
|
For various types of assets – sound effects especially – there are also generators available which can be tweaked for unique results.
|
||||||
|
Pick your poison!
|
||||||
|
|
||||||
|
## Building the World
|
||||||
|
|
||||||
|
Metroidvania style games are typically semi-open world.
|
||||||
|
While the player can explore the game world freely, certain areas are gated off at the start.
|
||||||
|
|
||||||
|
Let's take a look at this map.
|
||||||
|
|
||||||
|
![Castlevania: Circle of the Moon Map](images/ccotm_map.jpg)
|
||||||
|
|
||||||
|
*source: http://castlevania.wikia.com/*
|
||||||
|
|
||||||
|
It displays the whole layout of the castle – each wing (aka section or area) coloured differently.
|
||||||
|
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.
|
||||||
|
The small, red rooms are save points; while the small, white rooms are teleporters.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The player needs to face off the vicious boss of a wing in order to progress.
|
||||||
|
After defeating such a boss, a new item or ability is acquired which grants her passage to other, previously inaccessible, wings of the castle.
|
||||||
|
|
||||||
|
Here is an example:
|
||||||
|
|
||||||
|
![Castlevania: Circle of the Moon Kickboots Banner](images/ccotm_kickboots_banner.png)
|
||||||
|
|
||||||
|
*source: http://castlevania.wikia.com/*
|
||||||
|
|
||||||
|
The final boss of the *Machine Tower* is the *Iron Golem* (left).
|
||||||
|
From it, the player obtains the *Kick Boots* (centre) which enables jumping off walls to gain more height (right).
|
||||||
|
With them, the *Chapel Tower* can now be accessed.
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
A room essentially consists of a 2D array of tiles.
|
||||||
|
Together, the tiles build the floor, ceiling, and walls of the room.
|
||||||
|
Platforms are added to enable vertical traversal.
|
||||||
|
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.
|
||||||
|
|
||||||
|
Game entities (eg items, enemies, doors) can be placed via XY coordinates.
|
||||||
|
The doors are particularly important as they are responsible for interconnecting rooms.
|
||||||
|
|
||||||
|
The layout of each room in your game should be fully determined by a single file – preferably human-readable.
|
||||||
|
It *could* look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
MAP Room01
|
||||||
|
BACKGROUND assets/images/background/library.png
|
||||||
|
MUSIC assets/music/ancient_tomb.ogg
|
||||||
|
TILES
|
||||||
|
. = none
|
||||||
|
w = Wall01
|
||||||
|
c = Ceiling01
|
||||||
|
f = Floor01
|
||||||
|
p = Platform01
|
||||||
|
LAYOUT
|
||||||
|
wcccccccccccccccccw
|
||||||
|
w.................w
|
||||||
|
w.................w
|
||||||
|
w..........ppp....w
|
||||||
|
w.................w
|
||||||
|
w.................w
|
||||||
|
fffffffffffffffffff
|
||||||
|
ENTITIES
|
||||||
|
( 2, 5) Player()
|
||||||
|
( 8, 5) Enemy_BookTossingRedneck()
|
||||||
|
(12, 2) SmallHealthPotion()
|
||||||
|
(18, 5) Door01_right(Room01_exit, Room02_entry)
|
||||||
|
```
|
||||||
|
|
||||||
|
Tiles and game entities are defined elsewhere, yet some of them require arguments for their instantiation.
|
||||||
|
In this example, the door gets a designation (`Room01_exit`) and is connected to some other door (`Room02_entry`).
|
||||||
|
|
||||||
|
## Hearing the World
|
||||||
|
|
||||||
|
Commonly, the music playing is either determined by the room or wing.
|
||||||
|
The selected tune is repeated until a room / wing with different background music is entered.
|
||||||
|
|
||||||
|
A more advanced technique is to use different variations of the same background music and blend them together depending on the situation.
|
||||||
|
|
||||||
|
For instance:
|
||||||
|
the player enters a room, no enemy in sight;
|
||||||
|
a *softer* version of the tune is playing.
|
||||||
|
As the player progresses through the room, enemies awake and engage the player;
|
||||||
|
the tune becomes more dramatic.
|
||||||
|
One of the enemies lands a devastating blow, depleting the player's health bar almost entirely;
|
||||||
|
a *high tension* version fades in – additionally the player's low health causes an annoying alarm to be played.
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
SDL_mixer is probably all you need for this.
|
||||||
|
It provides separate sound channels, each with its own volume setting.
|
||||||
|
|
||||||
|
One simple way to blend variations of the same track is by playing all of them on separate channels and adjusting the volume accordingly.
|
||||||
|
|
||||||
|
Obviously, sound effects are played on different channels than background music so they do not interfere.
|
||||||
|
|
||||||
|
## Entities of the Game
|
||||||
|
|
||||||
|
Having rooms and wings would be a bit pointless without anything that interacts with it, wouldn't it?
|
||||||
|
|
||||||
|
All of the objects in your game are referred to as entities.
|
||||||
|
Entities interact with the game world as well as with each other.
|
||||||
|
Their complexity varies greatly, from a simple key that can be picked up to unlock doors, to AI controlled enemies.
|
||||||
|
|
||||||
|
Most of them are visible (rendered) and have collision so they can be interacted with.
|
||||||
|
Others may be almost unnoticeable by the player – like a point in 2D space which just emits a screeching noise.
|
||||||
|
|
||||||
|
Typically, entities have a position, rotation, scale, and a type; the latter may come with additional state and ways of interaction.
|
||||||
|
A more complex alternative for typing entities is by using an entity component system (see below).
|
||||||
|
|
||||||
|
### Rendering
|
||||||
|
|
||||||
|
Displaying (static) entities is straight forward.
|
||||||
|
|
||||||
|
![Castlevania: Circle of the Moon Items](images/ccotm_items.png)
|
||||||
|
|
||||||
|
*source: https://strategywiki.org/wiki/Castlevania:_Circle_of_the_Moon*
|
||||||
|
|
||||||
|
[Sprites](https://en.wikipedia.org/wiki/Sprite_(computer_graphics)) are essentially bitmap images with transparent background.
|
||||||
|
The position, rotation, and scale for rendering the sprite is determined by the position, rotation, and scale of the entity respectively.
|
||||||
|
Offsets may be added, but apart from that, that's it.
|
||||||
|
|
||||||
|
### Animation
|
||||||
|
|
||||||
|
More dynamic entities, like the player character (PC) or enemies, often feature animations.
|
||||||
|
These start with simple idle and walking animations, but can also turn into breathtaking combat and dodging choreographies.
|
||||||
|
|
||||||
|
![Castlevania: Circle of the Moon Nathen Sprite Map](images/ccotm_nathen_sprite_map.png)
|
||||||
|
|
||||||
|
*modified: removed middle part for brevity*
|
||||||
|
|
||||||
|
The key idea is to use multiple sprites instead of just one.
|
||||||
|
Each animation frame is a new sprite.
|
||||||
|
When an animation is played, the related sprites are displayed one after another to simulate motion.
|
||||||
|
|
||||||
|
Multiple sprites are combined and put into the same image, which is then called a *sprite map*.
|
||||||
|
The rendering API allows us to select which part of the image to use for rendering.
|
||||||
|
A simple rectangle selection is used to pick the sprite for the current frame.
|
||||||
|
|
||||||
|
#### Implementation
|
||||||
|
|
||||||
|
Composing a sprite map is trivial, yet we are still missing some information.
|
||||||
|
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.
|
||||||
|
|
||||||
|
This can be solved via a simple text file.
|
||||||
|
Like with the room layout, determine the format yourself, preferably human-readable.
|
||||||
|
A hypothetical example is provided:
|
||||||
|
|
||||||
|
```
|
||||||
|
ANIMATION Enemy_BookTossingRedneck
|
||||||
|
SPRITES assets/images/sprites/enemies/book_tossing_redneck.png
|
||||||
|
ITEMS
|
||||||
|
idle 0ms ( 0, 0) (16, 16)
|
||||||
|
drinking 0ms (16, 0) (32, 16)
|
||||||
|
walking 500ms ( 0, 16) (16, 32)
|
||||||
|
(16, 16) (32, 32)
|
||||||
|
(32, 16) (48, 32)
|
||||||
|
(48, 16) (64, 32)
|
||||||
|
```
|
||||||
|
|
||||||
|
First we state to which entity the animation file belongs and where the corresponding sprite map can be found.
|
||||||
|
It consists of 6 sprites, the first two are displayed when the entity is idling or drinking respectively.
|
||||||
|
As both only consist of a single sprite, the timing is irrelevant and therefore set to zero.
|
||||||
|
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.
|
||||||
|
After the forth sprite has been displayed, the animation loops, starting over with the first sprite.
|
||||||
|
|
||||||
|
### Collision
|
||||||
|
|
||||||
|
So far we can display the game world and entities – even with animations – yet we still lack basic interaction.
|
||||||
|
This is where collision comes in.
|
||||||
|
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.
|
||||||
|
|
||||||
|
If the player runs into a wall, the wall should of course stop the player and prevent her from running out of bounds.
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Implementation
|
||||||
|
|
||||||
|
There are various different ways one can implement collision for 2D side-scrollers.
|
||||||
|
All of them come with their own benefits and drawbacks.
|
||||||
|
I'll leave things for you to decide.
|
||||||
|
|
||||||
|
## Stats
|
||||||
|
|
||||||
|
Sometimes Metroidvania-like games implement mechanics originating from role-playing games (RPGs).
|
||||||
|
|
||||||
|
A prime example is the introduction of stats.
|
||||||
|
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.
|
||||||
|
This scale factor is determined by the entity's stats (aka attributes).
|
||||||
|
|
||||||
|
These stats change over the course of the game.
|
||||||
|
Typically, the player character's stats are improved by earning experience points (XP) and levelling up.
|
||||||
|
Experience is often earned by slaying enemies, completing quests, and alike.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## AI
|
||||||
|
|
||||||
|
Enemies just standing around, doin' nothing ain't particularly interesting.
|
||||||
|
|
||||||
|
Even very simple and deterministic AI can be sufficient.
|
||||||
|
Like:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void do_something()
|
||||||
|
{
|
||||||
|
static bool moved_forward = true;
|
||||||
|
|
||||||
|
if (player_in_range()) {
|
||||||
|
attack_player();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moved_forward) {
|
||||||
|
move_backward();
|
||||||
|
} else {
|
||||||
|
move_forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
moved_forward = !moved_forward;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, one can add randomness to make enemies less predictable.
|
||||||
|
For instance: one out of ten times, use a different attack which is faster and does more damage.
|
||||||
|
|
||||||
|
Leverage randomness and other factors to create interesting boss AIs.
|
||||||
|
The distance to the player could determine which kind of attack the boss uses.
|
||||||
|
The player's health could dictate how aggressive the boss becomes.
|
||||||
|
The number of possibilities is endless.
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
For simplicity, the AI can be hardcoded in the logic of an enemy.
|
||||||
|
|
||||||
|
Alternatively, you may want to look into [behaviour trees](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)).
|
||||||
|
|
||||||
|
![Behaviour Tree Example](images/behaviour_tree_example.png)
|
||||||
|
|
||||||
|
## HUD and Menus
|
||||||
|
|
||||||
|
The head-up display (HUD) is a crucial component as it conveys important information to the player.
|
||||||
|
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.
|
||||||
|
|
||||||
|
![Castlevania Circle of the Moon Hud Bars](images/ccotm_hud_bars.png)
|
||||||
|
|
||||||
|
*source: http://castlevania.wikia.com*
|
||||||
|
|
||||||
|
Not all aspects of the game can be done (reasonably) by moving the player character around.
|
||||||
|
Certain tasks require a menu system.
|
||||||
|
This starts at the main menu of the game, along with the pause and option menus.
|
||||||
|
|
||||||
|
![Castlevania Circle of the Moon Pause Menu](images/ccotm_pause_menu.png)
|
||||||
|
|
||||||
|
*source: https://strategywiki.org/wiki/Castlevania:_Circle_of_the_Moon*
|
||||||
|
|
||||||
|
The pause menu displays the players stats (health, attributes, experience etc) and provides access to various submenus – inventory management among them.
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
SDL provides basic shape drawing functionality.
|
||||||
|
With it, you can easily build an abstraction layer for creating rudimentary user interface (UI) components.
|
||||||
|
Alternatively, you can simply use images.
|
||||||
|
|
||||||
|
SDL_ttf allows you to use fonts for printing text.
|
||||||
|
Please supply used fonts along with all assets.
|
||||||
|
|
||||||
|
## Entity Component System
|
||||||
|
|
||||||
|
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).
|
||||||
|
While this paradigm is probably overkill for a small project like this, you may scavenge some of its ideas.
|
||||||
|
|
||||||
|
In ECS, entities are just containers for components.
|
||||||
|
Components, however, determine how the associated entity behaves, how it can be interacted with, and so on.
|
||||||
|
|
||||||
|
Take the player entity for a moment, in terms of the ECS architecture, it would have:
|
||||||
|
|
||||||
|
- a component for rendering sprites
|
||||||
|
- a component which deals with animations
|
||||||
|
- a component which emits sound
|
||||||
|
- a physics collider or bounding box
|
||||||
|
- a component which models health
|
||||||
|
- …
|
||||||
|
|
||||||
|
You basically create a huge toolbox of little things (components) that can be mixed and matched to compose a full-blown entity.
|
||||||
|
|
||||||
|
![Unity Inspector](images/unity_inspector.png)
|
||||||
|
|
||||||
|
The figure above shows the inspector window of the [Unity](https://unity3d.com/) editor.
|
||||||
|
The inspector displays the components attached to the selected entity.
|
||||||
|
Here, the entity named *Cube* consists of a *Mesh Filter*, *Box Collider*, and *Mash Renderer* component.
|
||||||
|
The *Transform* is part of the entity itself and gives it its position, rotation, and scale.
|
||||||
|
As can be seen from the screen-shot, each component possesses properties that can be tweaked, like, the material used for mesh rendering.
|
||||||
|
|
||||||
|
This leaves us with the *system* part.
|
||||||
|
Each system (audio system, physics system, rendering, *whatever you can think of*) interacts with the associated components possessed by entities.
|
||||||
|
|
||||||
|
## Main Game Loop
|
||||||
|
|
||||||
|
Often, in addition to ECS, games use the *main game loop* paradigm.
|
||||||
|
In combination with ECS this is commonly presented as a straightforward sequence:
|
||||||
|
|
||||||
|
1. Take the current game state.
|
||||||
|
2. Invoke all systems.
|
||||||
|
Each system generates events based on the game state.
|
||||||
|
For instance, one event indicates that the jump button is pressed, another that two entities collide, and so on.
|
||||||
|
3. Gather emitted events in a queue.
|
||||||
|
4. Process each event one after another, mutating the game state.
|
||||||
|
Upon exhausting the queue, the next game state is reached.
|
||||||
|
5. Move on to rendering.
|
||||||
|
6. `goto 1`
|
||||||
|
|
||||||
|
See also this figure, taken from *[How to Think Like a Computer Scientist](http://openbookproject.net/thinkcs/python/english3e/index.html)*:
|
||||||
|
|
||||||
|
![Main Game Loop Example](images/game_loop.png)
|
||||||
|
|
||||||
|
However, what looks easy on paper can be rather challenging in reality.
|
||||||
|
First, certain events may stay in conflict with each other.
|
||||||
|
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.
|
||||||
|
Now, do you move the player up, or away from the projectile due to knock-back?
|
||||||
|
Or both?
|
||||||
|
And what if a wall is there preventing the player character from being pushed in this specific direction.
|
||||||
|
|
||||||
|
Even in this tiny example, the order in which events are processed matters.
|
||||||
|
They may even build circular dependencies with each other.
|
||||||
|
|
||||||
|
As it turns out, such an *invoke everything first, ask questions later* approach comes with a lot of problems.
|
||||||
|
One way to improve upon this is by doing stuff in phases, plus ensuring that the modified game state is always valid.
|
||||||
|
|
||||||
|
The whole topic of game engine architecture is huge and most decisions are trade-offs.
|
||||||
|
I recommend that you try not to over-think this.
|
||||||
|
Keep your cool and experiment until you find something that kinda works.
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
Develop debugging utilities as early as possible.
|
||||||
|
Consider the following:
|
||||||
|
|
||||||
|
- log output
|
||||||
|
- show bounding boxes, game entity positions, damage numbers etc; on screen
|
||||||
|
- *noclip* (disable collision + player can fly)
|
||||||
|
- *god mode* (player doesn't take any damage)
|
||||||
|
- infinite ammo / mana / resources
|
||||||
|
- spawning entities in front of the player
|
||||||
|
- directly load a specific map
|
||||||
|
- goto x y
|
||||||
|
- …
|
||||||
|
|
||||||
|
Another helpful technique is the use of fallback assets.
|
||||||
|
Let's say, a certain sprite cannot be loaded for whatever reason.
|
||||||
|
Instead of crashing the application with a cryptic error code, log a warning and use an *error sprite* instead.
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
|
## Goals (11 Points)
|
||||||
|
|
||||||
|
- (2) two consecutive areas, each:
|
||||||
|
- with its own tone (architecture, sprites, music, enemies etc)
|
||||||
|
- with a handful of rooms each
|
||||||
|
- a boss guarding an item / ability
|
||||||
|
- (2) basic movement
|
||||||
|
- running left / right
|
||||||
|
- jumping
|
||||||
|
- interacting with game entities (doors, chests etc)
|
||||||
|
- (1) save / load with dedicated save points (rooms)
|
||||||
|
- (1) one or more advanced movement mechanics
|
||||||
|
- eg double jump, wall jump, sliding
|
||||||
|
- needs to be unlocked
|
||||||
|
- (1) main combat
|
||||||
|
- melee and/or ranged
|
||||||
|
- hitting enemies
|
||||||
|
- getting hit by enemies
|
||||||
|
- (2) enemies
|
||||||
|
- attack the player character as it gets in range
|
||||||
|
- variants with melee attacks
|
||||||
|
- variants with ranged attacks
|
||||||
|
- bosses are capable of using different attacks
|
||||||
|
- (1) map
|
||||||
|
- shows discovered rooms and their doorways
|
||||||
|
- shows discovered save points
|
||||||
|
- shows where the player is currently located
|
||||||
|
- (1) menus
|
||||||
|
- main menu
|
||||||
|
- new game
|
||||||
|
- load game
|
||||||
|
- exit
|
||||||
|
- pause menu
|
||||||
|
- shows player stats
|
||||||
|
- inventory management
|
||||||
|
- continue
|
||||||
|
- go to main menu
|
||||||
|
- game over
|
||||||
|
- player dies
|
||||||
|
- player defeats final boss
|
166
assignment2/rpg_specification.md
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
# RPG Specification
|
||||||
|
|
||||||
|
We describe a simple 2D RPG with two layers.
|
||||||
|
|
||||||
|
The first layer is an interactive *overworld* which is explored by the player.
|
||||||
|
There, the player can talk to other characters and manage their resources (items, equipment, …).
|
||||||
|
|
||||||
|
The second layer is a turn-based combat system as typically found in JRPGs.
|
||||||
|
|
||||||
|
For references, have a look at these titles / series:
|
||||||
|
|
||||||
|
- Octopath Traveler
|
||||||
|
- Final Fantasy
|
||||||
|
- Chrono Trigger
|
||||||
|
- Pokémon
|
||||||
|
- Persona
|
||||||
|
- Trails of Cold Steel
|
||||||
|
|
||||||
|
After playing some gam-… ehh… doing some research, you should be familiar with the core game concepts and mechanics of the genre.
|
||||||
|
|
||||||
|
Before moving on, I want you to make up your mind about the setting your game takes place in.
|
||||||
|
While a fantasy world with magic and monsters sounds straight forward, don't overlook other possibilities like cyberpunk, steampunk, post apocalypse and so on.
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Assets
|
||||||
|
|
||||||
|
But, what about all the assets (sprites, music, sound effects, …) that are required for such a game?
|
||||||
|
|
||||||
|
Well, you don't have to look far.
|
||||||
|
As this is still about learning C++, I don't expect you to spend your precious time on creating all of them by hand.
|
||||||
|
There are megatons of assets for free- / non-commercial- / educational-use available online.
|
||||||
|
I am pretty sure you'll find something among them which suits your setting.
|
||||||
|
Maybe some slight modifications are needed here and there, but nothing that should distract you from learning C++.
|
||||||
|
|
||||||
|
For various types of assets — sound effects especially — there are also generators available which can be tweaked for unique results.
|
||||||
|
Pick your poison!
|
||||||
|
|
||||||
|
## Building the World
|
||||||
|
|
||||||
|
As already mentioned, the first layer of your game is an overworld where the player interacts with other characters and so on.
|
||||||
|
This overworld is typically composed of safe-zones (like villages or small settlements) and wilderness where enemies are encountered.
|
||||||
|
Often, certain sections of the overworld remain gated off until the player has advanced far enough into the story.
|
||||||
|
|
||||||
|
Here is the overworld of Pokémon Red and Blue (1996)
|
||||||
|
|
||||||
|
![Pokémon Red and Blue Map](images/pokemon_map.png)
|
||||||
|
|
||||||
|
### Overworld Implementation
|
||||||
|
|
||||||
|
A 2D array of tiles can be used to describe the map itself.
|
||||||
|
Entities (characters, chests, enemies, …) can be added by simply stating their coordinates.
|
||||||
|
|
||||||
|
I recommend to put the world definition into a dedicated file, following a human-readable, easy-to-parse file-format.
|
||||||
|
Alternatively, just put the definition in your code.
|
||||||
|
|
||||||
|
## Viewing the World
|
||||||
|
|
||||||
|
[Sprites](https://en.wikipedia.org/wiki/Sprite_(computer_graphics)) are essentially bitmap images with transparent background.
|
||||||
|
They are used for everything that needs to be displayed.
|
||||||
|
|
||||||
|
The *transform* (position, rotation, scale) of an entity determines where and how the corresponding sprite is rendered.
|
||||||
|
|
||||||
|
### Animations
|
||||||
|
|
||||||
|
Dynamic entities, like the player character (PC) or enemies, often feature animations.
|
||||||
|
These start with simple idle and walking animations, but can also turn into breath-taking combat and dodging choreographies.
|
||||||
|
|
||||||
|
![Sprite Map](images/sprite_map.png)
|
||||||
|
|
||||||
|
The key idea is to use multiple sprites instead of just one.
|
||||||
|
Each animation frame is a new sprite.
|
||||||
|
When an animation is played, the related sprites are displayed one after another to simulate motion.
|
||||||
|
|
||||||
|
Multiple sprites are combined and put into the same image, which is then called a *sprite map*.
|
||||||
|
The rendering API allows us to select which part of the image to use for rendering.
|
||||||
|
A simple rectangle selection is used to pick the sprite for the current frame.
|
||||||
|
|
||||||
|
## Hearing the World
|
||||||
|
|
||||||
|
Commonly, the background music is either determined by the section of the overworld or enemy encounter (regular battle theme, boss battle theme, …).
|
||||||
|
The selected tune is repeated until the player moves to a new section or finishes the encounter.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Audio Implementation
|
||||||
|
|
||||||
|
SDL_mixer is probably all you need for this.
|
||||||
|
It provides separate sound channels, each with its own volume setting.
|
||||||
|
Sound effects are played on different channels than background music so they do not interfere.
|
||||||
|
|
||||||
|
If you wanna go for something fancy, with more features, have a look at [OpenAL].
|
||||||
|
|
||||||
|
## Interacting with the World
|
||||||
|
|
||||||
|
Most interaction between the player and the game world is done through message boxes and menu systems.
|
||||||
|
For this, you need to implement some basic UI system.
|
||||||
|
You already know how to render sprites, font support is available via [SDL_ttf].
|
||||||
|
|
||||||
|
### Interaction Implementation
|
||||||
|
|
||||||
|
Often, a scripting engine is added to the game engine for scripting the interactions between characters, combat, and so on.
|
||||||
|
However, this goes beyond the scope of this project.
|
||||||
|
|
||||||
|
My recommendation is to simply hard-code the logic and script of your game in C++.
|
||||||
|
Think about creating your own little embedded domain-specific language (EDSL) on top of C++.
|
||||||
|
Consider trying [Boost Coroutine](https://theboostcpplibraries.com/boost.coroutine) for adding asynchronous programming paradigms.
|
||||||
|
|
||||||
|
## Story
|
||||||
|
|
||||||
|
Again, the focus of the side project lies on learning C++.
|
||||||
|
Your RPG should feature a short story with a twist near the end.
|
||||||
|
Nothing fancy, just something to keep the player interested while exploring the overworld.
|
||||||
|
|
||||||
|
## Combat AI
|
||||||
|
|
||||||
|
Similar to your other game logic components, the combat AI may be hard-coded in C++.
|
||||||
|
[Behaviour trees](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)) are often utilised for creating game AI.
|
||||||
|
Maybe give them a try.
|
||||||
|
Consider adding randomness to your decision making progress to make enemies less predictable.
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
Develop debugging utilities as early as possible.
|
||||||
|
Consider the following:
|
||||||
|
|
||||||
|
- Log output
|
||||||
|
- Show details (positions, damage numbers, decision making process, …) on screen
|
||||||
|
- *God mode* (player's combatants can't die)
|
||||||
|
- Infinite resources (items, spells, …)
|
||||||
|
- …
|
||||||
|
|
||||||
|
Another helpful technique is the use of fall-back assets.
|
||||||
|
Let's say, a certain sprite cannot be loaded for whatever reason.
|
||||||
|
Instead of crashing the application with a cryptic error code, log a warning and use an *error sprite* instead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Goals (11 Points)
|
||||||
|
|
||||||
|
- (2) Overworld with 2 different sections:
|
||||||
|
- Each with its own tone (architecture, sprites, music, enemies, …)
|
||||||
|
- The second is only accessible after some story progress
|
||||||
|
- Player can roam the world and interact with other entities
|
||||||
|
- (1) Characters
|
||||||
|
- Player can talk to other characters
|
||||||
|
- (1) Resources
|
||||||
|
- Player can manage acquired resources through dedicated menus
|
||||||
|
- (1) Stats
|
||||||
|
- Player's combatants have stats that influence the combat
|
||||||
|
- Player's combatants get experience from combat, increasing their stats
|
||||||
|
- Stats are influenced by equipment
|
||||||
|
- (3) Combat
|
||||||
|
- Turn-based
|
||||||
|
- Player selects which attacks, spells, items, etc. to use on which target
|
||||||
|
- Enemies use attacks, spells, items, etc. to combat the player
|
||||||
|
- *Game Over* when all of player's combatants are dead
|
||||||
|
- (1) Save points
|
||||||
|
- Player can save her progress at specific points in the game
|
||||||
|
- Saved progress is persistent across play sessions (application termination)
|
||||||
|
- (1) Audio
|
||||||
|
- Background music
|
||||||
|
- Sound effects
|
||||||
|
- (1) Main menu
|
||||||
|
- New game
|
||||||
|
- Load game
|
||||||
|
- Exit
|