This week I fixed a problem that has been popping up in our game every once in a while. Our game normally runs at smooth 60 frames per second, but every once in a while the game would start to stutter and just generally become very un-smooth, man. The stuttering would last a few seconds and then the game picked up again and was buttery smooth once more. I’ve got some profiling routines in our game to measure what is going on, but every time I started profiling when this effect would show up, it would magically disappear, and the profile data would show that we’re perfectly within the desired frame times. So what is going on?
Our main game loop waits for the video buffers to swap, and then it checks how much time has passed to determine how many times to step the game. A time-accumulator float value is used to keep track of the time elapsed since the game was stepped, which is a pretty basic practice in most games. It guarantees that the game is stepped at 60hz on average, over multiple frames. The general code looks something like this:
staticconstfloat GameTimeStep =1.0f/60.0f;
void Game::Update(float dt)
m_timeAccumulator += Util::Clamp(dt, 0.0f, 0.1f); // Clamp the value to prevent spiral of doomwhile (m_timeAccumulator >= GameTimeStep)
m_timeAccumulator -= GameTimeStep;
So in this code, m_timeAccumulator is the float value in the Game class that is increased by the delta time every frame, and then the game is stepped however many times to make sure the game gets simulated 60 times per second. This piece of code handles variable frame rates quite well, if the delta time dt is 1/30th of a second, the game will just step twice in a row, to maintain the 60x per second simulation rate.
(Oh, a small note about the spiral of doom. This happens when the game takes longer to step than ‘real time’. By capping the incoming delta time, we potentially slow down the in-game time, but at least the game doesn’t grind to a halt.)
Ok, we’re all good then, right? The code above should fix all our problems, no? Well, not exactly. The code above has one problem that is a bit hidden, and it only really shows up if the render rate is the same as the game step rate AND your time accumulator is very close to a multiple of your game step time. For example, in our game the problem only occasionally shows up when running in a mode that is 60Hz.
Hopefully the graph below will make it more clear:
Lots of info in the graph above, so here’s some explanation. The black line at the top shows the perfect 60x per second game update time we want to have. The green dots are measured frame times. We measure our elapsed frame time directly after a the swap-buffers call, using the highest resolution timers available. I have found that despite using the most precise measuring routines, there will still be a bit of variation. In the graph you can see that the green dot are somewhat randomly offset from the perfect rate we’d like to have, this is the variation. And this variation is what is causing the problem I’m talking about. Sometimes the measured frame time is a bit before, sometimes it’s a bit after the actual time.
The perfect amount of game steps to take every frame in this case (60Hz render, 60fps game step) is of course 1. In fact on the console games I’ve shipped, I sometimes just hard-coded one game step per render frame, but on PC’s with wildly varying video modes and video cards, this isn’t viable. The preferred game steps are represented by the blue outlined boxes for each rendered frame. However, using the code above, the actual game steps done per frame is shown by the magenta boxes. This shows that in the first render frame, the game isn’t stepped at all, and the next render frame steps the game twice to make up for it. Then there’s another frame of zero game steps, etc. In other words, very un-smooth behaviour.
Now, how did I fix this? Well, in our game it doesn’t really matter too much if the game step is running at exactly 60 fps, or if it’s running at a slightly higher rate. So, I introduced a fuzz factor:
The fuzz area is basically a time difference that is ‘acceptable’ when determining how many times to step the simulation. It’s only really used ‘downwards’, so for example if your game step is 10ms, and you get a frame time of 9ms, you might still deem that acceptable, and step the game. If your frame time was 12ms, then the fuzz factor isn’t technically needed, and the game is stepped.
Note that the frame steps are exactly where we want them! Buttery smooth. The game update code now looks like this:
The fuzz factor is set to 0.96, which basically allows the game to speed up from 60Hz to about 62.5Hz.
Since I put in the code above, the occasional stuttering when running in a video mode of 60Hz seems to have disappeared from our game. When running at different frame rates such as 50Hz the game will still ‘catch up’ every once in a while causing a one-frame jitter. This is distracting and not preferable, but I don’t currently know of a way to fix this other than running the simulation at a much higher rate to get more granularity (not really feasible in our case), or by interpolating everything in the renderer (which might introduce frame lag, which is also undesirable).
Anyway, I am happy this problem is no longer an issue, and thought I’d share the idea if you’ve seen problems similar to this. Yay, one step closer to shipping! :)
Alright, that’s it for this week, keep those frames buttery smooth!
As usual, today at 4pm there will be another Dev Stream with Jouste the Drawbarian. Don’t miss it!
This week is a follow up to Dev Blog 197, which was the first dev-blog on networking. I’ve been working on getting the networking system up and running for Viking Squad, and there are a ton of little peculiarities I could talk about, but I’ll start with a bit more of the basics, in particular about how to squeeze all the unused bits out of your precious network bandwidth.
After setting up the low level networking system described in Dev Blog 197, we are now able to send and receive messages from remote clients. These messages get bundled up into a network packet and sent, only to be broken into individual messages again when they reach the other side. Now, bandwidth is always a concern, so we want to try and minimize the packet sizes, which means minimizing the size of the message that get sent.
If you’re familiar with C#, you’re probably aware of the BinaryReader and BinaryWriter classes to save and load binary data. These are constructed with a stream, and then you can read and write different data types to these streams.
For example, say we have this hypothetical entity that we need to save to the network. (Note: this isn’t what our internal entity class looks like, this is just an example to illustrate what I am talking about). This is what the code would look like:
private UInt16 m_entityID; // Unique ID to identify the entity byprivate Vector3 m_position; // 3D Position of the entity in the worldprivate Vector3 m_velocity; // Velocity of the entity in the worldprivatebool m_onGround; // Boolean saving if this entity is on the ground or not.// .. More data herepublicvoidSaveToNetwork(BinaryWriter writer)
writer.Write(m_entityID); // 16
writer.Write(m_position.X); // 32
writer.Write(m_position.Y); // 32
writer.Write(m_position.Z); // 32
writer.Write(m_velocity.X); // 32
writer.Write(m_velocity.Y); // 32
writer.Write(m_velocity.Z); // 32
writer.Write(m_onGround); // 8// 216 bits total = 27 bytes
m_entityID = reader.ReadUInt16();
m_position.X = reader.ReadSingle();
m_position.Y = reader.ReadSingle();
m_position.Z = reader.ReadSingle();
m_velocity.X = reader.ReadSingle();
m_velocity.Y = reader.ReadSingle();
m_velocity.Z = reader.ReadSingle();
m_onGround = reader.ReadBoolean();
As you can see it needs 27 bytes to save the state to the network. The smallest unit of data that can be written using BinaryReader and BinaryWriter is a byte, which is actually quite large if you think about it. For example, when the SaveToNetwork function writes the m_onGround boolean value, it will write an entire byte (8 bits) to save the state of the boolean. That’s 7 wasted bits to write 1 bit of actual data!
Now, how about we take this one step further? How about saving all values using exactly the amount of bits we think it needs? And in our loading code, we use the same number of bits to read the value and store it in our variable. To make this easy to do, I implemented my own BitStreamReader and BitStreamWriter classes. These have the same functions to read and write data types, except they also require a number of bits, and in some cases a minimum and maximum value. Internally they pack data in bit by bit, making it possible to waste no bits when saving the entity state. If you’re interested in the internals of these streams, just click here to see what the C# code looks like.
To be able to save the maximum amount of space, we need to know what the limits are for each of our variables. The m_entityID is a 16 bit value, but we don’t see it ever going over 1024. In this example, we’ll assume that the X Coordinate is always between -150.0f and 150.0f, and the Y and Z coordinates are always between -6.0f and 6.0f. The velocity is always between -10.0 and 10.0.
Now how do we save this efficiently? Well, lets go through them one by one:
m_entityID: This value will never go over 1024, so we can save this unsigned integer by using only 10 bits.
For m_position, we’ll need to be able to save the floating point values using a specific amount of bits, while retaining a minimum precision. Say we determine we want a precision of about 0.02 units. To be able to save the X coordinate we would need to divide up (150.0 – (-150.0)) = 300.0 into 300.0 / 0.02 = 15000 parts. The nearest larger power of two would be 2^14 = 16384. So we’d need 14 bits to save the float and get a precision of 0.01831. For the Y and Z coordinates we do a similar calculation, and come to the conclusion that we can save using 9 bits, giving us a precision of 0.02343.
For the m_velocity we decide we don’t need as much precision, and we can handle 0.1 unit of precision. This allows us to save the X, Y and Z component using just 8 bits each.
The m_onGround just needs one bit to save, since it’s just a true or false value.
The code would look something like this:
private UInt16 m_entityID; // Unique ID to identify the entity byprivate Vector3 m_position; // 3D Position of the entity in the worldprivate Vector3 m_velocity; // Velocity of the entity in the worldprivatebool m_onGround; // Boolean saving if this entity is on the ground or not.// .. More data herepublicvoidSaveToNetwork(BitStreamWriter writer)
writer.Write(m_entityID, 10); // 10
writer.Write(m_position.X, -150.0f, 150.0f, 14); // 14
writer.Write(m_position.Y, -6.0f, 6.0f, 9); // 9
writer.Write(m_position.Z, -6.0f, 6.0f, 9); // 9
writer.Write(m_velocity.X, -10.0f, 10.0f, 8); // 8
writer.Write(m_velocity.Y, -10.0f, 10.0f, 8); // 8
writer.Write(m_velocity.Z, -10.0f, 10.0f, 8); // 8
writer.Write(m_onGround); // 1// 67 bits total = 8.375 = 9 bytes
m_entityID = reader.ReadUInt16(10);
m_position.X = reader.ReadSingle(-150.0f, 150.0f, 14);
m_position.Y = reader.ReadSingle(-6.0f, 6.0f, 9);
m_position.Z = reader.ReadSingle(-6.0f, 6.0f, 9);
m_velocity.X = reader.ReadSingle(-10.0f, 10.0f, 8);
m_velocity.Y = reader.ReadSingle(-10.0f, 10.0f, 8);
m_velocity.Z = reader.ReadSingle(-10.0f, 10.0f, 8);
m_onGround = reader.ReadBoolean();
Now, what we ended up with is a routine that can save the entity data with the precision we needed, in just 9 bytes. That’s 1/3rd of the original implementation! Of course this is a hypothetical entity, but you can see here that there are huge space savings to be had if we know the constraints of our variables.
Alright, that’s it for this week, I need to get back to more network implementation. Writing this blog post made me realize how many little interesting things I did to make the networked game work better. It’s probably worth another future blog post or two!
As usual, today at 4pm there will be another Dev Stream with Jouste the Drawbarian. Don’t miss it!
This week I’ll talk about a cool little visual effect I put in the game: Light shafts. It’s a pretty standard visual effect, so it’s definitely nothing ground breaking, but it does make the game look that much better for very little effort. It was very quick to implement (read: a nice distraction from the monstrous networking task :) ), and I really like the effect.
A while back on our twitch stream I worked on implementing a compute shader to calculate proper volumetric fog, much like the amazing Assassins Creed volumetric fog. I abandoned this approach because it was actually pretty difficult to make it look good in a side scrolling brawler. It was hard to control the fog, created a ton of weird blend mode issues (especially with additive blending) and it also raised the minimum spec because of the compute shader.
Instead of proper volumetric lighting, I’ve opted for a manually placed animated light shafts. This gives us more control over the look, and it also works well in our existing chunk building system.
Last weekend I was playing through Brothers, a tale of two Sons (which is a gorgeous looking game by the way), and they use light shafts in multiple places to create a really cool atmosphere. I wondered how to implement these, as they fluctuate the light over time, yet are always smooth. This blog is about what I came up with.
The light shaft is rendered using alpha blending, using a user-defined color, and using a calculated alpha.
The geometry is 4 triangles, with coded UV’s. the U coordinate is used as the actual U coordinate in the noise texture, and the V coordinate is just used as an alpha multiplier.
The alpha is the product of the user-defined color’s alpha, the sample from the texture, the V coordinate (used as alpha multiplier), and the edge smoothing alpha. The edge smoothing is simply using the (0-1) U coordinate as a parameter for a sine function in the shader to generate a multiplier between 0 and 1 that nicely blends the edges to zero.
The texture coordinates used to render the light shaft is using a thin horizontal sliver of the texture (stretched over the mesh), and is animated over multiple frames to generate the modulation effect in the light shaft.
Here’s a video of the effect in the game:
That’s pretty much it! Simple, but effective. I love putting things like that in the game.
Oh, and as a reminder, we moved our weekly dev-stream to Friday, because Jesse is still in Japan.
In a small development team of three, you often end up having to take care of many different things. In the last week I spent time trying to optimize my rendering, reducing temporary memory allocations in the game-play code, fixing continuity bugs in the network code, as well as updating our build machine to include Mac OSX builds. So many different things that it’s hard to chose one to write a blog about! Well, I’ll just pick the build machine one.
To get Viking Squad working on OSX, I’ve been working on converting the engine code to SDL 2.0 in my ‘spare’ hours over the last few months. Since my engine already supports multiple platforms (Windows, PS4, XBox, iOS, Android, etc), I’ve been able to concentrate platform specific code in a few small areas. Adding another target for SDL2 in those spots was fairly straightforward. Luckily I’d been keeping the OpenGL renderer up to date, so once I was able to create a window in SDL2, getting it to render to the window was easy. We’re using FMod for audio and besides linking the proper libraries there’s nothing I had to do to get it running. Great!
The next hurdle was to get game-pads working on OSX. At first I tried plugging in my wired XBox 360 controller, but I quickly found out that it’s actually not straightforward at all to get that working on a Mac. Third party drivers need to be downloaded and installed, which I don’t like, so I quickly grabbed my PS4 controller, and that worked straight away. I guess I will have to try installing the third party driver to make sure XBox 360 controller work properly at some point. The process of converting to SDL2 was so smooth, that I actually converted my windows build (running D3D11) to SDL2 as well.
Now the build was working properly, the next task was to get it building on a build machine. I had an older 13″ Macbook laying around, which I re-purposed to be our brand new MacOS build drone. I upgraded it to El Capitan, and since all my build tools are written in C#, I installed Mono to see if I would be able to run my console-applications. I was a bit startled that I could simply run windows EXE’s by typing ‘mono SomeProgram.exe’ in a terminal window. It just works. Just like that, I had a working OSX based build drone!
The current build process to upload a new build to Steam is as follows:
1. Build Windows RTM game, and copy to the network drive on completion.
2. Build OSX RTM game (in parallel as step 1), and copy to the network drive on completion.
3. Run a custom Steam uploader tool after both 1 and 2 are complete.
The steam uploader tool is another C# console app that checks if the latest Windows and OSX builds present on the network drive are exactly the same build number, and copies them into the proper place to upload to steam. In this step, the windows data files are copied to the OSX build path. Once everything is in place, and verified, an external tool is called to upload the build to Steam. That’s it!
All in all this took a fair amount of time to get set up, with lots of snags and issues along the way, but it’s all working now. Putting a new build on steam for both Windows and OSX is now only three mouse clicks (one for each step mentioned above). I wanted it to be 1 click, but I guess I can can live with three.
That’s it for this week. Keep those build machines build machining!
And, as usual, today at 4pm there will be another Dev Stream with Jouste the Drawbarian. Don’t miss it!
Last week we didn’t do much developing, but lots of networking. (That is, meeting people, socializing, going to parties, etc. Not actual network programming). Maybe we should have, but we didn’t plan any press meeting for our game this year. We’ve been working so hard getting the game done that it has become a bit of a marathon, so it was nice to take a break and see what everybody else is up to.
On Monday and Tuesday we attended the fantastic Independent Games Summit, which is pretty much the reason we go to GDC every year. It’s a lot of fun seeing talks by people who are in the same boat as you. The main GDC often has talks that don’t really apply to small indies.
On Wednesday I met up with a few new friends who are all into networking, which in this case IS about network programming. It was good to meet them face to face rather than as an icon on our Slack page! Good conversation was had, and I am hopeful that many more good conversations will follow. It’s good to have friends when you’re small.
On Thursday we visited the Twitch headquarters in San Francisco. Our good friend Mos toured us around the office and treated us for lunch. It was great to see the inner workings of Twitch, and man I’m kinda super jealous of their offices. So modern and so much room for activities!!
Coming back from GDC I always have a bit of a weird feeling. Meeting so many friendly peers is super awesome, and going to the parties is always a ton of fun. Seeing all the games people are working on is super neat as well.
Now I feel completely social-ed out though. I feel like I could work alone in an attic for half a year just to recover. Also, every year I come back wishing I was more prolific. Seeing all these cool games people are working on makes me want to make a few small games, instead of working on this giant multi year project we’re working on now. Viking Squad is taking up all of our energy, and I feel guilty when I spend time on anything else. Time to get this baby out the door and start working on toy projects again… soon. Soon. SOON!
Welcome back, followers of the fearsome! This week I fixed a problem that has been popping up in our game every once in a while. Our game normally runs at smooth 60 frames per second, but every once in a while the game would start to stutter and just generally become very un-smooth, man. The […]