Execució de Unity WebGL amb ASP.NET Core

Pàgina actualitzada :
Data de creació de la pàgina :

Entorn de verificació

Windows
  • Finestres 11
Editor d'unitat
  • 25.3.2020
Estudi visual
  • Estudi visual 2022
ASP.NET Nucli
  • ASP.NET Core 6.0
Serveis d'Informació d'Internet (IIS)
  • IIS 10.0

Al principi

Apreneu a executar una sortida de joc com WebGL a Unity en un servidor web que executi ASP.NET Core. Per als programes de jocs, utilitzeu la sortida dels consells següents. L'exemple del joc utilitza el microjoc de plataformes 2D, que es pot crear des del Unity Hub.

Explicaré com configurar "WebGL sense comprimir", "WebGL comprimit amb Gzip" i "WebGL comprimit amb Brotli" per executar jocs WebGL. El procediment és el mateix per a tots ells.

Estem utilitzant Visual Studio 2022, ASP.NET Core 6.0, però probablement funcionaran versions anteriors. Tanmateix, l'estructura del codi inicial és diferent per a cada versió, així que enteneu les diferències vosaltres mateixos.

Creació d'un projecte bàsic ASP.NET

Inicieu "Visual Studio 2022" des del menú d'inici.

Seleccioneu Crea un projecte nou.

Aquesta vegada, seleccioneu "ASP.NET Core Web App" com a exemple. Si esteu executant ASP.NET Core, podeu executar altres plantilles, però haureu de seguir cada plantilla sobre com crear-la.

Definiu el nom i la ubicació del projecte arbitràriament.

Deixeu la informació addicional tal qual.

El projecte ha estat creat.

Executeu WebGL sense comprimir

Prepareu un programa WebGL creat sense compressió.

Assegureu-vos que el joc funcioni ràpidament

ASP.NET Proveu d'executar un joc WebGL amb menys configuracions sense seguir l'etiqueta bàsica.

Al ASP.NET Core, no podeu accedir a alguns fitxers WebGL emesos per Unity en l'estat predeterminat. Fer-ho accessible.

Programa.cs

Obert des del teu Program.cs projecte. S'aplica a versions Startup.cs anteriors de ASP.NET Core.

Afegiu un espai de noms a la part superior del codi i substituïu el següent al codi app.UseStaticFiles(); :

// ここから追加
using Microsoft.AspNetCore.StaticFiles;
// ここまで追加

var builder = WebApplication.CreateBuilder(args);

// --- 省略 ---

app.UseHttpsRedirection();

//app.UseStaticFiles();
// ここから追加
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".data"] = "application/octet-stream";
provider.Mappings[".wasm"] = "application/wasm";

app.UseStaticFiles(new StaticFileOptions()
{
  ContentTypeProvider = provider,
});
// ここまで追加

app.UseRouting();

// --- 省略 ---

.data, de manera que quan s'accedeix a un fitxer, .wasm es pot retornar al client amb l'especificat Content-Type .

Desplegament WebGL

Col·loca les següents carpetes d'arxius d'Unity en el teu projecte wwwroot .

  • Índex.html
  • Construir
  • TemplateData

Index.cshtmlindex.html Obriu l'enllaç per accedir-hi.

<!-- 省略 -->

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<!-- 追加 -->
<a href="index.html">index.html</a>

Executeu el programa i assegureu-vos que el joc funciona.

Executar un programa WebGL en una pàgina Razor

El joc anterior s'executava en un fitxer HTML estàtic, de manera que s'executa en un lloc que no té res a veure amb ASP.NET Core. No és molt desitjable per a la uniformitat del programa, de manera que mourem el comportament del fitxer HTML a Razor Pages.

Feu clic amb el botó dret a la carpeta Pages del projecte per afegir-ne un de nou.

Seleccioneu Pàgina d'afaitar - Buida. No s'especifica cap nom en particular, però s'afegeix aquí WebGL.cshtml .

Es mostra el codi.

