flutter-unity-view-widget icon indicating copy to clipboard operation
flutter-unity-view-widget copied to clipboard

Flutter Unity Widget stuck waiting for `UnityReady` until hot restart

Open LegendAF opened this issue 3 years ago • 1 comments

Describe the bug Unity does not show up until a hot restart happens. After digging into some code and looking at https://github.com/juicycleff/flutter-unity-view-widget/issues/540 it appears that the issue lay with the function EditUnityAppControllerMM in XCodePostBuild.cs.

Using 2020.3.31f1 I was seeing this issue while using 2020.3.6f1 I did NOT see this issue.

Digging into the code for UnityAppController.cs after XCodePostBuild.cs is run I am seeing:

2020.3.31f1:

{
    NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");

    UnityInitApplicationGraphics();

    // we make sure that first level gets correct display list and orientation
    [[DisplayManager Instance] updateDisplayListCacheInUnity];

    UnityLoadApplication();
    Profiler_InitProfiler();

    [self showGameUI];
    [self createDisplayLink];

    UnitySetPlayerFocus(1);

    AVAudioSession* audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory: AVAudioSessionCategoryAmbient error: nil];
    if (UnityIsAudioManagerAvailableAndEnabled())
    {
        if (UnityShouldPrepareForIOSRecording())
        {
            [audioSession setCategory: AVAudioSessionCategoryPlayAndRecord error: nil];
    // Modified by https://github.com/juicycleff/flutter-unity-view-widget
    [[NSNotificationCenter defaultCenter] postNotificationName: @"UnityReady" object:self];
}
        else if (UnityShouldMuteOtherAudioSources())
        {
            [audioSession setCategory: AVAudioSessionCategorySoloAmbient error: nil];
        }
    }

    [audioSession setActive: YES error: nil];
    [audioSession addObserver: self forKeyPath: @"outputVolume" options: 0 context: nil];
    UnityUpdateMuteState([audioSession outputVolume] < 0.01f ? 1 : 0);

#if UNITY_REPLAY_KIT_AVAILABLE
    void InitUnityReplayKit();  // Classes/Unity/UnityReplayKit.mm

    InitUnityReplayKit();
#endif
}

2020.3.6f1:

{
    NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");

    UnityInitApplicationGraphics();

    // we make sure that first level gets correct display list and orientation
    [[DisplayManager Instance] updateDisplayListCacheInUnity];

    UnityLoadApplication();
    Profiler_InitProfiler();

    [self showGameUI];
    [self createDisplayLink];

    UnitySetPlayerFocus(1);

    AVAudioSession* audioSession = [AVAudioSession sharedInstance];
    [audioSession setActive: YES error: nil];
    [audioSession addObserver: self forKeyPath: @"outputVolume" options: 0 context: nil];
    UnityUpdateMuteState([audioSession outputVolume] < 0.01f ? 1 : 0);

#if UNITY_REPLAY_KIT_AVAILABLE
    void InitUnityReplayKit();  // Classes/Unity/UnityReplayKit.mm

    InitUnityReplayKit();
#endif
    // Modified by https://github.com/juicycleff/flutter-unity-view-widget
    [[NSNotificationCenter defaultCenter] postNotificationName: @"UnityReady" object:self];
}

As you can probably see, the needed code gets put in the wrong spot.

[[NSNotificationCenter defaultCenter] postNotificationName: @"UnityReady" object:self];

To fix this, you can edit EditUnityAppControllerMM in XCodePostBuild.cs to be:

    private static void EditUnityAppControllerMM(string path)
    {

        var inScope = false;
        var markerDetected = false;

        EditCodeFile(path, line =>
        {
            if (line.Trim() == "@end")
            {
                return new string[]
                {
                    "",
                    "// Added by " + TouchedMarker,
                    "extern \"C\" void OnUnityMessage(const char* message)",
                    "{",
                    "    if (GetAppController().unityMessageHandler) {",
                    "        GetAppController().unityMessageHandler(message);",
                    "    }",
                    "}",
                    "",
                    "extern \"C\" void OnUnitySceneLoaded(const char* name, const int* buildIndex, const bool* isLoaded, const bool* IsValid)",
                    "{",
                    "    if (GetAppController().unitySceneLoadedHandler) {",
                    "        GetAppController().unitySceneLoadedHandler(name, buildIndex, isLoaded, IsValid);",
                    "    }",
                    "}",
                    line,

                };
            }

            inScope |= line.Contains("- (void)startUnity:");
            markerDetected |= inScope && line.Contains(TouchedMarker);

            if (inScope && line.Trim() == "#endif")
            {
                inScope = false;

                if (markerDetected)
                {
                    return new string[] { line };
                }
                else
                {
                    return new string[]
                    {
                        "#endif",
                        "    // Modified by " + TouchedMarker,
                        @"    [[NSNotificationCenter defaultCenter] postNotificationName: @""UnityReady"" object:self];",
                    };
                }
            }

            return new string[] { line };
        });

    }

Basically, changing the search from } to #endif.

This solution also is prone to breaking if Unity were to ever remove the #endif or to place more. We need to figure out a better solution for this, but I have not invested a ton of time into that part yet.

LegendAF avatar Mar 29 '22 22:03 LegendAF

OH MY GOD I LOVE YOU, I have been stuck on this all day.

Agree, solution needs to be a little bit more robust for different unity versions.

Also, your username checks out, you are a legend lol

lfwells avatar Apr 08 '22 12:04 lfwells

Did not work for me :( Still have the same white screen the first time... Hope for a fix soon.

kamami avatar Jan 10 '23 16:01 kamami

@kamami can you give your exact details like unity version, unitypackage version, ios version and flutter version?

timbotimbo avatar Jan 10 '23 16:01 timbotimbo

@timbotimbo sure :)

Unity: 2021.3.16f1 Flutter-Unity-Widget: ^2022.2.0 and ^2022.3.0-alpha1 Flutter: master, 3.7.0-14.0.pre.13 iOS: 16.2

kamami avatar Jan 10 '23 18:01 kamami

I somehow overlooked this specific issue before, but this function seems to be the root cause for the bug in issue 540 that was patched with a workaround in UnityPlayerUtils.swift (plugin2022.1.1).

Looking for the line with a closing bracket } without indentation seems more robust than the #endif. For anyone trying this out, don't forget to delete the ios\unityLibrary folder as unity won't update the files if it has already edited them once.

I'm not sure how kamami still gets the bug but i'll do some experimentation.

timbotimbo avatar Jan 10 '23 22:01 timbotimbo