IsKeyPressed weirdness

Post » Sat Nov 17, 2012 7:24 pm

Often IsKeyPressed doesn't fire when I press one of the buttons, and then when I try again it fires double/triple. It almost seems as if the key press is detected but doesn't "reach" the script and sort of gets queued instead. The consecutive multiple executions of the orders mess up the results of the scripts, and although I know my problems will be gone when the key Events of SKSE are finished, it greatly hinders me in the current development of my scripts because it takes so much time to figure out if undesired results are due to script bugs or multiple executions.

Now it may be that there is just something wrong with my script causing this weirdness, although I do use States to prevent multiple threads. Here is the script handling the keypresses:

Spoiler
Scriptname SettlerMainScript extends QuestImport InputImport MathObjectReference[] Property GhostHouses  AutoObjectReference[] Property GhostArray  AutoObjectReference Property Ghost  AutoGhostBuildingExtraData Property GhostScript  AutoDoorExtraData Property DoorScript AutoObjectReference Property StorageCell  AutoObjectReference DoorExObjectReference DoorInObjectReference NewMarkerActivator Property Marker AutoActor Property NPC AutoReferenceAlias Property DoorAlias AutoReferenceAlias Property NPCAlias AutoBool BuildingInt BuildingIndexInt RefIndexInt DoorIndexFloat AngleStep = 5.0Float MoveStep = 100.0Float xPosFloat yPosFloat zPosFloat zAngleFloat xAngleFloat yAngleFloat xPosSubjectFloat yPosSubjectFloat zPosSubjectFloat zAngleSubjectfloat CalcFloat DistanceInt DoOnceEvent OnInit()RegisterForUpdate(0.2)EndEventEvent OnUpdate()GoToState("Running")if IsKeyPressed(210)  if Building == False   Building = True   GhostScript = Ghost as GhostBuildingExtraData   GhostScript.Obstructions = 0   Ghost.MoveTo(Game.GetPlayer(), -1000, 0, 0, False)   xPos = Ghost.GetPositionX()   yPos = Ghost.GetPositionY()   zPos = Ghost.GetPositionZ()   xAngle = Ghost.GetAngleX()   yAngle = Ghost.GetAngleY()   zAngle = Ghost.GetAngleZ()    ElseIf Building == True   Building = False   Ghost.MoveTo(StorageCell)  EndIfEndIfIf Building == True  if IsKeyPressed(200)   Ghost.TranslateTo(xPos, (yPos + MoveStep), zPos, 0, 0, zAngle, 200, 0)   yPos += MoveStep  Elseif IsKeyPressed(203)   Ghost.TranslateTo((xPos - MoveStep), yPos, zPos, 0, 0, zAngle, 200, 0)   xPos -= MoveStep  Elseif IsKeyPressed(205)   Ghost.TranslateTo((xPos + MoveStep), yPos, zPos, 0, 0, zAngle, 200, 0)   xPos += MoveStep  Elseif IsKeyPressed(208)   Ghost.TranslateTo(xPos, (yPos - MoveStep), zPos, 0, 0, zAngle, 200, 0)   yPos -= MoveStep  Elseif IsKeyPressed(209)   Ghost.TranslateTo(xPos, yPos, (zPos - (MoveStep / 2)), 0, 0, zAngle, 200, 0)   zPos -= MoveStep  Elseif IsKeyPressed(201)   Ghost.TranslateTo(xPos, yPos, (zPos + (MoveStep / 2)), 0, 0, zAngle, 200, 0)   zPos += MoveStep  Elseif IsKeyPressed(51)   Ghost.TranslateTo(xPos, yPos, zPos, xAngle, yAngle, (zAngle + AngleStep), 50, 30)   zAngle += AngleStep  Elseif IsKeyPressed(52)   Ghost.TranslateTo(xPos, yPos, zPos, xAngle, yAngle, (zAngle - AngleStep), 50, 30)   zAngle -= AngleStep  ElseIf IsKeyPressed(78)   AngleStep *= 2   MoveStep *= 2   Debug.Notification("Step size: " + AngleStep)  ElseIf IsKeyPressed(74)   AngleStep *= 0.5   MoveStep *= 0.5   Debug.Notification("Step size: " + AngleStep)  ElseIf IsKeyPressed(14)   if BuildingIndex < 2	BuildingIndex += 1   Else	BuildingIndex = 0   EndIf   Ghost.MoveTo(StorageCell)   GhostScript = Ghost as GhostBuildingExtraData   GhostScript.Obstructions = 0   GhostHouses[BuildingIndex].Disable()   GhostHouses[BuildingIndex].MoveTo(Game.GetPlayer(), 0, 0, 0, False)   GhostHouses[BuildingIndex].SetPosition(xPos, yPos, zPos)   GhostHouses[BuildingIndex].SetAngle(xAngle, yAngle, zAngle)   GhostHouses[BuildingIndex].Enable()   Ghost = GhostHouses[BuildingIndex]   Debug.Notification("Switched to next building: " + BuildingIndex)  ElseIf IsKeyPressed(48)   GhostScript = Ghost as GhostBuildingExtraData   if GhostScript.Obstructions == 0	Ghost.StopTranslation()	RefIndex = GhostScript.Used.Find(False)	if RefIndex < 0	 ;Debug.MessageBox("No more instances available of this building")	Else	 DoorIndex = 0	 GhostScript.Used[RefIndex] = True	 GhostScript.Exterior[RefIndex].MoveTo(Ghost)	 While (DoorIndex < GhostScript.DoorType.Length)	  DoorEx = Ghost.PlaceAtMe(GhostScript.DoorType[DoorIndex], 1, True, False)	  Distance = sqrt(pow(GhostScript.DoorExxPos[DoorIndex], 2) + pow(GhostScript.DoorExyPos[DoorIndex], 2))	  Calc = (atan(GhostScript.DoorExxPos[DoorIndex] / GhostScript.DoorExyPos[DoorIndex]) + zAngle)	  DoorEx.SetPosition((xPos - (sin(Calc) * Distance)), (yPos - (cos(Calc) * Distance)), (zPos + GhostScript.DoorExzPos[DoorIndex]))	  DoorEx.SetAngle(0, 0, (zAngle + GhostScript.DoorExzAngle[DoorIndex]))		  DoorIn = GhostScript.NorthMarker[RefIndex].PlaceAtMe(GhostScript.DoorType[DoorIndex], 1, True, False)	  DoorIn.SetPosition((GhostScript.DoorInxPos[DoorIndex]), (GhostScript.DoorInyPos[DoorIndex]), (GhostScript.DoorInzPos[DoorIndex]))	  DoorIn.SetAngle(0, 0, (GhostScript.DoorInzAngle[DoorIndex]))			  DoorScript = DoorIn as DoorExtraData	  xPosSubject = DoorEx.GetPositionX() - (sin(zAngle + GhostScript.DoorExzAngle[DoorIndex]) * 150)	  yPosSubject = DoorEx.GetPositionY() - (cos(zAngle + GhostScript.DoorExzAngle[DoorIndex]) * 150)	  NewMarker = DoorEx.PlaceAtMe(Marker, 1, True, False)	  NewMarker.SetPosition(xPosSubject, yPosSubject, (zPos + GhostScript.DoorInzPos[DoorIndex]))	  NewMarker.SetAngle(0, 0, zAngle + (GhostScript.DoorExzAngle[DoorIndex]) + 180)	  DoorScript.TeleportMarker = NewMarker	  if DoOnce == 0	   NPC.MoveTo(Game.GetPlayer())	   DoorAlias.ForceRefTo(NewMarker)	   NPCAlias.TryToEvaluatePackage()	   DoOnce = 1	  EndIf		  DoorScript = DoorEx as DoorExtraData	  xPosSubject = DoorIn.GetPositionX() + (sin(GhostScript.DoorInzAngle[DoorIndex]) * 150)	  yPosSubject = DoorIn.GetPositionY() + (cos(GhostScript.DoorInzAngle[DoorIndex]) * 150)	  NewMarker = DoorIn.PlaceAtMe(Marker, 1, True, False)	  NewMarker.SetPosition(xPosSubject, yPosSubject, (GhostScript.DoorInzPos[DoorIndex]))	  NewMarker.SetAngle(0, 0, (GhostScript.DoorInzAngle[DoorIndex]) + 180)	  DoorScript.TeleportMarker = NewMarker	  DoorIndex += 1	 EndWhile	 NewMarker = GhostScript.Northmarker[RefIndex]	 NewMarker.SetAngle(0, 0, zAngle)	EndIf   Else	Debug.MessageBox("Can't build due to " + GhostScript.Obstructions + " obstructions")   EndIf  EndIfEndIfGoToState("")EndEventState RunningEndState

