Rularea Unity WebGL cu ASP.NET Core

Pagina actualizată :
Data creării paginii :

Mediul de verificare

Windows
  • Ferestre 11
Unity Editor
  • 2020.3.25F1
Visual Studio
  • Visual Studio 2022
ASP.NET Nucleul
  • ASP.NET Core 6.0
Internet Information Services (IIS)
  • IIS 10.0

La început

Aflați cum să rulați o ieșire de joc ca WebGL în Unity pe un server web care rulează ASP.NET Core. Pentru programele de joc, utilizați cele rezultate în pașii de sfaturi de mai jos. Exemplul jocului folosește 2D Platformer Microgame, care poate fi creat din Unity Hub.

Voi explica cum să configurați "WebGL necomprimat", "WebGL comprimat cu Gzip" și "WebGL comprimat cu Brotli" pentru a rula jocuri WebGL. Procedura este aceeași pentru toți.

Folosim Visual Studio 2022 ASP.NET Core 6.0, dar versiunile mai vechi vor funcționa probabil. Cu toate acestea, structura codului inițial este diferită pentru fiecare versiune, așa că vă rugăm să înțelegeți singuri diferențele.

Crearea unui ASP.NET proiect de bază

Lansați "Visual Studio 2022" din meniul Start.

Selectați Creare proiect nou.

De data aceasta, selectați "ASP.NET Core Web App" ca eșantion. Dacă rulați pe ASP.NET Core, puteți rula alte șabloane, dar va trebui să urmați fiecare șablon pentru a-l construi.

Setați numele și locația proiectului în mod arbitrar.

Lăsați informațiile suplimentare așa cum sunt.

Proiectul a fost creat.

Rulați WebGL necomprimat

Pregătiți un program WebGL creat fără compresie.

Asigurați-vă că jocul funcționează rapid

ASP.NET Încercați să rulați un joc WebGL cu mai puține setări fără a urma eticheta Core.

În ASP.NET Core, nu puteți accesa unele fișiere WebGL emise de Unity în starea implicită. Faceți acest lucru accesibil.

Programul.cs

Deschideți din proiectul dvs Program.cs . Se aplică versiunilor Startup.cs anterioare ASP.NET Core .

Adăugați un spațiu de nume în partea de sus a codului și înlocuiți următoarele în cod 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, astfel încât atunci când un fișier este accesat, .wasm acesta poate fi returnat clientului cu .Content-Type

Implementare WebGL

Plasați următoarele foldere de fișiere din Unity în proiectul wwwroot dvs.

  • Index.html
  • Zidi
  • TemplateData

Index.cshtmlindex.html Deschideți linkul pentru a-l putea accesa.

<!-- 省略 -->

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

Rulați programul și asigurați-vă că jocul funcționează.

Rulați un program WebGL pe o pagină Razor

Jocul anterior a rulat pe un fișier HTML static, deci rulează într-un loc care nu are nimic de-a face cu ASP.NET Core. Nu este foarte de dorit pentru uniformitatea programului, așa că vom muta comportamentul fișierului HTML în Razor Pages.

Faceți clic dreapta pe folderul Pagini din proiect pentru a adăuga un element nou.

Selectați Razor Page - Empty. Nu este specificat un nume special, dar este adăugat aici WebGL.cshtml .

Codul este afișat.

index.html Consultați conținutul fișierului și portați la WebGL.cshtml . link Există câteva puncte ciudate, cum ar fi modul de plasare a etichetei, dar o voi lăsa așa cum este de dragul simplității explicației.

@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 Adăugați un link la .

<!-- 省略 -->

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

Încercați să rulați programul. Puteți vedea că antetul și subsolul _Layout.cshtml sunt afișate pe pagina WebGL în conformitate cu .

Modificarea locației fișierelor WebGL

Am plasat fișierul WebGL direct sub , dar această metodă îl va suprascrie atunci când plasați două sau mai multe fișiere WebGL wwwroot . Voi explica cum să puneți acest lucru în foldere separate și să îl mutați.

În primul rând, creați un folder nou numit "webgl" și mutați-l acolo. Cele două foldere care trebuie Buildmutate sunt , TemplateData . index.html L-am portat deja pe Razor Pages și îl pot șterge în siguranță.

