Getting the turnangle of a unit [resolved, finally]

Getting the turnangle of a unit [resolved, finally]

Discuss game development here, from a distinct game project to an accessible third-party mutator, down to the interaction and design of individual units if you like.

Moderator: Moderators

Post Reply
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Getting the turnangle of a unit [resolved, finally]

Post by rattle »

I could need some help on figuring out whether a turn angle of a piece is positive or negative. Atm it's a value I don't know (but can assume)...

Code: Select all

...
CurrentPos = get PIECE_XZ(body);
TurnAngle =  get XZ_ATAN(CurrentPos - LastPos);
...
This gets me the current turn angle, but it's always facing to the north of the engine relative to the base (body in this case).

Subtracting the zero angle from it locks the angle to the north of the unit as long TurnAngle is zero...

Code: Select all

TurnAngle =  get XZ_ATAN(CurrentPos - LastPos) - XZ_ATAN(0)
... but goes crazy if it's not zero, it even negates as the unit turns.

What I'm trying to figure out is how to get animated tracks of a tank to reverse when turning, and I can't do that with an unknown TurnAngle.

I kinda assumed it'd be 90 degrees by default (0 to 32768k)... when the unit turns to the left the value is within 0 to 16384 and when it turns to the right it's within 16384 and 32768 (depening on your multiplier of course, I'm using none atm)...

Help is appreciated...

PS: I spent the last three days on that matter, I'm kinda desperate :P
PPS: I've taken a look at several scripts using turn angles made by various people (zwzsg, gnome, and more) but neither provided a solution, but were great help understanding the way angles work, none the less...
Last edited by rattle on 12 Oct 2006, 18:18, edited 1 time in total.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

This gives you the angle of the movement vector. Not sure what you're going to do with it. If in doubt use the counter to see what kinds of values you get.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

Include the texture please :) it's hard to tell a number without them.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

Made my own... texture, that is
http://rattle.from-hell.net/counter.zip
Last edited by rattle on 24 Sep 2006, 01:53, edited 1 time in total.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

Oh, oops...
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

That counter is brilliant by the way. Haven't got a clue though how to get the unit offset angle comlpetely removed from a turn angle so only the actual turning angle remains. I can't get the z-coords ouf the angle thus it's never really zero and still got a (small) angle depending on the direction the unit faces to.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

You could try adding two pieces to the left and right of the unit, calculate the angle between them and compare it to the previous check.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

Thanks... that solved it. What have I been spending the last few days on... :P

Oh, I added a sign to your counter using an empty vertex with the coords X=-100 Y=23 Z=0 and added the following to the UpdateCounter(...) function...

Code: Select all

if (val < 0) {
		emit-sfx 0 from sign;
		val = 0 - val;
	}
Displays negative values as well :)

Now for the turning angle:

Code: Select all

...
LastPos = get PIECE_XZ(wheel1) - get PIECE_XZ(wheel10);
while (TRUE)
{
	// Piece on the left - piece on the right.
	CurrentPos = get PIECE_XZ(wheel1) - get PIECE_XZ(wheel10);
	TurnAngle = get XZ_ATAN(CurrentPos - LastPos) - get XZ_ATAN(0);
	call-script UpdateCounter(TurnAngle);
	sleep 250;
	LastPos = CurrentPos;
}
...
That's all I needed... it didn't work before because I worked with the angle of the distance it traveled... it works now even when it doesn't move but turns. Thanks a bunch :)
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

Duh. Stupid negativity again, from one or two angles only though. This is annoying... if I only needed a delta angle it wouldn't be such a pain in the ass but I need it to be positive or negative (or have fixed positive values, i.e. 0k-16k = left, 16k-32k = right).
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Need help on turn angles

Post by zwzsg »

Personnaly I use get XZ_ATAN(17) to get the current heading of the unit. At first look it may seems strange to have only a constant, and to not mention any piece, but that is because, hmm, lemme quote a year old post of mine:
Smoth wrote:However, I would LOVE! to see the script for the north south lock.
zwzsg wrote:Ok.

I have made some long ago for my 2D units, but only recently[1 year ago now], I understood the proper way and wrote it properly.

[...]

The new cleaner north south lock script has been used only on [...]

It reads:
turn philactere_tourrelle to y-axis get XZ_ATAN(17) now;
turn philactere_pencheage to x-axis <26.565> now;


