Optimize WebGL build and video loading

This commit is contained in:
yangbear
2026-07-14 00:16:34 +08:00
parent ecfc382b03
commit 53f856e44e
20 changed files with 203 additions and 33 deletions
+5 -5
View File
@@ -10224,7 +10224,7 @@ VideoPlayer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1301023629}
m_Enabled: 1
m_VideoClip: {fileID: 32900000, guid: 11aaf6dcdb9c34927bf1917486d2f3e6, type: 3}
m_VideoClip: {fileID: 0}
m_TargetCameraAlpha: 1
m_TargetCamera3DLayout: 0
m_TargetCamera: {fileID: 0}
@@ -10234,7 +10234,7 @@ VideoPlayer:
m_TargetMaterialProperty: <noninit>
m_RenderMode: 2
m_AspectRatio: 2
m_DataSource: 0
m_DataSource: 1
m_TimeUpdateMode: 2
m_PlaybackSpeed: 1
m_AudioOutputMode: 2
@@ -10242,13 +10242,13 @@ VideoPlayer:
- {fileID: 0}
m_DirectAudioVolumes:
- 1
m_Url:
m_Url: StreamingAssets/Videos/bandage_demo.mp4
m_EnabledAudioTracks: 01
m_DirectAudioMutes: 01
m_ControlledAudioTrackCount: 1
m_PlayOnAwake: 1
m_PlayOnAwake: 0
m_SkipOnDrop: 1
m_Looping: 0
m_Looping: 1
m_WaitForFirstFrame: 1
m_FrameReadyEventEnabled: 0
m_VideoShaders: []
@@ -138,7 +138,7 @@ public class ShoseClickHandler : MonoBehaviour
var uiMgr = FindObjectOfType<UIManager>();
if (uiMgr != null)
{
uiMgr.ShowVideo("tuoXieZi.mp4");
uiMgr.ShowVideo("bandage_demo.mp4");
}
}
+32 -3
View File
@@ -1,5 +1,6 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
public class UIManager : MonoBehaviour
{
@@ -179,12 +180,40 @@ public class UIManager : MonoBehaviour
if (_videoPanel != null)
{
_videoPanel.SetActive(true);
var vp = _videoPanel.GetComponentInChildren<UnityEngine.Video.VideoPlayer>(true);
if (vp != null) vp.Play();
Debug.Log("UIManager: 显示并播放 VideoPanel");
var controller = _videoPanel.GetComponent<VideoController>();
if (controller != null)
{
controller.PlayVideo(fileName);
return;
}
var vp = _videoPanel.GetComponentInChildren<VideoPlayer>(true);
if (vp != null)
{
ConfigureStreamingVideo(vp, fileName);
vp.Play();
}
Debug.Log("UIManager: 显示并播放 VideoPanel: " + fileName);
}
}
private void ConfigureStreamingVideo(VideoPlayer vp, string fileName)
{
vp.source = VideoSource.Url;
vp.url = BuildStreamingVideoUrl(fileName);
vp.playOnAwake = false;
vp.isLooping = true;
vp.renderMode = VideoRenderMode.RenderTexture;
vp.audioOutputMode = VideoAudioOutputMode.Direct;
}
private string BuildStreamingVideoUrl(string fileName)
{
string safeName = string.IsNullOrEmpty(fileName) ? "bandage_demo.mp4" : fileName;
string baseUrl = Application.streamingAssetsPath.TrimEnd('/');
return baseUrl + "/Videos/" + safeName;
}
public void HideVideo()
{
AutoFindPanels();
+6 -1
View File
@@ -25,7 +25,10 @@ public class VideoController : MonoBehaviour
if (_videoPlayer != null)
{
_videoPlayer.source = VideoSource.Url;
_videoPlayer.playOnAwake = false;
_videoPlayer.isLooping = true;
_videoPlayer.audioOutputMode = VideoAudioOutputMode.Direct;
_videoPlayer.loopPointReached += OnVideoEnd;
}
@@ -38,7 +41,9 @@ public class VideoController : MonoBehaviour
{
if (_videoPlayer == null) return;
string path = System.IO.Path.Combine(Application.streamingAssetsPath, "Videos", fileName);
string safeName = string.IsNullOrEmpty(fileName) ? "bandage_demo.mp4" : fileName;
string path = Application.streamingAssetsPath.TrimEnd('/') + "/Videos/" + safeName;
_videoPlayer.source = VideoSource.Url;
_videoPlayer.url = path;
// 创建 RenderTexture
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c91be64b792e4f4c9bb88ad3a017138
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: da187d03eaa6a4896b3620405107f4b3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c9a030881ca8d442aacfbce873a2577c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
+27
View File
@@ -1,6 +1,33 @@
const cacheName = "DefaultCompany-运动伤害现场急救虚拟仿真实验-0.1.0";
const contentToCache = [
"Build/7a2dbded24d57e056180125b1583e7c4.loader.js",
"Build/62981e01aece13a94bac539c85228c75.framework.js.gz",
"Build/c1ac39c2807bce9c7f9071edf08b1ab0.data.gz",
"Build/e89ebbd31642308ad16cecffa889584e.wasm.gz",
"TemplateData/style.css"
];
self.addEventListener('install', function (e) {
console.log('[Service Worker] Install');
e.waitUntil((async function () {
const cache = await caches.open(cacheName);
console.log('[Service Worker] Caching all: app shell and content');
await cache.addAll(contentToCache);
})());
});
self.addEventListener('fetch', function (e) {
e.respondWith((async function () {
let response = await caches.match(e.request);
console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
if (response) { return response; }
response = await fetch(e.request);
const cache = await caches.open(cacheName);
console.log(`[Service Worker] Caching new resource: ${e.request.url}`);
cache.put(e.request, response.clone());
return response;
})());
});
Binary file not shown.
+4 -4
View File
@@ -57,11 +57,11 @@
}
var buildUrl = "Build";
var loaderUrl = buildUrl + "/Build.loader.js";
var loaderUrl = buildUrl + "/7a2dbded24d57e056180125b1583e7c4.loader.js";
var config = {
dataUrl: buildUrl + "/Build.data.unityweb",
frameworkUrl: buildUrl + "/Build.framework.js.unityweb",
codeUrl: buildUrl + "/Build.wasm.unityweb",
dataUrl: buildUrl + "/c1ac39c2807bce9c7f9071edf08b1ab0.data.gz",
frameworkUrl: buildUrl + "/62981e01aece13a94bac539c85228c75.framework.js.gz",
codeUrl: buildUrl + "/e89ebbd31642308ad16cecffa889584e.wasm.gz",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "运动伤害现场急救虚拟仿真实验",
+98 -12
View File
@@ -17,23 +17,36 @@ const MIME = {
'.svg': 'image/svg+xml',
'.json': 'application/json',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.mp4': 'video/mp4',
'.m4v': 'video/mp4',
'.webm': 'video/webm',
'.ogg': 'video/ogg',
};
function extType(p) {
return MIME[path.extname(p).toLowerCase()] || 'application/octet-stream';
}
function headersForFile(fp, data) {
function originalCompressedPath(fp) {
if (fp.endsWith('.unityweb')) return fp.slice(0, -'.unityweb'.length);
if (fp.endsWith('.gz')) return fp.slice(0, -'.gz'.length);
if (fp.endsWith('.br')) return fp.slice(0, -'.br'.length);
return fp;
}
function headersForFile(fp, size, encoding) {
const headers = {
'Content-Type': extType(fp),
'Content-Length': data.length,
'Content-Type': extType(originalCompressedPath(fp)),
'Content-Length': size,
'X-Content-Type-Options': 'nosniff',
'Accept-Ranges': 'bytes',
};
if (fp.endsWith('.unityweb')) {
const originalPath = fp.slice(0, -'.unityweb'.length);
headers['Content-Type'] = extType(originalPath);
headers['Content-Encoding'] = data[0] === 0x1f && data[1] === 0x8b ? 'gzip' : 'br';
if (encoding != null) {
headers['Content-Encoding'] = encoding;
headers['Cache-Control'] = 'public, max-age=604800';
} else if (/\.(mp4|m4v|webm|ogg)$/i.test(fp)) {
headers['Cache-Control'] = 'public, max-age=604800';
} else if (fp.endsWith('.loader.js')) {
headers['Cache-Control'] = 'no-cache';
@@ -44,6 +57,33 @@ function headersForFile(fp, data) {
return headers;
}
function detectUnityEncoding(fp, callback) {
if (fp.endsWith('.gz')) {
callback(null, 'gzip');
return;
}
if (fp.endsWith('.br')) {
callback(null, 'br');
return;
}
if (!fp.endsWith('.unityweb')) {
callback(null, null);
return;
}
fs.open(fp, 'r', (openErr, fd) => {
if (openErr) { callback(openErr); return; }
const buf = Buffer.alloc(2);
fs.read(fd, buf, 0, 2, 0, (readErr) => {
fs.close(fd, () => {});
if (readErr) { callback(readErr); return; }
callback(null, buf[0] === 0x1f && buf[1] === 0x8b ? 'gzip' : 'br');
});
});
}
// 首页:基于原始 index.html,注入启动封面(不修改任何 Unity 加载逻辑)
function serveIndex(res) {
try {
@@ -160,12 +200,58 @@ function serveIndex(res) {
// 静态文件
function serveStatic(req, res) {
let fp = path.join(ROOT, req.url.split('?')[0]);
let requestPath;
try {
requestPath = decodeURIComponent(req.url.split('?')[0]);
} catch (err) {
res.writeHead(400);
res.end('Bad Request');
return;
}
let fp = path.join(ROOT, requestPath);
if (!fp.startsWith(ROOT)) { res.writeHead(403); res.end('Forbidden'); return; }
fs.readFile(fp, (err, data) => {
if (err) { res.writeHead(404); res.end('Not Found'); return; }
res.writeHead(200, headersForFile(fp, data));
res.end(data);
fs.stat(fp, (err, stat) => {
if (err || !stat.isFile()) { res.writeHead(404); res.end('Not Found'); return; }
detectUnityEncoding(fp, (encodingErr, encoding) => {
if (encodingErr) { res.writeHead(500); res.end('Internal Server Error'); return; }
const range = req.headers.range;
if (range) {
const match = /^bytes=(\d*)-(\d*)$/.exec(range);
if (!match) {
res.writeHead(416, { 'Content-Range': `bytes */${stat.size}` });
res.end();
return;
}
let start;
let end;
if (match[1] === '' && match[2] !== '') {
const suffixLength = parseInt(match[2], 10);
start = Math.max(stat.size - suffixLength, 0);
end = stat.size - 1;
} else {
start = match[1] === '' ? 0 : parseInt(match[1], 10);
end = match[2] === '' ? stat.size - 1 : parseInt(match[2], 10);
}
if (Number.isNaN(start) || Number.isNaN(end) || start > end || end >= stat.size) {
res.writeHead(416, { 'Content-Range': `bytes */${stat.size}` });
res.end();
return;
}
const headers = headersForFile(fp, end - start + 1, encoding);
headers['Content-Range'] = `bytes ${start}-${end}/${stat.size}`;
res.writeHead(206, headers);
fs.createReadStream(fp, { start, end }).pipe(res);
return;
}
res.writeHead(200, headersForFile(fp, stat.size, encoding));
fs.createReadStream(fp).pipe(res);
});
});
}
+6 -6
View File
@@ -790,21 +790,21 @@ PlayerSettings:
blurSplashScreenBackground: 1
spritePackerPolicy:
webGLMemorySize: 32
webGLExceptionSupport: 1
webGLNameFilesAsHashes: 0
webGLExceptionSupport: 0
webGLNameFilesAsHashes: 1
webGLShowDiagnostics: 0
webGLDataCaching: 0
webGLDataCaching: 1
webGLDebugSymbols: 0
webGLEmscriptenArgs:
webGLModulesDirectory:
webGLTemplate: APPLICATION:PWA
webGLAnalyzeBuildSize: 0
webGLAnalyzeBuildSize: 1
webGLUseEmbeddedResources: 0
webGLCompressionFormat: 1
webGLWasmArithmeticExceptions: 0
webGLLinkerTarget: 1
webGLThreadsSupport: 0
webGLDecompressionFallback: 1
webGLDecompressionFallback: 0
webGLInitialMemorySize: 32
webGLMaximumMemorySize: 2048
webGLMemoryGrowthMode: 2
@@ -828,7 +828,7 @@ PlayerSettings:
QNX: 1
Stadia: 1
VisionOS: 1
WebGL: 1
WebGL: 2
Windows Store Apps: 1
XboxOne: 1
iPhone: 1