Need help calculating acceleration of rotating object

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
argv
Posts: 184
Joined: Tue Aug 30, 2016 4:47 pm

Need help calculating acceleration of rotating object

Post by argv »

I have an actor that uses +ROLLSPRITE to continuously rotate. It has an angular velocity (degrees per tic), which determines how quickly it rotates and in which direction. The angular velocity can change (accelerate/decelerate), but only at a limited rate (e.g. ±1 degree per tic per tic).

I need this actor to spin down such that, when the angular velocity reaches zero, its roll is also zero. How do I compute the correct rate of deceleration (within the limit) to accomplish this?

I've attached what I have so far. To use it:
  1. Load the attached pk3 (with a Doom 1/2/TNT/Plutonia/Freedoom IWAD).
  2. give GrabSpinner
  3. use GrabSpinner
  4. Hold down your alt fire button to select a desired angular velocity. It's shown on the primary ammo counter, and increases by one per tic until you let go of alt fire.
  5. Hold down your fire button. A shotgun will appear in front of you and spin up to the angular velocity you selected. Its current angular velocity is shown on the secondary ammo counter.
  6. Let go of the fire button. The shotgun will spin down, then disappear once the angular velocity reaches zero. It should be straight and horizontal (roll=0) when the angular velocity reaches zero, but it'll almost certainly land on some other angle instead.
Attachments
spin.pk3
(7.82 KiB) Downloaded 28 times
User avatar
KeksDose
 
 
Posts: 595
Joined: Thu Jul 05, 2007 6:13 pm
Contact:

Re: Need help calculating acceleration of rotating object

Post by KeksDose »

The problem can be solved by seeing which angle you attain after t tics and equating them with your constraints. I assumed you do angle += ang_vel, ang_vel -= deceleration. I also assumed ang_vel is positive, but you can just switch the sign and check.

v0 is your angular velocity entering the stopping sequence (t = 0).
c is your deceleration constant.
q is how many degrees left to rotate until you're back at 0°.
q0 is your angle entering the stopping sequence.
T is for how many tics you want this stopping sequence to last.

I found this by setting:

Code: Select all

v(t = 0) = v0
v(1) = v0 - c
v(2) = v0 - 2c
...
v(T) = v0 - Tc
You set a constraint: v(T) = v0 - Tc = 0, then c = v0 / T.

Your rotation at any time is found by adding them up from a starting rotation q0:

Code: Select all

Deg(t = 0) = q0
Deg(1) = q0 + v(0)         = q0 + v0
Deg(2) = q0 + v(0) + v(1)  = q0 + v0 + v0 - c
Deg(3)                     = q0 + v0 + v0 + v0 - c - 2c
...
Deg(T) = q0 + v(0) + ... + v(T - 1) = q0 + (T - 1) * v0 - (1 + 2 + ... + T - 1) c =
       = q0 + (T - 1) * v0 - 0.5 * T(T - 1) c
The last equals sign uses the Gauss summation formula for the coefficient of c.

Then we want Deg(T) = q0 + q, that is, (T-1)*v0 - 0.5 T(T-1) c = q. This is a linear equation in c and easily solved:

rate = 2/T * ( ang vel - degrees left / (T-1) )

From before, we had rate = ang vel / T. With that equation we find that degrees left / ang vel = T, which appears physically sensible, suggesting my ideas are not too far-fetched. Now you can find rate with the equation by calculating T and using my formula. To summarise:

T = degrees left / ang vel
rate = 2/T * ( ang vel - degrees left / (T - 1) )


For the constraint |rate| <= 1: Calculate T, then rate, then if rate does not fit the constraint, don't enter the stopping sequence and keep on rotating as usual (edit: or decrease v by 1, but don't enter stop sequence). Try again next tic. You'll likely experience slight overshooting. Play with that yourself.

Note that you should be able to use the very same formulas for some linear, decelerating movement which you want to end in a certain position, by substituting degrees left with distance left, and angular velocity with movement velocity.

edit: I didn't mention this, but you'll have an easier time doing a stopping motion like this by ditching the idea of "I have to add a velocity every tic" during the stopping sequence. I'd linearly interpolate the angle towards the end angle* (watch for crossing 360°), and if the difference between new and old position is greater than the velocity as the stopping sequence started (again, no longer changing velocity), I'd set the new position to be the old + velocity instead. If the new position is close enough to the desired angle, lock onto it and exit the stopping sequence. This is far easier to work with.