index.html Consulteu el contingut del fitxer i el port a WebGL.cshtml . link Hi ha alguns punts estranys, com ara com col·locar l'etiqueta, però ho deixaré tal qual en nom de la simplicitat d'explicació.

@page
@model UnityPublishWebglAspNetCore.Pages.WebGLModel
@{
}

<div id="unity-container" class="unity-desktop">
  <canvas id="unity-canvas" width=960 height=600></canvas>
  <div id="unity-loading-bar">
    <div id="unity-logo"></div>
    <div id="unity-progress-bar-empty">
      <div id="unity-progress-bar-full"></div>
    </div>
  </div>
  <div id="unity-warning"> </div>
  <div id="unity-footer">
    <div id="unity-webgl-logo"></div>
    <div id="unity-fullscreen-button"></div>
    <div id="unity-build-title">Platformer</div>
  </div>
</div>

@section Scripts {
  <link rel="shortcut icon" href="TemplateData/favicon.ico">
  <link rel="stylesheet" href="TemplateData/style.css">
  <script>
    var container = document.querySelector("#unity-container");
    var canvas = document.querySelector("#unity-canvas");
    var loadingBar = document.querySelector("#unity-loading-bar");
    var progressBarFull = document.querySelector("#unity-progress-bar-full");
    var fullscreenButton = document.querySelector("#unity-fullscreen-button");
    var warningBanner = document.querySelector("#unity-warning");

    // 一時的なメッセージバナー/リボンを数秒間表示するか、
    // type == 'error'の場合はキャンバスの上部に永続的なエラーメッセージを表示します。
    // type == 'warning'の場合、黄色のハイライト色が使用されます。
    // この関数を変更または削除して、重要ではない警告とエラーメッセージがユーザーに表示されるように
    // 視覚的に表示される方法をカスタマイズします。
    function unityShowBanner(msg, type) {
      function updateBannerVisibility() {
        warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
      }
      var div = document.createElement('div');
      div.innerHTML = msg;
      warningBanner.appendChild(div);
      if (type == 'error') div.style = 'background: red; padding: 10px;';
      else {
        if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
        setTimeout(function() {
          warningBanner.removeChild(div);
          updateBannerVisibility();
        }, 5000);
      }
      updateBannerVisibility();
    }

    var buildUrl = "Build";
    var loaderUrl = buildUrl + "/WebGL.loader.js";
    var config = {
      dataUrl: buildUrl + "/WebGL.data",
      frameworkUrl: buildUrl + "/WebGL.framework.js",
      codeUrl: buildUrl + "/WebGL.wasm",
      streamingAssetsUrl: "StreamingAssets",
      companyName: "DefaultCompany",
      productName: "Platformer",
      productVersion: "2.1.0",
      showBanner: unityShowBanner,
    };

    // デフォルトでは、Unity は WebGL キャンバスレンダリングのターゲットサイズを
    // キャンバス要素の DOM サイズ(window.devicePixelRatio でスケーリング)と一致させます。
    // この同期がエンジン内で発生しないようにする場合は、これを false に設定し、
    // 代わりにサイズを大きくします。 キャンバスの DOM サイズと WebGL は、
    // ターゲットサイズを自分でレンダリングします。
    // config.matchWebGLToCanvasSize = false;

    if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
      container.className = "unity-mobile";
      // モバイルデバイスでフィルレートのパフォーマンスを低下させないようにし、
      // モバイルブラウザで低 DPI モードをデフォルト/オーバーライドします。
      config.devicePixelRatio = 1;
      unityShowBanner('WebGL builds are not supported on mobile devices.');
    } else {
      canvas.style.width = "960px";
      canvas.style.height = "600px";
    }
    loadingBar.style.display = "block";

    var script = document.createElement("script");
    script.src = loaderUrl;
    script.onload = () => {
      createUnityInstance(canvas, config, (progress) => {
        progressBarFull.style.width = 100 * progress + "%";
      }).then((unityInstance) => {
        loadingBar.style.display = "none";
        fullscreenButton.onclick = () => {
          unityInstance.SetFullscreen(1);
        };
      }).catch((message) => {
        alert(message);
      });
    };
    document.body.appendChild(script);
  </script>
}

