Actor rotation (value clamping)

Discussions about Coding and Scripting
Post Reply
User avatar
ANUBITEK
Adept
Posts: 261
Joined: Sun Dec 28, 2014 1:10 am
Location: Anubitek

Actor rotation (value clamping)

Post by ANUBITEK »

Got a math question. I need to understand a concept before I go rooting around in some math code. When I took apart the turret in UT, I grabbed some rotation code in a timer:
Turret Timer

Code: Select all

	function Timer()
	{
		if (Enemy == none || Enemy.bDeleteMe || Enemy.Health <= 0)
			EnemyNotVisible();
		else
		{
			DesiredRotation = rotator(Enemy.Location - Location);
			DesiredRotation.Yaw = DesiredRotation.Yaw & 65535;
			if (LineOfSightTo(Enemy))
			{
				if (bFired && DesiredRotation.Pitch < 2000 &&
					(Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) < 1000 ||
				 	Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) > 64535))
				{
					Shoot();
				}
				else
				{
					bFired = true;
					SetTimer(FireRate, true);
				}
			}
		}
	} 
What happens in the code is the enemy (player) has already been found, with each call of timer() based on the turret's fire rate the turret rotates to face the player then shoot() is called. But my question has to do with this particular snippet of the above code:

Code: Select all

			DesiredRotation = rotator(Enemy.Location - Location);
			DesiredRotation.Yaw = DesiredRotation.Yaw & 65535;
			if (LineOfSightTo(Enemy))
			{
				if (bFired && DesiredRotation.Pitch < 2000 &&
					(Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) < 1000 ||
				 	Abs(DesiredRotation.Yaw - (Rotation.Yaw & 65535)) > 64535))
Can anyone explain to me the purpose of the additional number "65535", as well as why the single "&" operator is being used? I know it has to do with bit shifting, but I don't understand why it is used.


>>>>>>>>>>>>>>>>>>>>
Mobile edit:
It just occured to me that the value 65535 is 256^2, is this the extent of what the rotational value of an object can reach before it goes back to 0?
>>>>>>>>>>>>>>>>>>>>


On a related note, found something for determining relative rotation, though it appears to only covers four angles of an actor.
https://forums.unrealengine.com/showthr ... ite-system
Spoiler

Code: Select all

Sprite A if , Dot(Enemy forward vector , (playerLocation - EnemyLocation).Normalized) >0.7

Sprite D if , Abs( Dot(Enemy forward vector ,(playerLocation - EnemyLocation).Normalized) )<0.7 && Dot(enemy Right Vectorc,(playerLocation - EnemyLocation).Normalized)<0

Sprite B if ,Abs( Dot(Enemy forward vector ,(playerLocation - EnemyLocation).Normalized) )<0.7 && Dot(enemy Right Vectorc,(playerLocation - EnemyLocation).Normalized)>0

Sprite C if , Dot(Enemy forward vector,(playerLocation - EnemyLocation).Normalized) <-0.7 
Anyone got the time to direct me to an example of Dot that could make sense to me? Looking at the Vectors page unfortunately leaves me stumped on Dot as it is used in this context.
Last edited by ANUBITEK on Sun Dec 18, 2016 12:15 am, edited 2 times in total.
<<| http://uncodex.ut-files.com/ |>>

Code reference for UGold, UT99, Unreal2, UT2k3, UT3
Additional Beyond Unreal Wiki Links
wiki.beyondunreal.com/Legacy:Console_Bar
wiki.beyondunreal.com/Exec_commands#Load
wiki.beyondunreal.com/Legacy:Exec_Directive#Loading_Other_Packages
wiki.beyondunreal.com/Legacy:Config_Vars_And_.Ini_Files
wiki.beyondunreal.com/Legacy:INT_File
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Turret [rotation] Question

Post by Barbie »

In Unreal engine a rotation of 65536 (=2^16) UU is the same as a rotation of 360 degree (°). In general, every end position of a rotation above 360°can be given as a value of the interval [0..360]; if you use degree, it can be calculated by a Modulo operation: EndYaw = Yaw Modulo 360. This simply cuts off the full circles of the rotation.

If the rotation is given in UU, the 16 LSB are the equivalent to the interval [0°..360°]. The bit AND operator "&" with 65535d (=1111.1111.1111.1111b) keeps the most right 16 bits and zeros the other bits. This is equivalent to the above mentioned Modulo operation.

