Menjalankan Unity WebGL dengan ASP.NET Core

Halaman Diperbarui :
Tanggal pembuatan halaman :

Lingkungan verifikasi

Windows
  • jendela 11
Editor Kesatuan
  • 25f1/3/2020
Visual Studio
  • Visual Studio 2022
ASP.NET Inti
  • ASP.NET inti 6.0
Layanan Informasi Internet (IIS)
  • IIS 10.0

Pada awalnya

Pelajari cara menjalankan output game sebagai WebGL di Unity di server web yang menjalankan ASP.NET Core. Untuk program game, gunakan output yang ada di langkah-langkah tips di bawah ini. Contoh game menggunakan 2D Platformer Microgame, yang dapat dibuat dari Unity Hub.

Saya akan menjelaskan cara mengatur "WebGL Tidak Terkompresi", "WebGL dikompresi dengan Gzip", dan "WebGL dikompresi dengan Brotli" untuk menjalankan game WebGL. Prosedurnya sama untuk semuanya.

Kami menggunakan Visual Studio 2022, ASP.NET Core 6.0, tetapi versi yang lebih lama mungkin akan berfungsi. Namun, struktur kode awal berbeda untuk setiap versi, jadi harap pahami perbedaannya sendiri.

Membuat Proyek ASP.NET Core

Luncurkan "Visual Studio 2022" dari menu mulai.

Pilih Buat Proyek Baru.

Kali ini, pilih "ASP.NET Core Web App" sebagai sampel. Jika Anda menjalankan ASP.NET Core, Anda dapat menjalankan template lain, tetapi Anda harus mengikuti setiap template tentang cara membuatnya.

Tetapkan nama dan lokasi proyek secara sewenang-wenang.

Biarkan informasi tambahan apa adanya.

Proyek telah dibuat.

Jalankan WebGL yang tidak terkompresi

Siapkan program WebGL yang dibuat tanpa kompresi.

Pastikan game bekerja dengan cepat

ASP.NET Coba jalankan game WebGL dengan pengaturan yang lebih sedikit tanpa mengikuti etiket Inti.

Di ASP.NET Core, Anda tidak dapat mengakses beberapa file WebGL yang dipancarkan oleh Unity dalam status default. Buat ini dapat diakses.

Program.cs

Buka dari proyek Anda Program.cs . Berlaku untuk versi Startup.cs ASP.NET Core sebelumnya.

Tambahkan namespace ke bagian atas kode dan ganti yang berikut ini dalam kode 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, sehingga ketika file diakses, .wasm dapat dikembalikan ke klien dengan .Content-Type

Penerapan WebGL

Tempatkan folder file berikut dari Unity di proyek wwwroot Anda.

  • indeks.html
  • Membangun
  • Data Templat

Index.cshtmlindex.html Buka tautan sehingga Anda dapat mengaksesnya.

<!-- 省略 -->

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

Jalankan program dan pastikan game berfungsi.

Jalankan program WebGL di halaman Razor

Game sebelumnya berjalan pada file HTML statis, sehingga berjalan di tempat yang tidak ada hubungannya dengan ASP.NET Core. Ini sangat tidak diinginkan untuk keseragaman program, jadi kita akan memindahkan perilaku file HTML ke Razor Pages.

Klik kanan folder Pages dari proyek Anda untuk menambahkan item baru.

Pilih Razor Page - Empty. Tidak ada nama khusus yang ditentukan, tetapi ditambahkan di sini WebGL.cshtml .

Kode ditampilkan.

index.html Lihat konten file dan port ke WebGL.cshtml . link Ada beberapa poin aneh, seperti cara menempatkan tag, tetapi saya akan membiarkannya apa adanya demi kesederhanaan penjelasan.

@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 Tambahkan tautan ke .

<!-- 省略 -->

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

Cobalah untuk menjalankan program. Anda dapat melihat bahwa header dan footer _Layout.cshtml ditampilkan pada halaman WebGL sesuai dengan .

Mengubah lokasi file WebGL

Saya menempatkan file WebGL langsung di bawah , tetapi metode ini akan menimpanya ketika Anda menempatkan dua atau lebih file WebGL wwwroot . Saya akan menjelaskan cara meletakkan ini di folder terpisah dan memindahkannya.

Pertama-tama, buat folder baru bernama "webgl" dan pindahkan ke sana. Dua folder Buildyang akan dipindahkan adalah , TemplateData . index.html Saya sudah memindahkannya ke Razor Pages dan dapat menghapusnya dengan aman.

