Author Topic: (SOLVED) How do I make a gun fire multiple projectiles at specific angles?  (Read 30293 times)

EDIT: SOLVED, I'll post the solution once the code has been cleaned up because right now it's so inefficient it's disgusting.

So I want a weapon to fire five projectiles every time the trigger is pulled.
One of them goes right where the cursor is pointed. The others are offset from the original projectile in a + shape, such that the angle between the "center" shot and any other given shot is 10 degrees.
Is there a relatively concise way to do this? And if so, what is it? Note that I'm using a Tier Tac weapon as a base and so returning parent onFire might not be the best idea.
« Last Edit: April 26, 2015, 07:35:58 PM by King of the Bill »

Are you sure you want it to be ten degrees off the center-line?  To put it another way, that's a line with a slope of sind(10), or ~0.173648. Every ~5.758770 TU, the deflected projectiles fall (or rise) 1 TU - assuming a muzzle height of 2 TU, they'll only make it 20 studs from the player for a perfectly horizontal shot!

Are you sure you want it to be ten degrees off the center-line?  To put it another way, that's a line with a slope of sind(10), or ~0.173648. Every ~5.758770 TU, the deflected projectiles fall (or rise) 1 TU - assuming a muzzle height of 2 TU, they'll only make it 20 studs from the player for a perfectly horizontal shot!
Ok, I'm not sure I explained this thing properly. The idea here was that the shots are fired at angles, and the shots in question will have no gravity modifier (because gravity isn't supposed to affect the projectiles for the weapon I'm trying to create). Maybe twenty degrees would be better, I'm not quite certain but the specific number can be adjusted during testing. The idea is still that it fires five shots in a + shape; one is fired with the angle and velocity precisely as is expected of the normal firing function (factoring in player movement and whatnot), one is fired with the expected angle and velocity except turned so that it's pointing about ten-to-twenty degrees upwards of the "center" shot, one is fired like so except turned to point ten-to-twenty degrees to the left, another ten-to-twenty to the right, another ten-to-twenty downwards of the "center" shot.
If I'm still not being clear, or if I'm just making it even less clear, let me know and I'll try to create some sort of image that explains it.
It's basically a 3D version of the "Wave Shot" from Vectorman, if that's familiar at all.

Maybe twenty degrees would be better,

Twenty degrees would have a slope of sind(20), or ~0.342020.  Once again assuming a muzzle height of 2, shots with a deflection of 20 degrees would travel a grand total of 11.7 studs.

Also, I'm not entirely sure if there's a clean way to deflect shots by precise angles from precise center lines.  I do know a way, but I'm going to wait to see if anyone else posts before posting it, because it's not exactly pretty.

One slightly inefficient, but very easy to understand, method is to use my Advanced Vector Math resource, particularly the vectorRotateEuler function.


In the onFire function, do something like this:
Code: [Select]
%vec1 = %player.getMuzzleVector(%slot);
%vec2 = vectorRotateEuler(%vec1, "0 0 10");
%vec3 = vectorRotateEuler(%vec1, "0 0 -10");
%vec4 = vectorRotateEuler(%vec1, "10 0 0");
%vec5 = vectorRotateEuler(%vec1, "-10 0 0");

Obviously change 10 to the angle you want, in degrees.

You would then loop through 1-5 in a for loop, and spawn a projectile from the muzzlePoint in the direction of %vec[%i].

particularly the vectorRotateEuler function.

oh god when will people stop throwing around this horribly inefficient function i wrote years ago

oh god when will people stop throwing around this horribly inefficient function i wrote years ago

When there's an easy to use alternative that works.

So out of curiosity, is there ANY other way to do this? I'm still working on this thing and it's a mess at the moment.
See, it turns out that trying to use the following code causes it to do some weird stuff. Namely, it only lines up in the correct + shape along one axis. If fired anywhere other than directly along that one axis, it completely forgets up.

