user10968767
user10968767

Reputation:

OnGUI and Coroutine moving an object: really strange behavior

In the title it is not easy to summarize this behavior in the best way. I have an Editor and a Monobehaviour executed in [ExecuteInEditMode]. I need to move objects by selecting them with the mouse. I thought I didn't encounter any difficulties as I perform this operation with both a character and sprite. But I was wrong. When I select a 3D object (not the character) I can pick and drag it but if I hold the mouse button down without moving it it tends to slip away as if it were falling but on the Y and Z axes. If I deselect its collider the issue does not happen but obviously the collider is necessary for my operations. I have removed all the unnecessary lines from the code, hoping to solve the problem, but it remains.

    void OnGUI() {
    if (moveChar) {
        StartCoroutine("WaitChar");
    } else if (moveSpot) {
        StartCoroutine("WaitSpot");
    }
    Event e = Event.current;
    Vector3 mousePosition = e.mousePosition;
    mousePosition.y = Screen.height - mousePosition.y;
    Ray ray = cam.ScreenPointToRay(mousePosition);
    RaycastHit hitInfo;
    if (Physics.Raycast(ray, out hitInfo)) {
        mousePositionWorld = hitInfo.point;
        if (e.rawType == EventType.MouseDown) {
            switch (e.button) {
                case 0: 
                    if (isSelectedSphere) {
                        moveSpot = true;
                    } 
                    break;
                case 1:
                    if (isSelectedChar) {
                        moveChar = true;
                    } 
                    break;
            }
        } else if (e.rawType == EventType.MouseUp) {
            switch (e.button) {
                case 0: 
                    if (moveSpot) {
                        moveSpot = false;
                    } 
                    break;
                case 1:
                    if (moveChar) {
                        moveChar = false;
                    }
                    break;
            }
        }
    }
}

public IEnumerator WaitChar() {
    character.transform.position = mousePositionWorld;
    yield return new WaitForSeconds(0.1f);
}

public IEnumerator WaitSpot() {
    MoveSpot.transform.position = mousePositionWorld;
    MoveSpot.transform.localScale = Vector3.one * ((new Plane(cam.transform.forward, 
                                                    cam.transform.position).GetDistanceToPoint(
                                                    MoveSpot.transform.position)) / radiusPoint);
    yield return new WaitForSeconds(0.1f);
}

I have simplified everything with two public variables to choose the object to move. What I do is very simple, I don't understand why I get this behavior. With the character I have no problem (not even with the sprites, which for the moment I removed from the code). When I click with the right mouse button I run the coroutine that moves the character to the cursor coordinates and continues like this until I release the button. No problem, the character moves along with the cursor without problem. However, if I execute the same operation (left button) with a 3D object like Sphere, Cylinder, Cube it moves following the mouse but if I don't move the cursor and don't release the button the object slides away with the Y and the Z axes that increase in negative. As I said, if I deactivate the collider the object remains at the cursor position. The problem is that the spheres cannot be selected through public variables but directly from the script so I need the collider. I probably don't know Unity well enough to know the reason for this behavior. Can you clarify this for me?

Upvotes: 1

Views: 274

Answers (1)

OnGui is bad (let me explain)

First, OnGui is part of Unity's IMGui system (stands for IMmediate Gui), and has been heavily depreciated in favor of uGui, which is a lot easier to work with for a number of reason. And one of those reasons is....

OnGui is called more than once per frame

Because OnGui is called multiple times per frame, that means that Event.current hasn't changed, meaning moveChar hasn't changed, meaning your coroutine gets started more than once.

And there's no real way around this. Its going to be almost impossible to do what you want using OnGui. It was never meant to handle this sort of logic.

Everything you have should be done inside Update() instead and replacing things like e.rawType == EventType.MouseDown with their Input relatives, eg. Input.GetMouseDown(0).

Upvotes: 2

Related Questions