Index.cshtmlWebGL Afegiu un enllaç a .

<!-- 省略 -->

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<!-- 追加 -->
<ul>
  <li><a href="index.html">index.html</a></li>
  <li><a href="WebGL">WebGL</a></li>
</ul>

Proveu d'executar el programa. Podeu veure que la capçalera i el peu de pàgina es mostren a la pàgina _Layout.cshtml WebGL segons .

Canviar la ubicació dels fitxers WebGL

He col·locat el fitxer WebGL directament a sota de , però aquest mètode el sobreescriurà quan col·loqueu dos o més fitxers WebGL wwwroot . Explicaré com posar-ho en carpetes separades i moure'l.

En primer lloc, creeu una carpeta nova anomenada "webgl" i moveu-la allà. Les dues carpetes a Buildmoure són , TemplateData . index.html Ja l'he portat a Pàgines d'afaitar i el puc esborrar amb seguretat.

Ja que WebGL.cshtml hem aprofundit en la jerarquia de carpetes, també aprofundirem en el camí descrit en . La correcció és de 3 línies.

Abans de la correcció

<link rel="shortcut icon" href="TemplateData/favicon.ico">
<link rel="stylesheet" href="TemplateData/style.css">
    var buildUrl = "Build";

Després de la correcció

<link rel="shortcut icon" href="webgl/TemplateData/favicon.ico">
<link rel="stylesheet" href="webgl/TemplateData/style.css">
    var buildUrl = "webgl/Build";

Executeu el programa per veure si funciona correctament.

Execució de WebGL comprimit amb Gzip

L'extensió del fitxer comprimit per Gzip és , i és .gz un fitxer que pot ser manejat per ASP.NET Core, Unity WebGL Content-Type i es gestionen de manera diferent i requereixen conversió.

Primer, creeu una pàgina i un desplegament de fitxers WebGL.

Col·locació de fitxers WebGL

wwwrootwebgl-gzip Creeu una carpeta sota Gzip i copieu la carpeta , del fitxer BuildTemplateData WebGL que heu creat amb Gzip.

Crear una pàgina d'afaitar

Aquesta vegada WebGLGzip.cshtml creeu el fitxer utilitzant el mateix procediment que quan no es comprimeix.

El codi és el següent en referència a Sortida en index.html unitat. El camí coincideix amb la carpeta que heu creat webgl-gzip anteriorment per als fitxers WebGL.

@page
@model UnityPublishWebglAspNetCore.Pages.WebGLGzipModel
@{
}

<div id="unity-container" class="unity-desktop">
  <canvas id="unity-canvas" width=960 height=600></canvas>
  <div id="unity-loading-bar">
    <div id="unity-logo"></div>
    <div id="unity-progress-bar-empty">
      <div id="unity-progress-bar-full"></div>
    </div>
  </div>
  <div id="unity-warning"> </div>
  <div id="unity-footer">
    <div id="unity-webgl-logo"></div>
    <div id="unity-fullscreen-button"></div>
    <div id="unity-build-title">Platformer</div>
  </div>
</div>

