Tick Time Debt
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;
Super::Tick(DeltaSeconds);
... <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.)
Note: This article originally appeared on my Tumblr and was reposted here in 2019.
Posted: 2014-09-18
Basic Bullet Scripting
Really basic bullet scripting is in!
I can queue up changes to any of the projectile state values like
acceleration, velocity, angle, angular velocity, max speed, and
radius. I can set it to happen at some time in the bullet’s lifetime.
Unfortunately, the blueprints for this are clunky and huge. So I need
to simplify and streamline it a bit.
Next up: Bullets spawning child bullets.
Note: This article originally appeared on my Tumblr and was reposted here in 2019.
Posted: 2014-09-17