Exécution de Unity WebGL avec ASP.NET Core

Page mise à jour :
Date de création de la page :

Environnement de vérification

Windows
  • Fenêtres 11
Éditeur Unity
  • 2020.3.25f1
Visual Studio
  • Visual Studio 2022
ASP.NET noyau
  • ASP.NET Noyau 6.0
Services Internet (IIS)
  • IIS 10.0

Au début

Découvrez comment exécuter une sortie de jeu en tant que WebGL dans Unity sur un serveur Web exécutant ASP.NET Core. Pour les programmes de jeu, utilisez ceux qui sortent dans les étapes de conseils ci-dessous. L’exemple de jeu utilise le microjeu de plateforme 2D, qui peut être créé à partir du hub Unity.

Je vais vous expliquer comment configurer « Uncompressed WebGL », « WebGL compressed with Gzip » et « WebGL compressed with Brotli » pour exécuter des jeux WebGL. La procédure est la même pour tous.

Nous utilisons Visual Studio 2022 ASP.NET Core 6.0, mais les anciennes versions fonctionneront probablement. Cependant, la structure du code initial est différente pour chaque version, veuillez donc comprendre les différences vous-même.

Création d’un projet principal ASP.NET

Lancez « Visual Studio 2022 » à partir du menu Démarrer.

Sélectionnez Créer un nouveau projet.

Cette fois, sélectionnez « ASP.NET Core Web App » comme exemple. Si vous utilisez ASP.NET Core, vous pouvez exécuter d’autres modèles, mais vous devrez suivre chaque modèle pour savoir comment le créer.

Définissez arbitrairement le nom et l’emplacement du projet.

Laissez les informations supplémentaires telles quelles.

Le projet a été créé.

Exécuter WebGL non compressé

Préparez un programme WebGL créé sans compression.

Assurez-vous que le jeu fonctionne rapidement

ASP.NET Essayez d’exécuter un jeu WebGL avec moins de paramètres sans suivre l’étiquette de base.

Dans ASP.NET Core, vous ne pouvez pas accéder à certains fichiers WebGL émis par Unity dans l’état par défaut. Rendez-le accessible.

Programme .cs

Ouvrez à partir de votre Program.cs projet. S’applique aux versions Startup.cs antérieures de ASP.NET Core.

Ajoutez un espace de noms en haut du code et remplacez ce qui suit dans le code 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 sorte que lors de l’accès à un fichier, .wasm il peut être renvoyé au client avec le fichier .Content-Type

Déploiement WebGL

Placez les dossiers de fichiers suivants de Unity dans votre projet wwwroot .

  • index.html
  • Construire
  • TemplateData (Données de modèle)

Index.cshtmlindex.html Ouvrez le lien pour pouvoir y accéder.

<!-- 省略 -->

<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>

Exécutez le programme et assurez-vous que le jeu fonctionne.

Exécuter un programme WebGL sur une page Razor

Le jeu précédent fonctionnait sur un fichier HTML statique, il fonctionne donc dans un endroit qui n’a rien à voir avec ASP.NET Core. Ce n’est pas très souhaitable pour l’uniformité du programme, nous allons donc déplacer le comportement du fichier HTML vers Razor Pages.

Cliquez avec le bouton droit sur le dossier Pages de votre projet pour ajouter un nouvel élément.

Sélectionnez Razor Page - Empty. Il n’y a pas de nom particulier spécifié, mais il est ajouté ici WebGL.cshtml .

Le code s’affiche.

index.html Reportez-vous au contenu du fichier et portez à WebGL.cshtml . link Il y a quelques points étranges, comme la façon de placer l’étiquette, mais je vais la laisser telle quelle par souci de simplicité d’explication.

@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 Ajoutez un lien vers .

<!-- 省略 -->

<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>

Essayez d’exécuter le programme. Vous pouvez voir que l’en-tête et le pied de page sont affichés sur la page _Layout.cshtml WebGL conformément à .

Modifier l’emplacement des fichiers WebGL

J’ai placé le fichier WebGL directement sous , mais cette méthode l’écrase lorsque vous placez deux fichiers WebGL wwwroot ou plus. Je vais expliquer comment mettre cela dans des dossiers séparés et le déplacer.

Tout d’abord, créez un nouveau dossier appelé « webgl » et déplacez-le là. Les deux dossiers à Builddéplacer sont , TemplateData . index.html Je l’ai déjà porté sur Razor Pages et je peux le supprimer en toute sécurité.

