Detecting Where the Player is Aiming?

Post » Wed Jun 20, 2012 7:25 pm

I'm trying to create a unique spell that has some unique spell effects, casting animations, and casting conditions. In order to do what I want to do, I will need to detect where the player is aiming his crosshairs. Naturally, since there is no native function for doing this, I would have to find a way to do this manually.

In my research, I came across this video: http://www.youtube.com/watch?v=Bzg9v442bw8

After looking at the mods code, I found that it actually does exactly what I want to do. However, the code used to accomplish that is...in my opinion, very poorly written. I could copypaste his code, but I do not want to do this. I want to fully understand how it works, and write it myself. The math the author used in his code was, well, either way over my head, or once again, poorly written. I was hoping someone could help explain it to me, or give me a better solution.
Scriptname killmoonMagicCircleScript extends activemagiceffect  import Mathimport UtilityInt Property index  Autofloat Property Energy  Auto  float Property Stamina  Autofloat Property Damage  Auto  ; initial position vectorfloat Property xPos  Auto  float Property yPos  Auto  float Property zPos  Auto  ; fly vectorfloat Property dx Auto  float Property dy  Auto  float Property dz  Auto  ; privatefloat Property ang  Auto  float Property xAng  Auto  float Property yAng  Auto  float Property zAng  Auto  float Property x  Auto  float Property x2  Auto  float Property sin  Auto  float Property cos  Auto  ObjectReference Property Circle01  AutoObjectReference Property Circle02  AutoObjectReference Property Circle03  AutoObjectReference Property Dummy  AutoObjectReference Property Cast  Auto  ObjectReference Property Target  AutoSpell Property killMoon  AutoIdle Property moonIdle  Auto  killmoonQuestScript Property killmoonQuest  Auto  Event OnEffectStart(Actor Target, Actor Caster)	Actor PlayerActor =  Game.GetPlayer()	if PlayerActor.IsWeaponDrawn() && !PlayerActor.IsFlying()		Debug.ToggleCollisions()		PlayerActor.PlayIdle(moonIdle)		Debug.ToggleCollisions()		Energy = PlayerActor.getav("Destruction") as float		Energy = Energy  * 2		Stamina = PlayerActor.getav("Stamina") as float		Damage = Stamina - Energy		if Damage > 0			PlayerActor.damageAV("Stamina", Energy)		else			PlayerActor.damageAV("Stamina", Stamina)			PlayerActor.damageAV("Health", Damage)		endif				Utility.wait(0.5)		killmoonQuest.indexCircle += 1		killmoonQuest.indexCircle = killmoonQuest.indexCircle % 3		if killmoonQuest.indexCircle == 1			Dummy =  Circle01		elseif killmoonQuest.indexCircle == 2			Dummy =  Circle02		else			Dummy =  Circle03		endif		ang = PlayerActor.GetAngleZ()		zang = ang		if ang > 180			ang = ang - 360		endif		x = ang*0.0174532925		x2 = x*x		sin = x*(1-(x2/6)*(1-(x2/20)*(1-(x2/42)*(1-(x2/72)*(1-x2/110)))))		cos = 1-(0.5)*x2*(1-(x2/12)*(1-(x2/30)*(1-(x2/56)*(1-x2/90))))		dx = 10*sin		dy = 10*cos		; vertical fly vector		ang = PlayerActor.GetAngleX()		xang = ang*cos ; uses settings from Horizontal Vector		yang = -ang*sin		x = -ang*0.0174532925		x2 = x*x		sin = x*(1-(x2/6)*(1-(x2/20)*(1-(x2/42)*(1-(x2/72)*(1-x2/110)))))		cos = 1-(0.5)*x2*(1-(x2/12)*(1-(x2/30)*(1-(x2/56)*(1-x2/90))))		dz = 10*sin		; initial position vector (to add to player coords)		xPos = 7*dx*cos		yPos = 7*dy*cos		zPos = 138+5*dz		Dummy.Enable()		Dummy.moveTo(PlayerActor, xPos , yPos , zPos )		Dummy.setAngle(xAng, yAng, zAng)		Cast.Enable()		Cast.moveTo(PlayerActor, xPos , yPos , zPos )		xPos = 7*dx*cos		yPos = 7*dy*cos		zPos = 8*dz		Target.Enable()		Target.moveTo(Dummy, xPos , yPos , zPos )			Utility.wait(0.5)		killMoon.Cast(Cast ,Target)		Utility.wait(1)		Cast.InterruptCast()		Dummy.Disable()		Cast.Disable()		Target.Disable()	endifEndEvent
There's a bit more code in a couple other scripts, but this is the bulk that I wish to understand.

