microsoft / MixedRealityToolkit-Unity

This repository is for the legacy Mixed Reality Toolkit (MRTK) v2. For the latest version of the MRTK please visit https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity
https://aka.ms/mrtkdocs
MIT License
6k stars 2.12k forks source link

Teleport Marker doesn't survive scene change #1388

Closed genereddick closed 5 years ago

genereddick commented 6 years ago

Overview

Running a project with multiple scenes, when a new scene is loaded the Mixed Reality Teleport Script loses its reference to the initial instance of Teleport Marker instantiated in the first scene. The Teleport Manager doesn't survive the scene load and a new one isn't created in the new scene.

Expected Behavior

No errors, can select using Controller

Actual Behavior

Three errors, xbox controller no longer functions, can't select or interact

MissingReferenceException: The variable teleportMarker of MixedRealityTeleport doesn't exist anymore.
You probably need to reassign the teleportMarker variable of the 'MixedRealityTeleport' script in the inspector.

HoloToolkit.Unity.InputModule.MixedRealityTeleport.EnableMarker () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:285)
HoloToolkit.Unity.InputModule.MixedRealityTeleport.StartTeleport () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:214)
HoloToolkit.Unity.InputModule.MixedRealityTeleport.HandleGamepad () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:124)
HoloToolkit.Unity.InputModule.MixedRealityTeleport.Update () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:103)

MissingReferenceException: The object of type 'Animator' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
HoloToolkit.Unity.InputModule.MixedRealityTeleport.PositionMarker () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:316)
HoloToolkit.Unity.InputModule.MixedRealityTeleport.Update () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:109)

MissingReferenceException: The variable teleportMarker of MixedRealityTeleport doesn't exist anymore.
You probably need to reassign the teleportMarker variable of the 'MixedRealityTeleport' script in the inspector.
HoloToolkit.Unity.InputModule.MixedRealityTeleport.DisableMarker () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:298)
HoloToolkit.Unity.InputModule.MixedRealityTeleport.FinishTeleport () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:236)
HoloToolkit.Unity.InputModule.MixedRealityTeleport.HandleGamepad () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:129)
HoloToolkit.Unity.InputModule.MixedRealityTeleport.Update () (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/MixedRealityTeleport.cs:103)

Steps to reproduce

Create a project with two scenes. Load initial scene then load second scene. Use xbox left thumbstick.

You can see that the Mixed Reality Teleport Script on MixedRealityCameraParent in the Unity Editor is null.

Unity Editor Version

2017.2.op1-MRTP4

Mixed Reality Toolkit Release Version

master

StephenHodgson commented 6 years ago

No steps to repro?

genereddick commented 6 years ago

Updated

genereddick commented 6 years ago

TeleportMarker is instantiated in the MixedRealityTeleport script Start function.

    private void Start()
            {
            ...
                if (teleportMarker != null)
                {
                teleportMarker = Instantiate(teleportMarker);
                teleportMarker.SetActive(false);

                animationController = teleportMarker.GetComponentInChildren<Animator>();
                if (animationController != null)
                {
                    animationController.StopPlayback();
                }
            }
        }

Because this script is on the MixedRealityCameraParent which has DontDestroyOnLoad, the Start function is only called on the first scene load. The teleportMarket is created in the first scene, but doesn't survive the scene load and it doesn't look like there is a mechanism to reinstantiate it in new scenes.

cvasquez-github commented 6 years ago

Using Unity 2017.2.0p1 - MRTP4 64bit + HoloToolkit-Unity-v1.2017.2.0

I have another issue when changing scene, controllers disappear. Throws exception on line 496 of InputManager.cs, sourceStateEventData is null on next scene.

NullReferenceException: Object reference not set to an instance of an object
HoloToolkit.Unity.InputModule.InputManager.RaiseSourceDetected (IInputSource source, UInt32 sourceId, System.Object[] tags) (at Assets/HoloToolkit/Input/Scripts/Utilities/Managers/InputManager.cs:496)
HoloToolkit.Unity.InputModule.InteractionInputSource.OnEnable () (at Assets/HoloToolkit/Input/Scripts/InputSources/InteractionInputSource.cs:189)
StephenHodgson commented 6 years ago