And here is a video of what it does. It makes the intent of the script obvious and also shows some of the problems with the keypresses. When I get a messagebox about obstructions for example it means the script tried to place a second building immediately afterwards.

http://www.youtube.com/watch?v=3LodqREbsaI


If anyone can tell me how I can fix the described problem I would be very grateful. :smile:
User avatar
M!KkI
 
Posts: 3401
Joined: Sun Jul 16, 2006 7:50 am

Post » Sat Nov 17, 2012 3:01 pm

State Running
EndState

I think you need to define an empty OnUpdate event in that state, or the empty state's event will be used.
User avatar
Marquis T
 
Posts: 3425
Joined: Fri Aug 31, 2007 4:39 pm

Post » Sat Nov 17, 2012 10:05 am

:D

Thanks, you are right ofcourse! I completely forgot that.
User avatar
Sista Sila
 
Posts: 3381
Joined: Fri Mar 30, 2007 12:25 pm

Post » Sat Nov 17, 2012 11:37 am

@Maeg,

The problem with multiple keypressed sent for single keystroke is because of Papyrus threading. For every Update interval (in your case, 0.2 sec), there is a new instance of OnUpdate that running. Sometimes, your previous OnUpdate code didn't finish yet, resulting in multiple keypressed.

You can try to use different state to eliminate the extra threads

