Fixed Timestep and Custom UGameEngine in UE4

Note: This article originally appeared on my Tumblr and was reposted here in 2019.

Okay. Fine. Everyone who told me to fix the timestep was right.

This is an update now that I’m finally back to tinkering with this project after dealing with a bunch of more important stuff (like getting a new job).

I got the math right for the projectile integration, then dealt with the weird time delta debt problem. Unfortunately, problems like the time delta debt permeated every single layer of the system. I solved the problem, but could not find a clean way to implement the solution that doesn’t make everything an unmaintainable, buggy mess.

This also means I’m abandoning the sync-action-to-the-music stuff, because the gameplay progression is now dependent on the framerate, and can quickly get out of sync with the music.

For the record, making UE4 do a fixed timestep for everything (to keep all the matinee stuff and whatever in sync) was about as difficult as the calculus to integrate the bullet movement, so it’s not like I was saving myself any effort either way.

I’ve also realized that I spent too much time worrying about making everything accessible to UE4’s Blueprints system. When going back and looking at the blueprints I actually made for the simple demo bullet pattern I had in there, they were a mess. Took me a while to figure out what the heck they were intending to do! I have no one to blame but myself for that one, I guess. The interface I came up with just doesn’t translate cleanly to blueprints.

As of UE4 4.5, the “hot reload” feature works pretty well. So I guess from now on I’ll just worry about having a C++ API for bullet scripting and I’ll rely on the hot reload for my fast iteration on designs.

So now onto the technical part of making UE4 use a fixed timestep.

Epilogue (added 2019-01-14)

I'm no longer working on this game in Unreal Engine. I started a project, called libdanmaku, to act as a scriptable gameplay system for making these Touhou-like bullet hell sh'mups. It's written as an independent library, with the original intent to use it both with Unreal Engine and possibly others. It used Lua as a scripting engine, instead of UE4 blueprints, and had an API that allowed an application using it to track in-game objects and represent them with whatever fancy graphics they wanted to, with all the logic, collision detection, bullet behavior, and so on being handled by the library.

The UE4 sh'mup project continued a bit using this gameplay engine, but eventually it was abandoned. The gameplay engine lives on, however. It's currently (as of this writing) being migrated from Lua to my own scripting system (Ninkasi) that I built specifically for this project. It's being used in a game with a custom graphics engine that's also due for a re-write!

One hell of a long project, but I love it, and I'm probably never going to stop!

Posted: 2014-09-20

Prototypey Bullet Patterns

Note: This article originally appeared on my Tumblr and was reposted here in 2019.

I got some simple bullet patterns up and running. This started off as a re-implementation of one of the samples that came with Danmakufu, but I messed with it a bit.

I’m still working on streamlining the Blueprints interface to bullet scripting.

And now, I sleep.

Posted: 2014-09-19

Tick Time Debt

Note: This article originally appeared on my Tumblr and was reposted here in 2019.

So despite all that fancy pants math I did, I was still having a problem with bullet positions.


Set up to fire at 1/5 second intervals, and with just enough velocity that each shot should come out just touching the tail of the last one, I was still ending up with weird gaps and inconsistent spacing between the shots.


This is my cooldown code to see if we should fire.


And after firing, add time to the cooldown.


(We should probably loop these until Shot Cooldown Time Left is greater than 0. That way if we slow down so much that the cooldown is passed multiple times in one tick, we just fire multiple bullets in one frame.)

Even simplifying the math down to nothing but linear velocity with no acceleration and no angle stuff didn’t seem to help. I was still getting inconsistent spacing. The more inconsistent the framerate was, the more inconsistent the spacing was.

It turns out there was something else about that condition where we pass so much time that we have to fire multiple bullets in one frame: They all end up stacked on top of each other, because they’re all really fired at the same time with the same velocity. It also turns out they get a full tick for the frame they were spawned on.

So there’s our problem. Bullets are actually getting spawned at the time of the tick call instead of our desired every fifth of a second. That’s why everything spawned in one frame is all stacked onto the same spot.

The solution here was to make a “time debt” that bullets need to make up for. This might involve dropping time from its first tick, or adding time to it.

When the shot cooldown timer goes negative, it means we should have fired a shot that many seconds ago, so shots need to make up for this time that they would miss out on. That’s pretty easy to handle. Just add to the time delta for its first keyframe.

void AShooterProjectile::Tick(float DeltaSeconds) {

    DeltaSeconds += TickTimeDebt;
    TickTimeDebt = 0.0f;


    ... <snip> ...


(Projectile code is all done in C++ instead of Blueprints.)

We also need to make up for the fact that it’s going to get the whole time delta for this frame, meaning it’s going to be simulating, effectively, before it ever existed. So I just subtracted the entire time delta for the frame we’re on.


With this code, its first tick (which happens on this frame) will only be the amount of time it should have from the moment it should have been created based on our shot cooldown thingy.

So what does it look like now?


Aww yeah! Pixel perfect spacing, even with a variable timestep! (Minus floating point inaccuracy and whatever.)

Posted: 2014-09-18

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 [ 51 ] 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

Previous | Next