Thanks! Yeah the devlog were made a few years ago. I stopped because no one were reading hehe.
Now i might add some more (or even post mortem) 👀
Optimizing a 2D Plataformer
Well, this is a never ending topic. There are a trillion ways to optimize a game. Not that Dogurai is a heavy game that needs to run at 900 FPS or would run at 5 fps in a bad PC. But, you know, it's always good to have something running smoothly.
So far, what takes more time to process on every frame of the current game is rendering. I'm using an old OpenGL 1.X with SDL2. As a side project, I'm porting the engine to run on
modern OpenGL and be easy to port for new libraries (or even consoles; we might need that). I'm no specialist in OpenGL and I am still struggling to make it right, so this devlog will be on the logic side, especially collision.
As I was told in college, the process of collision detection is usually an operation with magnitude O^2 which means that for every object, a loop on every other object is needed. So, having 100 objects means 1000 collision checks. In Dogurai, most of the checks are based on rects.
But, you know, that is not always necessary.
In Dogurai, I created two types of object lists. Collision and Creature. So in that way, thinking we have 100 objects, but only 90 are collision blocks or platforms and the other 10 are the player with all creatures in a given map, there is no need to do a big O^2 operation. Collision blocks and platforms don't interact with each other, and neither do monsters, unless it is with the player.
That removed a HUGE part of all the branches needed to check the collision. Now, going further there is no need to check collision of the player on EVERY FRAME. If the player is not moving, there is no reason to check collision with the collision blocks (only with monsters).
This is a screenshot of the game showing the collision box of everything:
All the collisions block were placed in the editor. Each on its grid, creating the ground and the ceiling. That can be improved, in Dogurai maps there are no slopes or curved blocks. We can take advantage of that by doing a merge on each tile side by side creating a single long block:
(Note the bottom ground is missing because it's a rendering optimization that removed it because it's too far)
Where before a level would easily have around 4000 collision blocks, this number is now less than 500. That's something!
Another improvement was made because of one of my decisions. The collision works based on the current speed multiplied by the delta time (the time between each frame), and sometimes a given object can be moving fast, or there can be some kind of frame spike due to a screen resize or something, which can cause an object to enter a little inside another. Having a player stick inside a wall is not good. Another thing that can happen is that the speed doesn't match the position exactly and the player stays 1 pixel away from the ground... That's basic collision in games. So I made a function to check if the collision happened, it adjusts the speed to "perfectly" match where the player should be. This is a little heavy compared to a single collision check. But it happens only on collision.
Okay, that's a little heavy but not that much.
Now think about it. The game can be modded using Lua scripts. Most of these optimizations gave some juicy extra FPS... Using that in Lua would drain it. Collision check in a script language is terrible. So most of the functions are native and can be called without risking the performance. Yet the need might come to step through objects that might collide with the player. Based on that, I changed things so most of the collisions are managed by a function that returns a list of objects near the object.
Here is a gif. When a tile gets lit, it's because the collision is being checked and is considered near:
Using a cheap collision with low accuracy to detect near objects, then on only those (usually 2 or 4 objetos) the precise collision is checked. Also, handling 2~4 instances on a Lua script won't kill the frame rate of 300 fps.
And yes, that's only for the collision group. Overall, I reduced checks from 3000 to ~500 cheap ones and 2~4 medium-expensiveish ones.
There might be better ways. I am no master when it comes to optimizing stuff and some are really overkill yet those were super fun to make.
First I am apologizing for this late devlog. These past few weeks were a little busy, with BiG Festival going on and later on BRING (wich is an indie showcase event from ouw town), so I didn't get much time to do these before now.
During this BRING I've talked with some ppl about finetuning the game.
2 years ago, on the 6th or like 8th edition of BRING, me and Thiago decided
to show up with 3 games on the festival. Those 3 were Rat Trap, Snake Scape
and Dogurai. Those two first games needed only a few modifications. Dogurai needed
a complete remake, mostly because the version we had was from GBJam 2014 and that
was not even close to the quality of the games we had at the time.
So during a week I had to remake everything, with Guax at my side
telling me to do something here and there, to increase/reduce the animation time of some things.
Some were completely... how do I say... had to be perfect, like: "move 1 pixel left
and decrease the collision box with 2 pixels. Also activate immunity only during frame 5"
or "during frames 1-3 he have to reduce the speed, then on frame 4 the damage is done, but
the others he wont deal damage and do something else."
See, its not real hard to put it to work, but there's so many small things here and there
that it becomes a enormous load of work.
The game was "ready" and we went to the festival. On the first hours, people really liked the game,
but not that much. The game was still raw. Can you tell when a beef is cooked? Like, can you
really say 1 second ago it was raw, now its cooked? Its not something you tweak here or there
and the game become nice.
The game was preety raw (or rare?), and all the awesome pepopple playing and giving a feedback about
the controls is what cooked the game in high fire on the oven. I say that because as peopple gave the
feedback, I fixed right away during the festival. Many small issues where like
* when you fall from a block, you already fall on full speed. Not accelerating from 0 to full speed
* when jumping, if you touch the ceiling, you stay in the air until the gravity pulls you back again
And so on.
This made the game a little more enjoyable.
During the next weeks, we've been doing the finetune. Increase from 8 pixels per second the player speed
to 8,5. The gravity form 3 to 3,8. The animation speed of a given frame was 100 ms, we changed to 85ms
Adding a small block near a big jump, so it wont kill the player, but shows the path or where a enemy stands.
Removing the hability of a flying enemy to shoot during some plataforms, it's a way to make it easier without
making it trivial.
And this made the game feel so good. Small changes. Decimal values were added/removed. Those were
Past BRING, we met a guy who said on the first time he played the game he did not like it. He said now he
really enjoyed the game and loved it. He was playing the first level and then went playing the whole game
(even the unfinished levels). And he was playing that with a huge smile.
It doesn't matter that much if the game have a cool mechanic (the quick time event combo), or cool boss fights... If the gameplay
feels a little clunky that can make a nice game be a medium-low game.
The process of creating any level in Dogurai takes so long because every enemy, every new thing we add
has to be really well thought out and tested lots of times to make sure it fits nice there and feels nice too.
Today`s devlog will be about the inconsistent style of technology used on level creation (or rather how the tools evolved over time
As i said in the last devlog, the game itself was a learning experience. During its creation time, we were constantly learning something new. Because of that, every new level, every new feature meant a new tool or resource we could use or do something in a better way. If someone ever does any data mining in the game and gets access to the game assets, they will know what I am talking about.
It started back in 2014, during the Game Dev class at the University of Brasilia, Thiago and I made a small rogue-like game and a level editor for it. Since the course demanded the use of C++ as the programming language, I made the skeleton of the actual frameowrk back then. Once the Jam began the editor was adapted to make a 2D plataforming game.
Only in 2016, when we decided to work seriously on the game, we had to change some things. The first level to be made, rather the Sewer one, was the Desert. Back then, the level was only tiles, collision blocks, monster spawn pointns, health items and plataforms (also 2 more blocks to spawn the player and the boss, respectively). Since this level had the bike gimmick I created a block called QuickTimeEventBlock.
So far so good, the editor had an option to add a text or a number up to 255 times per block so it was something like: add the block and set a text named “bike”. In the game code, a few functions were used to determine the behavior of the block. Then the third level was being made, the water one (blue). In this level you encounter moving plataforms, pistons and conveyor belts. These gimmicks couldn’t be made as quick time events, since those blocks would interact and all… they just wouldn’t fit. So a new type was created, the SpecialBlocks. That would help in creating any game objects and things out there that had some other interaction with the player.
By that time, I did not think about unifying everything in a single block type, mostly so I wouldn’t have to remake the levels that were already stable.
Then, came the Lua interface in the game. The Lua block was created. During the developement of the interface, the fire level was being created, so only a few things in there are in Lua. When I was about to finish it, the ice level was almost finished. The ice blowers in the level are in Lua.
Now the level 6 is a mix of all of those before. No more words.
And level 7 is… what can I say… pretty. 95% Lua, including its unique monsters and its boss.
I could remake everything now, right? Yeah. Nowdays, I could completely delete the QuickTimeEventBlock so I would have to remake only a few things in the desert level, but that doesn’t mean I’m going to. The game is already far more delayed than we planned to go back and remake mechanics from previous levels that were already tested a billion times, working just fine… it’s not a option.
Well, this might be the first but not the last devlog.
I am Matheus Almeida, also know as Mock the bear, the programmer of Dogurai.
Since ive seen so many devlogs out there, i think its time to start one as well
That way, anyone who likes the game will be able to followup the development.
Im planning to write this weekly until the release.
Also talk about the current stage of the game, about solution we had, minor features
major bugs we encountered and so on.
On this very first devlog, i was thinking in bring to you something about the core of the game.
I said before, the game WAS beeing made entirely on c++ from an framework i made on college.
That might made some think: wow, that is going to take ages! Sincerly not, it took me ages to build up my
framework to the point its usable. Might went a little faster if i was using unreal, unity or just another language?
Yeah, pretty sure.
Yeah i know, i took the long way. But if i had the oportunity to go back in time, i would done the same thing. All the things i learn
from doing this (and the framework), is something that no book or teacher could teach.
The thing is, all of us are overloaded with work at some point, but not on a consistent rate. Some of the time
someone is idle while other have 1000 things to do. The most busy is the guy making all the sprites (<3),
he is putting his heart and soul on this, and as you seen, came out very nice!
He is the one who have most things to do, so most of the time, i have litarally nothing to do unless wait for
the sprite sheet of some game thing. See, i can program a boss or some enemy without its sprites, but at some time
i will have to remake like 60% of it, because finicky thing like: the player should rise at the frame 4 and wait like
0.3833843 seconds until change for its frame (12,3) then go back and loop animation 7 times increasing the speed [...]
Most of the hard work to me is sync all the actions with the animations and some pixel/timing perfect alignment.
As i have some spare time, i keep rewriting some features to make it better or faster. One of the features i added during
a long idle time while the artist, Thiago, was doing a HUGE work on the tiles and enemys, i decided to add some Lua integration,
mostly for modding.
My motivation was at first create an easy way to make monsters, bosses and game mechanics without adding a new class/object every time,
that was getting really messy. My code style is more like lets make everything reusable or easy to change. Adding an final object
not mutable to the core game made me a little unconfortable. I mean, its the right thing to do, you add an object called Door, and its
juts a door. No need to pass some crazy data to it behave like a piston or something... That bothers me somehow.
If i did something like that, at some point, one object hour inherit from that and be just a door... sooo, yeah.
So, during long 2 months (some times i had to stall it to focus on the main game), i was working with some dark c++11 features
and dark magic (templates) to make a easy way to bind game objects and any other instance i want with two lines and it works on lua.
Worked so well, now most of the game mechanics are written on Lua and take me like, 3 mins to write something that
would take 30. I have less control of the game on Lua also i can make something simple as a door within a fraction of the time.
Now the interface seems stable (its been more than 2 months i found a bug), and here how looks like some code that
create a object that can be a door. When the player collide with it, set a flag, once the player leaves it become solid.
I know, the player can collide and go back, then block the way... Was just simple example.
So yeah, See you next week.