@section Scripts {
  <link rel="shortcut icon" href="webgl-gzip/TemplateData/favicon.ico">
  <link rel="stylesheet" href="webgl-gzip/TemplateData/style.css">
  <script>
    var container = document.querySelector("#unity-container");
    var canvas = document.querySelector("#unity-canvas");
    var loadingBar = document.querySelector("#unity-loading-bar");
    var progressBarFull = document.querySelector("#unity-progress-bar-full");
    var fullscreenButton = document.querySelector("#unity-fullscreen-button");
    var warningBanner = document.querySelector("#unity-warning");

    // 一時的なメッセージバナー/リボンを数秒間表示するか、
    // type == 'error'の場合はキャンバスの上部に永続的なエラーメッセージを表示します。
    // type == 'warning'の場合、黄色のハイライト色が使用されます。
    // この関数を変更または削除して、重要ではない警告とエラーメッセージがユーザーに表示されるように
    // 視覚的に表示される方法をカスタマイズします。
    function unityShowBanner(msg, type) {
      function updateBannerVisibility() {
        warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
      }
      var div = document.createElement('div');
      div.innerHTML = msg;
      warningBanner.appendChild(div);
      if (type == 'error') div.style = 'background: red; padding: 10px;';
      else {
        if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
        setTimeout(function() {
          warningBanner.removeChild(div);
          updateBannerVisibility();
        }, 5000);
      }
      updateBannerVisibility();
    }

    var buildUrl = "webgl-gzip/Build";
    var loaderUrl = buildUrl + "/WebGL_Gzip.loader.js";
    var config = {
      dataUrl: buildUrl + "/WebGL_Gzip.data.gz",
      frameworkUrl: buildUrl + "/WebGL_Gzip.framework.js.gz",
      codeUrl: buildUrl + "/WebGL_Gzip.wasm.gz",
      streamingAssetsUrl: "StreamingAssets",
      companyName: "DefaultCompany",
      productName: "Platformer",
      productVersion: "2.1.0",
      showBanner: unityShowBanner,
    };

    // デフォルトでは、Unity は WebGL キャンバスレンダリングのターゲットサイズを
    // キャンバス要素の DOM サイズ(window.devicePixelRatio でスケーリング)と一致させます。
    // この同期がエンジン内で発生しないようにする場合は、これを false に設定し、
    // 代わりにサイズを大きくします。 キャンバスの DOM サイズと WebGL は、
    // ターゲットサイズを自分でレンダリングします。
    // config.matchWebGLToCanvasSize = false;

    if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
      container.className = "unity-mobile";
      // モバイルデバイスでフィルレートのパフォーマンスを低下させないようにし、
      // モバイルブラウザで低 DPI モードをデフォルト/オーバーライドします。
      config.devicePixelRatio = 1;
      unityShowBanner('WebGL builds are not supported on mobile devices.');
    } else {
      canvas.style.width = "960px";
      canvas.style.height = "600px";
    }
    loadingBar.style.display = "block";

    var script = document.createElement("script");
    script.src = loaderUrl;
    script.onload = () => {
      createUnityInstance(canvas, config, (progress) => {
        progressBarFull.style.width = 100 * progress + "%";
      }).then((unityInstance) => {
        loadingBar.style.display = "none";
        fullscreenButton.onclick = () => {
          unityInstance.SetFullscreen(1);
        };
      }).catch((message) => {
        alert(message);
      });
    };
    document.body.appendChild(script);
  </script>
}

Modificar per permetre la navegació a Index.cshtml aquesta pàgina.

<!-- 省略 -->

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<!-- 追加 -->
<ul>
  <li><a href="index.html">index.html</a></li>
  <li><a href="WebGL">WebGL</a></li>
  <li><a href="WebGLGzip">WebGLGzip</a></li>
</ul>

Programa de fixació.cs

app.UseStaticFiles Modifiqueu la part que processa el mètode de la manera següent.

Abans de la correcció

// ここから追加
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".data"] = "application/octet-stream";
provider.Mappings[".wasm"] = "application/wasm";

app.UseStaticFiles(new StaticFileOptions()
{
  ContentTypeProvider = provider,
});
// ここまで追加

Després de la correcció

// ここから追加
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".data"] = "application/octet-stream";
provider.Mappings[".wasm"] = "application/wasm";
provider.Mappings[".br"] = "application/octet-stream";   // .br ファイルにアクセスできるように追加
provider.Mappings[".js"] = "application/javascript";     // 後の変換の為に追加