See also: https://wiki.beyondunreal.com/Legacy:Rotator
<EDIT>
  1. The above applies for rotations with positive values; otherwise the sign has to be taken into account.
  2. Corrected the binary representation of 65535d
</EDIT>
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
Chris
Experienced
Posts: 134
Joined: Mon Nov 24, 2014 9:27 am

Re: Turret [rotation] Question

Post by Chris »

Like barbie said, the expression (Yaw & 65535) simply means that you clamp the value of Yaw to 16bits. Writing it in hexa as 0xFFFF would make it easier to read tho.
User avatar
ANUBITEK
Adept
Posts: 261
Joined: Sun Dec 28, 2014 1:10 am
Location: Anubitek

Re: Turret [rotation] Question

Post by ANUBITEK »

In Unreal engine a rotation of 65536 (=2^16) UU is the same as a rotation of 360 degree (°). In general, every end position of a rotation above 360°can be given as a value of the interval [0..360]; if you use degree, it can be calculated by a Modulo operation: EndYaw = Yaw Modulo 360. This simply cuts off the full circles of the rotation.
I think I understand what you've posted, so here is the way I am reading this: hypothetically if the rotational value would equal 720° using the method of implementing the Modulo (EndYaw = Yaw % 360), I would get a 360° instead? Or if the value would be 400°, it would instead be 40° (400-360=40)? That's what I understand from what you've posted, including the link.
<<| http://uncodex.ut-files.com/ |>>

Code reference for UGold, UT99, Unreal2, UT2k3, UT3
Additional Beyond Unreal Wiki Links
wiki.beyondunreal.com/Legacy:Console_Bar
wiki.beyondunreal.com/Exec_commands#Load
wiki.beyondunreal.com/Legacy:Exec_Directive#Loading_Other_Packages
wiki.beyondunreal.com/Legacy:Config_Vars_And_.Ini_Files
wiki.beyondunreal.com/Legacy:INT_File
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Turret [rotation] Question

Post by Barbie »

You got it correctly, I think.
But just a remark: 720 modulo 360 results in 0, not in 360 (of course 0° rotation has the same end position as a 360° rotation)
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
User avatar
ANUBITEK
Adept
Posts: 261
Joined: Sun Dec 28, 2014 1:10 am
Location: Anubitek

Re: Actor rotation (value clamping)

Post by ANUBITEK »

Barbie, I have another question. I am using a pawn's rotation to determine what sprite they should get, but when my pawn reaches a certain rotation value, I begin having errors. Is there a way to restrict a pawn's rotation value (or at least calculate it as if it was) only between 0 and 65536? I start by grabbing a pawn's current rotation, but what can I do to make their value appear between 0 and 65536 for calculation? Let's assume their starting yaw value is -16384, no other value matters as they aren't calculated for what I need.

Note: I get errors with any value above 163840 and anything below 0. Anything between 0 and 65536 doesn't give me an issue.
<<| http://uncodex.ut-files.com/ |>>

Code reference for UGold, UT99, Unreal2, UT2k3, UT3
Additional Beyond Unreal Wiki Links
wiki.beyondunreal.com/Legacy:Console_Bar
wiki.beyondunreal.com/Exec_commands#Load
wiki.beyondunreal.com/Legacy:Exec_Directive#Loading_Other_Packages
wiki.beyondunreal.com/Legacy:Config_Vars_And_.Ini_Files
wiki.beyondunreal.com/Legacy:INT_File
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Actor rotation (value clamping)

Post by Barbie »

I guess you have to normalize the rotation (transform it to a rotation 0...360°) before doing other math on it. I'd try it with such like this:

Code: Select all

if (yaw < 0)
{
	bCounterclockwise = true;
	yaw = - yaw;
}
yaw = yaw % 65536; // throw away all multiple rotations of 360°
if (bCounterclockwise && yaw != 0)
	yaw = 65536 - yaw; // make counterclockwise to clockwise
(Consider that this code as well as the math is untested... Corrections are welcome!)
The term "(counter)clockwise" just indicates the direction of rotation - I'm not sure if it is the "real" direction in a left-handed coordinate system that UT uses.
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
User avatar
ANUBITEK
Adept
Posts: 261
Joined: Sun Dec 28, 2014 1:10 am
Location: Anubitek