Amidst all of that, he calculated where the player was aiming, placed an activator at that location (I think), and then had a separate activated cast a spell at that location. There is some other stuff in there, such as moving the spell effect activator in front of the player, but I'm not concerned about that. My only concern, is figuring out how in gods name the players targeted location was found. Would anyone be able to help with this?
User avatar
James Potter
 
Posts: 3418
Joined: Sat Jul 07, 2007 11:40 am

Post » Wed Jun 20, 2012 7:48 pm

Wouldn't using
ObjectReference aPlayerEvent OnSomeEvent()          aPlayer = Game.GetPlayer()YourSpell.Cast(aPlayer)EndEvent

Cause a spell to be shot in the angle at which the player is facing? Also, couldn't you use GetCombatTarget() on the player to get which ever actor he's looking at (Using it on the player returns the current enemy the player is looking at)?
User avatar
Star Dunkels Macmillan
 
Posts: 3421
Joined: Thu Aug 31, 2006 4:00 pm

Post » Thu Jun 21, 2012 12:52 am

Wouldn't using
ObjectReference aPlayerEvent OnSomeEvent()		  aPlayer = Game.GetPlayer()YourSpell.Cast(aPlayer)EndEvent

Cause a spell to be shot in the angle at which the player is facing? Also, couldn't you use GetCombatTarget() on the player to get which ever actor he's looking at (Using it on the player returns the current enemy the player is looking at)?
I'm not so sure about the first, but I suppose it'd be better to test that before I go wasting a bunch of time. And I could use GetCombatTarget, but I want it to be an aimed spell, not a target actor spell.
User avatar
Miguel
 
Posts: 3364
Joined: Sat Jul 14, 2007 9:32 am

Post » Thu Jun 21, 2012 5:39 am

It's been a long time since I was in a math class, but looks like the author might be using Taylor expansions to calculate the angles. Give me a second and I'll look through the scripts I wrote for a way that uses trigonometry instead.
User avatar
Danielle Brown
 
Posts: 3380
Joined: Wed Sep 27, 2006 6:03 am

Post » Thu Jun 21, 2012 10:57 am

I'm not so sure about the first, but I suppose it'd be better to test that before I go wasting a bunch of time. And I could use GetCombatTarget, but I want it to be an aimed spell, not a target actor spell.

Alright, I'll test to see if the projectile comes out of the right angle/position.

It's been a long time since I was in a math class, but looks like the author might be using Taylor expansions to calculate the angles. Give me a second and I'll look through the scripts I wrote for a way that uses trigonometry instead.

Well, I know your really good at Trigonometry :biggrin:
User avatar
x a million...
 
Posts: 3464
Joined: Tue Jun 13, 2006 2:59 pm

Post » Thu Jun 21, 2012 5:20 am

Thanks.
User avatar
naomi
 
Posts: 3400
Joined: Tue Jul 11, 2006 2:58 pm

Post » Wed Jun 20, 2012 9:57 pm

Well I couldn't find it, must have deleted it. This should work, but haven't tried it out in the game:

