Optimize WebGL build and video loading
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c91be64b792e4f4c9bb88ad3a017138
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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:
|
||||
Binary file not shown.
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.
BIN
Binary file not shown.
Binary file not shown.
@@ -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
@@ -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
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user