GameObject state update delayed?

I am having an issue with Unity correctly reading position changes (and destroying) of GameObjects. I have attached a sample project.

In the project, there are three platforms and two cubes. If you click (tap) a cube, it will be selected. Click it again to deselect. Click on an empty space when a cube is selected and it will move there and get deselected.

Each platform has a label telling you whether it is occupied or not. This gets updated on two occasions:

A) If any platform or cube is clicked, the status of all cubes and platforms is updated before performing any logic, to have an accurate response. This works as it should.
B) If a cube is selected and you click an empty space, the label gets updated a second time after the cube has moved, to reflect the new state of things. This is where the problem creeps in.

The cube moves and gets deselected, as it should, but the labels on the platforms don’t update, until you click something again!

I put some debug Print() statements in the code, which gives the expected output:
3x “occupants updated.”
1x “cube moved.”
3x “occupants updated.”
3x “UI updated.”

So that means the code for updating occupants (which includes performing an OverlapBox test) and updating the UI does run after the cube was moved, but it still shows the old information before the move.

What gives?

Any suggested solutions to this?

Cheers!

6940553–815731–Timing Bug.zip (728 KB)

If you post a code snippet, ALWAYS USE CODE TAGS:

How to use code tags: https://discussions.unity.com/t/481379

If you Destroy() an object it won’t be destroyed until end of frame, so if you ask if it is still there, then answer will be yes.

Beyond the initial debugging output you have, to help gain more insight into your problem, I recommend liberally sprinkling even more Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run?
  • what are the values of the variables involved? Are they initialized?

Knowing this information will help you reason about the behavior you are seeing.

How to report your problem productively in the Unity3D forums:

http://plbm.com/?p=220

Good to know. Does this apply to destroying only, or does the same happen when moving a GameObject as well? How would you suggest approach working around this issue? Coroutines? Or is there a more straightforward way?

The rest of your response I find to be quite unhelpful and condescending, sadly. Why tell me, IN CAPS, to use code tags, when I’m actually not posting any code snippets? Also, your recommendation to put in Debug.Log() statements in to display info in realtime is something I am doing already (albeit using Print() instead), as I described quite clearly in my OP. Did you actually read the whole thing? I described what is happening, what I want to happen, I did my research as well as I could, tested it in my actual project and then even prepared a sample mini-project that clearly demonstrates the issue. Not sure what more I can provide.

Cheers!

Here is the code if that helps in any way.

using System;
using UnityEngine;
using UnityEngine.UI;

public class Detector : MonoBehaviour
{

    public static GameObject selectedCube;
    public static Text selectedDisplay;

    [SerializeField] private Text spotDisplay;

    private GameObject occupyingCube;
    public static Action _UpdateUI_;
    public static Action _UpdateOccupants_;

    private void Awake()
    {

        _UpdateOccupants_ += GetOccupants;
        _UpdateUI_ += UpdateAllUI;

        if (selectedDisplay == null)
        {

            selectedDisplay = GameObject.Find("Selected Display").GetComponent<Text>();

        }

        GetOccupants();
        UpdateAllUI();

    }

    private void OnMouseUpAsButton()
    {

        _UpdateOccupants_();

        if (selectedCube == null)
        {

            if (occupyingCube != null)
            {

                selectedCube = occupyingCube;

            }
            else
            {

                selectedCube = null;

            }

        }
        else if (selectedCube == occupyingCube)
        {

            selectedCube = null;

        }
        else if (occupyingCube == null)
        {

            print(selectedCube.transform.position);
            selectedCube.transform.position = new Vector3(gameObject.transform.position.x, selectedCube.transform.position.y, selectedCube.transform.position.z);
            print($"The {selectedCube.name} cube moved to {gameObject.name}");
            print(selectedCube.transform.position);
            selectedCube = null;
            _UpdateOccupants_();

        }

        _UpdateUI_();

    }

    private void GetOccupants()
    {

        Collider[] hitColliders = Physics.OverlapBox(new Vector3(gameObject.transform.position.x, 1, gameObject.transform.position.z), transform.localScale / 2, Quaternion.identity);

        if (hitColliders.Length > 1)
        { // There is a cube present on this tile.

            foreach (var collider in hitColliders)
            {

                if (collider.tag == "Cube")
                {

                    occupyingCube = collider.gameObject;

                }

            }

        }
        else
        {

            occupyingCube = null;

        }

        if (occupyingCube == null)
        {

            print($"Updated {gameObject.name}: no occupants here.");

        }
        else
        {

            print($"Updated {gameObject.name}: {occupyingCube.name} cube here.");

        }

    }