Scriptname Example extends ActiveMagicEffectActivator Property DummyObject Auto{The stuff we'll use for targets.}Float Property TargetDistance = 2000.0 Auto{Distance from the player to place the target.)Float Property SourceDistance = 50.0 Auto{Distance from the player to place the source.)Spell Property DamageSpell Auto{The spell we're going to cast at the target.}Event OnEffectStart(Actor akTarget, Actor akCaster)	;akTarget is player, this is a self target spell	float AngleX = Math.Abs(akTarget.GetAngleX())	float AngleZ = akTarget.GetAngleZ()	float DistanceZ = TargetDistance * Math.Sin(AngleX)	float DistanceXY = TargetDistance * Math.Cos(AngleX)	float DistanceX = DistanceXY * Math.Sin(AngleZ)	float DistanceZ = DistanceXY * Math.Cos(AngleZ)	ObjectReference SpellTarget = akTarget.PlaceAtMe(DummyObject)	SpellTarget.MoveTo(akTarget, DistanceX, DistanceY, DistanceZ)	float Mult = SourceDistance / TargetDistance	ObjectReference SpellSource = akTarget.PlaceAtMe(DummyObject)	SpellSource.MoveTo(akTarget, DistanceX * Mult, DistanceY * Mult, DistanceZ * Mult)	DamageSpell.Cast(SpellSource, SpellTarget)	Utility.Wait(1)	SpellTarget.Disable()	SpellTarget.Delete()	SpellSource.Disable()	SpellSource.Delete()EndEvent
User avatar
Wanda Maximoff
 
Posts: 3493
Joined: Mon Jun 12, 2006 7:05 am

Post » Thu Jun 21, 2012 3:12 am

Well I couldn't find it, must have deleted it. This should work, but haven't tried it out in the game:

Scriptname Example extends ActiveMagicEffectActivator Property DummyObject Auto{The stuff we'll use for targets.}Float Property TargetDistance = 2000.0 Auto{Distance from the player to place the target.)Float Property SourceDistance = 50.0 Auto{Distance from the player to place the source.)Spell Property DamageSpell Auto{The spell we're going to cast at the target.}Event OnEffectStart(Actor akTarget, Actor akCaster)	;akTarget is player, this is a self target spell	float AngleX = Math.Abs(akTarget.GetAngleX())	float AngleZ = akTarget.GetAngleZ()	float DistanceZ = TargetDistance * Math.Sin(AngleX)	float DistanceXY = TargetDistance * Math.Cos(AngleX)	float DistanceX = DistanceXY * Math.Sin(AngleZ)	float DistanceZ = DistanceXY * Math.Cos(AngleZ)	ObjectReference SpellTarget = akTarget.PlaceAtMe(DummyObject)	SpellTarget.MoveTo(akTarget, DistanceX, DistanceY, DistanceZ)	float Mult = SourceDistance / TargetDistance	ObjectReference SpellSource = akTarget.PlaceAtMe(DummyObject)	SpellSource.MoveTo(akTarget, DistanceX * Mult, DistanceY * Mult, DistanceZ * Mult)	DamageSpell.Cast(SpellSource, SpellTarget)	Utility.Wait(1)	SpellTarget.Disable()	SpellTarget.Delete()	SpellSource.Disable()	SpellSource.Delete()EndEvent
Ok, I'll play around with it, thanks.
User avatar
Elisha KIng
 
Posts: 3285
Joined: Sat Aug 18, 2007 12:18 am

Post » Thu Jun 21, 2012 2:50 am

I forgot that the player's Z position is set at their feet. So you need to adjust the Z component of the MoveTo functions a bit higher. Maybe like this:

MoveTo(akTarget, DistanceX, DistanceY, DistanceZ + (akTarget.GetHeight() * 0.75) )
User avatar
Eileen Müller
 
Posts: 3366
Joined: Fri Apr 13, 2007 9:06 am

Post » Thu Jun 21, 2012 9:06 am

Ok, results are back. You're gonna' like them :happy:

Using Cast() on the player with a target spell will cause the projectile to come out of the current angle (or whatever you want to say). Point is, the spell will shoot at whatever direction/angle/position you're looking at.
User avatar
Josh Trembly
 
