Jump to content

SetPosition() not moving an object reference


Recommended Posts

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

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

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

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

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).

  • Like 1
Link to comment
Share on other sites

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!

image.png.9fea75525e3f639ef5efcb22e9157b92.png

 

 

Link to comment
Share on other sites

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

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

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...