Puisque WebGL.cshtml nous avons approfondi la hiérarchie des dossiers, nous allons également approfondir le chemin décrit dans . La correction est de 3 lignes.

Avant correction

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

Après correction

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

Exécutez le programme pour voir s’il fonctionne correctement.

Exécution de WebGL compressé avec Gzip

L’extension du fichier compressé par Gzip est , et c’est .gz un fichier qui peut être géré par ASP.NET Core, Unity WebGL Content-Type et sont gérés différemment et nécessitent une conversion.

Tout d’abord, créez un déploiement et une page de fichiers WebGL.

Placement de fichier WebGL

wwwrootwebgl-gzip Créez un dossier sous Gzip et copiez le dossier , à partir du fichier BuildTemplateData WebGL que vous avez créé avec Gzip.

Créer une page Razor

Cette fois WebGLGzip.cshtml , créez le fichier en utilisant la même procédure que lors de la décompression.

Le code est le suivant en référence à Output dans index.html Unity. Le chemin d’accès correspond au dossier que vous avez créé webgl-gzip précédemment pour les fichiers 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>
}

Modifier pour autoriser la navigation vers Index.cshtml cette page.

<!-- 省略 -->

<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>

Programme de réparation.cs

app.UseStaticFiles Modifiez la partie qui traite la méthode comme suit.

Avant correction

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

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

Après correction

// ここから追加
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");
      }
    }
  },
});
// ここまで追加

Dans ASP.NET Core .gz , le fichier Content-Type application/x-gzip renvoie avec . En l’état, Unity WebGL sur le client ne peut pas le déterminer, donc .gz je le réécris et le renvoie en Content-Type fonction de l’extension du fichier qui exclut . Content-Encoding Je l’ai aussi défini parce que c’est gzip nécessaire.

Au fait, j’ai également mis le code Brotli ensemble, afin que vous puissiez utiliser ce code tel qu’il est dans la correspondance Brotli de l’article suivant. Brotli définit également .br la réécriture Content-Encoding br pour qu’elle corresponde Content-Type à l’extension du fichier sans . Cependant, le fichier est inaccessible par défaut dans ASP.NET Core,.br je l’ai donc provider.Mappings ajouté avec .br .

Après cela, exécutez le débogage et vérifiez si le jeu fonctionne correctement.

Si vous l’avez configuré correctement et que le jeu ne s’affiche toujours pas, essayez de vider le cache de votre navigateur Web pour effacer vos cookies.

Exécution de WebGL compressé avec Brotli

La procédure est presque la même que Gzip, remplaçant la partie Gzip par Brotli. Toutefois, les fichiers Brotli (.br) ne sont pas accessibles par défaut dans ASP.NET Core. Vous devez le configurer pour pouvoir y accéder, mais si vous utilisez le code au moment de Gzip, il est pris en charge.

Tout d’abord, créez un déploiement et une page de fichiers WebGL.

Placement de fichier WebGL

wwwrootwebgl-brotli Sous Créer un dossier, copiez le dossier , à partir du BuildTemplateData fichier WebGL que vous avez créé avec Brotli.

Créer une page Razor

Créez le fichier en suivant WebGLBrotli.cshtml les mêmes étapes que pour Gzip.

Le code est le suivant en référence à Output dans index.html Unity. Le chemin d’accès correspond au dossier que vous avez créé webgl-brotli précédemment pour les fichiers 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>
}

Modifier pour autoriser la navigation vers Index.cshtml cette page.

<!-- 省略 -->

<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>

Programme de réparation.cs

Si vous le modifiez lorsque Gzip est pris en charge, vous pouvez utiliser le même code.

Après l’avoir corrigé, exécutez debug et vérifiez si le jeu fonctionne.

Si vous l’avez configuré correctement et que le jeu ne s’affiche toujours pas, essayez de vider le cache de votre navigateur Web pour effacer vos cookies.

Présentation des symptômes d’accès aux fichiers Brotli sur un serveur Web IIS

Brotli n’est pas pris en charge par IIS par défaut, une configuration côté IIS est donc requise. Il semble que cela puisse être géré en effectuant des réglages avancés, mais je ne l’expliquerai pas dans cette astuce. Veuillez vous référer aux informations liées ci-dessous.

message d'erreur

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)