Code: [Select]
function vwaveImage::onFire(%this,%obj,%slot)
{
%projectile = vwaveProjectile;
%obj.playThread(2, shiftLeft);
%obj.toolAmmo[%obj.currTool]--;
%obj.AmmoSpent[%obj.currTool]++;

             commandToClient(%obj.client,'bottomPrint',"<just:right><font:impact:24><color:fff000>Wave Shot<font:impact:34>\c6" @ %obj.toolAmmo[%obj.currTool] @ " / " @ %obj.client.quantity["Vwaveshots"] @ "", 4, 2, 3, 4);

%spread = 0.01;
%vector = %obj.getMuzzleVector(%slot);
%objectVelocity = %obj.getVelocity();
%vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
%velocity = VectorAdd(%vector1,%vector2);

//%vec1 = %player.getMuzzleVector(%slot);
%vec0 = %velocity;
%vec1 = vectorRotateEuler(%vec0, "0 0 10");
%vec2 = vectorRotateEuler(%vec0, "0 0 -10");
%vec3 = vectorRotateEuler(%vec0, "10 0 0");
%vec4 = vectorRotateEuler(%vec0, "-10 0 0");

for(%shell=0; %shell<5; %shell++)
{
%p = new (%this.projectileType)()
{
dataBlock = %projectile;
initialVelocity = %vec[%shell];
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
}
return %p;
}
Incidentally, the completely-loving-up is actually the same results I was getting by modifying the Tier+Tac shot spread function, just along a different axis because I used different numbers. Can anyone tell me what I'm doing wrong here?
For those curious, here's the commented out chunk with the Tier Tac stuff though it might not be very accurate.
Code: [Select]
%spread = 0.01;
%vector = %obj.getMuzzleVector(%slot);
%objectVelocity = %obj.getVelocity();
%vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
%velocity = VectorAdd(%vector1,%vector2);
//%a = (-0.5) * 10 * 3.1415926 * %spread;
//%b = (0.5) * 10 * 3.1415926 * %spread;
//%c = (0) * 10 * 3.1415926 * %spread;
//echo("Velocity is " @ %velocity);
//main
//%velocityfinish0 = %velocity;
//up
//%mat1 = MatrixCreateFromEuler(%a @ " " @ %c @ " " @ %c);
//%velocityfinish1 = MatrixMulVector(%mat1, %velocity);
//down
//%mat2 = MatrixCreateFromEuler(%b @ " " @ %c @ " " @ %c);
//%velocityfinish2 = MatrixMulVector(%mat2, %velocity);
//left
//%mat3 = MatrixCreateFromEuler(%c @ " " @ %a @ " " @ %c);
//%velocityfinish3 = MatrixMulVector(%mat3, %velocity);
//right
//%mat4 = MatrixCreateFromEuler(%c @ " " @ %b @ " " @ %c);
//%velocityfinish4 = MatrixMulVector(%mat4, %velocity);
for(%shell=0; %shell<5; %shell++)
{
%p = new (%this.projectileType)()
{
dataBlock = %projectile;
initialVelocity = %velocityfinish[%shell];
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
}
return %p;
« Last Edit: April 12, 2015, 02:30:24 PM by King of the Bill »

When there's an easy to use alternative that works.

MatrixMulVector(MatrixCreateFromEuler(...), ...)

MatrixMulVector(MatrixCreateFromEuler(...), ...)
The funny part here is that I basically just noticed that when I forgeted both functions up in the same way (or at least, neither one is working like I want it to, so I assume I forgeted it up somewhere).
In case I wasn't clear before, I'd like the new velocities to be relative to the player's aim vector.

So here's the current onFire function in all its glorious dysfunctionality. If anyone can figure out why it only fires properly when aligned along a certain axis, let me know.

Code: [Select]
function vwaveImage::onFire(%this,%obj,%slot)
{
%projectile = vwaveProjectile;

//if(vectorLen(%obj.getVelocity()) < 0.1)
//{
// %spread = 0.0075; //0.002
//}
//else
//{
// %spread = 0.01; //0.0025
//}

//%shellcount = 8;

%obj.playThread(2, shiftLeft);
//%shellcount = 8;

//%obj.toolAmmo[%obj.currTool]--;
//%obj.AmmoSpent[%obj.currTool]++;

//%obj.spawnExplosion(TTRecoilProjectile,"1 1 1");
             commandToClient(%obj.client,'bottomPrint',"<just:right><font:impact:24><color:fff000>Wave Shot<font:impact:34>\c6" @ %obj.toolAmmo[%obj.currTool] @ " / " @ %obj.client.quantity["Vwaveshots"] @ "", 4, 2, 3, 4);



%spread = 0.01;
%vector = %obj.getMuzzleVector(%slot);
//%vector = "1 0 0";
echo("vector is " @ %vector);
%objectVelocity = %obj.getVelocity();
%vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
echo("vector1 is " @ %vector1);
%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
%a = (-0.5) * 10 * 3.1415926 * %spread;
%b = (0.5) * 10 * 3.1415926 * %spread;
%c = (0) * 10 * 3.1415926 * %spread;
%mat1 = MatrixCreateFromEuler(%a @ " " @ %c @ " " @ %c);
echo("Mat1 is " @ %mat1);
%mat2 = MatrixCreateFromEuler(%b @ " " @ %c @ " " @ %c);
echo("Mat2 is " @ %mat2);
%mat3 = MatrixCreateFromEuler(%c @ " " @ %c @ " " @ %a);
echo("Mat3 is " @ %mat3);
%mat4 = MatrixCreateFromEuler(%c @ " " @ %c @ " " @ %b);
echo("Mat4 is " @ %mat4);
//%mat1 = MatrixCreateFromEuler("1 0 0");
//echo("Mat1 is " @ %mat1);
//%mat2 = MatrixCreateFromEuler("-1 0 0");
//echo("Mat2 is " @ %mat2);
//%mat3 = MatrixCreateFromEuler("0 1 0");
//echo("Mat3 is " @ %mat3);
//%mat4 = MatrixCreateFromEuler("0 -1 0");
//echo("Mat4 is " @ %mat4);
//%velocity = VectorAdd(%vector1,%vector2);
%vecrot0 = %vector1;
echo("Vecrot0 is " @ %vecrot0);
%vecrot1 = MatrixMulVector(%mat1, %vector1);
echo("Vecrot1 is " @ %vecrot1);
%vecrot2 = MatrixMulVector(%mat2, %vector1);
echo("Vecrot2 is " @ %vecrot2);
%vecrot3 = MatrixMulVector(%mat3, %vector1);
echo("Vecrot3 is " @ %vecrot3);
%vecrot4 = MatrixMulVector(%mat4, %vector1);
echo("Vecrot4 is " @ %vecrot4);

%velocity0 = VectorAdd(%vecrot0,%vector2);
echo("Velocity0 is " @ %velocity0);
%velocity1 = VectorAdd(%vecrot1,%vector2);
echo("Velocity1 is " @ %velocity1);
%velocity2 = VectorAdd(%vecrot2,%vector2);
echo("Velocity2 is " @ %velocity2);
%velocity3 = VectorAdd(%vecrot3,%vector2);
echo("Velocity3 is " @ %velocity3);
%velocity4 = VectorAdd(%vecrot4,%vector2);
echo("Velocity4 is " @ %velocity4);

//echo("Velocity is " @ %velocity);
//main
//%velocityfinish0 = %velocity;
//up
//%velocityfinish1 = MatrixMulVector(%mat1, %velocity);
//down
//%velocityfinish2 = MatrixMulVector(%mat2, %velocity);
//left
//%velocityfinish3 = MatrixMulVector(%mat3, %velocity);
//right
//%velocityfinish4 = MatrixMulVector(%mat4, %velocity);

//%vec1 = %player.getMuzzleVector(%slot);
//%vec0 = %velocity;
//%vec1 = vectorRotateEuler(%vec0, "0 0 10");
//echo("Vec1 is " @ %vec1);
//%vec2 = vectorRotateEuler(%vec0, "0 0 -10");
//%vec3 = vectorRotateEuler(%vec0, "10 0 0");
//%vec4 = vectorRotateEuler(%vec0, "-10 0 0");

for(%shell=0; %shell<5; %shell++)
{
%p = new (%this.projectileType)()
{
dataBlock = %projectile;
initialVelocity = %vecrot[%shell];
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
}
return %p;
}
« Last Edit: April 12, 2015, 03:11:18 PM by King of the Bill »

Okay, so I still haven't figured out what's broken. Can someone figure out what I'm doing wrong here? Here's the code as it currently stands (including commented out remnants):
Code: [Select]
function vwaveImage::onFire(%this,%obj,%slot)
{
%obj.playThread(2, shiftLeft);

//%obj.toolAmmo[%obj.currTool]--;
//%obj.AmmoSpent[%obj.currTool]++;
            commandToClient(%obj.client,'bottomPrint',"<just:right><font:impact:24><color:fff000>Wave Shot<font:impact:34>\c6" @ %obj.toolAmmo[%obj.currTool] @ " / " @ %obj.client.quantity["Vwaveshots"] @ "", 4, 2, 3, 4);



%spread = 0.01;
%vector = %obj.getMuzzleVector(%slot);
echo("vector is " @ %vector);
%vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
echo("vector1 is " @ %vector1);

%objectVelocity = %obj.getVelocity();
%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
echo("vector2 is " @ %vector2);

%a = (-0.5) * 10 * 3.1415926 * %spread;
%b = (0.5) * 10 * 3.1415926 * %spread;
%c = (0) * 10 * 3.1415926 * %spread;
//%mat1 = MatrixCreateFromEuler(%a @ " " @ %a @ " " @ %a);
//%mat2 = MatrixCreateFromEuler(%b @ " " @ %b @ " " @ %b);
//%mat3 = MatrixCreateFromEuler(%a @ " " @ %b @ " " @ %a);
//%mat4 = MatrixCreateFromEuler(%b @ " " @ %a @ " " @ %b);
//%mat1 = MatrixCreateFromEuler("1 0 0");
//%mat2 = MatrixCreateFromEuler("-1 0 0");
//%mat3 = MatrixCreateFromEuler("0 1 0");
//%mat4 = MatrixCreateFromEuler("0 -1 0");
%mat1 = "0 0 0 1 0 0 .2";
%mat2 = "0 0 0 -1 0 0 .2";
%mat3 = "0 0 0 0 0 1 .2";
%mat4 = "0 0 0 0 0 -1 .2";
echo("Mat1 is " @ %mat1);
echo("Mat2 is " @ %mat2);
echo("Mat3 is " @ %mat3);
echo("Mat4 is " @ %mat4);
//%velocity = VectorAdd(%vector1,%vector2);
//VectorAdd
//MatrixMulVector

%vecrot0 = %vector1;
echo("Vecrot0 is " @ %vecrot0);
%vecrot1 = MatrixMulVector(%mat1, %vector1);
echo("Vecrot1 is " @ %vecrot1);
%vecrot2 = MatrixMulVector(%mat2, %vector1);
echo("Vecrot2 is " @ %vecrot2);
%vecrot3 = MatrixMulVector(%mat3, %vector1);
echo("Vecrot3 is " @ %vecrot3);
%vecrot4 = MatrixMulVector(%mat4, %vector1);
echo("Vecrot4 is " @ %vecrot4);

%velocity0 = VectorAdd(%vecrot0,%vector2);
//echo("Velocity0 is " @ %velocity0);
%velocity1 = VectorAdd(%vecrot1,%vector2);
//echo("Velocity1 is " @ %velocity1);
%velocity2 = VectorAdd(%vecrot2,%vector2);
//echo("Velocity2 is " @ %velocity2);
%velocity3 = VectorAdd(%vecrot3,%vector2);
//echo("Velocity3 is " @ %velocity3);
%velocity4 = VectorAdd(%vecrot4,%vector2);
//echo("Velocity4 is " @ %velocity4);

//echo("Velocity is " @ %velocity);
//main
//%velocityfinish0 = %velocity;
//up
//%velocityfinish1 = MatrixMulVector(%mat1, %velocity);
//down
//%velocityfinish2 = MatrixMulVector(%mat2, %velocity);
//left
//%velocityfinish3 = MatrixMulVector(%mat3, %velocity);
//right
//%velocityfinish4 = MatrixMulVector(%mat4, %velocity);

//%vec1 = %player.getMuzzleVector(%slot);
//%vec0 = %velocity;
//%vec1 = vectorRotateEuler(%vec0, "0 0 10");
//echo("Vec1 is " @ %vec1);
//%vec2 = vectorRotateEuler(%vec0, "0 0 -10");
//%vec3 = vectorRotateEuler(%vec0, "10 0 0");
//%vec4 = vectorRotateEuler(%vec0, "-10 0 0");

for(%shell=0; %shell<5; %shell++)
{
%p = new (%this.projectileType)()
{
dataBlock = %projectile;
initialVelocity = %velocity[%shell];
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
}
return %p;
}
My best guess is that I'm misusing matrixmulvector, but I don't know what I'm doing wrong specifically or how to fix it.

What I ususally do when I find that some math is wrong and working with vectors isn't coming out as expected, is to try it from an entirely different approach.
So instead of rotating the vector by an angle, let's try just doing vector addition.

Code: [Select]
%vec0 = %player.getMuzzleVector(%slot);

%forw = vectorScale(%vec0, 10); //Our projectiles will go 10 units forward, and one to each side. You can work out the angle using trig
%right = vectorCross(%vec0, "0 0 1");
%up = vectorCross(%vec0, %right); //May actually be down, but it doesnt matter

%vec1 = vectorAdd(%forw, %right);
%vec2 = vectorSub(%forw, %right);
%vec3 = vectorAdd(%forw, %up);
%vec4 = vectorSub(%forw, %up);

//Might want to normalize these vectors too


I believe this should work. It probably isn't 10 degree angles (i just pulled out numbers that seemed nice), but you can do the math and work out what numbers will make it be 10 degrees.

Sweet, that looks helpful, but what does "normalizing" the vectors mean in the context of this use? I'm not even sure what it means normally, actually.

Still isn't working, it's having the same issues that matrixmulvector did. It's only working correctly when I fire along a single axis.
Here's screenshots of what is happening no matter how I set this bullstuff up:
Firing in an upwards diagonal direction:

Firing along the X axis (this is the "axis it works along" right now)

Firing along the Z axis

Firing along the Y axis (bottom of the image is positive X, top is negative X, i think)


So here's the problem as far as I can figure. The muzzlevector is relative to the player's aim. The vectors that it is interacting with (when trying to calculate the offset projectiles, this would be the "0 0 1" component of %right in your example) are NOT relative to the player. Those are relative to the server/map, so it is inconsistent based on the discrepancy of the player's angle and the map's angle.
If anyone knows how to make all vectors in this script relative to the player's angle (rather than absolute), please help.

Sweet, that looks helpful, but what does "normalizing" the vectors mean in the context of this use? I'm not even sure what it means normally, actually.
normalizing a vector (using the vectorNormalize) function  means reducing it to a magnitude (length) of 1


Firing along the Z axis
Firing along the Y axis (bottom of the image is positive X, top is negative X, i think)
In BL, Z is up, not Y