Posts: 3381
Joined: Fri Nov 02, 2007 9:25 am

Post » Thu Jun 21, 2012 9:57 am

Ok, results are back. You're gonna' like them :happy:

Using Cast() on the player with a target spell will cause the projectile to come out of the current angle (or whatever you want to say). Point is, the spell will shoot at whatever direction/angle/position you're looking at.
How bout that. Honestly didn't expect that to work, so I kind of feel bad now that RandomNoob has went and made me a formula. ><

Anyways, thanks for the help guys, and I'll probably end up using that formula if the cast thing doesn't work out with how I set things up.
User avatar
Cedric Pearson
 
Posts: 3487
Joined: Fri Sep 28, 2007 9:39 pm

Post » Wed Jun 20, 2012 6:54 pm

No problem, it'd come in useful if you ever felt the need to cast stuff from other objects like in the mod you linked. Or here, this is something I made back when someone on the forums was talking about making a magic missile spell:

http://www.youtube.com/watch?v=Ex0AxGFheEg

It works by spawning the activator with an explosion, but I could take the script I just made up and add it in so there's no need for any projectiles to determine a spell target.
User avatar
Betsy Humpledink
 
Posts: 3443
Joined: Wed Jun 28, 2006 11:56 am

Post » Thu Jun 21, 2012 7:14 am

No problem, it'd come in useful if you ever felt the need to cast stuff from other objects like in the mod you linked. Or here, this is something I made back when someone on the forums was talking about making a magic missile spell:

http://www.youtube.com/watch?v=Ex0AxGFheEg

It works by spawning the activator with an explosion, but I could take the script I just made up and add it in so there's no need for any projectiles to determine a spell target.
Well, if I cannot use the .cast method from an activator, then I actually will still have to use your formula to do what I want to do. Similar to the video I linked, I need to create a sort of "ritual circle" in front of the player, and cast the spell from that activator.

EDIT: And cool spell you've got there in the vid. Does it still function properly when you're in first person?
User avatar
Miss K
 
Posts: 3458
Joined: Sat Jan 20, 2007 2:33 pm

Post » Wed Jun 20, 2012 7:01 pm

Yeah it does. I describe how to make it http://www.creationkit.com/User:Fg109.
User avatar
Claire Mclaughlin
 
Posts: 3361
Joined: Mon Jul 31, 2006 6:55 am

Post » Wed Jun 20, 2012 7:11 pm

Yeah it does. I describe how to make it http://www.creationkit.com/User:Fg109.
Cool. Thanks.
User avatar
Alexis Estrada
 
Posts: 3507
Joined: Tue Aug 29, 2006 6:22 pm

Post » Wed Jun 20, 2012 9:05 pm

In your first bit of code where you are doing angle calculations, I noticed you set create two DistanceZ's. I'm assuming this will of course, break the whole formula. Which of the two should I change?
        float AngleX = Math.Abs(akTarget.GetAngleX())        float AngleZ = akTarget.GetAngleZ()        float DistanceZ = TargetDistance * Math.Sin(AngleX)        float DistanceXY = TargetDistance * Math.Cos(AngleX)        float DistanceX = DistanceXY * Math.Sin(AngleZ)        float DistanceZ = DistanceXY * Math.Cos(AngleZ)
User avatar
jasminε
 
Posts: 3511
Joined: Mon Jan 29, 2007 4:12 am

Post » Wed Jun 20, 2012 9:07 pm

One but of advice : Use RemoteCast() instead, so this way you can pin the blame on the Player, and actors won't act as if they were hit by nothing.
User avatar
Isabella X
 
Posts: 3373
Joined: Sat Dec 02, 2006 3:44 am

Post » Wed Jun 20, 2012 8:44 pm

My mistake, that's a typo. The second DistanceZ is supposed to DistanceY.
User avatar
CORY
 
Posts: 3335
Joined: Sat Oct 13, 2007 9:54 pm

