浮动小游戏已经基本搞好
新做了两个设置界面 一些bug修复和优化
This commit is contained in:
@@ -3,6 +3,9 @@ using System.IO;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
@@ -29,6 +32,30 @@ public class WebViewLauncher : MonoBehaviour
|
||||
// Event invoked when web process main window handle becomes available
|
||||
public event Action WebViewReady;
|
||||
|
||||
[Header("Control Buttons (optional)")]
|
||||
public Button refreshButton; // 刷新当前webview页面
|
||||
public Button closeButton; // 关闭webview
|
||||
public Button restartButton; // 重启webview
|
||||
public Button backButton; // 返回
|
||||
public Button forwardButton; // 前进
|
||||
public Button startRestartButton; // 新增:根据运行状态启动或重启 webview2
|
||||
public Button forceKillButton; // 强制结束进程按钮
|
||||
public Button hideWindowButton; // 隐藏webview窗口
|
||||
public Button showWindowButton; // 显示webview窗口
|
||||
|
||||
[Header("Autostart")]
|
||||
public UnityEngine.UI.Toggle autostartToggle; // UI toggle to control auto-start
|
||||
private bool autostart = true;
|
||||
private const string PREF_AUTOSTART = "WebView_AutoStart";
|
||||
|
||||
[Header("Homepage / URL UI")]
|
||||
public Button homepageButton; // 返回主页
|
||||
public InputField urlInputField; // 显示/输入当前 url
|
||||
[Tooltip("Homepage URL (can be file:// or http(s) or local path)")]
|
||||
public string homepageUrl = "";
|
||||
[Header("Go button")]
|
||||
public Button goButton; // 点击跳转,和回车效果一致
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null)
|
||||
@@ -38,11 +65,52 @@ public class WebViewLauncher : MonoBehaviour
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
// wire up optional UI buttons
|
||||
if (refreshButton != null) refreshButton.onClick.AddListener(RefreshWebView);
|
||||
if (closeButton != null) closeButton.onClick.AddListener(CloseWebView);
|
||||
if (restartButton != null) restartButton.onClick.AddListener(RestartWebView);
|
||||
if (backButton != null) backButton.onClick.AddListener(GoBackWebView);
|
||||
if (forwardButton != null) forwardButton.onClick.AddListener(GoForwardWebView);
|
||||
if (startRestartButton != null) startRestartButton.onClick.AddListener(OnStartRestartButtonClicked);
|
||||
if (forceKillButton != null) forceKillButton.onClick.AddListener(OnForceKillClicked);
|
||||
if (hideWindowButton != null) hideWindowButton.onClick.AddListener(OnHideWindowClicked);
|
||||
if (showWindowButton != null) showWindowButton.onClick.AddListener(OnShowWindowClicked);
|
||||
|
||||
if (homepageButton != null) homepageButton.onClick.AddListener(OnHomepageClicked);
|
||||
if (urlInputField != null) urlInputField.onEndEdit.AddListener(OnUrlInputEndEdit);
|
||||
if (goButton != null) goButton.onClick.AddListener(OnGoButtonClicked);
|
||||
|
||||
// Load autostart preference and bind toggle
|
||||
autostart = PlayerPrefs.GetInt(PREF_AUTOSTART, 1) == 1;
|
||||
if (autostartToggle != null)
|
||||
{
|
||||
autostartToggle.isOn = autostart;
|
||||
autostartToggle.onValueChanged.AddListener(OnAutostartToggleChanged);
|
||||
}
|
||||
|
||||
// set default homepage if empty
|
||||
if (string.IsNullOrEmpty(homepageUrl))
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
string candidate = Path.Combine(h5EditorPath, "_officialDocs", "openings", "index.html");
|
||||
if (File.Exists(candidate))
|
||||
homepageUrl = new Uri(candidate).AbsoluteUri;
|
||||
else
|
||||
homepageUrl = startUrl;
|
||||
#else
|
||||
string candidate = Path.Combine(Application.streamingAssetsPath, "h5LG", "_officialDocs", "openings", "index.html");
|
||||
if (File.Exists(candidate))
|
||||
homepageUrl = new Uri(candidate).AbsoluteUri;
|
||||
else
|
||||
homepageUrl = startUrl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// 扫描 H5 文件夹
|
||||
// Ensure H5 root exists
|
||||
string h5Folder = GetH5FolderPath();
|
||||
if (!Directory.Exists(h5Folder))
|
||||
{
|
||||
@@ -50,12 +118,28 @@ public class WebViewLauncher : MonoBehaviour
|
||||
Debug.Log("Created H5 folder: " + h5Folder);
|
||||
}
|
||||
|
||||
// Prefer explicit openings index as startup page when available
|
||||
string preferred = Path.Combine(h5Folder, "_officialDocs", "openings", "index.html");
|
||||
if (File.Exists(preferred))
|
||||
{
|
||||
startUrl = new Uri(preferred).AbsoluteUri;
|
||||
Debug.Log("Using preferred startup page: " + startUrl);
|
||||
// show initial url in input field
|
||||
if (urlInputField != null)
|
||||
urlInputField.text = GetDisplayUrl(startUrl);
|
||||
|
||||
if (autostart) LaunchWebView();
|
||||
return;
|
||||
}
|
||||
|
||||
// 扫描 H5 文件夹
|
||||
var h5SubFolders = ScanH5Folders(h5Folder);
|
||||
if (h5SubFolders.Count > 0)
|
||||
{
|
||||
Debug.Log("Found H5 folders: " + string.Join(", ", h5SubFolders));
|
||||
string selectedFolder = h5SubFolders[0]; // 选择第一个
|
||||
startUrl = "file://" + Path.Combine(h5Folder, selectedFolder, "index.html");
|
||||
string candidatePath = Path.Combine(h5Folder, selectedFolder, "index.html");
|
||||
startUrl = new Uri(candidatePath).AbsoluteUri; // ensure proper file:/// URI
|
||||
Debug.Log("Selected H5 folder: " + selectedFolder + ", URL: " + startUrl);
|
||||
}
|
||||
else
|
||||
@@ -63,19 +147,87 @@ public class WebViewLauncher : MonoBehaviour
|
||||
Debug.LogWarning("No H5 subfolders with index.html found in: " + h5Folder);
|
||||
}
|
||||
|
||||
LaunchWebView();
|
||||
// show initial url in input field if available (use shortened display for local files that are indexed)
|
||||
if (urlInputField != null)
|
||||
{
|
||||
urlInputField.text = GetDisplayUrl(startUrl);
|
||||
}
|
||||
|
||||
if (autostart) LaunchWebView();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
KillWebView();
|
||||
|
||||
// remove listeners
|
||||
if (refreshButton != null) refreshButton.onClick.RemoveListener(RefreshWebView);
|
||||
if (closeButton != null) closeButton.onClick.RemoveListener(CloseWebView);
|
||||
if (restartButton != null) restartButton.onClick.RemoveListener(RestartWebView);
|
||||
if (backButton != null) backButton.onClick.RemoveListener(GoBackWebView);
|
||||
if (forwardButton != null) forwardButton.onClick.RemoveListener(GoForwardWebView);
|
||||
if (startRestartButton != null) startRestartButton.onClick.RemoveListener(OnStartRestartButtonClicked);
|
||||
if (forceKillButton != null) forceKillButton.onClick.RemoveListener(OnForceKillClicked);
|
||||
if (hideWindowButton != null) hideWindowButton.onClick.RemoveListener(OnHideWindowClicked);
|
||||
if (showWindowButton != null) showWindowButton.onClick.RemoveListener(OnShowWindowClicked);
|
||||
if (autostartToggle != null) autostartToggle.onValueChanged.RemoveListener(OnAutostartToggleChanged);
|
||||
|
||||
if (homepageButton != null) homepageButton.onClick.RemoveListener(OnHomepageClicked);
|
||||
if (urlInputField != null) urlInputField.onEndEdit.RemoveListener(OnUrlInputEndEdit);
|
||||
if (goButton != null) goButton.onClick.RemoveListener(OnGoButtonClicked);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// if input field focused and Enter pressed -> navigate
|
||||
if (urlInputField != null && urlInputField.isFocused && (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter)))
|
||||
{
|
||||
string text = urlInputField.text;
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
OpenUrl(text);
|
||||
// remove focus
|
||||
EventSystem.current?.SetSelectedGameObject(null);
|
||||
urlInputField.DeactivateInputField();
|
||||
}
|
||||
}
|
||||
|
||||
// clicking elsewhere should unfocus input field (end edit)
|
||||
if (Input.GetMouseButtonDown(0) && urlInputField != null && urlInputField.isFocused)
|
||||
{
|
||||
var sel = EventSystem.current != null ? EventSystem.current.currentSelectedGameObject : null;
|
||||
if (sel != urlInputField.gameObject)
|
||||
{
|
||||
urlInputField.DeactivateInputField();
|
||||
EventSystem.current?.SetSelectedGameObject(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnGoButtonClicked()
|
||||
{
|
||||
if (urlInputField == null) return;
|
||||
string text = urlInputField.text;
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
OpenUrl(text);
|
||||
EventSystem.current?.SetSelectedGameObject(null);
|
||||
urlInputField.DeactivateInputField();
|
||||
}
|
||||
}
|
||||
|
||||
public void KillWebView()
|
||||
{
|
||||
if (webProcess != null && !webProcess.HasExited)
|
||||
{
|
||||
webProcess.Kill();
|
||||
try
|
||||
{
|
||||
webProcess.Kill();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("KillWebView failed: " + e.Message);
|
||||
}
|
||||
webProcess = null;
|
||||
}
|
||||
}
|
||||
@@ -169,10 +321,24 @@ public class WebViewLauncher : MonoBehaviour
|
||||
string final;
|
||||
try
|
||||
{
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out var parsed) && !string.IsNullOrEmpty(parsed.Scheme))
|
||||
if (IsLikelyWebUrl(url))
|
||||
{
|
||||
// If it's already a file/http/https URI, use its AbsoluteUri
|
||||
final = parsed.AbsoluteUri;
|
||||
if (!Uri.TryCreate(url, UriKind.Absolute, out var parsed) || string.IsNullOrEmpty(parsed.Scheme))
|
||||
{
|
||||
final = "http://" + url;
|
||||
}
|
||||
else
|
||||
{
|
||||
final = parsed.AbsoluteUri;
|
||||
}
|
||||
}
|
||||
else if (url.StartsWith("localhost::"))
|
||||
{
|
||||
// Map localhost::display back to actual file URI
|
||||
string relativePath = url.Substring("localhost::".Length).Replace('/', Path.DirectorySeparatorChar);
|
||||
string h5Folder = GetH5FolderPath();
|
||||
string fullPath = Path.Combine(h5Folder, relativePath);
|
||||
final = new Uri(fullPath).AbsoluteUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -190,6 +356,10 @@ public class WebViewLauncher : MonoBehaviour
|
||||
Debug.Log($"OpenUrl requested. original='{url}', normalized='{final}'");
|
||||
startUrl = final;
|
||||
|
||||
// update input field display
|
||||
if (urlInputField != null)
|
||||
urlInputField.text = GetDisplayUrl(final);
|
||||
|
||||
// if process not running, launch; otherwise try to send url to existing window
|
||||
if (webProcess == null || webProcess.HasExited)
|
||||
{
|
||||
@@ -222,6 +392,160 @@ public class WebViewLauncher : MonoBehaviour
|
||||
// after relaunch, WaitForMainWindowHandle will invoke WebViewReady when handle ready
|
||||
}
|
||||
|
||||
// Try to send a simple command string to the webview process via WM_COPYDATA. Returns true when SendMessage returned non-zero.
|
||||
bool TrySendCommand(string cmd)
|
||||
{
|
||||
if (webProcess == null || webProcess.HasExited)
|
||||
{
|
||||
Debug.LogWarning("TrySendCommand: webProcess not running");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sent = WebViewWin32.TrySendUrlToWindow(webProcess, cmd);
|
||||
Debug.Log($"TrySendCommand('{cmd}') returned: {sent}");
|
||||
return sent;
|
||||
}
|
||||
|
||||
// UI control methods
|
||||
public void RefreshWebView()
|
||||
{
|
||||
// Try sending a refresh command; fallback to restarting to refresh
|
||||
if (TrySendCommand("CMD:REFRESH"))
|
||||
{
|
||||
Debug.Log("Sent REFRESH command to webview");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("REFRESH command not supported, restarting webview to refresh");
|
||||
KillWebView();
|
||||
LaunchWebView();
|
||||
}
|
||||
|
||||
public void CloseWebView()
|
||||
{
|
||||
Debug.Log("Closing webview process");
|
||||
KillWebView();
|
||||
}
|
||||
|
||||
public void RestartWebView()
|
||||
{
|
||||
Debug.Log("Restarting webview process");
|
||||
KillWebView();
|
||||
LaunchWebView();
|
||||
}
|
||||
|
||||
public void GoBackWebView()
|
||||
{
|
||||
if (TrySendCommand("CMD:BACK"))
|
||||
{
|
||||
Debug.Log("Sent BACK command to webview");
|
||||
return;
|
||||
}
|
||||
Debug.LogWarning("BACK command not supported by webview");
|
||||
}
|
||||
|
||||
public void GoForwardWebView()
|
||||
{
|
||||
if (TrySendCommand("CMD:FORWARD"))
|
||||
{
|
||||
Debug.Log("Sent FORWARD command to webview");
|
||||
return;
|
||||
}
|
||||
Debug.LogWarning("FORWARD command not supported by webview");
|
||||
}
|
||||
|
||||
void OnHomepageClicked()
|
||||
{
|
||||
// Always navigate to StreamingAssets/h5LG/_officialDocs/openings/index.html
|
||||
string candidate = Path.Combine(Application.streamingAssetsPath, "h5LG", "_officialDocs", "openings", "index.html");
|
||||
if (!File.Exists(candidate))
|
||||
{
|
||||
Debug.LogWarning("Homepage index not found at: " + candidate + ". Falling back to homepageUrl or startUrl.");
|
||||
if (!string.IsNullOrEmpty(homepageUrl))
|
||||
{
|
||||
LaunchWebViewWithUrl(homepageUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
LaunchWebViewWithUrl(startUrl);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
string fileUri = new Uri(candidate).AbsoluteUri;
|
||||
LaunchWebViewWithUrl(fileUri);
|
||||
|
||||
// OpenUrl will update the input field using GetDisplayUrl(final). Ensure display shows the shortened localhost form.
|
||||
if (urlInputField != null)
|
||||
urlInputField.text = GetDisplayUrl(fileUri);
|
||||
}
|
||||
|
||||
void OnUrlInputEndEdit(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return;
|
||||
OpenUrl(text);
|
||||
}
|
||||
|
||||
void OnStartRestartButtonClicked()
|
||||
{
|
||||
if (webProcess == null || webProcess.HasExited)
|
||||
{
|
||||
Debug.Log("WebView process not running. Starting webview.");
|
||||
StartWebView();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("WebView process running. Restarting webview.");
|
||||
RestartWebView();
|
||||
}
|
||||
}
|
||||
|
||||
void OnForceKillClicked()
|
||||
{
|
||||
Debug.Log("Force killing webview process");
|
||||
KillWebView();
|
||||
}
|
||||
|
||||
void OnHideWindowClicked()
|
||||
{
|
||||
if (webProcess == null || webProcess.HasExited)
|
||||
{
|
||||
Debug.LogWarning("HideWindow: webview process not running");
|
||||
return;
|
||||
}
|
||||
bool ok = WebViewWin32.TryShowWindow(webProcess, false);
|
||||
Debug.Log("HideWindow clicked, result: " + ok);
|
||||
}
|
||||
|
||||
void OnShowWindowClicked()
|
||||
{
|
||||
if (webProcess == null || webProcess.HasExited)
|
||||
{
|
||||
Debug.LogWarning("ShowWindow: webview process not running");
|
||||
return;
|
||||
}
|
||||
bool ok = WebViewWin32.TryShowWindow(webProcess, true);
|
||||
Debug.Log("ShowWindow clicked, result: " + ok);
|
||||
}
|
||||
|
||||
public void StartWebView()
|
||||
{
|
||||
LaunchWebView();
|
||||
}
|
||||
|
||||
public void StopWebView()
|
||||
{
|
||||
KillWebView();
|
||||
}
|
||||
|
||||
void OnAutostartToggleChanged(bool value)
|
||||
{
|
||||
autostart = value;
|
||||
PlayerPrefs.SetInt(PREF_AUTOSTART, autostart ? 1 : 0);
|
||||
PlayerPrefs.Save();
|
||||
Debug.Log("Autostart toggled: " + autostart);
|
||||
}
|
||||
|
||||
string GetExePath()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
@@ -298,4 +622,113 @@ public class WebViewLauncher : MonoBehaviour
|
||||
|
||||
return WebViewWin32.TrySetWindowRect(webProcess, x, y, w, h);
|
||||
}
|
||||
|
||||
string GetDisplayUrl(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
return url;
|
||||
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(url);
|
||||
if (uri.Scheme == Uri.UriSchemeFile)
|
||||
{
|
||||
string localPath = uri.LocalPath;
|
||||
string h5Folder = GetH5FolderPath();
|
||||
if (localPath.StartsWith(h5Folder, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string relativePath = localPath.Substring(h5Folder.Length).TrimStart(Path.DirectorySeparatorChar);
|
||||
return $"localhost::{relativePath.Replace(Path.DirectorySeparatorChar, '/')}";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("GetDisplayUrl: failed to parse url '" + url + "' : " + e.Message);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
bool IsLikelyWebUrl(string url)
|
||||
{
|
||||
return Regex.IsMatch(url, @"^(https?:\/\/|www\.|[a-zA-Z0-9-]+\.[a-zA-Z]{2,})");
|
||||
}
|
||||
|
||||
string NormalizeToAbsolute(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
Debug.LogWarning("NormalizeToAbsolute called with empty url");
|
||||
return url;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out var parsed) && parsed.IsAbsoluteUri)
|
||||
{
|
||||
return parsed.AbsoluteUri;
|
||||
}
|
||||
|
||||
if (url.StartsWith("localhost::"))
|
||||
{
|
||||
// Map localhost::display back to actual file URI
|
||||
string relativePath = url.Substring("localhost::".Length).Replace('/', Path.DirectorySeparatorChar);
|
||||
string h5Folder = GetH5FolderPath();
|
||||
string fullPath = Path.Combine(h5Folder, relativePath);
|
||||
return new Uri(fullPath).AbsoluteUri;
|
||||
}
|
||||
|
||||
// Treat as local file path
|
||||
string abs = Path.GetFullPath(url);
|
||||
return new Uri(abs).AbsoluteUri; // yields file:///C:/...
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("NormalizeToAbsolute: failed to normalize url '" + url + "' : " + e.Message);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchWebViewWithUrl(string url)
|
||||
{
|
||||
string finalUrl = NormalizeToAbsolute(url);
|
||||
Debug.Log($"LaunchWebViewWithUrl requested. original='{url}', normalized='{finalUrl}'");
|
||||
startUrl = finalUrl;
|
||||
|
||||
// update input field display
|
||||
if (urlInputField != null)
|
||||
urlInputField.text = GetDisplayUrl(finalUrl);
|
||||
|
||||
// if process not running, launch; otherwise try to send url to existing window
|
||||
if (webProcess == null || webProcess.HasExited)
|
||||
{
|
||||
Debug.Log("WebView process not running. Launching with URL: " + finalUrl);
|
||||
LaunchWebView();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Log($"WebView process running. PID={webProcess.Id}, MainWindowHandle=0x{webProcess.MainWindowHandle.ToInt64():X}");
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 尝试通过 WM_COPYDATA 将 URL 发送给已有进程窗口(需要 fdBrowser 实现接收处理)
|
||||
bool sent = WebViewWin32.TrySendUrlToWindow(webProcess, finalUrl);
|
||||
Debug.Log($"TrySendUrlToWindow returned: {sent}");
|
||||
if (sent)
|
||||
{
|
||||
Debug.Log("Sent URL to existing WebView process: " + finalUrl);
|
||||
// Notify followers that webview content may have changed and they should reapply positioning
|
||||
WebViewReady?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送失败,退回到重启进程打开 URL 的方式
|
||||
Debug.Log("Failed to send URL to existing process, restarting to open: " + finalUrl);
|
||||
KillWebView();
|
||||
LaunchWebView();
|
||||
// after relaunch, WaitForMainWindowHandle will invoke WebViewReady when handle ready
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user