浮动小游戏已经基本搞好
新做了两个设置界面 一些bug修复和优化
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public class loadLittleGamesPrefab : MonoBehaviour
|
||||
{
|
||||
@@ -12,44 +14,66 @@ public class loadLittleGamesPrefab : MonoBehaviour
|
||||
|
||||
[Header("Prefab")]
|
||||
public GameObject littleGames_card_Prefab;
|
||||
[Tooltip("默认icon图")]
|
||||
public Sprite default_card_icon_sprite;
|
||||
|
||||
[Header("Refresh")]
|
||||
public Button refreshButton; // 点击触发重新扫描并刷新左侧列表
|
||||
|
||||
[Header("H5 Root Path (optional, leave empty to use WebViewLauncher settings)")]
|
||||
public string h5RootPath;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// attach refresh listener if button provided
|
||||
if (refreshButton != null)
|
||||
{
|
||||
refreshButton.onClick.AddListener(RefreshList);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (refreshButton != null)
|
||||
refreshButton.onClick.RemoveListener(RefreshList);
|
||||
}
|
||||
|
||||
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>
|
||||
// Clear children of a content transform
|
||||
void ClearContent(GameObject content)
|
||||
{
|
||||
if (content == null) return;
|
||||
for (int i = content.transform.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
var child = content.transform.GetChild(i).gameObject;
|
||||
Destroy(child);
|
||||
}
|
||||
}
|
||||
|
||||
// Public method to allow external callers to refresh
|
||||
public void RefreshList()
|
||||
{
|
||||
// clear all three contents then reload
|
||||
ClearContent(officialDocs_scrollView_content);
|
||||
ClearContent(officialGames_scrollView_content);
|
||||
ClearContent(thirdPartyGames_scrollView_content);
|
||||
LoadAllLittleGames();
|
||||
}
|
||||
|
||||
public void LoadAllLittleGames()
|
||||
{
|
||||
string root = GetH5RootPath();
|
||||
string root = ResolveH5RootPath();
|
||||
if (string.IsNullOrEmpty(root) || !Directory.Exists(root))
|
||||
{
|
||||
Debug.LogWarning("H5 root path not found: " + root);
|
||||
return;
|
||||
}
|
||||
|
||||
// 目标一级子目录名称(与 WebViewLauncher 中保持一致)
|
||||
string[] categories = { "_officialDocs", "_officialGames", "_thirdPartyGames" };
|
||||
string[] categories = new string[] { "_officialDocs", "_officialGames", "_thirdPartyGames" };
|
||||
|
||||
foreach (string cat in categories)
|
||||
{
|
||||
@@ -57,95 +81,148 @@ public class loadLittleGamesPrefab : MonoBehaviour
|
||||
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))
|
||||
string indexHtml = Path.Combine(subdir, "index.html");
|
||||
if (!File.Exists(indexHtml))
|
||||
continue;
|
||||
|
||||
// read xml if exists
|
||||
string xmlPath = Path.Combine(subdir, "info.xml");
|
||||
Dictionary<string, string> meta = null;
|
||||
if (File.Exists(xmlPath))
|
||||
meta = ReadInfoXml(xmlPath);
|
||||
|
||||
// target content
|
||||
GameObject target = GetTargetContent(cat);
|
||||
if (target == null || littleGames_card_Prefab == null)
|
||||
{
|
||||
// 也可能 index.html 在更深一层,但按照约定通常是 subdir/index.html
|
||||
Debug.LogWarning("Missing target content or prefab for category: " + cat);
|
||||
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);
|
||||
GameObject item = Instantiate(littleGames_card_Prefab, target.transform);
|
||||
var card = item.GetComponent<game_card_Prefab>();
|
||||
if (card == null)
|
||||
continue;
|
||||
|
||||
// defaults
|
||||
string defName = "未知Html文件";
|
||||
string defAuthor = "未知作者";
|
||||
string defDesc = "暂无相关描述信息";
|
||||
string defIsOfficial = "未知来源";
|
||||
string defSource = "自定义渠道";
|
||||
string defDateRaw = "19700101";
|
||||
|
||||
string cardName = GetMeta(meta, "card_name") ?? defName;
|
||||
string author = GetMeta(meta, "authorName") ?? defAuthor;
|
||||
string desc = GetMeta(meta, "description") ?? defDesc;
|
||||
string isOfficial = GetMeta(meta, "isOfficial");
|
||||
string source = GetMeta(meta, "source");
|
||||
string dateRaw = GetMeta(meta, "yyyy_mm_dd");
|
||||
string cardIcon = GetMeta(meta, "card_icon");
|
||||
string versionCode = GetMeta(meta, "versionCode");
|
||||
|
||||
if (string.IsNullOrEmpty(dateRaw)) dateRaw = defDateRaw;
|
||||
|
||||
if (cat == "_officialDocs" || cat == "_officialGames")
|
||||
{
|
||||
isOfficial = "官方";
|
||||
source = "小游戏DLC";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(isOfficial)) isOfficial = "第三方来源";
|
||||
if (string.IsNullOrEmpty(source)) source = defSource;
|
||||
}
|
||||
|
||||
// 实例化 prefab
|
||||
GameObject go = Instantiate(littleGames_card_Prefab, targetContent.transform);
|
||||
// ensure no nulls
|
||||
if (string.IsNullOrEmpty(cardName)) cardName = defName;
|
||||
if (string.IsNullOrEmpty(author)) author = defAuthor;
|
||||
if (string.IsNullOrEmpty(desc)) desc = defDesc;
|
||||
if (string.IsNullOrEmpty(isOfficial)) isOfficial = defIsOfficial;
|
||||
if (string.IsNullOrEmpty(source)) source = defSource;
|
||||
|
||||
// 设置数据(indexPath 使用本地文件绝对路径)
|
||||
string absIndexPath = indexPath; // 使用绝对路径,WebViewLauncher.OpenUrl 会添加 file://
|
||||
var prefabComp = go.GetComponent<game_card_Prefab>();
|
||||
if (prefabComp != null)
|
||||
string formattedDate = FormatDate(dateRaw);
|
||||
|
||||
card.SetData(indexHtml, cardName, author, desc, isOfficial, source, formattedDate, versionCode);
|
||||
|
||||
// Set default icon first (if provided)
|
||||
if (card.card_icon_image != null && default_card_icon_sprite != null)
|
||||
{
|
||||
// 若来自官方分类,覆盖 isOfficial 与 source 字段为固定值
|
||||
string finalIsOfficial = meta.GetValueOrDefault("isOfficial");
|
||||
string finalSource = meta.GetValueOrDefault("source");
|
||||
card.card_icon_image.sprite = default_card_icon_sprite;
|
||||
card.card_icon_image.enabled = true;
|
||||
}
|
||||
|
||||
if (cat == "_officialDocs" || cat == "_officialGames")
|
||||
// load icon if specified (override default)
|
||||
if (!string.IsNullOrEmpty(cardIcon))
|
||||
{
|
||||
string iconPath = cardIcon;
|
||||
if (!Path.IsPathRooted(iconPath))
|
||||
iconPath = Path.Combine(subdir, iconPath);
|
||||
|
||||
if (File.Exists(iconPath))
|
||||
{
|
||||
finalIsOfficial = "官方";
|
||||
finalSource = "小游戏DLC";
|
||||
try
|
||||
{
|
||||
byte[] bytes = File.ReadAllBytes(iconPath);
|
||||
Texture2D tex = new Texture2D(2, 2);
|
||||
if (tex.LoadImage(bytes))
|
||||
{
|
||||
Sprite sp = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
|
||||
if (card.card_icon_image != null)
|
||||
{
|
||||
card.card_icon_image.sprite = sp;
|
||||
card.card_icon_image.enabled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Failed to LoadImage for card_icon: " + iconPath + ". Using default sprite if available.");
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogWarning("Failed to load card_icon: " + e.Message + ". Using default sprite if available.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非官方分类统一标记为第三方来源
|
||||
finalIsOfficial = "第三方来源";
|
||||
// 若 info.xml 没有 source,则可以保留原值或设置默认,这里若无则为空
|
||||
if (string.IsNullOrEmpty(finalSource))
|
||||
finalSource = "";
|
||||
Debug.LogWarning("card_icon not found: " + iconPath + ". Using default sprite if available.");
|
||||
}
|
||||
|
||||
// 格式化日期为 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
|
||||
);
|
||||
}
|
||||
// if no cardIcon specified and no default was assigned earlier, ensure image disabled
|
||||
else
|
||||
{
|
||||
if (card.card_icon_image != null && card.card_icon_image.sprite == null)
|
||||
card.card_icon_image.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string GetH5RootPath()
|
||||
string ResolveH5RootPath()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(h5RootPath) && Directory.Exists(h5RootPath))
|
||||
return h5RootPath;
|
||||
|
||||
// 从 WebViewLauncher 获取编辑器路径
|
||||
var w = GameObject.FindObjectOfType<WebViewLauncher>();
|
||||
if (w != null)
|
||||
var launcher = GameObject.FindObjectOfType<WebViewLauncher>();
|
||||
if (launcher != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return w.h5EditorPath;
|
||||
return launcher.h5EditorPath;
|
||||
#else
|
||||
return Path.Combine(Application.streamingAssetsPath, "h5LG");
|
||||
#endif
|
||||
}
|
||||
|
||||
// 作为回退,使用 StreamingAssets/h5LG
|
||||
return Path.Combine(Application.streamingAssetsPath, "h5LG");
|
||||
}
|
||||
|
||||
GameObject GetContentByCategory(string categoryName)
|
||||
GameObject GetTargetContent(string category)
|
||||
{
|
||||
switch (categoryName)
|
||||
switch (category)
|
||||
{
|
||||
case "_officialDocs": return officialDocs_scrollView_content;
|
||||
case "_officialGames": return officialGames_scrollView_content;
|
||||
@@ -162,54 +239,43 @@ public class loadLittleGamesPrefab : MonoBehaviour
|
||||
|
||||
try
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.Load(xmlPath);
|
||||
var root = doc.DocumentElement;
|
||||
if (root == null)
|
||||
return dict;
|
||||
XmlElement root = doc.DocumentElement;
|
||||
if (root == null) return dict;
|
||||
|
||||
// 读取预定义字段
|
||||
string[] keys = { "card_name", "authorName", "description", "isOfficial", "source", "yyyy_mm_dd" };
|
||||
foreach (var k in keys)
|
||||
string[] keys = new string[] { "card_name", "authorName", "description", "isOfficial", "source", "yyyy_mm_dd", "card_icon", "versionCode" };
|
||||
foreach (string k in keys)
|
||||
{
|
||||
var node = root.SelectSingleNode(k);
|
||||
if (node != null)
|
||||
dict[k] = node.InnerText;
|
||||
XmlNode node = root.SelectSingleNode(k);
|
||||
if (node != null) dict[k] = node.InnerText;
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Failed to read info.xml: " + xmlPath + " error: " + e.Message);
|
||||
Debug.LogWarning("ReadInfoXml failed: " + ex.Message);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
// 将日期文本尝试解析成 yyyy\nmm\ndd 格式,若解析失败则做简单替换(支持 2025-12-23, 2025/12/23, 2025_12_23, 20251223 等)
|
||||
string FormatDateWithNewlines(string raw)
|
||||
string GetMeta(Dictionary<string, string> meta, string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(raw))
|
||||
return "";
|
||||
if (meta == null) return null;
|
||||
string v;
|
||||
if (meta.TryGetValue(key, out v)) return v;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试按常见分隔符拆分
|
||||
string FormatDate(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];
|
||||
}
|
||||
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 位作为年,接下来的两位为月,之后为日
|
||||
string digits = Regex.Replace(raw, "\\D", "");
|
||||
if (digits.Length == 8) return digits.Substring(0, 4) + "\n" + digits.Substring(4, 2) + "\n" + digits.Substring(6, 2);
|
||||
if (digits.Length >= 6)
|
||||
{
|
||||
string y = digits.Substring(0, 4);
|
||||
@@ -218,13 +284,8 @@ public class loadLittleGamesPrefab : MonoBehaviour
|
||||
return y + "\n" + m + "\n" + d;
|
||||
}
|
||||
|
||||
// 最后回退直接把原文本按字符换行(有限长度)
|
||||
return raw.Replace("-", "\n").Replace("/", "\n").Replace("_", "\n");
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
void Update() { }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user