Post » Thu Jun 21, 2012 6:33 am

One but of advice : Use RemoteCast() instead, so this way you can pin the blame on the Player, and actors won't act as if they were hit by nothing.
Ah, right. I'll do that, thanks.

My mistake, that's a typo. The second DistanceZ is supposed to DistanceY.
Thanks.

In an effort to better understand just what the hell is going on, I've modified the original script in the OP, and adapted RandomNoob's formula to it. It seems to be working well enough, but unfortunately, this is only the case if the player is looking "upward". If he looks downward, the spell fires too high. I'm assuming I need to invert something, but I haven't quite figured that out yet.
Scriptname killmoonMagicCircleScript extends activemagiceffectObjectReference Property RitualCircle AutoObjectReference Property SpellSource Auto  ObjectReference Property SpellTarget AutoSpell Property killMoon AutoIdle Property moonIdle Auto  killmoonQuestScript Property killmoonQuest  Auto  Event OnEffectStart(Actor akTarget, Actor akCaster)	Actor Player =  Game.GetPlayer()	if Player.IsWeaponDrawn() && !Player.IsFlying()		Debug.ToggleCollisions()		Player.PlayIdle(moonIdle)		Debug.ToggleCollisions()		float Energy = Player.GetAV("Destruction") as float		Energy = Energy  * 2		float Stamina = Player.GetAV("Stamina") as float		float Damage = Stamina - Energy		If Damage > 0			Player.DamageAV("Stamina", Energy)		Else			Player.DamageAV("Stamina", Stamina)			Player.DamageAV("Health", Damage)		Endif		Utility.Wait(0.5)		; Store all player camera angles		float AngleX = Math.Abs(Player.GetAngleX())		float AngleY = Player.GetAngleY()		float AngleZ = Player.GetAngleZ()		; Create our distance variables		float SourceDistance = 10.0		float TargetDistance = 2000.0		; Calculate the ritual circle's position		float OffsetZ = SourceDistance * Math.Sin(AngleX)		float DistanceXY = SourceDistance * Math.Cos(AngleX)		float OffsetX = DistanceXY * Math.Sin(AngleZ)		float OffsetY = DistanceXY * Math.Cos(AngleZ)		; Place the RitualCircle in front of the Player		RitualCircle.Enable()		RitualCircle.MoveTo(Player, OffsetX, OffsetY, OffsetZ + (Player.GetHeight() * 0.75))		RitualCircle.SetAngle(AngleX, AngleY, AngleZ)		; Calculate the target position		OffsetZ = TargetDistance * Math.Sin(AngleX)		DistanceXY = TargetDistance * Math.Cos(AngleX)		OffsetX = DistanceXY * Math.Sin(AngleZ)		OffsetY = DistanceXY * Math.Cos(AngleZ)				; Place the spell source activator in front of the RitualCircle		float Mult = SourceDistance / TargetDistance		SpellSource.Enable()		SpellSource.MoveTo(RitualCircle, OffsetX * Mult, OffsetY * Mult, OffsetZ * Mult)				; Place the spell target at the players target location		SpellTarget.Enable()		SpellTarget.MoveTo(RitualCircle, OffsetX , OffsetY , OffsetZ)			Utility.Wait(0.5)		killMoon.Cast(SpellSource, SpellTarget)		Utility.Wait(1.0)		SpellSource.InterruptCast()		RitualCircle.Disable()		SpellSource.Disable()		SpellTarget.Disable()	endifEndEvent

Any ideas?
User avatar
SWagg KId
 
Posts: 3488
Joined: Sat Nov 17, 2007 8:26 am

Post » Thu Jun 21, 2012 4:48 am

Change

float AngleX = Math.Abs(Player.GetAngleX())

to

float AngleX = Player.GetAngleX() * -1

The Elder Scrolls games are weird in that looking up actually gives you a negative X angle and looking down gives a positive one. So I used the absolute value at first to make sure it would always be positive, but I forgot to account for when the player is looking down.
User avatar
c.o.s.m.o
 
