Page 1 of 1

DoomImp to throw fireballs in an arc that lands on target?

Posted: Fri Sep 01, 2017 12:34 pm
by wolfboyft
Hi, all. I have—to a distressing lack of avail—tried to make DoomImps (herein referred to as just imps) throw their DoomImpBalls (herein referred to as just fireballs) in an arc that lands on the player, like in Doom 4.

OK, fireballs now have Gravity set to 0.05 and the NOGRAVITY flag removed. Cool, that's them done (except I need to stop their gravity in their Death state; falling poofs of fire look pretty weird.) That's all great, but I want the imps to throw their fireballs with the right angle to hit the target (if not possible then not at all.)

So for the imps, I have this:

Code: Select all

ACTOR _DoomImp: DoomImp replaces DoomImp
	States {
			TROO EF 8 A_FaceTarget
			TROO G 6 A_CustomMeleeAttack(3 * random(1, 10), "imp/melee")
			Goto See
			TROO E 0 A_JumpIf(FastMonstersIsTrue, "FastMissile")
			TROO EF 8 A_FaceTarget
			TROO G 6 A_SpawnProjectile("DoomImpBall", 32, 0, 0, CMF_AIMDIRECTION, GetAppropriateAngleOrJumpIfOutOfRange("See", AAPTR_TARGET, 0.05, 10)) // Presumably CMF_AIMDIRECTION is the right flag to use? I couldn't _really_ tell.
			Goto See
			TROO EF 8 A_FaceTarget
			TROO G 6 A_SpawnProjectile("DoomImpBall", 32, 0, 0, CMF_AIMDIRECTION, GetAppropriateAngleOrJumpIfOutOfRange("See", AAPTR_TARGET, 0.05, 20))
Idiotically, I reverted from all attempts back to a hardcoded angle of 1 degree, but there's a placeholder function (not assuming that the answer will be so simple) where I don't know how to do something-- so not the 1 degree thing. Instead, the actor's distance on the XY plane (X as far as the 2D calculations are concerned,) the Z distance, the gravity that fireballs are subjected to and their speed (which is different with FastMonsters, so... gotta think about that one.)

My previous attempts, though, involved using GetDistance(false) (for the XY plane) and using it as variable x (we've got a rotated 2D representation of the plane on which the imp and the target lie) and then doing
to get the y variable (so done because how do i get the relative Z offset of the target in a cleaner way?), doing this (g = Gravity, v = Speed or FastSpeed)
(aborting the whole missile attack if the expression being square-rooted is negative, because that means that the ball cannot hit the target)

from wikipedia. Also, the plus-or-minus sign can be whichever, both get the right angle apparently

...yeah, that... and using the theta on the left to get the angle required. but... i can't really describe how it all went wrong now. In future I won't destroy previous attempts.

I've tried A_ThrowGrenade("DoomImpBall"), but it's not always accurate, especially if I'm not level with the imp.

So yeah, I really need some help coming up with an approach to this :(

EDIT: the post was slightly unfinished
EDIT2: got fastmissile and missile swapped, derp

Re: DoomImp to throw fireballs in an arc that lands on targe

Posted: Fri Sep 01, 2017 12:49 pm
by phantombeta
This equation doesn't seem to work with GZDoom.
I have no idea why, I think it's because it's made to work with continuous physics, not discrete physics.

To actually get this to even barely work I had to change it like this: (Keep in mind that I'm using ZScript, not DECORATE.)

Code: Select all

static double, double InterceptShotPitch (Vector3 shooterPos, Vector3 targetPos, double v, double grav) {
    double g = grav * 9.807,
        x = (targetPos.xy - shooterPos.xy).Length (),
        y = targetPos.z - shooterPos.z;

    double sq = (v*v*v*v) - g*(g*(x*x) + 2 * y * (v*v));

    if (sq >= 0) {
        sq = sqrt (sq);
        double x1 = atan2 ((v*v) + sq, g * x);
        double x2 = atan2 ((v*v) - sq, g * x);

        return x1, x2;
    } else
        return double.NaN, double.NaN;
And it still doesn't work properly. Nothing works right as the gravity value.
If you put in "9.807" (Earth's gravity in m/s²), it hits at somewhat intermediate range, but at long range the projectile goes over your head and at close range the projectile usually hits the ground instead of you.
I tried tweaking the gravity parameter, but nothing worked in all cases.

So, what's going on that makes it break? I have no idea.
I guess it might be one of these that makes it break:
  • GZDoom using discrete physics (i.e. deterministic, locked-step/per-tic physics)
  • Using map units that aren't anything like meters
  • Not applying gravity like physics normally does
Or any combination of those.
None of these are things we can change, so you'll have to find out some other way to do to calculate the right pitch.

(also, x1 is always a big arc, while the size of the arc in x2 increases with distance.)

Edit: As for making them not fall after hitting, add "A_NoGravity" and "A_Stop" in the Death state.

Re: DoomImp to throw fireballs in an arc that lands on targe

Posted: Fri Sep 01, 2017 2:41 pm
by KeksDose
phantombeta wrote:GZDoom using discrete physics (i.e. deterministic, locked-step/per-tic physics)
This is exactly the problem. Falling in a vacuum, the distance fallen is g * t^2 / 2. In Doom, you have to find it another way. On the ZDoom Wiki, it is stated gravity (probably a result of global * sector * actor gravity values) is subtracted from the falling velocity every tic. We can find a formula for the distance fallen that way, too. t is tics, so natural numbers only.

Lemme just solve this projectile arc problem once and for all, but by finding a vertical velocity instead of a shot inclination (it's easier):

v(t) = g * t

Doom applies your velocity with every tic. So s(t) = v(1) + v(2) + ... + v(t) = g * (1 + 2 + 3 + ... + t) = g * t * (t + 1) / 2

The last step is the fairly easy Gauss summation formula.

Now look, the function above is not purely quadratic in t as we are used to from Earth. It has a linear component, so of course there will be deviations which necessitate another formula to be found.

We can go ahead and decide upon some constraints. Basically, the fireball travels on a straight line when observed from above, so we can say it has a constant horizontal velocity. We call h horizontal velocity. Then:

l(t) = h * t is the horizontal distance travelled. Solving for t gives l / h = t(l). If we enter the actual horizontal distance between shooter and target, this gives the time it takes for the fireball to reach the target. Nice. Now we want to find a vertical velocity to fire the projectile with.

Let's say the shooter is at the height w, the projectile's vertical speed is z, then we can say the vertical projectile position v at a time t is:

v(t) = w + z * t - s(t) = w + z * t - g * (t * (t + 1) / 2)

Furthermore, we want to hit the target's z-position at the same time its horizontal position is hit. This means we use t = l / h and say r is the z-position of our target. This ends up being:

v(l/h) = r = w + z * l / h - g * (l / h * (l / h + 1) / 2)

Now solve this for z, the vertical projectile shooting velocity:

r - w = z * l / h - g * (l / h * (l / h + 1) / 2)
(r - w) + g * (l / h * (l / h + 1) / 2) = z * l / h
h / l * [ (r - w) + g * (l / h * (l / h + 1) / 2] = z

Now we'll make things easier on ourselves. r - w is the difference between shooter and target z-positions, the vector pointing away from the shooter. The z-distance. Let's call this b.

l / h is the horizontal target to shooter distance divided by the horizontal projectile starting velocity. Let's call this d. We arrive at the kind of readable: z = 1/d * ( b + g * (d * (d + 1) / 2) ) = b / d + g * (d + 1) / 2
= (r - w) * h / l + g * (h / l + 1) / 2

So in ACS terms:

Code: Select all

// Given the activator as the shooter, 'tid' as the target,
// 'vel_hor' as the horizontal starting velocity for the shot and
// 'gravity' as the actor's corrected gravity (which is by default 1.0):
int dx = GetActorX(tid) - GetActorX(0);
int dy = GetActorY(tid) - GetActorY(0);
int dz = GetActorZ(tid) - GetActorZ(0);
int hor = VectorLength(dx, dy);
int tics = FixedDiv(hor, vel_hor);
int inv = FixedDiv(vel_hor, hor);

// "(r - w) * h / l + g * (h / l + 1) / 2", which looks scary:
int vel_ver = FixedMul(inv, dz) + (FixedMul(gravity, tics + 1.0) >> 1); 
To summarise: All we did was find the vertical shooting velocity for a falling projectile in Doom physics if we assume it travelled with a constant horizontal velocity from one point to another. Then we did science.

Usage: You put in the gravity, horizontal distance and desired horizontal velocity of your projectile. Out comes the vertical velocity to create the shot arc. You can also scale the horizontal velocity with the distance, so it's close enough to having the projectile always fire off at the same overall velocity.

Re: DoomImp to throw fireballs in an arc that lands on targe

Posted: Fri Sep 01, 2017 6:48 pm
by 4thcharacter
The Infernal boss on Hell on Earth Starter Pack is able to launch fireballs in the air and after awhile it goes towards the player to make it seem like it goes in an arc.