Game Development Community

Projectile following a parabolic trajectory to a designated point

by Jack Stone · in Technical Issues · 01/12/2014 (4:10 pm) · 3 replies

Hello,

I am trying to implement grenades in a Tactical Squad-based game. I am selecting AIPlayers using a point and click interface, and right clicking on a point in the world, which I want to be the destination of the grenade.

However, I am not sure how to provide and initial vector to the projectile in order to get it to land at or near the target point while still travelling in a parabolic arc (I could just use a conventional straight-shooting projectile, but this would look horrible).

I found some excellent information here:
http://www.blitzbasic.com/Community/posts.php?topic=73320

I hope noone will mine me posting a link from a rival game engine! But the equations there seem to provide the correct angle to hit a target if the muzzle velocity is known. I can simply hardcode the muzzle velocity, so this should work.

However, I first need to be able to create a projectile which is offset from the muzzle vector by a given angle. I thought this was simple, I have done rotations before, but it seems I can only rotate my vectors about the z axis. I am using this function:

function RotateVectorOnY(%Vec,%angle){  
            %X = GetWord(%Vec,0);  
            %Y = GetWord(%Vec,1);  
            %Z = GetWord(%Vec,2);  
              
            %rdAngle=mDegToRad(%angle);  
              
            %rX = (%X*mCos(%rdAngle))+(%Z*mSin(%rdAngle));  
            %rY = %Y;  
            %rZ = (%X*mSin(%rdAngle))-(%Z*mCos(%rdAngle));  
              
              
            return %rX SPC %rY SPC %rZ;  
    }

To rotate, but the problem is that it only works when the muzzle vector is pointing in a specific direction. I think the problem is that I need to rotate about an arbitrary axis? As opposed to a fixed axis?

I don't know how exactly to do this. I came accross a few c++ examples for other engines and libraries, but nothing I can replicate in script.

Is there an easier way to fire a projecile along a parabolic path, landing at a fixed point?

Thanks for any help!


#1
01/12/2014 (4:34 pm)
Have a look at this:
/// <summary>
/// This function calculates a firing offset for ballistic projectiles.  Thanks to 
/// Bryce for the vast majority of this function -
/// https://www.garagegames.com/community/resources/view/19739
/// <summary>
/// <param name="pos">The target position.</param>
/// <param name="roundVel">The muzzle velocity of the projectile.</param>
/// <param name="mortarAim">True to use high arc aim, false to use flat arc aim.</param>
/// <param name="gMod">The amount of gravity to apply to the projectile.</param>
/// <return>Returns the z axis aim offset position - or how high above the target to aim.</return>
function AIPlayer::GetBallisticAimPos(%this, %pos, %roundVel, %mortarAim, %gMod)  
{  
    %posFlat = %pos;
    %thisPos = %this.getPosition();
    %posFlat.z = %thisPos.z;
    %x = VectorDist(%thisPos, %posFlat);  
    %y = %pos.z - %thisPos.z;  
    //error("X delta: " @ %x @ " -- Y delta: " @ %y);   

    %g = 9.82 * %gMod;  
    %r1 = mSqrt(mPow(%roundVel,4.0) - %g * (%g * (%x * %x) + ((%y / 4) * (%roundVel * %roundVel))));  

    if (%r1 $= "-1.#IND") // If not a real number, it's not possible to hit %pos, return -1  
        return -1;  

    %a1 = ((%roundVel*%roundVel) - %r1) / (%g * %x);  
    %a1 = mASin(%a1 / mSqrt((%a1 * %a1) + 1));  
    %angleOfReach = mRadToDeg(%a1);  
    if (%mortarAim)  
        %angleOfReach = 90 - %angleOfReach;  
    //error("Angle of reach is " @ %angleOfReach);  

    %offsetHeight = mTan(mDegToRad(%angleOfReach)) * %x;  

    //error(%this @ ": aim offset for gravity = " @ %offsetHeight);  
    return %offsetHeight;
}
It lets the AIPlayer handle the left/right aim and calculates a z offset for the muzzle to get the projectile (in this case a grenade) from A to B.

I had it set up so that if it couldn't hit the point it was aiming for it would move closer and check again.

Moving this into the engine makes it a lot faster.
#2
01/12/2014 (9:30 pm)
Richard, thank you! That's fantastic, your function worked great!

It's a very clever way of doing it actually, aiming at a point above the target, and letting the grenade fall onto the target? That looks much simpler than what I was trying to do with rotating the vector, but it accomplishes the same thing.

In case anyone else is interested, this is my full throw grenade function:

function squadthrowsmokegrenade(%obj,%location){

	echo("Squadthrowsmokegrenade: " @ %obj);
	%projectile =  SmokegrenadeProjectile;

	%muzzleVector = %obj.getMuzzleVector(0);

//I modified Richard's function slightly so that it's not a method of AIPLayer:
	%zoffset = getballisticaimpos(%obj,%location,%projectile.muzzleVelocity,1,1);
//%zoffset is now the height above the target where we want to aim. //Target is already facing the target after using setaimlocation()

//Create a new vector with the z component set to %zoffset: (I could //have overwritten %location here, but I needed it somewhere else)
	%tst = setword(%location,2,%zoffset);

//Now, grab a vector from the muzzle position to the target:
	%ft = vectorsub(%obj.getPosition(),%tst);
//Normalise:
	%ft = vectornormalize(%ft);

//And scale with the muzzlevelocity:
	%ft = vectorscale(%ft,%projectile.muzzleVelocity);

//For some odd reason, my projectile was being thrown in the wrong //direction on the x and y axes. These lines flip those axes. There is //probably a better way of doing this:
	%xn = getword(%ft,0);
	%yn = getword(%ft,1);

	%ft = setword(%ft,0,(%xn*-1));
	%ft = setword(%ft,1,(%yn*-1));

	// FInally, create the projectile object:
	%p = new (Projectile)()
	{
		dataBlock = %projectile;
		initialVelocity = %ft;
		initialPosition = %obj.getMuzzlePoint(0);
		sourceObject = %obj;
		sourceSlot = 1;
		client = %obj.client;
		sourceClass = %obj.getClassName();
	};
	MissionCleanup.add(%p);
}

Once again, thanks Richard!
#3
01/15/2014 (7:49 pm)
Thank Bryce - I got it from him... lol