Better way of calculating position in front of player?

Post » Mon Nov 19, 2012 4:49 am

Something that seems simple, and yet was (to me) actually rather difficult, was calculating the position directly in front of the player for various things (placing something there, etc). You have a known x, y, and z position, and a known Z rotation, and you need to translate that to some other x, y, z position in order to know where to place something in front of the player.

Here is the function I wrote that I use in several places in a couple of my mods. It sets two global variables , myXOffset and myYOffset, to the X and Y positions relative to the player that the object needs to be placed such that it is directly in front of them. In this instance it moves the object 100 game units forward and 30 units to one side along the arc; these could have been input parameters but I left them as constants in this case.

Spoiler

function GetOffset()	;Trigonometry time! We need to position the campsite item such that it is dropped in front of the player.	int myQuad = 1	float mySideC = 100.0	;Hypotenuse (distance to move the object forward)	float myAngleA = (Game.GetPlayer().GetAngleZ() + 30.0)		float myAngleC = 90.0		;Clamp to an angle less than 90 degrees.	if myAngleA > 90.0 && myAngleA <= 180.0		myAngleA -= 90.0		myQuad = 2	elseif myAngleA > 180.0 && myAngleA <= 270.0		myAngleA -= 180.0		myQuad = 3	elseif myAngleA > 270.0		myAngleA -= 270.0		myQuad = 4	endif		float mySideA	float mySideB		;Determine the length of the legs of the triangle for moving the player.	if myQuad == 1 || myQuad == 3		mySideA = math.sin(myAngleA) * mySideC				;Y offset		mySideB = math.cos(myAngleA) * mySideC				;X offset	else		mySideA = math.cos(myAngleA) * mySideC				;Y offset		mySideB = math.sin(myAngleA) * mySideC				;X offset	endif		;Figure out which direction the offset should travel based on the quadrant.			if myQuad == 1		;Move farther into the 1st quadrant		myXOffset = mySideA		myYOffset = mySideB	elseif myQuad == 2		;Move farther into the 2nd quadrant		myXOffset = mySideA		myYOffset = mySideB * -1.0	elseif myQuad == 3		;Move farther into the 3rd quadrant		myXOffset = mySideA * -1.0		myYOffset = mySideB * -1.0	elseif myQuad == 4		;Move farther into the 4th quadrant		myXOffset = mySideA * -1.0		myYOffset = mySideB	endifendFunction

It works flawlessly, but I've always thought that it was an inelegant solution to the problem. Has anyone else come up with a way of doing this that is shorter, simpler, sweeter? Thanks.
User avatar
Dean Brown
 
Posts: 3472
Joined: Fri Aug 31, 2007 10:17 pm

Post » Mon Nov 19, 2012 12:05 am

not sure what you are trying to acheive, but if the idea is just to place an object in front of you, you can make this really faster:


oref is the coord reference (in your case, the player)
oP, is the reference of the baseObject you want to move
xOffset, the distance in front of
fAngZ, the angle (if you want to be exactly in front of the player then zOffset = player.GetAngleZ())
zOffset, the altitude


objectreference oP = oRef.PlaceAtMe(baseObject)
oP.moveto(oRef,(xOffset * math.sin(fAngZ)),(xOffset * math.cos(fAngZ)), zOffset, true)
User avatar
Albert Wesker
 
Posts: 3499
Joined: Fri May 11, 2007 11:17 pm

Post » Mon Nov 19, 2012 2:34 am

It feels like you're trying to do half of the trig functions' jobs for them by narrowing the domain down to a single quadrant then extrapolating it to 4 quadrants later, when you could just let the trig functions deal with the full 360 degree domain. Wouldn't something like this work too?
Spoiler
Import MathActor Property PlayerRef AutoFloat[] Function GetOffsets(Float afDistance = 100.0, ObjectReference akSource = PlayerRef as ObjectReference)	Float A = akSource.GetAngleZ()	Float YDist = Cos(A)	Float XDist = Sin(A)	YDist *= afDistance	XDist *= afDistance	Float[] Offsets = New Float[2]	Offsets[0] = YDist	Offsets[1] = XDist	Return OffsetsEndFunction
Unfortunately I don't have access to Skyrim at the moment to try it out, but if it requires any tweaking at all I wouldn't imagine it to require much.