@cvasquez-github that bug was fixed and is a separate issue.

cvasquez-github commented 6 years ago

Great to know @StephenHodgson , thanks for letting me now that was fixed. Where can I get the fix for that bug? Thanks!

Update: adding InputManager (100) and InteractionInputSource (200) to the Script Execution Order prevents the issue.

StephenHodgson commented 6 years ago

Latest master branch. Will be included in a release soon.

martdob commented 6 years ago

Is there a workaround for the dying Teleport marker?

StephenHodgson commented 6 years ago

Add don't destroy on load to it.

genereddick commented 6 years ago

Actually it is not enough to just add don't destroy on load. Property assignments don't necessarily survive scene loading. In this case, even though the Teleport Marker now survives the scene load, the value of teleportMarker does not:

 private GameObject teleportMarker;

Which is assigned in the MixedRealityTeleport.cs script Start function:

 if (teleportMarker != null)
        {
            teleportMarker = Instantiate(teleportMarker);

When you load a new scene that value is now null as are some other properties. I'm still not totally clear on which values survive scene loads -- static values on all objects, behavior of properties on objects with dontdestroyonload, nested gameobjects all seem to have different rules. -- but I expect this issue and others related to multi-scene projects are going to continue to cause problems unless addressed across all singletons.

So here, in addition to adding DontDestroyOnLoad to the the TeleportMarker.prefab, you also need to add some code to reset the reference when the new scene is loaded. This applies to animationController as well.

genereddick commented 6 years ago

Three options:

  1. Instantiate TeleportMarker as a child of MixedRealityCameraParent. Then doesn't require DontDestroyOnLoad, but not sure of any implications of tying the position and rotation of the TeleportMaker to the camera.

  2. Make TeleportMarker a Singleton. Will both survive the scene load and will retain the reference in MixedRealityTeleport.

  3. Add some code to handle rewiring the object in MixedRealityTeleport. This could be done either through re Instantiating the TeleportMarker (in which case it doesn't need DontDestroyOnLoad), or just use Find to grab it and reassociate it (if TeleportMaker has DontDestroyOnLoad).

In MixedRealityTeleport.cs:

 private void Awake() {
    SceneManager.sceneLoaded += SceneLoaded;
}

 void SceneLoaded(Scene scene, LoadSceneMode mode) {
    if (teleportMarker != null) {
        teleportMarker = Instantiate(teleportMarker, this.transform);
        teleportMarker.SetActive(false);

        animationController = teleportMarker.GetComponentInChildren<Animator>();
        if (animationController != null) {
            animationController.StopPlayback();
        }
    }
}
StephenHodgson commented 6 years ago

I'm in favor of either 1 or 2.

genereddick commented 6 years ago

I believe #1 requires only the following change in MixedRealityTeleport.cs, line 87

teleportMarker = Instantiate(teleportMarker, this.transform);

I'll test it out and see if I find any issues related to being a child of the MixedRealityCameraParent.

Maximiliankk commented 6 years ago

Found a quick fix for this. I put this in MixedRealityTeleport.cs:

public void LoadMarker() { teleportMarker = Resources.Load("TeleportMarker") as GameObject; animationController = teleportMarker.GetComponent(); }

and then I have a small script on an empty object in the scene that is being switched to with this:

private void Awake() { GameObject.Find("MixedRealityCameraParent").GetComponent().LoadMarker(); }

I also set the script execution order so this will be earlier than the others, but i'm not sure if this is necessary or not.

blaynewatt commented 6 years ago

I'm seeing an issue when I run the SceneLauncher scene, and load a few scenes... the controller model seems to retain its original location prior to the scene reload, and the turn (left/right) on the joystick just cause a black plane to appear, without changing my view... is this all related to the same issue, and is the fix above valid to fix that?

example at : https://www.youtube.com/watch?v=hM5Ur_-4kiU

Thanks...

wiwei commented 5 years ago

Closing issues older than 180 days. If this is still an issue, please reactivate with recent information.