kniEngine / kni

KNI is a cross-platform C# game framework.
Other
141 stars 9 forks source link

BlazorGL canvas size is incorrect on Safari iOS after adding support for full screen PWA #1873

Open dtaylorus opened 1 month ago

dtaylorus commented 1 month ago

After adding support for full screen PWA mode (which includes specifying viewport-fit=cover in index.html), the BlazorGL canvas size extends under the menu bar at the bottom on Safari iOS (though it works correctly on Chrome iOS).

I was able to fix the issue by updating the back buffer size later in the initialization sequence.

In BlazorGameWindow.cs:

        internal void InitFocus()
        {
            // NOTE: HasFocus returns false and prevents the Activated event from firing
            //if (this.wasmWindow.Document.HasFocus())
            {
                // NOTE: The back buffer size has changed here on Safari iOS
                this.UpdateBackBufferSize();

                OnActivated();
            }
        }

Note this also fixes an issue where Game.OnActivated is not called on the BlazorGL platform.

What version of KNI does the bug occur on:

What operating system are you using:

What KNI platform are you using:

dtaylorus commented 1 month ago

To enable support for full screen PWA:

  1. Add the following to in index.html:
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, viewport-fit=cover" />
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <link rel="manifest" href="/manifest.json">
    <style>
        html, body {
            height: 100%;
            width: 100%;
            height: 100vh;
            width: 100vw;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        :root {
            --sat: env(safe-area-inset-top);
            --sar: env(safe-area-inset-right);
            --sab: env(safe-area-inset-bottom);
            --sal: env(safe-area-inset-left);
        }
    </style>
  1. Create a manifest.json, e.g.,
{
  "name": "My Awesome Game",
  "short_name": "My Game",
  "icons": [
    { "src": "/favicon.ico", "type": "image/x-icon", "sizes": "16x16 32x32" },
    { "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
    { "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" },
    { "src": "/icon-192-maskable.png", "type": "image/png", "sizes": "192x192", "purpose": "maskable" },
    { "src": "/icon-512-maskable.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable" }
  ],
  "theme_color": "#000000",
  "background_color": "#ffffff",
  "display": "standalone"
}
  1. Get the safe area margins to inset your game UI around screen notches and system UI elements:

Javascript module

export function getSafeAreaMargin()
{
    var top = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--sat"));
    var right = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--sar"));
    var bottom = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--sab"));
    var left = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--sal"));
    return { top: top, right: right, bottom: bottom, left: left };
}

C#

var safeArea = this._module.Invoke<SafeAreaValue>(@"getSafeAreaMargin");

where _module is a IJSInProcessObjectReference, e.g.:

var jsModuleObject = await ((IJSInProcessRuntime)runtime).InvokeAsync<IJSInProcessObjectReference>(@"import", @"./js/interop.js");
this._module = new JSInterop(jsModuleObject);

and SafeAreaValue is:

private sealed class SafeAreaValue
{
    public SafeAreaValue()
    {
    }

    public float Top
    {
        get;
        set;
    }

    public float Right
    {
        get;
        set;
    }

    public float Bottom
    {
        get;
        set;
    }

    public float Left
    {
        get;
        set;
    }
}

Note that the safe area needs to be updated when the screen orientation changes.

Along with the fix in BlazorGameWindow above, these changes enable a full screen PWA that looks identical to a full screen app, tested on iOS/Safari, iOS/Chrome, and Android/Chrome.

It might be nice to include this in the BlazorGL template and set the XNA TitleSafeArea so all of this would work out of the box.