Skip to content

Commit c9aaca4

Browse files
committed
Add WebGL showcase site and Pages deployment pipeline
1 parent 3505d3a commit c9aaca4

14 files changed

Lines changed: 716 additions & 4 deletions

File tree

.github/workflows/webgl-pages.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Build and Deploy WebGL
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
paths:
9+
- Assets/**
10+
- Packages/**
11+
- ProjectSettings/**
12+
- web/**
13+
- README.md
14+
- .github/workflows/webgl-pages.yml
15+
16+
permissions:
17+
contents: read
18+
pages: write
19+
id-token: write
20+
21+
concurrency:
22+
group: webgl-pages
23+
cancel-in-progress: true
24+
25+
jobs:
26+
build:
27+
runs-on: ubuntu-latest
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
with:
33+
lfs: true
34+
35+
- name: Build WebGL project
36+
uses: game-ci/unity-builder@v4
37+
env:
38+
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
39+
with:
40+
unityVersion: auto
41+
targetPlatform: WebGL
42+
buildMethod: BuildScript.BuildWebGLForPages
43+
44+
- name: Add .nojekyll marker
45+
run: touch Build/WebGL/.nojekyll
46+
47+
- name: Prepare showcase site artifact
48+
run: |
49+
rm -rf site-dist
50+
mkdir -p site-dist/unity
51+
cp -R web/. site-dist/
52+
cp -R Build/WebGL/. site-dist/unity/
53+
touch site-dist/.nojekyll
54+
55+
- name: Configure GitHub Pages
56+
uses: actions/configure-pages@v5
57+
58+
- name: Upload GitHub Pages artifact
59+
uses: actions/upload-pages-artifact@v3
60+
with:
61+
path: site-dist
62+
63+
deploy:
64+
needs: build
65+
runs-on: ubuntu-latest
66+
environment:
67+
name: github-pages
68+
url: ${{ steps.deployment.outputs.page_url }}
69+
70+
steps:
71+
- name: Deploy to GitHub Pages
72+
id: deployment
73+
uses: actions/deploy-pages@v4

Assets/Editor.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Editor/BuildScript.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using UnityEditor;
5+
using UnityEditor.Build.Reporting;
6+
using UnityEngine;
7+
8+
public static class BuildScript
9+
{
10+
private const string DefaultOutputPath = "Build/WebGL";
11+
private const string EmbedTemplate = "PROJECT:FullscreenEmbed";
12+
13+
[MenuItem("Tools/Build/WebGL (GitHub Pages)")]
14+
public static void BuildWebGLFromMenu()
15+
{
16+
BuildWebGL(DefaultOutputPath);
17+
}
18+
19+
public static void BuildWebGLForPages()
20+
{
21+
var buildPath = GetCommandLineArgument("-buildPath") ?? DefaultOutputPath;
22+
BuildWebGL(buildPath);
23+
}
24+
25+
private static void BuildWebGL(string outputPath)
26+
{
27+
var previousTemplate = PlayerSettings.WebGL.template;
28+
var previousCompressionFormat = PlayerSettings.WebGL.compressionFormat;
29+
var previousDecompressionFallback = PlayerSettings.WebGL.decompressionFallback;
30+
var previousSplashScreen = PlayerSettings.SplashScreen.show;
31+
32+
var enabledScenes = EditorBuildSettings.scenes
33+
.Where(scene => scene.enabled)
34+
.Select(scene => scene.path)
35+
.ToArray();
36+
37+
if (enabledScenes.Length == 0)
38+
{
39+
throw new InvalidOperationException("No enabled scenes found in Build Settings.");
40+
}
41+
42+
var absoluteOutputPath = Path.IsPathRooted(outputPath)
43+
? outputPath
44+
: Path.Combine(Directory.GetCurrentDirectory(), outputPath);
45+
46+
Directory.CreateDirectory(absoluteOutputPath);
47+
48+
// Keep compression efficient while still supporting static hosts like GitHub Pages.
49+
PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Brotli;
50+
PlayerSettings.WebGL.decompressionFallback = true;
51+
PlayerSettings.WebGL.template = EmbedTemplate;
52+
PlayerSettings.SplashScreen.show = false;
53+
54+
var buildPlayerOptions = new BuildPlayerOptions
55+
{
56+
scenes = enabledScenes,
57+
locationPathName = absoluteOutputPath,
58+
target = BuildTarget.WebGL,
59+
options = BuildOptions.None
60+
};
61+
62+
try
63+
{
64+
var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
65+
var summary = report.summary;
66+
67+
if (summary.result != BuildResult.Succeeded)
68+
{
69+
throw new Exception($"WebGL build failed with result: {summary.result}");
70+
}
71+
72+
Debug.Log($"WebGL build completed: {absoluteOutputPath}");
73+
}
74+
finally
75+
{
76+
PlayerSettings.WebGL.compressionFormat = previousCompressionFormat;
77+
PlayerSettings.WebGL.decompressionFallback = previousDecompressionFallback;
78+
PlayerSettings.WebGL.template = previousTemplate;
79+
PlayerSettings.SplashScreen.show = previousSplashScreen;
80+
}
81+
}
82+
83+
private static string GetCommandLineArgument(string key)
84+
{
85+
var args = Environment.GetCommandLineArgs();
86+
for (var index = 0; index < args.Length - 1; index++)
87+
{
88+
if (args[index] == key)
89+
{
90+
return args[index + 1];
91+
}
92+
}
93+
94+
return null;
95+
}
96+
}

Assets/Editor/BuildScript.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/CameraMovement.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,28 @@ public class CameraMovement : MonoBehaviour
66
[Range(0f, 90f)] [SerializeField] private float yRotationLimit = 60f;
77
[Range(0f, 90f)] [SerializeField] private float xRotationLimit = 60f;
88
[SerializeField] private float zoomSpeed = 10f;
9+
[SerializeField] private bool disableInWebBuild = true;
910

1011
private Camera _camera;
1112
private Vector2 _rotation = Vector2.zero;
1213
private const string XAxis = "Mouse X";
1314
private const string YAxis = "Mouse Y";
1415
private float _fieldOfView;
16+
private bool _movementDisabled;
1517

1618
private void Awake()
1719
{
1820
_rotation = transform.localRotation.eulerAngles;
1921
_camera = GetComponent<Camera>();
2022
_fieldOfView = _camera.fieldOfView;
23+
24+
_movementDisabled = disableInWebBuild && Application.platform == RuntimePlatform.WebGLPlayer;
2125
}
2226

2327
private void Update()
2428
{
29+
if (_movementDisabled) return;
30+
2531
UpdateRotation();
2632
UpdateCameraZoom();
2733
}
@@ -49,4 +55,4 @@ private void UpdateCameraZoom()
4955
_camera.fieldOfView = Mathf.Clamp(_fieldOfView, 35f, 100f);
5056
_fieldOfView = _camera.fieldOfView;
5157
}
52-
}
58+
}

Assets/Scripts/ObjSelector.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ private void RemoveTarget(Transform hitTransform)
137137

138138
private void OnHandleInteractionStart(Handle handle)
139139
{
140-
_cameraMovement.enabled = false;
140+
if (_cameraMovement != null) _cameraMovement.enabled = false;
141141
}
142142

143143
private static void OnHandleInteraction(Handle handle)
@@ -147,7 +147,7 @@ private static void OnHandleInteraction(Handle handle)
147147

148148
private void OnHandleInteractionEnd(Handle handle)
149149
{
150-
_cameraMovement.enabled = true;
150+
if (_cameraMovement != null) _cameraMovement.enabled = true;
151151
}
152152

153153
private void OnHandleDestroyed(Handle handle)
@@ -157,4 +157,4 @@ private void OnHandleDestroyed(Handle handle)
157157
handle.OnInteractionEndEvent -= OnHandleInteractionEnd;
158158
handle.OnHandleDestroyedEvent -= OnHandleDestroyed;
159159
}
160-
}
160+
}

Assets/WebGLTemplates.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/WebGLTemplates/FullscreenEmbed.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<!DOCTYPE html>
2+
<html lang="en-us">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
7+
<title>{{{ PRODUCT_NAME }}}</title>
8+
<style>
9+
html, body {
10+
margin: 0;
11+
padding: 0;
12+
width: 100%;
13+
height: 100%;
14+
overflow: hidden;
15+
background: #101214;
16+
}
17+
18+
#unity-container {
19+
position: fixed;
20+
inset: 0;
21+
}
22+
23+
#unity-canvas {
24+
width: 100%;
25+
height: 100%;
26+
display: block;
27+
background: {{{ BACKGROUND_FILENAME ? "'url(\"Build/" + BACKGROUND_FILENAME.replace(/"/g, '%22') + "\") center / cover'" : BACKGROUND_COLOR }}};
28+
}
29+
30+
#unity-loading {
31+
position: absolute;
32+
left: 50%;
33+
top: 50%;
34+
transform: translate(-50%, -50%);
35+
width: min(320px, 72vw);
36+
pointer-events: none;
37+
}
38+
39+
#unity-progress-track {
40+
width: 100%;
41+
height: 8px;
42+
border-radius: 999px;
43+
overflow: hidden;
44+
background: rgba(255, 255, 255, 0.18);
45+
}
46+
47+
#unity-progress-fill {
48+
width: 0%;
49+
height: 100%;
50+
background: #4ad5c7;
51+
transition: width 120ms linear;
52+
}
53+
54+
#unity-warning {
55+
position: absolute;
56+
left: 50%;
57+
top: 16px;
58+
transform: translateX(-50%);
59+
z-index: 20;
60+
color: #111;
61+
font-family: Arial, sans-serif;
62+
}
63+
</style>
64+
</head>
65+
<body>
66+
<div id="unity-container">
67+
<canvas id="unity-canvas" width={{{ WIDTH }}} height={{{ HEIGHT }}} tabindex="-1"></canvas>
68+
<div id="unity-loading">
69+
<div id="unity-progress-track">
70+
<div id="unity-progress-fill"></div>
71+
</div>
72+
</div>
73+
<div id="unity-warning"></div>
74+
</div>
75+
<script src="Build/{{{ LOADER_FILENAME }}}"></script>
76+
<script>
77+
var canvas = document.querySelector("#unity-canvas");
78+
var loadingElement = document.querySelector("#unity-loading");
79+
var progressElement = document.querySelector("#unity-progress-fill");
80+
var warningElement = document.querySelector("#unity-warning");
81+
82+
function unityShowBanner(message, type) {
83+
var div = document.createElement("div");
84+
div.textContent = message;
85+
div.style.padding = "8px 12px";
86+
div.style.marginTop = "8px";
87+
div.style.borderRadius = "8px";
88+
div.style.background = type === "error" ? "#ff6161" : "#ffd166";
89+
warningElement.appendChild(div);
90+
91+
if (type !== "error") {
92+
setTimeout(function() {
93+
if (div.parentNode === warningElement) {
94+
warningElement.removeChild(div);
95+
}
96+
}, 5000);
97+
}
98+
}
99+
100+
if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
101+
var meta = document.createElement("meta");
102+
meta.name = "viewport";
103+
meta.content = "width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes";
104+
document.head.appendChild(meta);
105+
}
106+
107+
var config = {
108+
arguments: [],
109+
dataUrl: "Build/{{{ DATA_FILENAME }}}",
110+
frameworkUrl: "Build/{{{ FRAMEWORK_FILENAME }}}",
111+
#if USE_THREADS
112+
workerUrl: "Build/{{{ WORKER_FILENAME }}}",
113+
#endif
114+
#if USE_WASM
115+
codeUrl: "Build/{{{ CODE_FILENAME }}}",
116+
#endif
117+
#if SYMBOLS_FILENAME
118+
symbolsUrl: "Build/{{{ SYMBOLS_FILENAME }}}",
119+
#endif
120+
streamingAssetsUrl: "StreamingAssets",
121+
companyName: {{{ JSON.stringify(COMPANY_NAME) }}},
122+
productName: {{{ JSON.stringify(PRODUCT_NAME) }}},
123+
productVersion: {{{ JSON.stringify(PRODUCT_VERSION) }}},
124+
showBanner: unityShowBanner
125+
};
126+
127+
createUnityInstance(canvas, config, function(progress) {
128+
progressElement.style.width = (progress * 100) + "%";
129+
}).then(function() {
130+
loadingElement.style.display = "none";
131+
}).catch(function(message) {
132+
alert(message);
133+
});
134+
</script>
135+
</body>
136+
</html>

0 commit comments

Comments
 (0)