# Hymn mini-devlog (Part 2) **Game link:** [Hymn](https://labg.itch.io/hymn) **Other parts:** [Part 1](https://hackmd.io/@4bNcOyqaSZ-TSF0OdM7r2Q/rywobJTVh) ![Thumbnail](https://i.imgur.com/Uxw0wGw.png) In last devlog, I wrote about how I framed the game's basic structure and collision detection and resolution. Collision, however important, is not all game's physics about. Today we will talk about another aspect of physics in Hymn down to the nitty-girtty. It is character's movement. ## I. Setup - Level editor While I was working on the barebone of our game, my teamates had been finishing off the arts and game mechanic drafts. The art is great and nothing much to say about . Game mechanic, on the other hand, proved to be a bit controversal between our group. Basically, the character is tasked to pass through several rooms. Each room is a level consisting of platform elements like walls and spikes. The player had freedom to move left and right, as well as jumping. We then decided some sort of power up would be the signature of our liltle game. For short, at the start of a level, players are given a fixed amount of *boost-s*, and they must use them wisely to jump over the platform to get to the exit door. Don't worry, there will be a lot of explaining in this devlog. To make a fine-tuned game mechanic, a lot of testing by the developers before the beta testing stage is required. I decided I needed to work on the level editor for that purpose first. A level editor should have these features at minimum: * Fast level building (can draw as efficiently as MSPaint pencil and shapes) * Multiple layers support * Customizable export * Quick pipeline when integrating to real game design A while back I made a study project featuring a map editor that allowed user to draw blocks in a texture into the canvas and export the data. The UI is super weird and UX is even worse: ![Protomap](https://i.imgur.com/58Ow974.png) I'm a bit dissapointed that this version does not satisfy any features I mentioned. However, having something to fix is better than building from source. So after a day of tweaking I eventually get to this: ![MapEditor](https://i.imgur.com/bZ4R95G.png) It does not look much visually, but I added customizable export, single-stroke brush style, and most importantly, collision blocks export. The output data is writtern in raw text file. For customising I would write a converter to my desired output. The game read files writtern in Lua. I chose it because it allows me to be more flexible when editing files, and already has C API. An output example: ```lua entities = 2 _1 = { c = 2, _1 = { t = "Transform", v = {box = {l = 128, t = 144, w = 16, h = 16}, rotation = 0} }, _2 = { t = "Render", v = {txtr = "Map", texcord = {l = 16, t = 112, w = 16, h = 16}} }, } _2 = { c = 2, _1 = { t = "Transform", v = {box = {l = 144, t = 144, w = 16, h = 16}, rotation = 0} }, _2 = { t = "Render", v = {txtr = "Map", texcord = {l = 16, t = 112, w = 16, h = 16}} }, } ``` Every entites are represented by a table, consisting of 3 elements: * `c`: number of components * Each component has: * `t`: component name * `v`: component data With all of that hooked up, I created a static test scene: ![MapTest](https://i.imgur.com/QSIa5cm.png) The jaggy lines in the background were used to see if the sprites are fit into tiles. Because of OpenGL rasterizing, not all of the lines are shown, but it was such minor problem so I did not try to fix it (it's only used in development phase anyway). ## II. Character base movement: Okay, we got everything ready for the most interesting part of the game: mechanics. Firstly, I implemented player's movements. Remember that Hymn's backbone is ECS system. So we need system to do the movement for us. With that in mind, I created `Movement` component: ```cpp struct Movement { float run_acceleration; float jump_height; // this variable is used for collision resolution bool on_ground; }; ``` Then, I will have a `PlayerControlSystem` that will update `Movement` component: ```cpp void PlayerControlSystem::update(sf::Time dt) { for (auto e : m_entities) { // Get components auto& movement = m_ecs->get_component<Movement>(e); auto& rigid = m_ecs->get_component<RigidBody>(e); // Update movement rigid.acc.x = 0; if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) rigid.acc.x = -movement.run_acceleration; if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) rigid.acc.x = movement.run_acceleration; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) { float gravity = std::abs(rigid.acc.y); // Set velocity so the jump will be at exact height rigid.vel.y = -std::sqrt(2 * movement.jump_height * gravity); } } } ``` This way, when the player presses movement buttons, the character will start to speed up and move to desired location. So we need a way to limit their speed, otherwise the player would just move faster and faster. One way to do it is to introduce friction in `RigidBody` component, then let the friction limits the player's speed in `RigidSystem`: ```cpp void RigidSystem::update(sf::Time dt) { for(auto& e : m_entities) { auto& tf = m_ecs->get_component<Transform>(e); auto& rg = m_ecs->get_component<Rigid>(e); rg.vel += rg.acc * dt.asSeconds(); rg.vel.x *= std::pow(rg.friction.x, dt.asSeconds()); rg.vel.y *= std::pow(rg.friction.y, dt.asSeconds()); } } ``` This code is closer to the physics in real life, however it's not always desired in 2D games. The problem is it lacks the *snappy-ness* that most 2D platformers provide. This is what I meant: ![Difference](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZDAxYWQ5ZjVmMjYzMzdiNGY2OTUxMzBhMmMyZDJlZGNhMmQxNzlkZCZlcD12MV9pbnRlcm5hbF9naWZzX2dpZklkJmN0PWc/XxqW1VdAXlhqLe7mMi/giphy.gif) Imagine your character is rushing and then breaks immediately because of the black pitch ahead. If they decrease their velocity by time (like in second figure), there is a chance that you will fall into the pitch without consent. I wanted to avoid that. So, I decided to just modify the velocity directly: ```cpp struct Movement { float speed; // ... }; void PlayerControlSystem::update(sf::Time dt) { for (auto e : m_entities) { // Get components auto& movement = m_ecs->get_component<Movement>(e); auto& rigid = m_ecs->get_component<RigidBody>(e); // Update movement rigid.vel.x = 0; if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) rigid.vel.x = -movement.speed; if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) rigid.vel.x = movement.speed; // ... } } ``` ## III. The boost