Deoarece WebGL.cshtml am aprofundat ierarhia folderelor, vom aprofunda și calea descrisă în . Corecția este de 3 linii.

Înainte de corectare

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

După corectare

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

Rulați programul pentru a vedea dacă funcționează corect.

Rularea WebGL comprimat cu Gzip

Extensia fișierului comprimat de Gzip este .gz , și este un fișier care poate fi gestionat de ASP.NET Core, Unity WebGL Content-Type și sunt tratate diferit și necesită conversie.

Mai întâi, creați o implementare și o pagină de fișiere WebGL.

Plasarea fișierelor WebGL

wwwrootwebgl-gzip Creați un folder sub Gzip și copiați folderul , din fișierul BuildTemplateData WebGL pe care l-ați creat cu Gzip.

Creați o pagină de ras

De data WebGLGzip.cshtml aceasta, creați fișierul utilizând aceeași procedură ca atunci când nu este comprimat.

Codul este după cum urmează, cu referire la Output in index.html Unity. Calea se potrivește cu folderul pe care l-ați creat webgl-gzip anterior pentru fișierele 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>
}

Modificați pentru a permite navigarea la Index.cshtml această pagină.

<!-- 省略 -->

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

Program de fixare.cs

app.UseStaticFiles Modificați partea care procesează metoda după cum urmează.

Înainte de corectare

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

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

După corectare

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

În ASP.NET Core .gz , fișierul Content-Type application/x-gzip revine cu . Așa cum este, Unity WebGL pe client nu îl poate determina, așa .gz că îl rescriu și îl returnez în funcție Content-Type de extensia fișierului care exclude . Content-Encoding De asemenea, l-am setat gzip pentru că este necesar.

Apropo, am pus și codul Brotli împreună, astfel încât să puteți utiliza acest cod așa cum este în corespondența Brotli a următorului articol. Brotli setează, de asemenea, .br rescrierea Content-Encoding br pentru a se potrivi cu Content-Type extensia fișierului fără . Cu toate acestea, fișierul este inaccesibil în mod implicit în ASP.NET Core,.br așa că provider.Mappings l-am adăugat cu .br .

După aceea, rulați depanarea și verificați dacă jocul funcționează corect.

Dacă l-ați configurat corect și jocul tot nu se afișează, încercați să goliți memoria cache a browserului web pentru a șterge cookie-urile.

Rularea WebGL comprimat cu Brotli

Procedura este aproape aceeași cu Gzip, înlocuind partea Gzip cu Brotli. Cu toate acestea, fișierele Brotli (.br) nu sunt accesibile în mod implicit în ASP.NET Core. Trebuie să îl configurați astfel încât să îl puteți accesa, dar dacă utilizați codul în momentul Gzip, acesta este acceptat.

Mai întâi, creați o implementare și o pagină de fișiere WebGL.

Plasarea fișierelor WebGL

wwwrootwebgl-brotli Sub Creare folder, copiați folderul , din BuildTemplateData fișierul WebGL pe care l-ați creat cu Brotli.

Creați o pagină de ras

Creați fișierul utilizând WebGLBrotli.cshtml aceiași pași ca și pentru Gzip.

Codul este după cum urmează, cu referire la Output in index.html Unity. Calea se potrivește cu folderul pe care l-ați creat webgl-brotli anterior pentru fișierele 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>
}

Modificați pentru a permite navigarea la Index.cshtml această pagină.

<!-- 省略 -->

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

Program de fixare.cs

Dacă îl modificați atunci când este acceptat Gzip, puteți utiliza același cod.

După remedierea acestuia, rulați depanarea și verificați dacă jocul funcționează.

Dacă l-ați configurat corect și jocul tot nu se afișează, încercați să goliți memoria cache a browserului web pentru a șterge cookie-urile.

Înțelegerea simptomelor de accesare a fișierelor Brotli pe un server web IIS

Brotli nu este acceptat de IIS în mod implicit, deci este necesară configurarea pe partea IIS. Se pare că poate fi gestionat făcând setări avansate, dar nu o voi explica în acest sfat. Vă rugăm să consultați informațiile legate mai jos.

Mesaj de eroare

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)