So here was a neat example by a member named scooter to make a laser guided rocket:
download/file.php?id=25099
I was wondering how would it be possible to add three reality based physics applied here.
1, The gravity starts to take effect within 3 seconds of launch
2, the initial thrust is very strong but like gravitational force, the rocket loses this thrust incrementally within the first 2 seconds
3, How to make the Rocket not explode on touching anything, and instead falling to the ground (dud)
I can see the ACS script inside and I understand it to some degree, but I don't know where I can make these changes. I already tried adding setactorproperty with APROP_gravity but that did nothing.
Thanks in advance to anyone who can guide me on this.
Laser guided RPG physics
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!)
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!)

 Posts: 161
 Joined: Fri Mar 14, 2014 12:40 am
Re: Laser guided RPG physics
So while I'm a bit letdown to see no response yet, I did some research on my own end to find the solution to this.
This is the script that makes this thing work.
On trail and testing I realized the biggest confusion comes from putting in something to direct the speed with the influence of gravity and an initial thrust.
Essentially this code:
Would anyone have suitable math equation I could put to use on the speed variable to make it function as such?
Thank you
This is the script that makes this thing work.
Code: Select all
/*
Guided Rocket Launcher
By Scotty
Set SC_DEBUG_MODE 1 in the console to use debug mode! It displays useless data in a hudmessage
and puts pretty blue trails behind the rockets. Ooooooh!
The constants below can be used to make quick changes to the behavior.
LATENCY: The length in time, in tics, with which the rockets recalculate their trajectories.
NUMBER_ROCKETS: The max number of rockets that you can have following the laser.
Any over will just fly straight where you were aiming.
ROCKET_SPEED: The speed with which the rocket will travel. (Duh)
ROCKET_CUTOFF: The minimum distance away from the laser for the rocket to recalculate
its angle and pitch. (If it gets too close to the laser, it will stop
updating its trajectory and just fly straight for wherever it was heading.
ROCKET_TURNVALUE: A fixed point number representing the max angle that rockets can adjust
per update. This creates a curve effect. Use 0 to ignore this and just
use whateverangle and pitch it needs to head straight for the laser.
*/
#include "zcommon.acs"
#define LATENCY 1
#define NUMBER_ROCKETS 100
#define ROCKET_SPEED 20
#define ROCKET_CUTOFF 32
#define ROCKET_TURNVALUE 0.02
bool rocket[NUMBER_ROCKETS] = {0};
int laserX = 0;
int laserY = 0;
int laserZ = 0;
function int abs (int x) {
if (x < 0)
return x;
return x;
}
//==============================================================================
//Finds the first zero element in the rocket[] array.
//This would be much more useful if I could pass in a pointer to the array!
//
//Parameters:
//elements  INT, the number of elements in the array.
//
//Returns: INT value for the first element with a 0; 1 if none were found.
function int checkForFirstZero (int elements) {
int result = 1;
int i;
bool found = 0;
//Cycle through each value in the rocket array
for(i = 0; i < elements  !found; i++) {
if(rocket[i] == 0) {
result = i;
found = 1;
}
}
return result;
}
//Taken and modified from a function on the zdoom wiki
//http://zdoom.org/wiki/GetTargetPitch
function int getTargetPitch (int tid1, int targetX, int targetY, int targetZ) {
int x, y, z, xy, a;
x = GetActorX(tid1)  targetX;
y = GetActorY (tid1)  targetY;
z = GetActorZ(tid1)  targetZ;
a = VectorAngle(x, y);
if ((a + 0.125) % 0.5 > 0.25)
xy = FixedDiv(y, sin(a));
else
xy = FixedDiv(x, cos(a));
return VectorAngle(xy, z);
}
//==============================================================================
//Returns the signed difference between two angles
//
//Parameters:
//angleA  FIXED, The first angle to compare
//angleB  FIXED, The second angle to compare
//
function int diffAngle(int angleA, int angleB) {
int result = angleA  angleB;
if(result > 0.5) {
result = 1.0;
} else if (result < 0.5) {
result += 1.0;
}
return result;
}
//==============================================================================
//This is a function that creates a curve effect on the projectile.
//Essentially what it does is takes a target angle, the projectile's current
//angle, finds the difference between the two, and adds or subtracts a small
//fixed numbner (changeValue) from the current angle and returns the result,
//so it eases closer to the result rather than just being the result.
//
//Parameters:
//targetAngle  FIXED, The target angle that the projectile needs to have
//currentAngle  FIXED, the current angle that the projectile has
//changeValue  FIXED, a small value to change the current angle by.
//
function int closeIn(int targetAngle, int currentAngle, int changeValue) {
int diff = diffAngle(currentAngle, targetAngle);
int result;
if(abs(diff) < changeValue) {
result = targetAngle;
} else if(diff > 0) {
result = currentAngle  changeValue;
} else if (diff < 0) {
result = currentAngle + changeValue;
}
return result;
}
//==============================================================================
//This is the meat and potatoes of the mod!
//This takes the TID of a rocket, the X, Y, and Z positions of a target (laser),
//Does math to figure out where the rocket needs to go and changes the velocity
//accordingly.
//
//Parameters:
//projectileTID  INT, The TID of the projectile
//targetX  INT, The target X coordinate
//targetY  INT, The target Y coordinate
//targetZ  INT, The target Z coordinate
//speed  INT, the speed with which the projectile will move
//cutoff  FIXED, the distance with which to make the projectile stop following the target.
//turnValue  FIXED, the amount per call the rocket is allowed to turn. Pass 0 to ignore this and just use the target angle.
//
//Returns: bool value that indicates if the projectile can follow its target.
//
function bool setVelocityToTarget(int projectileTID, int targetX, int targetY, int targetZ, int speed, int cutoff, int turnValue) {
//Fixed point variables which define the individual X, Y, and Z distances
//that the rocket is from the target.
int disX = ((targetX << 16)  getactorx(projectileTID));
int disY = ((targetY << 16)  getactory(projectileTID));
int disZ = ((targetZ << 16)  getactorZ(projectileTID));
//Int equivalents of the values above. These are used to determine the cutoff distance.
int idisX = disX / 65535;
int idisY = disY / 65535;
int idisZ = disZ / 65535;
//Actual distance the projectile is from the target.
int distance = sqrt(idisx * idisx + idisy * idisy + idisz * idisz);
int gravity = GetActorProperty(ProjectileTID, APROP_GRAVITY);
//speed needs to be replaced with thrust
//This is to check for a cutoff distance.
//This is used because without it, if you were to shoot the rocket downward at a
//specific range of angles, it doesn't want to touch the ground. Instead it
//goes berserk in place, hovering just above the floor.
if(distance > cutoff) {
//Target and Current Angle
int targetAngle = vectorAngle(disX, disY);
int currentAngle = getActorAngle(projectileTID);
//Target and Current Pitch
int targetPitch = getTargetPitch(projectileTid, laserX << 16, laserY << 16, laserZ << 16);
//Normalize the pitch to a positive value.
if(targetPitch < 0.5) {
targetPitch += 1.0;
}
//Grab the current pitch
int currentPitch = getActorPitch(projectileTID);
//This is intended to fix a small anomaly that causes a rocket to fly out of the
//launcher at 0.0 pitch at first then readjust, without regard to the player pitch.
if(currentPitch == 0) {
currentPitch = targetPitch;
}
//Next Angle and Pitch
int nextAngle, nextPitch;
//If the turnValue is not 0, we'll calculate the next angle by the difference
//between the target and current angles, plus or minus the turnvalue.
//This creates a curve effect!
if(turnValue != 0) {
nextAngle = closeIn(targetAngle, currentAngle, turnValue);
nextPitch = closeIn(targetPitch, currentPitch, turnValue);
//If the turnValue is 0, we'll just set the pitch and angle to what it needs to be.
//This was the original incarnation of this weapon used and it works really well
//if you're annoyed by the curve effect.
} else {
nextAngle = targetAngle;
nextPitch = targetPitch;
}
//Next X, Y, and Z velocities
//Trig here to make sure the combined X, Y, and Z velocities equal the speed that is passed in.
int nextVelX = fixedmul(Cos(nextAngle), Cos(nextPitch))*speed;
int nextVelY = fixedmul(Sin(nextAngle), Cos(nextPitch))*speed;
int nextVelZ = sin(nextPitch)*speed;
//Chage the velocity here using the calculated values above.
setActorVelocity(projectileTid, nextVelX, nextVelY, nextVelZ, 0, 0);
//Store the pitch and angle data on the actor's Pitch and Angle.
//Angle affects the look, and the values on both will be used next round to
//determine the velocities to set.
setActorPitch(projectileTID, nextPitch);
setActorAngle(projectileTID, nextAngle);
//Dumps debug data if the cvar SC_DEBUG_MODE is set to true.
if(getCvar("SC_DEBUG_MODE") == 1) {
hudmessagebold(
s:"TargetX: ", i: targetX,
s:"\nTargetY: ", i: targetY,
s:"\nTargetZ: ", i: targetZ,
s:"\nRocketX: ", f: getactorx(projectileTID),
s:"\nRocketY: ", f: getactory(projectileTID),
s:"\nRocketZ: ", f: getactorz(projectileTID),
s:"\nDisX: ", f: disx,
s:"\nDisY: ", f: disy,
s:"\nDisZ: ", f: disz,
s:"\nDist: ", f: sqrt((idisx * idisx) + (idisy * idisy)) << 16,
s:"\nTAngle: ", f: targetAngle,
s:"\nCAngle: ", f: currentAngle,
s:"\nTPitch: ", f: targetPitch,
s:"\nCPitch: ", f: currentPitch,
s:"\nXVel: ", f: nextVelX,
s:"\nYVel: ", f: nextVelY,
s:"\nZVel: ", f: nextVelZ;
HUDMSG_LOG, 10, 0, 0.1, 0.5, 35
);
SpawnSpotForced("Trail", projectileTID, 0, 0);
}
return TRUE;
}
return FALSE;
}
script "rocketFollow" (void) {
//Grab the first slot available for a rocket to occupy
int checkedArray = checkForFirstZero(NUMBER_ROCKETS);
//If the result is good...
if(checkedArray != 1) {
thing_changetid(0,20000 + checkedArray);
//Store the rocket's TID  20000 in the array. This will flag it as occupied.
rocket[activatorTID()  20000] = 1;
Bool follow = true;
//Then the rocket follows the laser until it's exploded or it gets too close to the laser.
while(rocket[activatorTID()  20000] == 1 && follow) {
follow = setVelocityToTarget(activatorTID(), laserX, laserY, laserZ, ROCKET_SPEED, ROCKET_CUTOFF, ROCKET_TURNVALUE);
delay(LATENCY);
}
}
}
script "rocketCleanup" (void)
{
if(activatorTID() != 0) {
//Open a slot in the array
rocket[activatorTID()  20000] = 0;
//Make sure the velocity is 0
setActorVelocity(activatorTID(), 0, 0, 0, 0, 0);
thing_changeTid(0, 0);
}
}
script "getLaserCoords" (int x, int y, int z) {
laserX = x;
laserY = y;
laserZ = Z;
}
Essentially this code:
Code: Select all
int nextVelX = fixedmul(Cos(nextAngle), Cos(nextPitch))*speed;
int nextVelY = fixedmul(Sin(nextAngle), Cos(nextPitch))*speed;
int nextVelZ = sin(nextPitch)*speed;
Thank you


 Posts: 593
 Joined: Thu Jul 05, 2007 6:13 pm
