15gudasc Posted November 3 Share Posted November 3 Hello! I have a scene that takes place in a train car, with a series of trees translating outside the car to suggest movement. The trees translate in a line and reset to the TreeStartPosition, where they begin the translation again. Tree.TranslateTo() moves the object reference successfully, but "tree.SetPosition()" doesn't reset its position. Instead, the tree stays at the TreeEndPosition. The debug notification "Resetting position" continuously fires after this, though, in roughly one second intervals. Any ideas why SetPosition() isn't doing anything? I assume it has something to do with the way I've built the "While" loops. Thank you! Float Property moveSpeed = 1500.0 Auto ; Speed of movement Float Property delayVariance = 1.0 Auto ; Maximum random delay in seconds ObjectReference Property TreeStartPosition Auto ; Starting position marker ObjectReference Property TreeEndPosition Auto ; Ending position marker Function MoveTree(ObjectReference tree) While True ; Move the tree to the end position tree.TranslateTo(TreeEndPosition.GetPositionX(), TreeEndPosition.GetPositionY(), TreeEndPosition.GetPositionZ(), 0.0, 0.0, 0.0, moveSpeed) ; Wait until it reaches the end position While tree.GetDistance(TreeEndPosition) > 10.0 Utility.Wait(0.1) EndWhile ; Reset the tree back to the starting position tree.SetPosition(TreeStartPosition.GetPositionX(), TreeStartPosition.GetPositionY(), TreeStartPosition.GetPositionZ()) Debug.Notification("Resetting position") ; A small random delay before the next cycle Utility.Wait(Utility.RandomFloat(0.0, delayVariance)) EndWhile EndFunction Link to comment Share on other sites More sharing options...
SKKmods Posted November 3 Share Posted November 3 How does the (While True) loop ever get broken/stop ? That looks like a guaranteed stackdump bfore worrying about anything else. You should be using events like OnTranslationComplete() to reset, not nasty while loops. If your new to this, the format for a non object attached script would be; Self.RegisterForRemoteEvent(thisTree, "OnTranslationComplete") and Event ObjectReference.OnTranslationComplete(ObjectReference akSender) Link to comment Share on other sites More sharing options...
15gudasc Posted November 4 Author Share Posted November 4 Thanks so much. I hadn't considered how to stop the loop–in my inexperience it seemed like a lower-priority objective until I got the rest working. Here's how I've rebuilt it. This lives in a quest script. Parts of the script seem to work–the Tree Array registration debug notifications fire, and the object references move as expected and reach their endpoint–but the OnTranslationComplete() event never fires. I never see the "Translation completed" debug notification. Some objects in TreesArray are static collections, if that matters. But others are single static objects. I of course have a separate function to UnregisterForRemotEvent. Function RegisterTreeMovement() ; Loop through each tree in the array Int i = 0 While i < TreesArray.Length TreesArray[i].RegisterForRemoteEvent(TreesArray[i], "OnTranslationComplete") i += 1 Debug.Notification("Tree" + TreesArray[i] + "registered") EndWhile BeginTreeMovement() EndFunction Function BeginTreeMovement() ; Loop through each tree in the array and start movement Int i = 0 While i < TreesArray.Length TreesArray[i].TranslateTo(TreeEndPosition.GetPositionX(), TreeEndPosition.GetPositionY(), TreeEndPosition.GetPositionZ(), 0.0, 0.0, 0.0, moveSpeed) i += 1 EndWhile EndFunction Event ObjectReference.OnTranslationComplete(ObjectReference akSender) ; Loop through each tree in the array to find the matching reference Debug.Notification("Translation completed") Int i = 0 While i < TreesArray.Length If TreesArray[i] == akSender ; Reset the tree back to the starting position akSender.SetPosition(TreeStartPosition.GetPositionX(), TreeStartPosition.GetPositionY(), TreeStartPosition.GetPositionZ()) Debug.Notification("Resetting tree position!") ; Add a small random delay before moving it again Utility.Wait(Utility.RandomFloat(0.0, delayVariance)) ; Restart the movement toward the end position BeginTreeMovement() EndIf i += 1 EndWhile EndEvent Link to comment Share on other sites More sharing options...
SKKmods Posted November 4 Share Posted November 4 Of course the script will not receive the event because you changed: Self.RegisterForRemoteEvent(thisTree, "OnTranslationComplete") to TreesArray.RegisterForRemoteEvent(TreesArray, "OnTranslationComplete") Be interesting to understand WHY you changed Self to the ObjectReferences which receive the native events anyway, but probably dont have a script attached to process them. Also there is no need for two seperate loops through the array, just do it once; Function GetSh1tDone() Int i = 0 While i < TreesArray.Length ObjectReference thisTree = TreesArray [ i ] ; spaces added for the nexus editor which thinks its markup Self.RegisterForRemoteEvent(thisTree, "OnTranslationComplete") thisTree.TranslateTo(TreeEndPosition.GetPositionX(), TreeEndPosition.GetPositionY(), TreeEndPosition.GetPositionZ(), 0.0, 0.0, 0.0, moveSpeed) i += 1 EndWhile EndFunction Link to comment Share on other sites More sharing options...
NeinGaming Posted November 5 Share Posted November 5 8 hours ago, 15gudasc said: Thanks so much. I hadn't considered how to stop the loop–in my inexperience it seemed like a lower-priority objective until I got the rest working. This threw me for such a loop once, if you'll pardon the pun. And to make everyone it happened to feel better: I then did it again, knowingly, thinking "yeah but I won't need it in this instance because I'm gonna X Y and Z", and then later "oh, crap". (though luckily I could just delete it from the save with ReSaver). The thing is, once the script is in that loop it doesn't matter what changes you do to the script -- the game will take the version from the save, as it probably tells you in the papyrus log, until there's a chance to swap it out, which there won't be while it's in this loop. So you either have to kill it with ReSaver, or start a new game (double check the papyrus log though that it's actually necessary). 1 Link to comment Share on other sites More sharing options...
15gudasc Posted November 5 Author Share Posted November 5 Uyy, that was a weird attempt to register the array items differently, as I was having trouble getting the event to execute. I reverted to your script immediately when it didn't compile. I must have copy/pasted this incorrect script in the midst of doing this. But yeah, incorporating your script makes the event occur, though unfortunately SetPosition still doesn't seem to execute. This means the event fires continuously, with an unending string of "Resetting" debug notifications. Unrelated, but I changed TranslateTo to TranslateToRef for simplicity. Some things I've tried with no luck: 1. MoveTo instead of SetPosition 2. SetPosition with numeric constants in place of the TreeStartPosition ref coordinates, just in case there was something wrong with the ref 3. Using Utility.Wait(0.1) right before MoveTo, just in case TranslateToRef was still running and "overruling" MoveTo 4. Disable/Enable/MoveTo Function MoveTrees() Int i = 0 While i < TreesArray.Length ObjectReference thisTree = TreesArray [ i ] Self.RegisterForRemoteEvent(thisTree, "OnTranslationComplete") thisTree.TranslateToRef(TreeEndPosition, moveSpeed) i += 1 EndWhile EndFunction Event ObjectReference.OnTranslationComplete(ObjectReference akSender) ; Loop through each tree in the array Int i = 0 While i < TreesArray.Length If TreesArray [ i ] == akSender ObjectReference thisTree = akSender Debug.Notification("Resetting " + TreesArray [ i ]) akSender.MoveTo(TreeStartPosition) ; Add a small random delay before moving it again Utility.Wait(Utility.RandomFloat(0.0, delayVariance)) ; Restart the movement toward the end position thisTree.TranslateToRef(TreeEndPosition, moveSpeed) EndIf i += 1 EndWhile EndEvent If it matters, here are the objects in TreesArray. Thanks a ton again! Link to comment Share on other sites More sharing options...
SKKmods Posted November 5 Share Posted November 5 Putting Utility.Wait into event driven scripts it really bad, the script can miss events whilst frozen. You need to read up on long running latent functions locking scipts. Before getting all clever with arrays it looks like you need to get your basic functions and events working with a single object. Link to comment Share on other sites More sharing options...
DlinnyLag Posted November 5 Share Posted November 5 33 minutes ago, SKKmods said: the script can miss events whilst frozen. Could you describe it a bit more detailed? I never faced that script miss a event. How it would happen? What are conditions? Link to comment Share on other sites More sharing options...
SKKmods Posted November 5 Share Posted November 5 Lock a script with long Utility.Wait() or While loops that use framerate bound functions and it will miss events which seem to queue for a while and are then cleared or lost. Or the world state has moved on and the event is no longer relevant. Best practice is to never mix latent functions and long running loops with events, put them in seperate scripts and never have a problem. Read up on LATENT and FRAME BOUND script functions to learn more. Link to comment Share on other sites More sharing options...
15gudasc Posted November 5 Author Share Posted November 5 Ahh, gotcha. In that case no Utility.Wait()! For the sake of closure, I managed to find a solution (with the help of AI) that works exactly as I needed--it's probably not aligned with best practices, though. Because I couldn't figure out why SetPosition() or MoveTo() wasn't working, this uses another TranslateToRef() with an extremely high movement speed to "snap" the obj back to the original position. And then, the loop continues until I call a function that stops movement and unregisters the objects for the event. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Float Property moveSpeed = 1500.0 Auto ; Speed of movement ObjectReference Property TreeStartPosition Auto ; Starting position marker ObjectReference Property TreeEndPosition Auto ; Ending position marker ObjectReference[] Property TreesArray Auto Const ; Array of Trees ObjectReference[] ResettingTreesArray ; Used to block the event from triggering as the Trees TranslateToRef back to the beginning Function MoveTrees() ResettingTreesArray = new ObjectReference[TreesArray.Length] Int i = 0 While i < TreesArray.Length ObjectReference thisTree = TreesArray [ i ] Self.RegisterForRemoteEvent(thisTree, "OnTranslationComplete") thisTree.TranslateToRef(TreeEndPosition, moveSpeed) i += 1 EndWhile EndFunction Event ObjectReference.OnTranslationComplete(ObjectReference akSender) Int index = TreesArray.Find(akSender) Int resetIndex = ResettingTreesArray.Find(akSender) If index != -1 && resetIndex == -1 ; If akSender is in the TreesArray index and is not currently in the reset phase ; Mark obj as resetting to avoid infinitely triggering the OnTranslationComplete event ResettingTreesArray[index] = akSender akSender.TranslateToRef(TreeStartPosition, 200000.0) ; extremely high movement speed to "snap" back to original position akSender.TranslateToRef(TreeEndPosition, moveSpeed) ResettingTreesArray[index] = None EndIf EndEvent Thank you for your help and insight, SKK! Extremely valuable and I am learning a lot from this process. Link to comment Share on other sites More sharing options...
Recommended Posts