Cipscis
User avatar
Christine Pane
 
Posts: 3306
Joined: Mon Apr 23, 2007 2:14 am

Post » Sun Nov 18, 2012 9:03 pm

Cipscis: The only modification I needed to make was swapping sin and cos and it works like a charm. Thanks!

Float[] Function GetPostionAwayFromRefFacing(ObjectReference akSource, Float afDistance = 100.0, Float afOffset = 0.0)		Float A = akSource.GetAngleZ() + afOffset		Float YDist = Sin(A)		Float XDist = Cos(A)		YDist *= afDistance		XDist *= afDistance		Float[] Offsets = New Float[2]		Offsets[0] = YDist		Offsets[1] = XDist		Return OffsetsEndFunction

Using a negative afDistance could let you place something directly behind the player... such as an undead when they least expect it /grin
User avatar
City Swagga
 
Posts: 3498
Joined: Sat May 12, 2007 1:04 am

Post » Mon Nov 19, 2012 1:58 am

I dont understand why you are using temporary vars where you can set everything in the return array directly...?
User avatar
x_JeNnY_x
 
Posts: 3493
Joined: Wed Jul 05, 2006 3:52 pm

Post » Sun Nov 18, 2012 9:49 pm

You're right, it could be done that way. I've chosen to do it this way because operators like *= don't work with array elements.

When the script is compiled, temporary variables are actually created anyway to store intermediate values used in more complex calculations, so I'm not worried about creating a few extra temporary variables.

Cipscis
User avatar
jessica sonny
 
Posts: 3531
Joined: Thu Nov 02, 2006 6:27 pm

Post » Mon Nov 19, 2012 12:06 am

I also don't see the benefit in using a temporary variable just so you can use the shortcut multiplication operator in this case.
Float[] Function GetPostionAwayFromRefFacing(ObjectReference akSource, Float afDistance = 100.0, Float afOffset = 0.0)		Float A = akSource.GetAngleZ() + afOffset		Float[] Offsets = New Float[2]		Offsets[0] = Sin(A) * afDistance		Offsets[1] = Cos(A) * afDistance		Return OffsetsEndFunction
Those calculations don't look any worse to me. But aren't you returning the point in a non-standard Y,X order?
User avatar
Chenae Butler
 
Posts: 3485
Joined: Sat Feb 17, 2007 3:54 pm

Post » Mon Nov 19, 2012 1:20 am

Probably, haha. It was written quickly and without putting too much thought into it, and I'm certainly not attached to the idea of using temporary variables. To be honest the main reason they were there was because at first there was no information returned so after writing the calculations I added the array so I could return the values.

Cipscis
User avatar
Esther Fernandez
 
Posts: 3415
Joined: Wed Sep 27, 2006 11:52 am

Post » Mon Nov 19, 2012 7:40 am

Right, but what about determining position far from the player? You would have to check whether there is a LOS, right? In such instance I prefer using projectiles. This way Im pushing it from Papyrus and it works much faster.
User avatar
Karine laverre
 
Posts: 3439
Joined: Tue Mar 20, 2007 7:50 am

Post » Mon Nov 19, 2012 9:27 am

Right, but what about determining position far from the player? You would have to check whether there is a LOS, right? In such instance I prefer using projectiles. This way Im pushing it from Papyrus and it works much faster.
Interesting in that case, however more details on how you achieve that without script would be great:
you place an object somewhere and you see if you can shot it?
User avatar
Alexx Peace
 
Posts: 3432
Joined: Thu Jul 20, 2006 5:55 pm

Post » Mon Nov 19, 2012 10:56 am

Interesting in that case, however more details on how you achieve that without script would be great:
you place an object somewhere and you see if you can shot it?

Create magic effect, type: fire and forget, target location. Add to that effect explosion. To that explosion add an activator/marker. Give a script to the activator with event OnInit(). When you fire the spell via cast command, the projectile will go where you are currently looking. During explosion an activator is spawned.The activator can give you its location and then can delete itself. Check out my mod Nature of the beast II for details. Its very fast and efficient.
User avatar
Zoe Ratcliffe
 
Posts: 3370
Joined: Mon Feb 19, 2007 12:45 am


Return to V - Skyrim