加入了浮动小游戏功能和设置界面
运行时请手动修改运行库目录fdBrowser
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
public class WebViewLauncher : MonoBehaviour
|
||||
{
|
||||
public static WebViewLauncher Instance { get; private set; }
|
||||
|
||||
[Header("Web Settings")]
|
||||
public string startUrl = "https://www.baidu.com";
|
||||
public Vector2 windowSize = new Vector2(960, 600);
|
||||
public Vector2 windowPosition = new Vector2(100, 100);
|
||||
|
||||
[Header("Editor Only Path")]
|
||||
public string editorExeRoot = @"E:\Unity\ban_total\fdBrowser";
|
||||
|
||||
[Header("H5 Folder Paths")]
|
||||
public string h5EditorPath = @"E:\Unity\ban_total\ban_test\Bansonic_Data\StreamingAssets\h5LG";
|
||||
public string officialDocsPath = "_officialDocs";
|
||||
public string officialGamesPath = "_officialGames";
|
||||
public string thirdPartyGamesPath = "_thirdPartyGames";
|
||||
|
||||
private Process webProcess;
|
||||
|
||||
// Event invoked when web process main window handle becomes available
|
||||
public event Action WebViewReady;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// 扫描 H5 文件夹
|
||||
string h5Folder = GetH5FolderPath();
|
||||
if (!Directory.Exists(h5Folder))
|
||||
{
|
||||
Directory.CreateDirectory(h5Folder);
|
||||
Debug.Log("Created H5 folder: " + h5Folder);
|
||||
}
|
||||
|
||||
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");
|
||||
Debug.Log("Selected H5 folder: " + selectedFolder + ", URL: " + startUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("No H5 subfolders with index.html found in: " + h5Folder);
|
||||
}
|
||||
|
||||
LaunchWebView();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
KillWebView();
|
||||
}
|
||||
|
||||
public void KillWebView()
|
||||
{
|
||||
if (webProcess != null && !webProcess.HasExited)
|
||||
{
|
||||
webProcess.Kill();
|
||||
webProcess = null;
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchWebView()
|
||||
{
|
||||
string exePath = GetExePath();
|
||||
|
||||
Debug.Log("WebView exe path: " + exePath);
|
||||
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
Debug.LogError("fdBrowser.exe not found: " + exePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始参数可以给 0,后续由 PanelFollower 接管
|
||||
string args = $"0 0 800 600 \"{startUrl}\"";
|
||||
|
||||
Debug.Log($"Launching: {exePath} {args}");
|
||||
|
||||
ProcessStartInfo psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = exePath,
|
||||
Arguments = args,
|
||||
UseShellExecute = true, // 改为 true 以确保 exe 能启动
|
||||
WorkingDirectory = Path.GetDirectoryName(exePath)
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
webProcess = Process.Start(psi);
|
||||
Debug.Log("WebView2Host started successfully. Process ID: " + webProcess.Id);
|
||||
|
||||
// Start coroutine to wait until main window handle is ready
|
||||
StartCoroutine(WaitForMainWindowHandle());
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError("Failed to start WebView2Host: " + e.Message + "\nPath: " + exePath + "\nArgs: " + args);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator WaitForMainWindowHandle()
|
||||
{
|
||||
if (webProcess == null)
|
||||
yield break;
|
||||
|
||||
float timeout = 5f; // seconds
|
||||
float elapsed = 0f;
|
||||
while (elapsed < timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
webProcess.Refresh();
|
||||
if (webProcess.MainWindowHandle != IntPtr.Zero)
|
||||
{
|
||||
Debug.Log($"WebViewLauncher: MainWindowHandle ready: 0x{webProcess.MainWindowHandle.ToInt64():X}");
|
||||
WebViewReady?.Invoke();
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("WebViewLauncher.WaitForMainWindowHandle exception: " + e.Message);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
elapsed += Time.deltaTime;
|
||||
}
|
||||
|
||||
Debug.LogWarning("WebViewLauncher: timeout waiting for main window handle");
|
||||
// still invoke event so follower can attempt positioning later
|
||||
WebViewReady?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从外部请求打开一个 url。如果 webview 没有运行则启动;如果已运行则尝试通过 WM_COPYDATA 发送 url 给已有窗口,失败则重启。
|
||||
/// 传入的 url 建议使用本地文件路径或以 file:// 开头的路径。
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
public void OpenUrl(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
Debug.LogWarning("OpenUrl called with empty url");
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize to absolute file:// or keep as web URL
|
||||
string final;
|
||||
try
|
||||
{
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out var parsed) && !string.IsNullOrEmpty(parsed.Scheme))
|
||||
{
|
||||
// If it's already a file/http/https URI, use its AbsoluteUri
|
||||
final = parsed.AbsoluteUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treat as local file path
|
||||
string abs = Path.GetFullPath(url);
|
||||
final = new Uri(abs).AbsoluteUri; // yields file:///C:/...
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("OpenUrl: failed to normalize url '" + url + "' : " + e.Message);
|
||||
final = url;
|
||||
}
|
||||
|
||||
Debug.Log($"OpenUrl requested. original='{url}', normalized='{final}'");
|
||||
startUrl = final;
|
||||
|
||||
// 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: " + final);
|
||||
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, final);
|
||||
Debug.Log($"TrySendUrlToWindow returned: {sent}");
|
||||
if (sent)
|
||||
{
|
||||
Debug.Log("Sent URL to existing WebView process: " + final);
|
||||
// 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: " + final);
|
||||
KillWebView();
|
||||
LaunchWebView();
|
||||
// after relaunch, WaitForMainWindowHandle will invoke WebViewReady when handle ready
|
||||
}
|
||||
|
||||
string GetExePath()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return Path.Combine(editorExeRoot, "fdBrowser.exe");
|
||||
#else
|
||||
// 优先使用 StreamingAssets 路径
|
||||
string streamingPath = Path.Combine(Application.streamingAssetsPath, "fdBrowser", "fdBrowser.exe");
|
||||
if (File.Exists(streamingPath))
|
||||
{
|
||||
Debug.Log("Using StreamingAssets path: " + streamingPath);
|
||||
return streamingPath;
|
||||
}
|
||||
// 回退到游戏根目录
|
||||
string gameRoot = Path.GetDirectoryName(Application.dataPath);
|
||||
string fallbackPath = Path.Combine(gameRoot, "fdBrowser", "fdBrowser.exe");
|
||||
Debug.Log("Using fallback path: " + fallbackPath);
|
||||
return fallbackPath;
|
||||
#endif
|
||||
}
|
||||
|
||||
string GetH5FolderPath()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return h5EditorPath;
|
||||
#else
|
||||
return Path.Combine(Application.streamingAssetsPath, "h5LG");
|
||||
#endif
|
||||
}
|
||||
|
||||
System.Collections.Generic.List<string> ScanH5Folders(string folderPath)
|
||||
{
|
||||
var result = new System.Collections.Generic.List<string>();
|
||||
if (!Directory.Exists(folderPath))
|
||||
{
|
||||
Debug.LogWarning("H5 folder does not exist: " + folderPath);
|
||||
return result;
|
||||
}
|
||||
|
||||
Debug.Log("Scanning H5 folders in: " + folderPath);
|
||||
string[] targetFolders = { officialDocsPath, officialGamesPath, thirdPartyGamesPath };
|
||||
foreach (string folderName in targetFolders)
|
||||
{
|
||||
string subFolderPath = Path.Combine(folderPath, folderName);
|
||||
if (!Directory.Exists(subFolderPath))
|
||||
{
|
||||
Directory.CreateDirectory(subFolderPath);
|
||||
Debug.Log("Created subfolder: " + subFolderPath);
|
||||
}
|
||||
|
||||
string indexPath = Path.Combine(subFolderPath, "index.html");
|
||||
if (!File.Exists(indexPath))
|
||||
{
|
||||
File.WriteAllText(indexPath, "<html><body><h1>" + folderName + "</h1></body></html>");
|
||||
Debug.Log("Created index.html in: " + subFolderPath);
|
||||
}
|
||||
|
||||
if (File.Exists(indexPath))
|
||||
{
|
||||
result.Add(folderName);
|
||||
Debug.Log("Found H5 folder with index.html: " + folderName);
|
||||
}
|
||||
}
|
||||
Debug.Log("Total H5 folders found: " + result.Count);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 给 PanelFollower 调用
|
||||
/// </summary>
|
||||
public bool TryUpdateWebViewRect(int x, int y, int w, int h)
|
||||
{
|
||||
if (webProcess == null || webProcess.HasExited)
|
||||
return false;
|
||||
|
||||
return WebViewWin32.TrySetWindowRect(webProcess, x, y, w, h);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49cec9d0b1891a24597a6ddbd149774a
|
||||
@@ -0,0 +1,156 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class WebViewPanelFollower : MonoBehaviour
|
||||
{
|
||||
public RectTransform panelRect; // 拖拽要跟随的 UI Panel 的 RectTransform
|
||||
public Camera uiCamera; // UI 相机,通常是 Canvas 的 Event Camera
|
||||
|
||||
private Rect lastPanelRect;
|
||||
private WebViewWin32.RECT lastUnityWindowRect;
|
||||
private bool hasLastRect = false;
|
||||
|
||||
// track subscription
|
||||
private bool subscribedToWebViewReady = false;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
TrySubscribeWebViewReady();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (subscribedToWebViewReady && WebViewLauncher.Instance != null)
|
||||
{
|
||||
WebViewLauncher.Instance.WebViewReady -= OnWebViewReady;
|
||||
subscribedToWebViewReady = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TrySubscribeWebViewReady()
|
||||
{
|
||||
if (!subscribedToWebViewReady && WebViewLauncher.Instance != null)
|
||||
{
|
||||
WebViewLauncher.Instance.WebViewReady += OnWebViewReady;
|
||||
subscribedToWebViewReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Ensure subscription in case WebViewLauncher wasn't ready earlier
|
||||
TrySubscribeWebViewReady();
|
||||
|
||||
if (panelRect == null || uiCamera == null || WebViewLauncher.Instance == null)
|
||||
return;
|
||||
|
||||
// 获取当前 panel 的屏幕矩形
|
||||
Rect currentPanelRect = GetPanelScreenRect(panelRect);
|
||||
|
||||
// 获取当前 Unity 窗口位置(仅在 build 中需要)
|
||||
WebViewWin32.RECT currentUnityWindowRect = new WebViewWin32.RECT();
|
||||
#if !UNITY_EDITOR
|
||||
if (!WebViewWin32.TryGetClientScreenRect(System.Diagnostics.Process.GetCurrentProcess(), out currentUnityWindowRect))
|
||||
return;
|
||||
#endif
|
||||
|
||||
// 检查是否需要更新
|
||||
if (!hasLastRect ||
|
||||
!ApproximatelyEqual(currentPanelRect, lastPanelRect)
|
||||
#if !UNITY_EDITOR
|
||||
|| !WindowRectsEqual(currentUnityWindowRect, lastUnityWindowRect)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int x, y;
|
||||
#if UNITY_EDITOR
|
||||
// 在编辑器中,直接使用屏幕坐标(相对于屏幕左上角)
|
||||
x = Mathf.RoundToInt(currentPanelRect.x);
|
||||
y = Mathf.RoundToInt(Screen.height - currentPanelRect.y - currentPanelRect.height);
|
||||
#else
|
||||
// 在 build 中,使用 Unity 窗口位置 + 相对坐标
|
||||
x = Mathf.RoundToInt(currentUnityWindowRect.Left + currentPanelRect.x);
|
||||
y = Mathf.RoundToInt(currentUnityWindowRect.Top + (Screen.height - currentPanelRect.y - currentPanelRect.height));
|
||||
#endif
|
||||
int w = Mathf.RoundToInt(currentPanelRect.width);
|
||||
int h = Mathf.RoundToInt(currentPanelRect.height);
|
||||
|
||||
// 更新 WebView 窗口
|
||||
bool success = WebViewLauncher.Instance.TryUpdateWebViewRect(x, y, w, h);
|
||||
if (success)
|
||||
{
|
||||
lastPanelRect = currentPanelRect;
|
||||
#if !UNITY_EDITOR
|
||||
lastUnityWindowRect = currentUnityWindowRect;
|
||||
#endif
|
||||
hasLastRect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnWebViewReady()
|
||||
{
|
||||
// Force next update to recalc and send position, and also perform immediate update
|
||||
hasLastRect = false;
|
||||
UpdatePositionNow();
|
||||
}
|
||||
|
||||
// perform immediate position update (same logic as in Update)
|
||||
void UpdatePositionNow()
|
||||
{
|
||||
if (panelRect == null || uiCamera == null || WebViewLauncher.Instance == null)
|
||||
return;
|
||||
|
||||
Rect currentPanelRect = GetPanelScreenRect(panelRect);
|
||||
|
||||
WebViewWin32.RECT currentUnityWindowRect = new WebViewWin32.RECT();
|
||||
#if !UNITY_EDITOR
|
||||
if (!WebViewWin32.TryGetClientScreenRect(System.Diagnostics.Process.GetCurrentProcess(), out currentUnityWindowRect))
|
||||
return;
|
||||
#endif
|
||||
|
||||
int x, y;
|
||||
#if UNITY_EDITOR
|
||||
x = Mathf.RoundToInt(currentPanelRect.x);
|
||||
y = Mathf.RoundToInt(Screen.height - currentPanelRect.y - currentPanelRect.height);
|
||||
#else
|
||||
x = Mathf.RoundToInt(currentUnityWindowRect.Left + currentPanelRect.x);
|
||||
y = Mathf.RoundToInt(currentUnityWindowRect.Top + (Screen.height - currentPanelRect.y - currentPanelRect.height));
|
||||
#endif
|
||||
int w = Mathf.RoundToInt(currentPanelRect.width);
|
||||
int h = Mathf.RoundToInt(currentPanelRect.height);
|
||||
|
||||
bool success = WebViewLauncher.Instance.TryUpdateWebViewRect(x, y, w, h);
|
||||
if (success)
|
||||
{
|
||||
lastPanelRect = currentPanelRect;
|
||||
#if !UNITY_EDITOR
|
||||
lastUnityWindowRect = currentUnityWindowRect;
|
||||
#endif
|
||||
hasLastRect = true;
|
||||
}
|
||||
}
|
||||
|
||||
Rect GetPanelScreenRect(RectTransform rect)
|
||||
{
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rect.GetWorldCorners(corners);
|
||||
|
||||
Vector2 min = RectTransformUtility.WorldToScreenPoint(uiCamera, corners[0]);
|
||||
Vector2 max = RectTransformUtility.WorldToScreenPoint(uiCamera, corners[2]);
|
||||
|
||||
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
|
||||
}
|
||||
|
||||
bool ApproximatelyEqual(Rect a, Rect b)
|
||||
{
|
||||
return Mathf.Approximately(a.x, b.x) &&
|
||||
Mathf.Approximately(a.y, b.y) &&
|
||||
Mathf.Approximately(a.width, b.width) &&
|
||||
Mathf.Approximately(a.height, b.height);
|
||||
}
|
||||
|
||||
bool WindowRectsEqual(WebViewWin32.RECT a, WebViewWin32.RECT b)
|
||||
{
|
||||
return a.Left == b.Left && a.Top == b.Top && a.Right == b.Right && a.Bottom == b.Bottom;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd1a1128616940247a10d6841baf0686
|
||||
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
public static class WebViewWin32
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool SetWindowPos(
|
||||
IntPtr hWnd,
|
||||
IntPtr hWndInsertAfter,
|
||||
int X, int Y, int cx, int cy,
|
||||
uint uFlags);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINT
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct COPYDATASTRUCT
|
||||
{
|
||||
public IntPtr dwData;
|
||||
public int cbData;
|
||||
public IntPtr lpData;
|
||||
}
|
||||
|
||||
const uint SWP_NOZORDER = 0x0004;
|
||||
const uint SWP_NOACTIVATE = 0x0010;
|
||||
const uint WM_COPYDATA = 0x004A;
|
||||
|
||||
public static bool TrySetWindowRect(Process process, int x, int y, int width, int height)
|
||||
{
|
||||
if (process == null || process.HasExited)
|
||||
return false;
|
||||
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
process.Refresh();
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
return false;
|
||||
}
|
||||
|
||||
SetWindowPos(
|
||||
process.MainWindowHandle,
|
||||
IntPtr.Zero,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
SWP_NOZORDER | SWP_NOACTIVATE
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetWindowRect(Process process, out RECT rect)
|
||||
{
|
||||
rect = new RECT();
|
||||
if (process == null || process.HasExited)
|
||||
return false;
|
||||
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
process.Refresh();
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetWindowRect(process.MainWindowHandle, out rect);
|
||||
}
|
||||
|
||||
public static bool TryGetClientScreenRect(Process process, out RECT rect)
|
||||
{
|
||||
rect = new RECT();
|
||||
if (process == null || process.HasExited)
|
||||
return false;
|
||||
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
process.Refresh();
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
return false;
|
||||
}
|
||||
|
||||
POINT point = new POINT { X = 0, Y = 0 };
|
||||
if (!ClientToScreen(process.MainWindowHandle, ref point))
|
||||
return false;
|
||||
|
||||
rect.Left = point.X;
|
||||
rect.Top = point.Y;
|
||||
rect.Right = point.X + UnityEngine.Screen.width;
|
||||
rect.Bottom = point.Y + UnityEngine.Screen.height;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试向指定进程的主窗口发送 WM_COPYDATA 消息,lParam 为包含 Unicode 字符串的 COPYDATASTRUCT。
|
||||
/// 接收方需要处理 WM_COPYDATA 并解释字符串为 URL 并导航。
|
||||
/// 返回 true 表示接收方可能已处理(基于 SendMessage 返回值),false 表示发送或接收未成功。
|
||||
/// </summary>
|
||||
public static bool TrySendUrlToWindow(Process process, string url)
|
||||
{
|
||||
if (process == null || process.HasExited || string.IsNullOrEmpty(url))
|
||||
return false;
|
||||
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
process.Refresh();
|
||||
if (process.MainWindowHandle == IntPtr.Zero)
|
||||
return false;
|
||||
}
|
||||
|
||||
IntPtr hWnd = process.MainWindowHandle;
|
||||
|
||||
IntPtr ptrString = IntPtr.Zero;
|
||||
IntPtr ptrCds = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
// Allocate Unicode string (null-terminated)
|
||||
ptrString = Marshal.StringToHGlobalUni(url);
|
||||
var cds = new COPYDATASTRUCT
|
||||
{
|
||||
dwData = IntPtr.Zero,
|
||||
cbData = (url.Length + 1) * 2,
|
||||
lpData = ptrString
|
||||
};
|
||||
|
||||
int size = Marshal.SizeOf(typeof(COPYDATASTRUCT));
|
||||
ptrCds = Marshal.AllocHGlobal(size);
|
||||
Marshal.StructureToPtr(cds, ptrCds, false);
|
||||
|
||||
IntPtr res = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ptrCds);
|
||||
long resVal = res.ToInt64();
|
||||
UnityEngine.Debug.Log($"WebViewWin32.TrySendUrlToWindow: Sent WM_COPYDATA to hWnd=0x{hWnd.ToInt64():X}, url={url}, SendMessage result={resVal}");
|
||||
|
||||
// Treat non-zero as handled/accepted by receiver. If receiver returns zero, consider not handled.
|
||||
return resVal != 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("WebViewWin32.TrySendUrlToWindow exception: " + e.Message);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ptrCds != IntPtr.Zero) Marshal.FreeHGlobal(ptrCds);
|
||||
if (ptrString != IntPtr.Zero) Marshal.FreeHGlobal(ptrString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6162b5a372404e246864c6f49105e618
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 170eecb5841893f439d955a3b5a94529
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,147 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using System;
|
||||
|
||||
public class game_card_Prefab : MonoBehaviour, IPointerClickHandler
|
||||
{
|
||||
[Header("Prefab基本信息")]
|
||||
public Image card_icon_image;
|
||||
public Text card_name_text;
|
||||
public Text authorName_text;
|
||||
public Text description_text;
|
||||
public Text _isOfficial_text;
|
||||
public Text source_text;
|
||||
public Text yyyy_mm_dd;
|
||||
|
||||
[Header("Optional clickable Button (auto-find if null)")]
|
||||
public Button clickButton;
|
||||
|
||||
// 存储对应 index.html 的本地路径(不含 file:// 前缀)
|
||||
private string indexFilePath;
|
||||
|
||||
// 给 loadLittleGamesPrefab 调用以填充文本和路径
|
||||
public void SetData(string indexPath, string cardName, string authorName, string description, string isOfficial, string source, string yyyyMmDd)
|
||||
{
|
||||
indexFilePath = indexPath;
|
||||
if (card_name_text != null) card_name_text.text = cardName ?? "";
|
||||
if (authorName_text != null) authorName_text.text = authorName ?? "";
|
||||
if (description_text != null) description_text.text = description ?? "";
|
||||
if (_isOfficial_text != null) _isOfficial_text.text = isOfficial ?? "";
|
||||
if (source_text != null) source_text.text = source ?? "";
|
||||
if (yyyy_mm_dd != null) yyyy_mm_dd.text = yyyyMmDd ?? "";
|
||||
|
||||
UnityEngine.Debug.Log($"game_card_Prefab.SetData: indexPath='{indexFilePath}', card_name='{cardName}', author='{authorName}', isOfficial='{isOfficial}', source='{source}'");
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// If no explicit Button assigned, try to find one on this GameObject or children
|
||||
if (clickButton == null)
|
||||
{
|
||||
clickButton = GetComponent<Button>();
|
||||
if (clickButton == null)
|
||||
clickButton = GetComponentInChildren<Button>(true);
|
||||
}
|
||||
|
||||
if (clickButton == null)
|
||||
{
|
||||
// Try to auto-add a Button to this GameObject so clicks are captured.
|
||||
// Ensure there's an Image component (Button requires Image for Raycast target)
|
||||
Image img = GetComponent<Image>();
|
||||
if (img == null)
|
||||
{
|
||||
img = gameObject.AddComponent<Image>();
|
||||
img.color = new Color(1, 1, 1, 0); // transparent
|
||||
}
|
||||
|
||||
clickButton = gameObject.AddComponent<Button>();
|
||||
UnityEngine.Debug.Log("game_card_Prefab.Awake: No Button found; added Image+Button to prefab at runtime to capture clicks.");
|
||||
}
|
||||
|
||||
// Check EventSystem presence
|
||||
if (EventSystem.current == null)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("game_card_Prefab.Awake: No EventSystem in scene. UI clicks and IPointerClickHandler won't work without an EventSystem.");
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (clickButton != null)
|
||||
{
|
||||
clickButton.onClick.AddListener(OnButtonClicked);
|
||||
UnityEngine.Debug.Log("game_card_Prefab: added onClick listener to Button");
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (clickButton != null)
|
||||
{
|
||||
clickButton.onClick.RemoveListener(OnButtonClicked);
|
||||
UnityEngine.Debug.Log("game_card_Prefab: removed onClick listener from Button");
|
||||
}
|
||||
}
|
||||
|
||||
void OnButtonClicked()
|
||||
{
|
||||
UnityEngine.Debug.Log($"game_card_Prefab.OnButtonClicked: indexPath='{indexFilePath}'");
|
||||
OpenIndexPath();
|
||||
}
|
||||
|
||||
// 点击时尝试通过 WebViewLauncher 打开对应的 index.html
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
UnityEngine.Debug.Log($"game_card_Prefab.OnPointerClick: indexPath='{indexFilePath}'");
|
||||
OpenIndexPath();
|
||||
}
|
||||
|
||||
void OpenIndexPath()
|
||||
{
|
||||
if (string.IsNullOrEmpty(indexFilePath))
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("game_card_Prefab clicked but index path is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// 优先使用 WebViewLauncher 单例
|
||||
if (WebViewLauncher.Instance != null)
|
||||
{
|
||||
UnityEngine.Debug.Log("Calling WebViewLauncher.OpenUrl with: " + indexFilePath);
|
||||
WebViewLauncher.Instance.OpenUrl(indexFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("WebViewLauncher instance not found. Attempting to start new launcher.");
|
||||
// 尝试查找场景中的 WebViewLauncher 或创建一个新的 GameObject
|
||||
var launcherObj = GameObject.FindObjectOfType<WebViewLauncher>();
|
||||
if (launcherObj != null)
|
||||
{
|
||||
UnityEngine.Debug.Log("Found launcher in scene, calling OpenUrl: " + indexFilePath);
|
||||
launcherObj.OpenUrl(indexFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a temporary launcher object to start webview
|
||||
UnityEngine.Debug.Log("Creating temporary WebViewLauncher and calling OpenUrl: " + indexFilePath);
|
||||
GameObject go = new GameObject("WebViewLauncher_Temp");
|
||||
var launcher = go.AddComponent<WebViewLauncher>();
|
||||
launcher.OpenUrl(indexFilePath);
|
||||
GameObject.DontDestroyOnLoad(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e862c8c48e4f8b49adae5e986da6e73
|
||||
@@ -0,0 +1,739 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &219047244547433570
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 872492842950718926}
|
||||
- component: {fileID: 7480212223569318349}
|
||||
- component: {fileID: 9193153990832272737}
|
||||
m_Layer: 5
|
||||
m_Name: author_name
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &872492842950718926
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 219047244547433570}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3566709735925945696}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 10.717, y: 14.07}
|
||||
m_SizeDelta: {x: 252.359, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7480212223569318349
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 219047244547433570}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &9193153990832272737
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 219047244547433570}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: cc180dff846d13a4d88ddaed6f77e5cd, type: 3}
|
||||
m_FontSize: 12
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 6
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: author_name
|
||||
--- !u!1 &964402434877421462
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 189438553718290088}
|
||||
- component: {fileID: 8767921256928017929}
|
||||
- component: {fileID: 5425439562977293950}
|
||||
m_Layer: 5
|
||||
m_Name: yyyyddmm
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &189438553718290088
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 964402434877421462}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3566709735925945696}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 171.06035, y: -15.7}
|
||||
m_SizeDelta: {x: 38.41, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8767921256928017929
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 964402434877421462}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5425439562977293950
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 964402434877421462}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: 775c674e81662c644b64550d2e8f74e0, type: 3}
|
||||
m_FontSize: 10
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 5
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: 'YYYY
|
||||
|
||||
MM
|
||||
|
||||
DD'
|
||||
--- !u!1 &2391360557458902149
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 604392950021451895}
|
||||
- component: {fileID: 5600536436187702497}
|
||||
- component: {fileID: 3389633104739560455}
|
||||
m_Layer: 5
|
||||
m_Name: card_name
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &604392950021451895
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2391360557458902149}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3566709735925945696}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 15.512863, y: 20.5}
|
||||
m_SizeDelta: {x: 262.884, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5600536436187702497
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2391360557458902149}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &3389633104739560455
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2391360557458902149}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: cc180dff846d13a4d88ddaed6f77e5cd, type: 3}
|
||||
m_FontSize: 20
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 2
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 3
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: CardNameHere
|
||||
--- !u!1 &3532408215758252597
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4583698199465520998}
|
||||
- component: {fileID: 301933785134177564}
|
||||
- component: {fileID: 6004099248657222439}
|
||||
m_Layer: 5
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4583698199465520998
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3532408215758252597}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3566709735925945696}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -160, y: 0}
|
||||
m_SizeDelta: {x: 50, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &301933785134177564
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3532408215758252597}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &6004099248657222439
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3532408215758252597}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 4997328413415617422, guid: a8ff5cf9bafe9f544bfde136473da3cc, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &5110857998175197173
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2622440068852620012}
|
||||
- component: {fileID: 82439723018948691}
|
||||
- component: {fileID: 5166008895911254744}
|
||||
m_Layer: 5
|
||||
m_Name: _isOfficial
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2622440068852620012
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5110857998175197173}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3566709735925945696}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 144.2521, y: 19.9}
|
||||
m_SizeDelta: {x: 94.529, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &82439723018948691
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5110857998175197173}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5166008895911254744
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5110857998175197173}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: cc180dff846d13a4d88ddaed6f77e5cd, type: 3}
|
||||
m_FontSize: 12
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 5
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: _isOfficial
|
||||
--- !u!1 &5331475481345189989
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3566709735925945696}
|
||||
- component: {fileID: 3041038222081474245}
|
||||
- component: {fileID: 6180074520896239301}
|
||||
- component: {fileID: 3634709248270434128}
|
||||
m_Layer: 5
|
||||
m_Name: botton
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &3566709735925945696
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5331475481345189989}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4583698199465520998}
|
||||
- {fileID: 604392950021451895}
|
||||
- {fileID: 872492842950718926}
|
||||
- {fileID: 2019306417107768352}
|
||||
- {fileID: 2622440068852620012}
|
||||
- {fileID: 2138782682665805939}
|
||||
- {fileID: 189438553718290088}
|
||||
m_Father: {fileID: 9116228060429372210}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 5.929, y: 0}
|
||||
m_SizeDelta: {x: 424.852, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3041038222081474245
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5331475481345189989}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &6180074520896239301
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5331475481345189989}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 7f3b1220317607e44bbb8f0e1998044a, type: 3}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &3634709248270434128
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5331475481345189989}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 6180074520896239301}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &8844525127622122013
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2019306417107768352}
|
||||
- component: {fileID: 6796448552036844055}
|
||||
- component: {fileID: 4780041332280513338}
|
||||
m_Layer: 5
|
||||
m_Name: source
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2019306417107768352
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8844525127622122013}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3566709735925945696}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 158.28172, y: 16.64}
|
||||
m_SizeDelta: {x: 66.47, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &6796448552036844055
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8844525127622122013}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4780041332280513338
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8844525127622122013}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: cc180dff846d13a4d88ddaed6f77e5cd, type: 3}
|
||||
m_FontSize: 12
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 8
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: source
|
||||
--- !u!1 &8979588775758457773
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 9116228060429372210}
|
||||
- component: {fileID: 1340434185471581111}
|
||||
m_Layer: 5
|
||||
m_Name: game_card_prefab
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &9116228060429372210
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8979588775758457773}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3566709735925945696}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1340434185471581111
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8979588775758457773}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9e862c8c48e4f8b49adae5e986da6e73, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
card_icon_image: {fileID: 6004099248657222439}
|
||||
card_name_text: {fileID: 3389633104739560455}
|
||||
authorName_text: {fileID: 9193153990832272737}
|
||||
description_text: {fileID: 8118505406259206449}
|
||||
_isOfficial_text: {fileID: 5166008895911254744}
|
||||
source_text: {fileID: 4780041332280513338}
|
||||
yyyy_mm_dd: {fileID: 5425439562977293950}
|
||||
clickButton: {fileID: 0}
|
||||
--- !u!1 &9033496028734522550
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2138782682665805939}
|
||||
- component: {fileID: 3647945832167543742}
|
||||
- component: {fileID: 8118505406259206449}
|
||||
m_Layer: 5
|
||||
m_Name: description
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2138782682665805939
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9033496028734522550}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3566709735925945696}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 10.130814, y: -15.7}
|
||||
m_SizeDelta: {x: 253.531, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3647945832167543742
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9033496028734522550}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &8118505406259206449
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9033496028734522550}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: 775c674e81662c644b64550d2e8f74e0, type: 3}
|
||||
m_FontSize: 15
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 3
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: "\u7B80\u4ECB\u4E0D\u5F97\u8D85\u8FC71234567891011121314151617181920212223242526272829"
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6f011f2d2221db49a0f07a8cf5900ca
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,230 @@
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
public class loadLittleGamesPrefab : MonoBehaviour
|
||||
{
|
||||
[Header("Scroll Views")]
|
||||
public GameObject officialDocs_scrollView_content;
|
||||
public GameObject officialGames_scrollView_content;
|
||||
public GameObject thirdPartyGames_scrollView_content;
|
||||
|
||||
[Header("Prefab")]
|
||||
public GameObject littleGames_card_Prefab;
|
||||
|
||||
[Header("H5 Root Path (optional, leave empty to use WebViewLauncher settings)")]
|
||||
public string h5RootPath;
|
||||
|
||||
void Start()
|
||||
{
|
||||
LoadAllLittleGames();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在 WebViewLauncher 的 h5 目录下扫描 three categories 的所有子文件夹(递归一层),
|
||||
/// 找到含有 index.html 的目录(例如: ...\h5LG\_thirdPartyGames\kjb\index.html),
|
||||
/// 读取同目录下的 info.xml(如果存在),并把 prefab 实例化到对应的 content 中。
|
||||
///
|
||||
/// info.xml 示例格式(放在同目录下,编码 UTF-8):
|
||||
/// <?xml version="1.0" encoding="utf-8"?>
|
||||
/// <Info>
|
||||
/// <card_name>示例游戏名</card_name>
|
||||
/// <authorName>作者名</authorName>
|
||||
/// <description>这里是描述文本</description>
|
||||
/// <isOfficial>True</isOfficial>
|
||||
/// <source>来源信息</source>
|
||||
/// <yyyy_mm_dd>2025-12-23</yyyy_mm_dd>
|
||||
/// </Info>
|
||||
///
|
||||
/// 请保证节点名如上所示以便能被正确读取。
|
||||
/// </summary>
|
||||
public void LoadAllLittleGames()
|
||||
{
|
||||
string root = GetH5RootPath();
|
||||
if (string.IsNullOrEmpty(root) || !Directory.Exists(root))
|
||||
{
|
||||
Debug.LogWarning("H5 root path not found: " + root);
|
||||
return;
|
||||
}
|
||||
|
||||
// 目标一级子目录名称(与 WebViewLauncher 中保持一致)
|
||||
string[] categories = { "_officialDocs", "_officialGames", "_thirdPartyGames" };
|
||||
|
||||
foreach (string cat in categories)
|
||||
{
|
||||
string catPath = Path.Combine(root, cat);
|
||||
if (!Directory.Exists(catPath))
|
||||
continue;
|
||||
|
||||
// 遍历 catPath 下的第一层子目录,检查是否含有 index.html
|
||||
string[] subdirs = Directory.GetDirectories(catPath);
|
||||
foreach (string subdir in subdirs)
|
||||
{
|
||||
string indexPath = Path.Combine(subdir, "index.html");
|
||||
if (!File.Exists(indexPath))
|
||||
{
|
||||
// 也可能 index.html 在更深一层,但按照约定通常是 subdir/index.html
|
||||
continue;
|
||||
}
|
||||
|
||||
// 读取 info.xml(可选)
|
||||
string infoXmlPath = Path.Combine(subdir, "info.xml");
|
||||
var meta = ReadInfoXml(infoXmlPath);
|
||||
|
||||
// 根据分类选择目标 content
|
||||
GameObject targetContent = GetContentByCategory(cat);
|
||||
if (targetContent == null || littleGames_card_Prefab == null)
|
||||
{
|
||||
Debug.LogWarning("Missing prefab or content for category: " + cat);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 实例化 prefab
|
||||
GameObject go = Instantiate(littleGames_card_Prefab, targetContent.transform);
|
||||
|
||||
// 设置数据(indexPath 使用本地文件绝对路径)
|
||||
string absIndexPath = indexPath; // 使用绝对路径,WebViewLauncher.OpenUrl 会添加 file://
|
||||
var prefabComp = go.GetComponent<game_card_Prefab>();
|
||||
if (prefabComp != null)
|
||||
{
|
||||
// 若来自官方分类,覆盖 isOfficial 与 source 字段为固定值
|
||||
string finalIsOfficial = meta.GetValueOrDefault("isOfficial");
|
||||
string finalSource = meta.GetValueOrDefault("source");
|
||||
|
||||
if (cat == "_officialDocs" || cat == "_officialGames")
|
||||
{
|
||||
finalIsOfficial = "官方";
|
||||
finalSource = "小游戏DLC";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非官方分类统一标记为第三方来源
|
||||
finalIsOfficial = "第三方来源";
|
||||
// 若 info.xml 没有 source,则可以保留原值或设置默认,这里若无则为空
|
||||
if (string.IsNullOrEmpty(finalSource))
|
||||
finalSource = "";
|
||||
}
|
||||
|
||||
// 格式化日期为 yyyy\nmm\ndd 的形式
|
||||
string rawDate = meta.GetValueOrDefault("yyyy_mm_dd");
|
||||
string formattedDate = FormatDateWithNewlines(rawDate);
|
||||
|
||||
prefabComp.SetData(absIndexPath,
|
||||
meta.GetValueOrDefault("card_name"),
|
||||
meta.GetValueOrDefault("authorName"),
|
||||
meta.GetValueOrDefault("description"),
|
||||
finalIsOfficial,
|
||||
finalSource,
|
||||
formattedDate
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string GetH5RootPath()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(h5RootPath) && Directory.Exists(h5RootPath))
|
||||
return h5RootPath;
|
||||
|
||||
// 从 WebViewLauncher 获取编辑器路径
|
||||
var w = GameObject.FindObjectOfType<WebViewLauncher>();
|
||||
if (w != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return w.h5EditorPath;
|
||||
#else
|
||||
return Path.Combine(Application.streamingAssetsPath, "h5LG");
|
||||
#endif
|
||||
}
|
||||
|
||||
// 作为回退,使用 StreamingAssets/h5LG
|
||||
return Path.Combine(Application.streamingAssetsPath, "h5LG");
|
||||
}
|
||||
|
||||
GameObject GetContentByCategory(string categoryName)
|
||||
{
|
||||
switch (categoryName)
|
||||
{
|
||||
case "_officialDocs": return officialDocs_scrollView_content;
|
||||
case "_officialGames": return officialGames_scrollView_content;
|
||||
case "_thirdPartyGames": return thirdPartyGames_scrollView_content;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, string> ReadInfoXml(string xmlPath)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
if (string.IsNullOrEmpty(xmlPath) || !File.Exists(xmlPath))
|
||||
return dict;
|
||||
|
||||
try
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(xmlPath);
|
||||
var root = doc.DocumentElement;
|
||||
if (root == null)
|
||||
return dict;
|
||||
|
||||
// 读取预定义字段
|
||||
string[] keys = { "card_name", "authorName", "description", "isOfficial", "source", "yyyy_mm_dd" };
|
||||
foreach (var k in keys)
|
||||
{
|
||||
var node = root.SelectSingleNode(k);
|
||||
if (node != null)
|
||||
dict[k] = node.InnerText;
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogWarning("Failed to read info.xml: " + xmlPath + " error: " + e.Message);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
// 将日期文本尝试解析成 yyyy\nmm\ndd 格式,若解析失败则做简单替换(支持 2025-12-23, 2025/12/23, 2025_12_23, 20251223 等)
|
||||
string FormatDateWithNewlines(string raw)
|
||||
{
|
||||
if (string.IsNullOrEmpty(raw))
|
||||
return "";
|
||||
|
||||
// 尝试按常见分隔符拆分
|
||||
char[] seps = new char[] { '-', '/', '_', ' ' };
|
||||
var parts = raw.Split(seps, System.StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
return parts[0] + "\n" + parts[1] + "\n" + parts[2];
|
||||
}
|
||||
|
||||
// 尝试连续数字 8 位
|
||||
var digits = System.Text.RegularExpressions.Regex.Replace(raw, "\\D", "");
|
||||
if (digits.Length == 8)
|
||||
{
|
||||
string y = digits.Substring(0, 4);
|
||||
string m = digits.Substring(4, 2);
|
||||
string d = digits.Substring(6, 2);
|
||||
return y + "\n" + m + "\n" + d;
|
||||
}
|
||||
|
||||
// 回退:尝试用第一个 4 位作为年,接下来的两位为月,之后为日
|
||||
if (digits.Length >= 6)
|
||||
{
|
||||
string y = digits.Substring(0, 4);
|
||||
string m = digits.Length >= 6 ? digits.Substring(4, 2) : "";
|
||||
string d = digits.Length >= 8 ? digits.Substring(6, 2) : "";
|
||||
return y + "\n" + m + "\n" + d;
|
||||
}
|
||||
|
||||
// 最后回退直接把原文本按字符换行(有限长度)
|
||||
return raw.Replace("-", "\n").Replace("/", "\n").Replace("_", "\n");
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d009618c8f589b49a50efcd92597ce2
|
||||
Reference in New Issue
Block a user