Posts: 3419
Joined: Sat Aug 12, 2006 9:21 am

Post » Thu Jun 21, 2012 2:21 am

Change

float AngleX = Math.Abs(Player.GetAngleX())

to

float AngleX = Player.GetAngleX() * -1

The Elder Scrolls games are weird in that looking up actually gives you a negative X angle and looking down gives a positive one. So I used the absolute value at first to make sure it would always be positive, but I forgot to account for when the player is looking down.
I'll try it out, thanks.

EDIT: Works great! Thanks for all the help. I should be able to do what I need to do now.
User avatar
Marilú
 
Posts: 3449
Joined: Sat Oct 07, 2006 7:17 am

Post » Thu Jun 21, 2012 3:42 am

This is a really cool thread.

I love the idea of finding where the player is looking through a script, but what is wrong with just doing:

Spell Property LocatorSpellProperty Auto
ReferenceAlias Property LocatorAliasProperty Auto

Float PosX
Float PosY
Float PosZ

Game.GetPlayer().Cast(LocatorSpellProperty)
PosX = LocatorAliasProperty.GetRef().GetPositionX()
PosX = LocatorAliasProperty.GetRef().GetPositionX()
PosX = LocatorAliasProperty.GetRef().GetPositionX()
LocatorAliasProperty.GetRef().DisableNoWait()
LocatorAliasProperty.GetRef().Delete()
LocatorAliasProperty.Clear()

Where LocatorSpell is some spell with no visual effects, aimed, no hit event, a super fast invisible projectile (maybe a beam would work? or some hitscan projectile?), an invisible explosion, and an invisible activator with a script that forces itself into a reference alias on an always running quest (doesn't require the alias to be filled) which is a property in this script called LocatorProperty


Not trying to undermine anything said here, just trying to understand the advantages or differences between each method.
User avatar
Tikarma Vodicka-McPherson
 
Posts: 3426
Joined: Fri Feb 02, 2007 9:15 am

Post » Thu Jun 21, 2012 11:14 am

This is a really cool thread.

I love the idea of finding where the player is looking through a script, but what is wrong with just doing:

Game.GetPlayer().Cast(LocatorSpell)

Where LocatorSpell is some spell with no visual effects, a super fast invisible projectile, an invisible explosion, and an invisible activator with a script that reports its location or perhaps even forces itself into a reference alias that can then be easily accessed as a property in other scripts?
Hmm, interesting. You think that would work? The calculations RandomNoob made are working just fine for me, but using that method would definitely be a lot "cleaner".

Though, even if that is the case, if you look in the video I posted in the OP, a really cool "ritual" spell effect is played in front of the player. In order to properly place that effect, an activator (or something similar) with the spell effect model must be placed in front of the player. In order to accomplish that, some sort of calculations need to be made.
User avatar
Jarrett Willis
 
Posts: 3409
Joined: Thu Jul 19, 2007 6:01 pm

Post » Wed Jun 20, 2012 9:11 pm

Well what I posted would only report the position of where the player is looking (and may not even work, havent tested it.)

I am not actually doing anything with that location once I have retrieved it in the script. Once you have the location though, you could use fg101s calculations to place activators in a clock like formation in front of the player and then use splinetranslateto() to "shoot" them at the target location, which would be PosX, PosY, PosZ. Or you could move the player to that location for a "blink" type spell, or anything really.

You could probably use a method of determining where the player is aiming at to do some other really cool things too, such as running it on the player as an alias in a quest on a certain animation event, such as on a weapon swing, then use a bit more scripting to determine the distance between where the player is looking and a certain node on the target to create locational damage effects. Knocking weapons out of NPCs hands, headshots, bleed damage when hitting the torso, ect.

Obviously I havent tried any of this but just some thoughts on the matter.
User avatar
Kelsey Hall
 
Posts: 3355
Joined: Sat Dec 16, 2006 8:10 pm


Return to V - Skyrim