Re: Laser guided RPG physics
I suppose nowadays you'd be better off using zscript for those sorta endeavours. :p
But what I say now mostly goes in acs as it does in zscript.
The acs won't allow you to use gravity, since the SetActorVelocity calls reset the zvelocity. You also cannot simply set it to add, because the nextVel* vars would then be used for acceleration instead, and your rocket would speed up very fast. You also cannot just reset the x and yvelocity, keeping z, and add the nextVel* results, because then, the rocket would speed up very fast still in the zdirection.
What you can do is count up the tics the script is running and, if the tics are past some value, add the actor's gravity value onto a separate vel_gravity. Then you subtract that value from nextVelZ.
For the speed falloff, count the tics the script is running and introduce a var speed_cur that is speed * f, where f is the speed modifier. Then we operate on f instead. Basically, you select a time t1 and t2 and before t1, f will be at its maximum, after t2, it'll be at its minimum and anywhere in between, it'll fall off linearly. Just look at this example which I didn't test:
But what I say now mostly goes in acs as it does in zscript.
The acs won't allow you to use gravity, since the SetActorVelocity calls reset the zvelocity. You also cannot simply set it to add, because the nextVel* vars would then be used for acceleration instead, and your rocket would speed up very fast. You also cannot just reset the x and yvelocity, keeping z, and add the nextVel* results, because then, the rocket would speed up very fast still in the zdirection.
What you can do is count up the tics the script is running and, if the tics are past some value, add the actor's gravity value onto a separate vel_gravity. Then you subtract that value from nextVelZ.
For the speed falloff, count the tics the script is running and introduce a var speed_cur that is speed * f, where f is the speed modifier. Then we operate on f instead. Basically, you select a time t1 and t2 and before t1, f will be at its maximum, after t2, it'll be at its minimum and anywhere in between, it'll fall off linearly. Just look at this example which I didn't test:
Code: Select all
int tics = 0; // Count this up every iteration of the following loop
int t1 = 10; // Max speed for first 10 tics
int t2 = 70; // Lowest speed reached in two seconds
int tics_slowdown = t2  t1;
// Normal speed factors:
int f_max = 1.0;
int f_min = 0.5;
// Inside of your loop:
int f = 0; // The speed factor
if(tics <= t1) {
f = f_max;
}
else if(t2 <= tics) {
f = f_min;
}
else {
// Fall linearly from (t1, 1.0) to (t2, 0.0):
f = (t2  tics) << 16;
f /= tics_slowdown;
// Move f into the desired range.
// Then f falls linearly from (t1, f_max) to (t2, f_min).
f = FixedMul(f, f_max  f_min) + f_min;
}
// Then change the current speed by that factor:
tics++;
cur_speed = FixedMul(f, speed);
// Finally multiply nextVel* by cur_speed instead of speed.

 Posts: 9693
 Joined: Sun Jan 04, 2004 5:37 pm
 Preferred Pronouns: They/Them
 Operating System Version (Optional): Debian Bullseye
 Location: Gotham City SAR, WyldLands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Re: Laser guided RPG physics