app.UseStaticFiles(new StaticFileOptions()
{
  ContentTypeProvider = provider,
  OnPrepareResponse = context =>
  {
    var path = context.Context.Request.Path.Value;
    var extension = Path.GetExtension(path);

    // 「.gz」「.br」ファイルにアクセスした場合は Content-Type と Content-Encoding を設定する
    if (extension == ".gz" || extension == ".br")
    {
      var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path) ?? "";
      if (provider.TryGetContentType(fileNameWithoutExtension, out string? contentType))
      {
        context.Context.Response.ContentType = contentType;
        context.Context.Response.Headers.Add("Content-Encoding", extension == ".gz" ? "gzip" : "br");
      }
    }
  },
});
// ここまで追加

En ASP.NET Core .gz , el fitxer Content-Type application/x-gzip retorna amb . Tal com està, Unity WebGL en el client no pot determinar-ho, així .gz que el reescric i el retorno segons Content-Type l'extensió del fitxer que exclou . Content-Encoding També ho poso perquè gzip és necessari.

Per cert, també he ajuntat el codi de Brotli, de manera que podeu utilitzar aquest codi tal com està a la correspondència Brotli del següent element. Brotli també .br estableix rewrite Content-Encoding br perquè coincideixi amb Content-Type l'extensió del fitxer sense . No obstant això, el fitxer és inaccessible per defecte en ASP.NET Core,.br així que provider.Mappings l'he afegit amb .br .

Després d'això, executeu la depuració i comproveu si el joc funciona correctament.

Si l'heu configurat correctament i el joc continua sense mostrar-se, proveu d'esborrar la memòria cau del navegador web per esborrar les galetes.

Execució de WebGL comprimit amb Brotli

El procediment és gairebé el mateix que Gzip, substituint la part Gzip per Brotli. No obstant això, els fitxers Brotli (.br) no són accessibles per defecte a ASP.NET Core. Heu de configurar-lo de manera que pugueu accedir-hi, però si utilitzeu el codi en el moment de Gzip, està suportat.

Primer, creeu una pàgina i un desplegament de fitxers WebGL.

Col·locació de fitxers WebGL

wwwrootwebgl-brotli A Crea una carpeta, copieu la carpeta , del BuildTemplateData fitxer WebGL que heu creat amb Brotli.

Crear una pàgina d'afaitar

Creeu el fitxer seguint WebGLBrotli.cshtml els mateixos passos que per a Gzip.

El codi és el següent en referència a Sortida en index.html unitat. El camí coincideix amb la carpeta que heu creat webgl-brotli anteriorment per als fitxers WebGL.

@page
@model UnityPublishWebglAspNetCore.Pages.WebGLBrotliModel
@{
}

<div id="unity-container" class="unity-desktop">
  <canvas id="unity-canvas" width=960 height=600></canvas>
  <div id="unity-loading-bar">
    <div id="unity-logo"></div>
    <div id="unity-progress-bar-empty">
      <div id="unity-progress-bar-full"></div>
    </div>
  </div>
  <div id="unity-warning"> </div>
  <div id="unity-footer">
    <div id="unity-webgl-logo"></div>
    <div id="unity-fullscreen-button"></div>
    <div id="unity-build-title">Platformer</div>
  </div>
</div>

