[ACS] Making a player face an actor

Archive of the old editing forum
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. This forum is archived - please use this set of forums to ask new questions.
Locked
User avatar
oODemonologistOo
Posts: 318
Joined: Tue Jul 17, 2007 10:30 am
Location: one of the labs of hell

[ACS] Making a player face an actor

Post by oODemonologistOo »

I've been trying to get this to work for some time and I believe the math is correct however somehow I'm getting a division by zero error.

Basically, I want the m1 actor to face m2 completely. (Angle and pitch)

Can anyone help me fix it?

Code: Select all

function void FaceActor(int m1, int m2) {
	int xdiff = GetActorX(m2) - GetActorX(m1);
	int ydiff = GetActorY(m2) - GetActorY(m1);
	int zdiff = (GetActorZ(m2) + GetActorProperty(m2, APROP_HEIGHT)) - (GetActorZ(m1) + GetActorProperty(m1, APROP_HEIGHT));
	int dist = sqrt_z(xdiff * xdiff + ydiff * ydiff);
	SetActorAngle(m1, VectorAngle(xdiff, ydiff));
	// with arctan, it's zdiff / 2d dist
	int tpitch = zdiff / dist;
	tpitch = VectorAngle(1.0, tpitch << 16);
	SetActorPitch(m1, tpitch);
}
User avatar
KeksDose
 
 
Posts: 596
Joined: Thu Jul 05, 2007 6:13 pm
Location: my laboratory
Contact:

Re: [ACS] Making a player face an actor

Post by KeksDose »

I can confirm the approach is correct. Let me try. :p

Consider the diffs, as they are fixed point, integer multiplication will yield a very large result. No clue about the sqrt_z function at this point, so I suspect one of two things: Either an overflow happens and typically results in a negative argument that is not specially handled by sqrt_z, so it just wrongfully returns 0, or it is not designed for finding a fixed square root, and typically results in 0 for those.

The safest bet here is to use [wiki=Sqrt]FixedSqrt[/wiki] and multiplying xdiff and ydiff using [wiki=FixedMul]FixedMul[/wiki]. If you must use sqrt_z, try just the latter to see if it makes a difference. If this fails, you may also bit shift the diffs right by 16 to get their integer component, yielding less precision. You may want to print the values of sqrt_z(9) and sqrt_z(9.0) to see what you get.

Alternatively, calculate all distances using [wiki=VectorLength]VectorLength[/wiki] and use VectorAngle(VectorLength(xdiff, ydiff), zdiff) to avoid dividing altogether. If you want to keep the atan approach, use FixedDiv or bit shift right by 16, depends on how you choose to handle the diffs.

Edit: Lastly though, if you do keep the division, you should still specially handle the case for dist == 0 and whether m1 is located above or below m2 to make sure that even the unlikeliest case has a sane, well-defined value.
User avatar
oODemonologistOo
Posts: 318
Joined: Tue Jul 17, 2007 10:30 am
Location: one of the labs of hell

Re: [ACS] Making a player face an actor

Post by oODemonologistOo »

When I try to print Vectorlength(xdiff, ydiff) I keep getting a 0. Am I meant to shift this left? The wiki says they are of type int so I probably shouldn't.

Note: Shifting them right like below makes no difference. The tpitch values I get are 0.25 if I'm below or 0.86 something if I'm above.

My code so far:

Code: Select all

function void FaceActor(int m1, int m2) {
	int xdiff = GetActorX(m2) - GetActorX(m1);
	int ydiff = GetActorY(m2) - GetActorY(m1);
	int zdiff = (GetActorZ(m2) + GetActorProperty(m2, APROP_HEIGHT)) - (GetActorZ(m1) + GetActorProperty(m1, APROP_HEIGHT));
	SetActorAngle(m1, VectorAngle(xdiff, ydiff));
	printbold(d: VectorLength(xdiff, ydiff));
	int tpitch = VectorAngle(VectorLength(xdiff >> 16, ydiff >> 16), zdiff >> 16);
	SetActorPitch(m1, tpitch);
}
User avatar
KeksDose
 
 
Posts: 596
Joined: Thu Jul 05, 2007 6:13 pm
Location: my laboratory
Contact:

Re: [ACS] Making a player face an actor

Post by KeksDose »

Could it be that m1 and m2 refer to the same actor? If the length is 0, then both xdiff and ydiff had to be 0. Perhaps one of m1 or m2 is 0, so the activator will be used, and the activator actually has a tid set, which the other argument happens to be. It could also be that either m1 nor m2 exist, so (0, 0, 0) is returned for all GetActorXYZ calls.

Edit: Since zdiff appears to be not 0, m1 and m2 being the same actor should not be the case.

It would be helpful if you posted the script this function is called from, described the setup of the things and who runs the script.

The above (and the scalar property of VectorAngle I attempt to demonstrate further down) should explain why you get only one of two values, likely depending on the sign of zdiff. Since the horizontal length is 0, you get the angle of a vector lying on the z-axis, and that should be either 0.75 or 0.25, not 0.86 or 0.25, as far as I know. If this 0.86 is not a typo, then we may have discovered something messed up, or I have a misconception here. =p

Also, I doubt the print cast type being d: changes anything here, if that possibly comes up. You could use f: to be sure, but in general, your code should have printed a rather large whole number.

There should be no need to shift the values in VectorAngle. Internally, you probably get something looking like

Code: Select all

sqrt[(xdiff * 2^-16)^2 + (ydiff * 2^-16)^2] / (zdiff * 2^-16)

which using power laws under the circumstance of a headache I simplified to:

sqrt(xdiff^2 + ydiff^2) / zdiff
using atan and setting x and y > 0, the remaining cases should be like, the same (ignoring the possibility of imprecision).

Another edit: This open bug may be relevant.
Last edited by KeksDose on Sun Jul 10, 2016 1:11 pm, edited 1 time in total.
User avatar
oODemonologistOo
Posts: 318
Joined: Tue Jul 17, 2007 10:30 am
Location: one of the labs of hell

Re: [ACS] Making a player face an actor

Post by oODemonologistOo »

It actually is 0.75, I messed up while reporting it. (I used the shifted version in this one).

Basically the script is finding the closest visible enemy player for me, and aiming straight at it. This is the script, even though I check m1 != m2 in any circumstance in the script.
Spoiler:
EDIT: Doing something like this fixed it:

Code: Select all

function void FaceActor(int m1, int m2) {
	int xdiff = GetActorX(m2) - GetActorX(m1);
	int ydiff = GetActorY(m2) - GetActorY(m1);
	int zdiff = (GetActorZ(m2) + GetActorProperty(m2, APROP_HEIGHT)) - (GetActorZ(m1) + GetActorProperty(m1, APROP_HEIGHT));
	int dist = AproxDistance(xdiff, ydiff);
	SetActorAngle(m1, VectorAngle(xdiff, ydiff));
	dist = FixedDiv(dist, 256.0);
	zdiff = FixedDiv(zdiff, 256.0);
	int tpitch = -VectorAngle(dist, zdiff);
//	printbold(d:dist, s: " ", f:tpitch);
	SetActorPitch(m1, tpitch);
}
AproxDistance returns the fixed length of vector. The bug you have shown encouraged me to scale the fixed values down, and now it works fine. Thanks.
Locked

Return to “Editing (Archive)”