* That is, add a fraction of the remaining distance. Over time, this fraction converges against 0 as you requested.
argv
Posts: 184
Joined: Tue Aug 30, 2016 4:47 pm

Re: Need help calculating acceleration of rotating object

Post by argv »

I figured out a complete solution to this problem, and posted it on GitHub at argv-minus-one/gzdoom-zscript-utils in the “ActorSpinner” folder, for anyone else who needs it.
Last edited by argv on Mon Jun 25, 2018 12:10 am, edited 1 time in total.
User avatar
KeksDose
 
 
Posts: 595
Joined: Thu Jul 05, 2007 6:13 pm
Contact:

Re: Need help calculating acceleration of rotating object

Post by KeksDose »

I made a blunder myself. Clearly I was supposed to substitute T for ang vel / rate and solve for rate. Then the result is exact, but in practice, numerically unstable enough that you'll miss by quite a margin even for not quite so small values (about 10° miss for 3° per tic velocity). If there's interest in that anyway: rate = (ang vel)^2 / (2 * degrees left + ang vel).

I'll hold onto it: The easiest and most precise way at the same time is to interpolate the current angle and the desired angle and snap onto it when the distance is small enough. You can compute a new velocity, ensure it doesn't speed up and find the deceleration on the tic as well. I'll post about this later.
argv
Posts: 184
Joined: Tue Aug 30, 2016 4:47 pm

Re: Need help calculating acceleration of rotating object

Post by argv »

I forgot to mention before, but one of my requirements is that the spinning object potentially make several revolutions before coming to a stop (in order to stay within the limit on deceleration rate). Hence, simply interpolating the angles won't work. I had tried to do the spin-down in two stages—flat deceleration, followed by interpolation—but wasn't able to get it to work well. This was likely to overshoot, requiring a very slow extra revolution to finally hit the target angle. Speeding up in response to an overshoot was likely to cause another overshoot. It was a mess.

The solution I came up with has no such problem. It's just constant deceleration, and gets a perfect landing (±2°) every time. It could be made more exact (by searching near the chosen deceleration rate for one that hits the target angle more exactly), but this yields pretty good results.

10° miss for every 3°/tic angular velocity would not have been acceptable. In the intended application (a spinning rod right in front of the player character), it needs to land within about 5° to not look weird, and it's spinning down from between 37°/tic and 80°/tic.
User avatar
KeksDose
 
 
Posts: 595
Joined: Thu Jul 05, 2007 6:13 pm
Contact:

Re: Need help calculating acceleration of rotating object

Post by KeksDose »