Yesterday I typed out a post explaining in general terms how the rockets in Hideous Destructor accomplish something similar (or used to, wrt the guided missile part), but I wasn't sure if it would be welcome since all of it was in ZScript and pretty much none of the ACS code here would be transferable.
Suffice it to say it involved:
A_ChangeVelocity (for forward thrust)
target.LineAttack (to find the spot where the player is pointing, instead of simply having the rocket adopt the player's angle)
vel.z (call every tic to emulate gravity; could use vel.z=GetGravity() instead)
A_SpawnItemEx (for creating a new dud rocket actor; you could try doing it all from the same actor but I find it a bit unwieldy)
(on a bit of a tangent, I just realized that A_FireProjectile returns an actor in ZScript, so you can manipulate a projectile's properties from the weapon  something I kinda really wish I'd bothered to look at this time last year!)
Suffice it to say it involved:
A_ChangeVelocity (for forward thrust)
target.LineAttack (to find the spot where the player is pointing, instead of simply having the rocket adopt the player's angle)
vel.z (call every tic to emulate gravity; could use vel.z=GetGravity() instead)
A_SpawnItemEx (for creating a new dud rocket actor; you could try doing it all from the same actor but I find it a bit unwieldy)
(on a bit of a tangent, I just realized that A_FireProjectile returns an actor in ZScript, so you can manipulate a projectile's properties from the weapon  something I kinda really wish I'd bothered to look at this time last year!)