    private void UpdateAllUI()
    {

        print("UI Updated.");

        if (occupyingCube != null)
        {

            spotDisplay.text = occupyingCube.name;
            print($"{gameObject.name}: {occupyingCube.name}");

            GameObject outline = occupyingCube.transform.GetChild(0).gameObject;

            if (occupyingCube == selectedCube)
            {

                outline.SetActive(true);

            }
            else
            {

                outline.SetActive(false);

            }

        }
        else
        {

            spotDisplay.text = "empty";

        }

        if (selectedCube != null)
        {

            selectedDisplay.text = $"selected: {selectedCube.name}";

        }
        else
        {

            selectedDisplay.text = $"selected: none";

        }

    }

}

When you set a transform.position, it happens immediately. When you drive something in physics land you need to use .MovePosition() on the RigidBody2D. I believe in that case the update does not occur until the physics sweep, but that’s just a guess.

Because you attached a project zip file that nobody here wants to open. I presume it contained source. Perhaps I am mistaken.

Which is why I prefaced it with:

For instance, line 94 above you put in debug output at the start of the function… but you didn’t put anything next to where the text is updated, which is the ACTUAL problem you’re seeing. It is inside of two (2) more conditional checks, neither of which you have spoken about, so to me it feels like maybe you didn’t emit enough Debug logging yet.

Remember, the debugging output is not for us, it’s for you. If you don’t want to put it in, then by all means, don’t.

Thanks for your reply.

That’s what I thought should happen, but it seems like there is an issue somewhere with that. I am using transform.position for the change.

Fair enough. If people don’t want to open it, that’s their right. I’ve seen people on the forum ask for project files before, so attached it in case someone wanted to look at it directly. Yes, it’s the full sample project folder.

I didn’t add more because you can see those changes in the UI itself and by the fact that the cube itself moves, so that’s the feedback loop. In all other instances it updates correctly, so the code itself works, and because other debug messages follow it, there clearly isn’t a show-stopping exception or so.

That said, for further clarity, I added more Print() statements – updated as well in my code snippet post above – but honestly I don’t feel it pushed me any closer to a solution, just confirmed what I was already seeing.

The output is the following:

Initial cube occupancy:
Updated Floor (3): blue cube here.
Updated Floor (2): green cube here.
Updated Floor (1): no occupants here.

Initial green cube position:
(0.0, 0.9, 0.0)

Updated green cube position:
The green cube moved to Floor (1)
(-1.8, 0.9, 0.0)

Updated cube occupancy (NO CHANGE!?):
Updated Floor (3): blue cube here.
Updated Floor (2): green cube here.
Updated Floor (1): no occupants here.

UI Updated.
Floor (3): blue
UI Updated.
Floor (2): green
UI Updated.
<Floor (1): none>

So, the transform.position is changed, BUT the OverlapCube still detects it in the old spot anyway. Is it possible that the transform.position is updated, but the Box Collider position doesn’t change until the end of frame, as with destroying a GameObject?

I am willing to accept this might not be a bug (as annoying as it is), but still wonder, what’s the best way to tackle this situation, then? Any suggestions? I’m not asking for anyone to code a solution for me, just if anyone has an idea to nudge me in a good direction, whether to use a coroutine or another approach.

Coincidentally this was just discussed in another thread:

https://discussions.unity.com/t/832844/4

However that thread would imply the opposite of what you’re seeing, that OverlapCube should get it correct.

However, the example code for Overlap*() methods shows calling it FROM FixedUpdate().

For more insight into interactions between moving stuff and colliding stuff, here is some timing diagram info:

https://docs.unity3d.com/Manual/ExecutionOrder.html

Interesting! I’m not using Update or FixedUpdate, but calling the OverlapBox directly.

Not sure if there is a difference between the 2D EdgeCollider and the 3D OverlapBox, but deep down on the physics side it should be the same, I’d think.

But the two might be the exact same issue, actually, even though it might seem to be opposite at first sight.

In the other case, he is spawning in an object that doesn’t register. I think that means that in both cases the Physics system is for some reason reporting the state at the end of last frame (or beginning of this frame, but before any logic is evaluated), not the current situation. So in the other case that would be no collision, because at the end of last frame the object was not yet spawned, and in my case it would report the GameObject in the old position, as that is where it was at the end of the previous frame.

I will probably write up a post about it in the Physics forum, as @MelvMay suggests, hopefully that will lead to some solutions, workarounds!

It could’ve equally called it from Update, Start etc. The example had to be called from somewhere and where it’s called doesn’t infer anything more complex.

A spatial query does exactly that, queries the world. If you have a collider in it then it’ll find it and doesn’t require the simulation.

For instance, you can create a bit of code anywhere that adds a CircleCollider2D then queries it and it’ll find it then remove it and query again and it’ll not see it. When you add a collider it’s there instantly.

The simulation step is required to move bodies under their own velocity or process MovePosition/MoveRotation commands, create/update contacts & perform physics callbacks. Queries don’t come in to it. Hope that clears up confusion. It’s actually pretty simple TBH.

That’s what I would’ve expected, yes. Any idea then why in this case if you call an OverlapBox, query and get a cube in a position, then move the cube away and call another OverlapBox to query again, the second OverlapBox reports the original cube still present, even though the transform has changed?