Quoting myself from old thread that has same problem

*NOTE: I think if yours update is too fast (0.01) for instance, you still have some issue.
Or if you could use RegisterForSingleUpdate somehow, it should be fine


Hmm, ok, I will just give you a sample code then

Auto State Default_State  Event OnUpdate()	GoToState("deathState")	if(IsKeyPressed(19))	   ;do something	endif	GoToState("Default_State")  EndEventEndState;{empty state of DEATH}State deathState  ; Die, stupid threadsEndState

The empty state of death will kill any unwanted threads. You still have to use key trap like the one you posted in the first post (used in Vegas?!) to read your input once.

The reason behind this is, as soon as you enter the state, you shut it, send any sub-sequence Event to the empty state (GoToState("death-state"))
User avatar
Taylrea Teodor
 
Posts: 3378
Joined: Sat Nov 18, 2006 12:20 am

Post » Sat Nov 17, 2012 9:05 am

I know rongphale, I did use states but forgot to define an empty Event so it didn't prevent multiple threads. Luckily ingenue noticed my forgetfulness.
User avatar
James Rhead
 
Posts: 3474
Joined: Sat Jul 14, 2007 7:32 am

Post » Sat Nov 17, 2012 6:45 pm

I'd also chime in on the use of RegisterForUpdate - from my testing, on some machines and game-states (if there are a lot of scripts producing errors, even vanilla scripts, or the graphics have been pushed a bit high for the machine) you might be lucky to get an update faster than 0.25 whatever you ask for, and the update frequency can fluctuate significantly depending on what's going on. That's certainly the situation on my machine/settings/savegame. The game itself runs fine, but there's little available CPU overhead for update events.

I'd say, as a matter of good practice, never use RegisterForUpdate; only ever use RegisterForSingleUpdate. That way you help to avoid the issue of persistent scripts causing problems in savegames, and your code is much more likely to run reliably on slower PCs.

Just my two-pence/cents worth :smile:.
User avatar
Czar Kahchi
 
Posts: 3306
Joined: Mon Jul 30, 2007 11:56 am

Post » Sat Nov 17, 2012 10:54 am

That's a good point, I guess I'll swap to that method.
User avatar
danni Marchant
 
Posts: 3420
Joined: Sat Oct 07, 2006 2:32 am


Return to V - Skyrim