26.565° is the angle between TA camera and the vertical, so I turn the piece of that angle to make sure it's parallel to surface of the screen and not to the the ingame horizontal surface. [irrelevant in Spring of course]

The interesting part is get XZ_ATAN(17).

Normally, the get XZ_ATAN(...) function is only used on a XZ difference. For instance, a typical use of XZ_ATAN would be:
turn loading_arm to y-axis get XZ_ATAN(get PIECE_XZ(base) - get UNIT_XZ(unit_id));
Basically, you feed that get XZ_ATAN(...) with a XZ difference, and it spouts the angle of the line between the two points you made the difference of.

A much important property of that function is that while the XZ are in the global coordinate and axis system, the angle is given into the local coordinate and axis system, the one tied to the unit. That is, in the exemple get XZ_ATAN(get PIECE_XZ(base) - get UNIT_XZ(unit_id)); the result will be 0=<0> (or maybe 32768=<180>, I often get confused) if the unit we want to load is in front of the transport. (Yes, the get XZ_ATAN(...) originally comes from the ship and hover transport scripts, but don't worry, you can use it in any script fine, transports just happen to be the units that traditionally use it).

So, what it means, is that when we give get XZ_ATAN(...) the difference between two absolute XZ coordinates, it calculates the absolute angle, then adds the offset angle of the unit

Now comes the tricky part: we just completly reverse the traditional use of get XZ_ATAN(...), instead of feeding it with an unknown angle to get that angle in the unit axis system, we feed it a known absolute angle, so as to read where is the zero of the local coordinate system. Knowing where that zero is placed tell us what is the unit direction.

So in fact we just feed it a cardinal point coordinate and read the difference to zero (that is, the value itself) of the output.
We understand get XZ_ATAN(vector_with_a_known_angle) as giving the angle between the unit direction and the known angle. Probably with a minus sign thrown in.

Since the way TA use to pack x and z into a xz is: xz=(x/65536) *65536 + (z/65536), you have
get XZ_ATAN(z') with 0<z'<65535 gives the angle between the unit heading and the north**
get XZ_ATAN(-z') with 0<z'<65535 gives the angle between the unit heading and the south**
get XZ_ATAN(65536*x') with 0<x'<65535 gives the angle between the unit heading and the west***
get XZ_ATAN(-65536*x') with 0<x'<65535 gives the angle between the unit heading and the east***

** There is a 50% that I confused north and south
*** There is a 50% that I confused west and east
And don't dismiss the possibility that I confused north-south and east-west too

So a line like:
turn shoulder to y-axis get XZ_ATAN(0-458752) now;
may look completly odd, but is the way to ensure that all the children of shoulder have their x and z axis aligned to the map x and z, (naming "map x+" the south and "map z+" the east)

Oh, and somehow my name written in explosion was mirror-inverted and not placed at the top left corner of the map under Spring, so I guess Spring doesn't work exactly the same. [note: I now believe it's the way spring position explosions which is wrong, the angle reading being (mostly) similar to TA]

[....]

Now, for a pratical exemple, heads over to Wormhole production and get my Arm Duck amphibious transport. I recall testing it to make sure it's Spring compatible, and if my memory serves me well, this is the most recent exemple of such a script I wrote. Skip the whole lengthy transportation scripty and go straight for the MovementAnimation() function.

The core of the part that interest us right now is:
var unitheading;
[...]
while(1)
{
unitheading=unitheading - get XZ_ATAN(7);//now it's unit heading diff
turn wheel1 to y-axis 3*unitheading speed <40>;
turn wheel2 to y-axis 3*unitheading speed <40>;
turn wheel3 to y-axis 1*unitheading speed <40>;
turn wheel4 to y-axis 1*unitheading speed <40>;
turn wheel5 to y-axis 0-1*unitheading speed <40>;
turn wheel6 to y-axis 0-1*unitheading speed <40>;
turn wheel7 to y-axis 0-3*unitheading speed <40>;
turn wheel8 to y-axis 0-3*unitheading speed <40>;
unitheading=get XZ_ATAN(7);//Now it's actually the unit heading (or the opposite, and maybe shifted 180°)
[...]
sleep 300;
}



You mentionned many times having issues because you don't know what range are the angle resulting in the difference of two angles. There is a simpe way around:
your_angle= angle blabla from stuff ATAN stuff stuff - another angle blabla from stuff ATAN stuff stuff;
while(your_angle<0)
{
your_angle=your_angle +65536;
}
while(your_angle>32767)
{
your_angle=your_angle -65536;
}
//There, now your angle has been modulo'ed to the <-180> <180> range.
//If you prefer <0> <360> range, just invert the order of the whiles.

Lastly, in TA, the xz plane is always horizontal, but I'm under the impression (not yet sure) that in Spring the xz plane tilt with the slope on non upright units. But in both TA and Spring the x and z got with get PIECE_XZ(..) and get UNIT_XZ(..) are absolute map coordinate (ok, previous statment just contradicted that around a bit) And both in Spring and TA, the get XZ_ATAN(..) gives an angle relative to the unit heading.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

I've taken a look at the troop transport script which seems to work the same as the duck's.

Anyway it's almost working how I want it, however...

Code: Select all

	// Current angle the unit faces to.
	LastPos = get PIECE_XZ(wheel1) - get PIECE_XZ(wheel10);

	while (TRUE)
	{
		// Piece on the left - piece on the right.
		CurrentPos = get PIECE_XZ(wheel1) - get PIECE_XZ(wheel10);

		// minus XZ_ATAN(0) gets me rid of the offset angle.
		TurnAngle = get XZ_ATAN(CurrentPos - LastPos) - get XZ_ATAN(0);

		// restricts the angle to 90 degrees
		while (TurnAngle < -16384) TurnAngle = TurnAngle + 16384;
		while (TurnAngle > 16384) TurnAngle = TurnAngle - 16384;

		// Function in KDR's counter unit, lets me see the current angle value
		call-script UpdateCounter(TurnAngle);
		sleep 250;
		LastPos = CurrentPos;
	}
This gives me either a positive angle between 0 and 16384 when turning to the left (or right, might have confused that) or negative between -16384 and 0 when turning to the right (or left :roll:), as lon as the unit is facing to the south!
When it's facing to the northern 180° the values are negated, e.g. left will be negative and right becomes positive. Any idea how to get around that?
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

The duck is more recent than the troop transport, and has a better script.

You got confused between "modulo 90°" and "restricted to "+-90°".

For instance, 105° modulo 90° is 15°, but 105° restricted to +-90° is 90°.
You wanted the latter, but wrote the former.

What you really want is:

Code: Select all

while(1)
{
	unitheading=get XZ_ATAN(7);//Current unit heading (or the opposite, and maybe shifted 180°)
	sleep 300;
	unitheading=unitheading - get XZ_ATAN(7);// difference between last and current unit heading

	// Those two while are to make sure the angle is moved to between -180° and +180 
	while(unitheading<0)
		{unitheading=unitheading +65536;}
	while(your_angle>32767)
		{unitheading=unitheading -65536;} 

	// Those two if are to make sure it can't go lower than -90° or higher than +90°
	if(unit_heading> 16384)
		{unit_heading=16384;}
	if(unit_heading<  0 - 16384)
		{unit_heading=0 - 16384;}

	call-script UpdateCounter(unit_heading); 
}
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

Code: Select all

	while(1)
	{
		unitheading=get XZ_ATAN(0);
		sleep 300;
		unitheading=unitheading - get XZ_ATAN(0);
		call-script UpdateCounter(unitheading);
	}
...seems to work. Is there any reason why not to use XZ_ATAN(0)?

The turning angle value however now depends on the sleep time, the more often it checks the smaller the value becomes... it's not really an angle but that doesn't matter much. Thanks :)
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

With XZ_ATAN(0), you're asking what direction is a null vector pointing toward! Or if you prefer, what is the direction of the line between two points that are on the exact same spot. I mean, mathematically, it's undefined! You're lucky spring accepts it, and always consider it as having the same direction, but it still sounds wrongs to me! And 7 is as quick to type as 0.

Yes, how much it has turned since last check may depends to how long ago was that last check. speed = distance / time, more ontopic, rotational speed = angle/time, I don't really see how you're expecting time to not matter.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

It was just symbolic, you know I've been trying to figure that one out for a week now (every now and then again) and blatantly failed and failed again... was just happy it appeared to work for the first time.

About the XZ_ATAN(0), it's not ATAN(0). I believe it's a relative angle (it's 32k when the unit is facing south) thus it's not a zero value. It has to be because ATAN(0) would be impossible.

And yes was just about to figure out how to get time into the angle to get the true angle. PS I'm really not very good at maths... ;)
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

get XZ_ATAN(xz1 - xz2) gives the angle of the vector that goes from the point xz1 to the point xz2.

That angle is measured relatively to the unit nose. That is, the unit own Z axis always point to a get XZ_ATAN angle of 0.
What I mean is that get XZ_ATAN(get PIECE_XZ(tail) - get PIECE_XZ(nose)) is always 0.

get XZ_ATAN(7) can be interpreted as get XZ_ATAN(7 - 0), where:
A packed xz of 7 stands for the point x=7*2^16 z=0
A packed xz of 0 stands for the point x=0 z=0

Therefore, get XZ_ATAN(7) is the angle of a vector pointing north*.
So, get XZ_ATAN(7) gives you the direction of the north*.
Still measured relatively to the unit's heading.
So, get XZ_ATAN(7) is the angle between north* and the unit heading.
So, get XZ_ATAN(7) is the angle of the unit on a map-fixed referential.
So, get XZ_ATAN(7) is the angle of the unit.

Got it?

I probably confused north and south, if not north and east, or north and west, but it doesn't matter.

Then, it's only a matter of computing the difference between the present instant value of get XZ_ATAN(7) and the value get XZ_ATAN(7) had some time ago, to get the angle difference between some time ago and now, so to get how much the unit turned between then and now. If the time between then and now is kept constant, how much it turned between then and now is proportionnal to how fast the unit has been turning.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

Think so. So get XZ_ATAN(0) == XZ_ATAN(7) == XZ_ATAN(12) because the system works like (x*65536)*65536 and z*65536 to pack coordinates, small values go entirely unnoticed?
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

Yes, XZ_ATAN(7) == XZ_ATAN(12). Not because small values are ignored, but just because they are all x=0 z>0 values, so pointing toward the same direction. Take a grid on a paper, with a pen place a point A at (0,7) and a point B at (0,12), then notice how OA and OB both point to the north.
(oops, I got confused between x and z in my last post, but anyway, it doesn't matter)

Yes, very small values get lost when an x and a z get packed into an xz. However, what you lose is the <1 pixel precision in TA, the <[0.4] precision if your prefer 3dobuilder/scriptor tradionnal unit, so it's a pretty negligible loss.

The system works like: packed_xz=(x/65536) *65536 + (z/65536)
(only makes sense if you remember there can only be integer, so division result are floored).

Or if you prefer C code:
#define UNPACKX(xz) ((signed short)((Uint32)(xz) >> 16))
#define UNPACKZ(xz) ((signed short)((Uint32)(xz) & 0xffff))
#define PACKXZ(x,z) (((int)(x) << 16)+((int)(z) & 0xffff))
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

Update: I finally did it (at least I think so).

Code: Select all

#define ANIM_SPEED 37.5

TurnControl()
{
	var RefreshRate, UnitHeading, TurnAngle;
	RefreshRate = ANIM_SPEED;
	UnitHeading = get XZ_ATAN(7);
	
	while (TRUE)
	{
		sleep RefreshRate;
		
		UnitHeading=get XZ_ATAN(7) - UnitHeading;

		if (UnitHeading != 0)
		{
			// Reset if the directions change
			if (UnitHeading < 0 AND TurnAngle > 0) TurnAngle = 0;
			else if (UnitHeading > 0 AND TurnAngle < 0) TurnAngle = 0;
			
			// Adding to the angle over time
			TurnAngle = TurnAngle + UnitHeading;
			
			// Left turn
			if (TurnAngle <= -8192)
			{
				bReverseLeft = TRUE;

			}
			// Right turn
			else if (TurnAngle >= 8192)
			{
				bReverseRight = TRUE;
			}
			else
			{
				bReverseLeft = FALSE;
				bReverseRight = FALSE;
			}
		}
		else
		{
			TurnAngle = 0;
			bReverseLeft = FALSE;
			bReverseRight = FALSE;
		}
		UnitHeading = get XZ_ATAN(7); // Last heading of the next iteration	
	}
}
Post Reply

Return to “Game Development”