Re: Actor rotation (value clamping)

Post by ANUBITEK »

So originally I had implemented your changes which seemed to have worked... Except for when I stand my camera directly in front of my pawn. Now I get an issue where if I am in front of the pawn and the pawn is looking toward the camera, I get a result of 0 instead of between 1-8 for a sprite index (an integer). I logged my issues, think you can take a look at what I have here? R1 is unadjusted, it is what I used to set the player to look at the camera.

log:

Code: Select all

ScriptLog: R1:  0,16384,0
ScriptLog: R2:  1044,12287,0
ScriptLog: R3:  64492,4097,0
ScriptLog: R3 Adjust:  64492,4097,0
ScriptLog: RotResult:  0.000000
ScriptLog: NewSpriteIndex:  0
function

Code: Select all

final function int AdjustDirIndex(Actor sAnimated, RPG_Camera Camera, out int NewSpriteIndex)
{
	local Rotator	R1,R2,R3;
	local int 		IndexMax, OldIndex;
	local float		RotResult;
	local bool		bCounterclockwise;
	IndexMax = 8;

	R1 = sAnimated.Rotation;
	log("R1: " @ R1);
	R2 = ( Rotator(Camera.Location - sAnimated.Location) + rot(0,-4096,0)  );
	log("R2: " @ R2);
	R3 = (R1 - R2);
	log("R3: " @ R3);
	
	[INSERT BELOW CODE HERE]

	RotResult = ( ( R3.Yaw / (65536 / IndexMax) ) ) - ( ( ( R3.Yaw / (65536 / IndexMax) ) ) % 1 );
	log("RotResult: " @ RotResult);

	NewSpriteIndex = int(RotResult);
	OldIndex = NewSpriteIndex;
	
	if ( OldIndex > IndexMax )
	{
		NewSpriteIndex = ( OldIndex % IndexMax );
		log("IndexAdjust: " @ NewSpriteIndex);
		OldIndex = NewSpriteIndex;
	}
	else
		log("NewSpriteIndex: " @ NewSpriteIndex);
	return NewSpriteIndex;
}
What was added:

Code: Select all

	if (R3.yaw < 0)
	{
		bCounterclockwise = true;
		R3.yaw =- R3.yaw;
	}
	if ( R3.yaw > 65536 )
		R3.yaw = R3.yaw % 65536; // throw away all multiple rotations of 360°
	if (bCounterclockwise && R3.yaw != 0)
		R3.yaw = 65536 - R3.yaw; // make counterclockwise to clockwise
	log("R3 Adjust: " @ R3);
<<| http://uncodex.ut-files.com/ |>>

Code reference for UGold, UT99, Unreal2, UT2k3, UT3
Additional Beyond Unreal Wiki Links
wiki.beyondunreal.com/Legacy:Console_Bar
wiki.beyondunreal.com/Exec_commands#Load
wiki.beyondunreal.com/Legacy:Exec_Directive#Loading_Other_Packages
wiki.beyondunreal.com/Legacy:Config_Vars_And_.Ini_Files
wiki.beyondunreal.com/Legacy:INT_File
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Actor rotation (value clamping)

Post by Barbie »

Until now I cannot say anything to the issue you got but I stumbled over the following:

Code: Select all

anyvalue % 1
Mathematically this does not change anything; is it a kind of type conversion?

And please, add some comments to your code telling what is the aim of those operations - otherwise the reader has to deduce it from the code what unnecessarily consumes his life time.
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
User avatar
ANUBITEK
Adept
Posts: 261
Joined: Sun Dec 28, 2014 1:10 am
Location: Anubitek

Re: Actor rotation (value clamping)

Post by ANUBITEK »

Higor got back to me on this one and gave me an understanding of how bit shifting works, so I'm gonna post the solution then his message.

Code: Select all

final function int AdjustDirIndex(Actor sAnimated, RPG_Camera Camera, out int AnimDir)
{
	local Rotator	R1,R2;

	R1 = sAnimated.Rotation; // Grab actor rotation
	R2 = Rotator(Camera.Location - sAnimated.Location);  // Grab rotation of camera-actor line
	
	AnimDir = ( ( (R1.Yaw - R2.Yaw) & 65535) + 4096) >> 13;  // Described below in detail

	return AnimDir;
}
So first do a function call ( AdjustDirIndex(testactor, aPawnWithAViewport, outputIndex) ).