Karena WebGL.cshtml kita telah memperdalam hierarki folder, kita juga akan memperdalam jalur yang dijelaskan dalam . Koreksinya adalah 3 baris.

Sebelum koreksi

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

Setelah koreksi

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

Jalankan program untuk melihat apakah itu berfungsi dengan baik.

Menjalankan WebGL yang dikompresi dengan Gzip

Ekstensi file yang dikompresi oleh Gzip adalah .gz , dan ini adalah file yang dapat ditangani oleh ASP.NET Core, Unity WebGL Content-Type dan ditangani secara berbeda dan memerlukan konversi.

Pertama, buat penyebaran dan halaman file WebGL.

Penempatan file WebGL

wwwrootwebgl-gzip Buat folder di bawah Gzip dan salin folder , dari file BuildTemplateData WebGL yang Anda buat dengan Gzip.

Buat Halaman Razor

Kali WebGLGzip.cshtml ini buat file menggunakan prosedur yang sama seperti saat tidak terkompresi.

Kode tersebut adalah sebagai berikut dengan mengacu pada Output in index.html Unity. Jalur cocok dengan folder yang Anda buat webgl-gzip sebelumnya untuk file 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>
}

Ubah untuk mengizinkan navigasi ke Index.cshtml halaman ini.

<!-- 省略 -->

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

Memperbaiki Program.cs

app.UseStaticFiles Ubah bagian yang memproses metode sebagai berikut.

Sebelum koreksi

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

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

Setelah koreksi

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

Di ASP.NET Core .gz , file Content-Type application/x-gzip kembali dengan . Karena itu, Unity WebGL pada klien tidak dapat menentukannya, jadi .gz saya menulis ulang dan mengembalikannya sesuai Content-Type dengan ekstensi file yang mengecualikan . Content-Encoding Saya juga mengaturnya gzip karena itu perlu.

Ngomong-ngomong, saya juga menggabungkan kode Brotli, sehingga Anda dapat menggunakan kode ini seperti dalam korespondensi Brotli dari item berikutnya. Brotli juga .br mengatur penulisan Content-Encoding br ulang agar sesuai dengan Content-Type ekstensi file tanpa . Namun, file tersebut tidak dapat diakses secara default di ASP.NET Core,.br jadi provider.Mappings saya menambahkannya dengan .br .

Setelah itu, jalankan debug dan periksa apakah game berfungsi dengan benar.

Jika kamu sudah mengaturnya dengan benar dan game masih tidak akan ditampilkan, coba bersihkan cache browser webmu untuk menghapus cookie.

Menjalankan WebGL dikompresi dengan Brotli

Prosedurnya hampir sama dengan Gzip, mengganti bagian Gzip dengan Brotli. Namun, file Brotli (.br) tidak dapat diakses secara default di ASP.NET Core. Anda perlu mengkonfigurasinya sehingga Anda dapat mengaksesnya, tetapi jika Anda menggunakan kode pada saat Gzip, itu didukung.

Pertama, buat penyebaran dan halaman file WebGL.

Penempatan file WebGL

wwwrootwebgl-brotli Di bawah Buat folder, salin folder , dari file WebGL yang BuildTemplateData Anda buat dengan Brotli.

Buat Halaman Razor

Buat file menggunakan WebGLBrotli.cshtml langkah yang sama seperti untuk Gzip.

Kode tersebut adalah sebagai berikut dengan mengacu pada Output in index.html Unity. Jalur cocok dengan folder yang Anda buat webgl-brotli sebelumnya untuk file 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>
}

Ubah untuk mengizinkan navigasi ke Index.cshtml halaman ini.

<!-- 省略 -->

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

Memperbaiki Program.cs

Jika Anda memodifikasinya saat Gzip didukung, Anda dapat menggunakan kode yang sama.

Setelah memperbaikinya, jalankan debug dan periksa apakah game berfungsi.

Jika kamu sudah mengaturnya dengan benar dan game masih tidak akan ditampilkan, coba bersihkan cache browser webmu untuk menghapus cookie.

Memahami Gejala Mengakses File Brotli di Server Web IIS

Brotli tidak didukung oleh IIS secara default, sehingga konfigurasi di sisi IIS diperlukan. Tampaknya ini dapat ditangani dengan membuat pengaturan lanjutan, tetapi saya tidak akan menjelaskannya dalam tip ini. Silakan lihat informasi yang ditautkan di bawah ini.

Pesan kesalahan

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)