Even if you print out the transform.position of the GameObject it supposedly collided with from within the Colliders[ ], that gives you the correct coordinates of the GameObject out of bounds for the collision.

The critical part is “move the cube”. By this you’re saying that you’re changing the Transform(?) which JUST changes the transform; the Rigidbody(2D) pose hasn’t been changed. The Unity Transform system doesn’t make calls to physics, rendering, audio (etc) components when you change a Transform. All that’s happened at that point is the Transform has changed but you can debug that and see it. This is why you should not drive the Rigidbody(2D) via the Transform; use it’s API because that is your proxy to any movement. If you change the Transform then that’s processed prior the simulation being stepped. You can also manually do it (but you really should avoid this) by calling Physics(2D).SyncTransforms(). If you’re calling that to get the Transform change to the body then you should re-evalulate doing so.

In short and make sure the"move the cube away" doesn’t just mean incorrectly modifying the Transform. Setting Rigidbody(2D).position or rotation does so instantly but note that’s instantly teleporting so causes overlaps/tunnelling etc.

Honestly, above you seem to be talking about 3D physics but above is talking about 2D physics. I’m a 2D physics dev so that’s the part I’m always talking about.

1 Like

Thanks a lot for this detailed answer, that answers a lot of the things I was wondering about!

I am “moving the cube” by simply changing it transform.position in its C# script. I wasn’t using Physics.SyncTransforms(), but tried it out just now and it turns out that it actually fixes the problem I had!

Now, I know you said not to use it, so a couple questions related to that:

My game doesn’t use Physics at all for any kind of movement or simulation, so I don’t care about overlaps or tunnelling. The only aspect of the Physics system I use is manually calling OverlapBoxes etc. to detect object proximity (I can do it manually because it is a turn-based game). In such a situation is it safe to use Physics.SyncTransforms() or would you suggest not to do that at all, ever?

If not Physics.SyncTransforms(), is there another method to manually sync the position of the various transforms of a GameObject I moved changing its transform.position? The GameObject only has a Transform, Mesh Filter, Mesh Renderer and a Box Collider, but no Rigidbody. I assume then that the only two things I need to sync are the Transform and the Box Collider?

Yes, my game is in 3D. Not sure why @Kurt-Dekker sent me to the post discussing 2D physics, but it turns out in this case that it seems to be something that applies to both, and I appreciate he took the time to read and answer, so great!

That was my thinking as I believe (and perhaps @MelvMay can confirm) that both physics systems have analogous timing considerations. In the edge collider link above, that OP was indeed in 2D land.

If I have some positions in my logical game world, I’d probably just do my own computations for closeness if that’s all you care about. Take the distance and act accordingly; no need to involve physics for what is essentially a geometric problem.

I think that would be overly complicated. There can be hundreds of map tiles in a level and tens of units. It would be a computational nightmare to check the distance between every unit and tile. Using physics and the OverlapBox I can filter the amount of tiles and units to only a certain area and then easily loop through the array the OverlapBox gives me for all the comparisons there.

Like anything you can do in Unity, nothing specific is bad but rather how it’s used. Calling SyncTransform can become a real performance hog if you move something, call it, move something call it, move something call it. Those ms will soon add up. If you have to call it, do it after you’ve moved everything. It’s automatically called once prior to the simulation step to ensure any Transform changes are read into the body poses.

If you’re moving stuff then add a Rigidbody(2D) set to Kinematic. Then set its Rigidbody(2D).position/rotation followed by your query. Doing this casues the body to move to that position instantly. Ignore the Transform. You can sync-up visuals later or wait until the simulation step which will automatically write the body pose to the Transform.

1 Like

Works great, and no need for the SyncTransform!

Would you recommend that in general, if you have objects in your game that need to be moved, to attach a Kinematic Rigidbody and move it through that? I almost exclusively modify transform.position, but am more than happy to switch if that’s a better way!

No and that’s the problem I see a lot; devs like to have some general rule to a specific problem. In your case I recommended this because you (seem) to want to perform a query immediately after changing the position of a Rigidbody. Changing the body pose is one that gives you best performance but you’ll have to decide what you want to do depending on your situation. Avoiding bad patterns like individually changing position then performing a query is recommended. In most cases it should be the other way around i.e. use queries to detect where something can move then ask for the move. This way you’re querying the last position.

If there’s a general recommendation/rule then it’s this: if it moves in 2D/3D physics then do so via the Rigidbody(2D) API. Transforms are written to during the simulation step. If your situation doe

2 Likes

The end of your answer got clipped, but I get the general idea, thanks again!

Ha ha sorry. :slight_smile:

I think I was going to say, if your situation doesn’t require that then you are still free to do it however you like using the profiler to guide what does and doesn’t scale well.

Just know that the Transform is decoupled from other components. Renderers look at it when they render, they don’t get updated when you change it. The same goes for physics and other systems. Changing a Transform does only that.

2 Likes