The function will grab the rotation of the first actor, which as far as I know can be any actor. Then a rotator is set based on the relative position of aPawnWithAViewport (or any actor really, modify it however you wish) and the first actor's location. Then the two actors' resulting Yaw is subtracted to get a general number to determine the facing direction of the target actor relative to aPawnWithAViewport.

You take that value and restrict it to 65535 (a value in between 0 and 65536, broken down is 256^2). After that is done, if my understanding is correct, you can now take the resulting bits that come of a negative and positive rotation that will reset to zero if their value is greater than 65535 (since 65536 is equivalent to 0 I think). You then offset the result of the two rotations by 4096 so when you are standing directly in front of the target actor that will have sprites, you will get a different sprite index based on the position you are standing if you are either at the front, back, either side, or at an angle of the sprite. Otherwise, you will get two different sprites if you are in front of the actor and are just slightly off center of the actor to either side. I'll post a video of this in practice when I get a new hard drive, mine is seeing it's last days. Then with the resulting integer, you take the 3 most important bits (1,2,4, which can be any value between 0 and 7 which gives us 8 different positions) and grab those for our position output. This last part with bit shifting is hazy to me, so I'll leave his message to explain that part:
Bitwise operators are your friend.

HEX representation of 0 (DWORD): 0x00000000
HEX representation of 65536 (DWORD): 0x00010000
Both are the same in unreal rotators, so we can completely discard any data beyond the first WORD (right to left)
If we need to operate on those values and our algorithms have a chance of being borked due to values outside of the 0-65535 (one less than 65536) range, then we need to quickly discard the additional data.

Bitwise AND is the right choice:

Code: Select all

local int RY;
RY = (R1.Yaw - R2.Yaw) & 65535;
What's going on here?
HEX representation of 65535 (DWORD): 0x0000FFFF
Bit representation of 65535: 0b00000000000000001111111111111111
Bit representation of 65536: 0b00000000000000010000000000000000
The bitwise AND keeps 1 if both sources are 1, sets to 0 otherwise.
This means that 65535 is a bitmask used to reduce DWORD into a WORD

So, this happens:
HEX representation of 0 (WORD): 0x0000
HEX representation of 65536 (WORD): 0x0000 (not recyprocal)
HEX representation of 65535 (WORD): 0xFFFF
HEX representation of -1 (WORD): 0xFFFF (not recyprocal)


Since you want to convert these into 8 directions and the 0/65536 reference point is a center, not a limit you'll have to 'right' offset this by 1/16 of the full range.
65536/16 = 4096
Now we update the snippet. (If I screwed up then just change signs until it looks ok)

Code: Select all

local int RY;
RY = (R1.Yaw - R2.Yaw) + 4096) & 65535;
With this done, and the coordinates offset by 1/16 of the range, we've correctly offset the 0 'reference' point so that it indicates a boundary between the first and second direction, instead of the center of the first direction.
This means that dividing by 8192 will give us the correct directions of a yaw coord.
But since we're dividing by 2^n values, let's do some more bitwise math here.
MAX_WORD is 0b1111111111111111 (65535, 16 bits)
MAX_DIR is 0b0000000000000111 (7, 3 bits)
The highest bits of the YAW coordinate are the ones we're after, we don't even need a division in this case, we just need to shift the bit set of the YAW coordinate by 16-3 places to the right.
Change the snippet to get the actual direction now:

Code: Select all

local int AnimDir;
AnimDir = ((RotationA.Yaw + RotationB.Way) + 4096) & 65535) >> 13;
<<| http://uncodex.ut-files.com/ |>>

Code reference for UGold, UT99, Unreal2, UT2k3, UT3
Additional Beyond Unreal Wiki Links
wiki.beyondunreal.com/Legacy:Console_Bar
wiki.beyondunreal.com/Exec_commands#Load
wiki.beyondunreal.com/Legacy:Exec_Directive#Loading_Other_Packages
wiki.beyondunreal.com/Legacy:Config_Vars_And_.Ini_Files
wiki.beyondunreal.com/Legacy:INT_File
Post Reply