@section Scripts {
  <link rel="shortcut icon" href="webgl-brotli/TemplateData/favicon.ico">
  <link rel="stylesheet" href="webgl-brotli/TemplateData/style.css">
  <script>
    var container = document.querySelector("#unity-container");
    var canvas = document.querySelector("#unity-canvas");
    var loadingBar = document.querySelector("#unity-loading-bar");
    var progressBarFull = document.querySelector("#unity-progress-bar-full");
    var fullscreenButton = document.querySelector("#unity-fullscreen-button");
    var warningBanner = document.querySelector("#unity-warning");

    // 一時的なメッセージバナー/リボンを数秒間表示するか、
    // type == 'error'の場合はキャンバスの上部に永続的なエラーメッセージを表示します。
    // type == 'warning'の場合、黄色のハイライト色が使用されます。
    // この関数を変更または削除して、重要ではない警告とエラーメッセージがユーザーに表示されるように
    // 視覚的に表示される方法をカスタマイズします。
    function unityShowBanner(msg, type) {
      function updateBannerVisibility() {
        warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
      }
      var div = document.createElement('div');
      div.innerHTML = msg;
      warningBanner.appendChild(div);
      if (type == 'error') div.style = 'background: red; padding: 10px;';
      else {
        if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
        setTimeout(function() {
          warningBanner.removeChild(div);
          updateBannerVisibility();
        }, 5000);
      }
      updateBannerVisibility();
    }

    var buildUrl = "webgl-brotli/Build";
    var loaderUrl = buildUrl + "/WebGL_Brotli.loader.js";
    var config = {
      dataUrl: buildUrl + "/WebGL_Brotli.data.br",
      frameworkUrl: buildUrl + "/WebGL_Brotli.framework.js.br",
      codeUrl: buildUrl + "/WebGL_Brotli.wasm.br",
      streamingAssetsUrl: "StreamingAssets",
      companyName: "DefaultCompany",
      productName: "Platformer",
      productVersion: "2.1.0",
      showBanner: unityShowBanner,
    };

    // デフォルトでは、Unity は WebGL キャンバスレンダリングのターゲットサイズを
    // キャンバス要素の DOM サイズ(window.devicePixelRatio でスケーリング)と一致させます。
    // この同期がエンジン内で発生しないようにする場合は、これを false に設定し、
    // 代わりにサイズを大きくします。 キャンバスの DOM サイズと WebGL は、
    // ターゲットサイズを自分でレンダリングします。
    // config.matchWebGLToCanvasSize = false;

    if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
      container.className = "unity-mobile";
      // モバイルデバイスでフィルレートのパフォーマンスを低下させないようにし、
      // モバイルブラウザで低 DPI モードをデフォルト/オーバーライドします。
      config.devicePixelRatio = 1;
      unityShowBanner('WebGL builds are not supported on mobile devices.');
    } else {
      canvas.style.width = "960px";
      canvas.style.height = "600px";
    }
    loadingBar.style.display = "block";

    var script = document.createElement("script");
    script.src = loaderUrl;
    script.onload = () => {
      createUnityInstance(canvas, config, (progress) => {
        progressBarFull.style.width = 100 * progress + "%";
      }).then((unityInstance) => {
        loadingBar.style.display = "none";
        fullscreenButton.onclick = () => {
          unityInstance.SetFullscreen(1);
        };
      }).catch((message) => {
        alert(message);
      });
    };
    document.body.appendChild(script);
  </script>
}

Modificar per permetre la navegació a Index.cshtml aquesta pàgina.

<!-- 省略 -->

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<!-- 追加 -->
<ul>
  <li><a href="index.html">index.html</a></li>
  <li><a href="WebGL">WebGL</a></li>
  <li><a href="WebGLGzip">WebGLGzip</a></li>
  <li><a href="WebGLBrotli">WebGLBrotli</a></li>
</ul>

Programa de fixació.cs

Si el modifiqueu quan el Gzip estigui suportat, podeu utilitzar el mateix codi.

Després de solucionar-ho, executeu la depuració i comproveu si el joc funciona.

Si l'heu configurat correctament i el joc continua sense mostrar-se, proveu d'esborrar la memòria cau del navegador web per esborrar les galetes.

Comprendre els símptomes d'accedir a fitxers Brotli en un servidor web IIS

Brotli no està suportat per IIS per defecte, de manera que cal configurar al costat IIS. Sembla que es pot gestionar fent configuracions avançades, però no ho explicaré en aquest consell. Consulteu la informació enllaçada a continuació.

Missatge d'error

Unable to load file webgl-brotli/Build/WebGL_Brotli.framework.js.br! Check that the file exists on the remote server. (also check browser Console and Devtools Network tab to debug)