Oh, I got the spin thing without you mentioning. I find you dismissed the idea of dragging too quickly, since you can add spins at your own leisure and drag towards desired angle + extra spins * 360° instead (it's a visual effect after all). If the acceleration on the next tic got too large, you can explicitly set rate so the next acceleration will be exactly the bound. You didn't state if you wanted a minimal amount of additional spins with a suitable deceleration constant, that's what. :p

In any case, our two approaches merely seem to make different compromises. Since you wanted a constant deceleration, losing a bit of precision isn't so bad with small weapon sprites. I want 100% precision and control over time, but it won't have constant deceleration (it's unnoticeable in practice, but may be a necessity for other reasons). So for the sake of completeness, here's what my code would pretty much look like, basically trivial to understand, with a lot of parameters to play with for strange people:

Code: Select all

int spins = ceiling(ang_vel * spins_per_velocity);
int actual_desired_ang = desired_ang + spins * 360;

// Output a drag rate that produces a bounded acceleration:
int rate = default_rate;
int test_ang = ang + rate * (actual_desired_ang - ang);
int test_ang_vel = test_ang - ang;
int test_accel = ang_vel - min(ang_vel, test_ang_vel); // 0 or +deceleration

if(max_accel < test_accel) {
   rate = (max_accel + ang_vel) / (actual_desired_ang - ang);
}

landing = true;

// Later:
if(landing) {
   new_ang = ang + rate * (actual_desired_ang - ang);
   ang_vel = min(ang_vel, new_ang - ang); // No speeding up.
}

ang += ang_vel;

// Stop condition:
if(abs(ang - actual_desired_ang) < 2 * ang_vel && landing) {
   ang_vel = 0;
   ang = actual_desired_angle;
   landing = false;
} 
The last bit can be adjusted to calculate the deceleration instead of velocity, so it fits in the vel + accel, ang + vel model. You can also find default_rate so it's guaranteed to slow down from the first tic onwards. Interesting problem nevertheless, which I think can be expanded to something much more useful.
User avatar
KeksDose
 
 
Posts: 595
Joined: Thu Jul 05, 2007 6:13 pm
Contact:

Re: Need help calculating acceleration of rotating object

Post by KeksDose »

Scratch everything. Precision issues came from using small numbers in acs. This solves the problem precisely using my summation attempt, but with a slight simplification, and finds a minimum number of spins to account for the deceleration bounds.

Code: Select all

// kd: Begin landing.
int spin_count = 1 + ceiling( (ang_vel * ang_vel / max_decel - 2 * (desired_ang - ang) - 1.0) / 2 * 360.0);

float adjusted_ang = desired_ang + spin_count * 360.0 + ang_vel / 2;
float deg_left = adjusted_ang - ang;
float time = 2 * deg_left / ang_vel + 1.0;
float ang_decel = ang_vel * ang_vel / (2 * deg_left + 1.0); 
Then call ang_vel -= ang_decel if 0 < time. Assume ang < desired_ang.

The missing term was literally ang_vel / 2. Something didn't account for t = 0, go figure. Will elaborate on work if necessary.
argv
Posts: 184
Joined: Tue Aug 30, 2016 4:47 pm

Re: Need help calculating acceleration of rotating object

Post by argv »

I've implemented your math, and pushed it as the “ActorSpinner/KeksDose-impl” branch on the GitHub repository. In particular, here's the actual implementation. If you want to try it out, download the zip of that branch, unzip it, then load it into the game as described in the “Using the Demo” instructions.

I had to alter it in two ways. First, because a negative angular velocity is perfectly valid, I give it the absolute value of the angular velocity, then flip the sign of the computed deceleration rate if appropriate. Second, because the assumption ang < desired_ang has a 50% chance of being violated, I add 360 to desired_ang if it < 0. Other than that, it should be a faithful translation of your code.

Unfortunately, it doesn't work correctly. If angular velocity > 0, the spinning object lands at about the correct angle, but does not respect the deceleration limit, and will stop instantly if already at the landing angle. If angular velocity < 0, the spinning object lands at a seemingly random angle. I had the same problem when trying to compute a deceleration rate without a brute-force search, although my math was not quite the same as yours.
User avatar
KeksDose
 
 
Posts: 595
Joined: Thu Jul 05, 2007 6:13 pm
Contact:

Re: Need help calculating acceleration of rotating object

Post by KeksDose »

Ah, just three minor issues. 1. In my excitement I wrote down the formula a bit wrong. The corrected line in your code is:

int spinCount = ceil((avel * avel / Accel - 2 * (desiredRoll - mo.Roll) - 1.) / (2 * 360.));

It's a leftover from prototyping in acs (during the week, I'm stuck with a potato).

2. I also removed the + 1. This should now ensure truly the minimal spin amount (for instance, if you briefly fired the spinner weapon, you'd get two very slow spins).

3. In your code, it should be degLeft = adjustedRoll - mo.Roll, not desiredRoll - mo.Roll. With these changes, it appeared to work correctly, at least in one direction with a bunch of angles I tested. I ended up with this:

Code: Select all

void LandAt(float desiredRoll) {
    SnapAngle = desiredRoll;
    
    if (desiredRoll < mo.Roll)
        desiredRoll += 360;
    
    let avel  = abs(Vel);
    let avel2 = avel * avel;
    
    float degLeft = desiredRoll - mo.Roll;
    int spinCount = ceil((avel2 / Accel - 2 * degLeft - 1.) / 720);
    float adjustedRoll = desiredRoll + spinCount * 360. + avel / 2;
    
    degLeft = adjustedRoll - mo.Roll;
    TmpAccelTime = 2 * degLeft / avel + 1;
    TmpAccel = avel2 / (2 * degLeft + 1.);
    if (Vel > 0.)
        TmpAccel = -TmpAccel;
}
argv
Posts: 184
Joined: Tue Aug 30, 2016 4:47 pm

Re: Need help calculating acceleration of rotating object

Post by argv »

Nice. Seems to work. I've committed and pushed out your changes.

There is still an outstanding problem, though: it doesn't land at the correct angle if the starting angular velocity is negative. I feel like that should be trivial to solve, but I don't actually know how…
Post Reply

Return to “Scripting”