From cf2408b1a98a87b6eb1265fe85d21b14e6ca0094 Mon Sep 17 00:00:00 2001 From: daixiawu Date: Mon, 29 Jun 2026 18:14:46 +0800 Subject: [PATCH 1/6] Fix map click drag threshold --- .../Scripts/TH1_Logic/Input/InputLogic.cs | 4 +-- .../Scripts/TH1_UI/UIBlockCameraDrag.cs | 30 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Unity/Assets/Scripts/TH1_Logic/Input/InputLogic.cs b/Unity/Assets/Scripts/TH1_Logic/Input/InputLogic.cs index 4524da992..c3503b175 100644 --- a/Unity/Assets/Scripts/TH1_Logic/Input/InputLogic.cs +++ b/Unity/Assets/Scripts/TH1_Logic/Input/InputLogic.cs @@ -145,7 +145,7 @@ namespace Logic } var camera = Camera.main; if (camera == null || Main.MapData?.MapConfig == null || Main.MapData.GridMap == null || Table.Instance == null) return; - Vector3 mousePosition = camera.ScreenToWorldPoint(Input.mousePosition); + Vector3 mousePosition = camera.ScreenToWorldPoint(UIBlockCameraDrag.ClickScreenPosition); Vector2Int cellPosition = Table.Instance.WorldToGrid(mousePosition); if (cellPosition.x >= Main.MapData.MapConfig.Width || cellPosition.x < 0 || cellPosition.y < 0 || cellPosition.y >= Main.MapData.MapConfig.Height) @@ -191,7 +191,7 @@ namespace Logic } var camera = Camera.main; if (camera == null || Main.MapData?.MapConfig == null || Main.MapData.GridMap == null || Table.Instance == null) return; - Vector3 mousePosition = camera.ScreenToWorldPoint(Input.mousePosition); + Vector3 mousePosition = camera.ScreenToWorldPoint(UIBlockCameraDrag.ClickScreenPosition); Vector2Int cellPosition = Table.Instance.WorldToGrid(mousePosition); if (cellPosition.x >= Main.MapData.MapConfig.Width || cellPosition.x < 0 || cellPosition.y < 0 || cellPosition.y >= Main.MapData.MapConfig.Height) diff --git a/Unity/Assets/Scripts/TH1_UI/UIBlockCameraDrag.cs b/Unity/Assets/Scripts/TH1_UI/UIBlockCameraDrag.cs index d67d0d6e1..2bbab23a1 100644 --- a/Unity/Assets/Scripts/TH1_UI/UIBlockCameraDrag.cs +++ b/Unity/Assets/Scripts/TH1_UI/UIBlockCameraDrag.cs @@ -6,10 +6,14 @@ using UnityEngine.EventSystems; // 例如:UIMouseBlocker,或者就保持 UIBlockCameraDrag 也可以,只要功能上能理解 public class UIBlockCameraDrag : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler { + private const float DragThresholdPixels = 10f; + public static bool IsPointerOnUI = false; public static bool ShouldBlockDrag = false; public static bool MoveEvent = false; public static bool DownUpEvent = false; + public static Vector3 PointerDownScreenPosition; + public static Vector3 ClickScreenPosition; public static Vector3 DragOrigin; public static Vector3 MoveVector; private static UIBlockCameraDrag _activeBlocker; @@ -25,6 +29,8 @@ public class UIBlockCameraDrag : MonoBehaviour, IPointerEnterHandler, IPointerEx ShouldBlockDrag = false; MoveEvent = false; DownUpEvent = false; + PointerDownScreenPosition = Vector3.zero; + ClickScreenPosition = Vector3.zero; DragOrigin = Vector3.zero; MoveVector = Vector3.zero; _activeBlocker = null; @@ -73,6 +79,8 @@ public class UIBlockCameraDrag : MonoBehaviour, IPointerEnterHandler, IPointerEx _activeBlocker = this; ShouldBlockDrag = true; MoveEvent = false; + PointerDownScreenPosition = eventData.position; + ClickScreenPosition = PointerDownScreenPosition; DragOrigin = GetMouseWorldPosition(); MoveVector = Vector3.zero; } @@ -83,7 +91,10 @@ public class UIBlockCameraDrag : MonoBehaviour, IPointerEnterHandler, IPointerEx if (eventData.button == PointerEventData.InputButton.Left) { if ((_activeBlocker == this || _isPointerInside) && !MoveEvent && !BanClick) + { + ClickScreenPosition = PointerDownScreenPosition; DownUpEvent = true; + } IsPointerOnUI = _isPointerInside; ClearDragState(); @@ -101,10 +112,25 @@ public class UIBlockCameraDrag : MonoBehaviour, IPointerEnterHandler, IPointerEx { if (ShouldBlockDrag) { - MoveVector = DragOrigin - GetMouseWorldPosition(); - if (MoveVector.magnitude > 0.01f) + if (!MoveEvent) { + var screenDelta = (Vector2)Input.mousePosition - (Vector2)PointerDownScreenPosition; + if (screenDelta.magnitude <= DragThresholdPixels) + { + MoveVector = Vector3.zero; + return; + } + MoveEvent = true; + DragOrigin = GetMouseWorldPosition(); + MoveVector = Vector3.zero; + return; + } + + MoveVector = DragOrigin - GetMouseWorldPosition(); + if (MoveVector.sqrMagnitude <= Mathf.Epsilon) + { + MoveVector = Vector3.zero; } } } From 091ba8e45325c722371c9ff5904991a480065a6f Mon Sep 17 00:00:00 2001 From: daixiawu Date: Mon, 29 Jun 2026 18:19:06 +0800 Subject: [PATCH 2/6] config: update gameplay questionnaire v2 --- .../DataAssets/QuestionnaireDataAssets.asset | 453 +++++++++++------- 1 file changed, 291 insertions(+), 162 deletions(-) diff --git a/Unity/Assets/BundleResources/DataAssets/QuestionnaireDataAssets.asset b/Unity/Assets/BundleResources/DataAssets/QuestionnaireDataAssets.asset index 9ad9683c6..9f2a70dfd 100644 --- a/Unity/Assets/BundleResources/DataAssets/QuestionnaireDataAssets.asset +++ b/Unity/Assets/BundleResources/DataAssets/QuestionnaireDataAssets.asset @@ -12,341 +12,470 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 9f2a6e7435b94d158cb76d9fd201a10a, type: 3} m_Name: QuestionnaireDataAssets m_EditorClassIdentifier: - DefaultQuestionnaireId: gameplay-feedback-2026-06 + DefaultQuestionnaireId: gameplay-feedback-2026-06-v2 MenuButtonText: "\u4F53\u9A8C\u53CD\u9988" QuestionnaireList: - - QuestionnaireId: gameplay-feedback-2026-06 + - QuestionnaireId: gameplay-feedback-2026-06-v2 + Revision: 1 + Status: 0 + Priority: 100 + StartAtUtc: "" + EndAtUtc: "" + AllowMultipleSubmissions: 1 + MaxSubmissionCount: 0 + MinSubmitIntervalHours: 0 + CanViewHistory: 1 + CanSubmitAfterExpired: 0 + TargetRules: + VersionWhitelist: [] + PlatformWhitelist: [] + LanguageWhitelist: [] + SteamOnly: 0 + BetaOnly: 0 + MinFinishedMatchCount: 0 Title: "TOHOTOPIA\u73A9\u6CD5\u4E0E\u7B56\u7565\u53CD\u9988\u95EE\u5377" - Description: "\u611F\u8C22\u4F60\u613F\u610F\u7559\u4E0B\u53CD\u9988\u3002\u8FD9\u4EFD\u95EE\u5377\u4E3B\u8981\u60F3\u4E86\u89E3\u89C4\u5219\u7406\u89E3\u3001\u7B56\u7565\u6DF1\u5EA6\u3001\u9635\u8425\u5E73\u8861\u3001\u5355\u5C40\u8282\u594F\u548C\u64CD\u4F5C\u8D1F\u62C5\uFF0C\u5927\u7EA6\u9700\u89816-8\u5206\u949F\u3002" + Description: "\u611F\u8C22\u4F60\u613F\u610F\u7559\u4E0B\u53CD\u9988\u3002\u8FD9\u4EFD\u95EE\u5377\u4E3B\u8981\u60F3\u4E86\u89E3\u89C4\u5219\u7406\u89E3\u3001\u7B56\u7565\u6DF1\u5EA6\u3001\u9635\u8425\u4E0E\u82F1\u96C4\u5E73\u8861\u3001\u5355\u5C40\u8282\u594F\u3001\u64CD\u4F5C\u8D1F\u62C5\u548C\u591A\u8BED\u8A00\u4F53\u9A8C\uFF0C\u5927\u7EA6\u9700\u89818-10\u5206\u949F\u3002" SubmitButtonText: "\u63D0\u4EA4" ResubmitButtonText: "\u91CD\u65B0\u586B\u5199" CloseButtonText: "\u5173\u95ED" - SubmittedMessage: "\u5DF2\u63D0\u4EA4\u3002\u4F60\u53EF\u4EE5\u91CD\u65B0\u586B\u5199\uFF0C\u518D\u6B21\u63D0\u4EA4\u4F1A\u8986\u76D6\u672C\u5730\u4E0A\u4E00\u4EFD\u7B54\u5377\u3002" + SubmittedMessage: "已提交。你可以在填写记录中查看这次答卷,也可以再次填写并新增一条记录。" UploadingMessage: "\u5DF2\u4FDD\u5B58\u672C\u5730\u7B54\u5377\uFF0C\u6B63\u5728\u4E0A\u4F20\u5230\u670D\u52A1\u5668..." - UploadSuccessMessage: "\u5DF2\u63D0\u4EA4\u5E76\u4E0A\u4F20\u5230\u670D\u52A1\u5668\u3002\u4F60\u53EF\u4EE5\u91CD\u65B0\u586B\u5199\uFF0C\u518D\u6B21\u63D0\u4EA4\u4F1A\u8986\u76D6\u672C\u5730\u7B54\u5377\u5E76\u4E0A\u4F20\u65B0\u7684\u670D\u52A1\u5668\u8BB0\u5F55\u3002" + UploadSuccessMessage: "已提交并上传到服务器。你可以在填写记录中查看这次答卷,也可以再次填写并上传新的记录。" UploadFailedMessage: "\u5DF2\u5728\u672C\u5730\u4FDD\u5B58\uFF0C\u4F46\u670D\u52A1\u5668\u4E0A\u4F20\u5931\u8D25\u3002\u8BF7\u786E\u8BA4 Steam \u5728\u7EBF\u540E\u7A0D\u540E\u91CD\u65B0\u63D0\u4EA4\u3002" UploadAuthFailedMessage: "\u5DF2\u5728\u672C\u5730\u4FDD\u5B58\uFF0C\u4F46\u5F53\u524D\u65E0\u6CD5\u83B7\u53D6 Steam \u767B\u5F55\u4FE1\u606F\uFF0C\u6682\u65F6\u6CA1\u6709\u4E0A\u4F20\u5230\u670D\u52A1\u5668\u3002" RequiredMessage: "\u8BF7\u5148\u5B8C\u6210\u5E26*\u7684\u5FC5\u7B54\u9898\u3002" - RefillHintText: "\u53EF\u4EE5\u91CD\u65B0\u586B\u5199\uFF0C\u63D0\u4EA4\u540E\u4F1A\u8986\u76D6\u4E0A\u4E00\u6B21\u672C\u5730\u7B54\u5377\u3002" + RefillHintText: "已清空当前填写内容,提交后会新增一条填写记录。" SaveFailedMessage: "\u672C\u5730\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002" Questions: - QuestionId: q01 QuestionType: 1 - Title: "\u4F60\u76EE\u524D\u7684\u603B\u6E38\u620F\u65F6\u957F\u5927\u7EA6\u662F\u591A\u5C11\uFF1F" + Title: "你目前的总游戏时长大约是多少?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: under_1h - Text: "1\u5C0F\u65F6\u4EE5\u5185" + Text: "1小时以内" - OptionId: 1_3h - Text: "1-3\u5C0F\u65F6" + Text: "1-3小时" - OptionId: 3_10h - Text: "3-10\u5C0F\u65F6" + Text: "3-10小时" - OptionId: 10h_plus - Text: "10\u5C0F\u65F6\u4EE5\u4E0A" + Text: "10小时以上" - QuestionId: q02 QuestionType: 1 - Title: "\u4F60\u73B0\u5728\u6700\u4E3B\u8981\u600E\u4E48\u73A9\u8FD9\u4E2A\u6E38\u620F\uFF1F" + Title: "你现在最主要怎么玩这个游戏?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: single_player_ai - Text: "\u4E3B\u8981\u6253\u5355\u4EBAAI" + Text: "主要打单人AI" - OptionId: multiplayer_friends - Text: "\u4E3B\u8981\u548C\u670B\u53CB\u8054\u673A" + Text: "主要和朋友联机" - OptionId: explore_factions - Text: "\u4E3B\u8981\u8BD5\u9635\u8425/\u82F1\u96C4" + Text: "主要试阵营/英雄" - OptionId: still_learning - Text: "\u8FD8\u5728\u719F\u6089\u57FA\u7840\u89C4\u5219" + Text: "还在熟悉基础规则" - QuestionId: q03 QuestionType: 1 - Title: "\u4F60\u5BF9\u7C7B\u4F3C4X/\u68CB\u76D8\u7B56\u7565\u6E38\u620F\u7684\u719F\u6089\u7A0B\u5EA6\u662F\uFF1F" + Title: "你当前主要使用哪种游戏语言?" + Hint: "" + Required: 1 + MaxSelectCount: 0 + Options: + - OptionId: zh_cn + Text: "简体中文" + - OptionId: zh_tw + Text: "繁体中文" + - OptionId: en + Text: "English" + - OptionId: ja + Text: "日本語" + - OptionId: ko + Text: "한국어" + - OptionId: other + Text: "Other" + - QuestionId: q04 + QuestionType: 1 + Title: "你对当前语言版本的文本质量满意吗?" + Hint: "" + Required: 1 + MaxSelectCount: 0 + Options: + - OptionId: satisfied + Text: "很满意,基本自然准确" + - OptionId: understandable + Text: "基本能理解,但有些句子别扭" + - OptionId: unclear + Text: "有不少误译或表达不清" + - OptionId: affects_rules + Text: "文本质量影响我理解规则" + - OptionId: not_sure + Text: "我没有注意或不好判断" + - QuestionId: q05 + QuestionType: 2 + Title: "你还希望游戏支持哪些语言?" + Hint: "可多选" + Required: 0 + MaxSelectCount: 0 + Options: + - OptionId: russian + Text: "Russian" + - OptionId: spanish + Text: "Spanish" + - OptionId: portuguese + Text: "Portuguese" + - OptionId: french + Text: "French" + - OptionId: german + Text: "German" + - OptionId: thai + Text: "Thai" + - OptionId: vietnamese + Text: "Vietnamese" + - OptionId: indonesian + Text: "Indonesian" + - OptionId: turkish + Text: "Turkish" + - OptionId: italian + Text: "Italian" + - OptionId: polish + Text: "Polish" + - OptionId: other + Text: "Other,请在最后开放题补充" + - OptionId: no_need + Text: "我不需要新增语言" + - QuestionId: q06 + QuestionType: 1 + Title: "你对类似4X/棋盘策略游戏的熟悉程度是?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: new_to_strategy - Text: "\u57FA\u672C\u662F\u65B0\u624B" + Text: "基本是新手" - OptionId: played_some - Text: "\u73A9\u8FC7\u4E00\u4E9B\u56DE\u5408/\u7B56\u7565\u6E38\u620F" + Text: "玩过一些回合或策略游戏" - OptionId: polytopia_like - Text: "\u719F\u6089Polytopia\u6216\u7C7B\u4F3C\u77ED\u5C40\u7B56\u7565\u6E38\u620F" + Text: "熟悉Polytopia或类似短局策略游戏" - OptionId: civ_4x_veteran - Text: "\u719F\u6089\u6587\u660E/4X/\u6218\u68CB\u7B49\u7B56\u7565\u6E38\u620F" - - QuestionId: q04 + Text: "熟悉文明、4X、战棋等策略游戏" + - QuestionId: q07 QuestionType: 1 - Title: "\u5728\u524D30\u5206\u949F\u5185\uFF0C\u4F60\u662F\u5426\u77E5\u9053\u81EA\u5DF1\u6BCF\u56DE\u5408\u5E94\u8BE5\u505A\u4EC0\u4E48\uFF1F" + Title: "你在开新局的前10回合,通常是否清楚自己应该优先做什么?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - - OptionId: clear - Text: "\u5F88\u6E05\u695A" - - OptionId: mostly_clear - Text: "\u5927\u81F4\u80FD\u5224\u65AD" - - OptionId: often_lost - Text: "\u7ECF\u5E38\u4E0D\u77E5\u9053\u4E0B\u4E00\u6B65\u505A\u4EC0\u4E48" - - OptionId: not_played_enough - Text: "\u8FD8\u6CA1\u73A9\u5230\u8DB3\u591F\u5224\u65AD" - - QuestionId: q05 + - OptionId: clear_plan + Text: "很清楚,有固定开局思路" + - OptionId: adapt + Text: "大致清楚,但会看局势摸索" + - OptionId: unclear_priority + Text: "经常不知道优先扩张、科技还是出兵" + - OptionId: trial_or_guide + Text: "主要靠试错或看攻略/问别人" + - OptionId: not_enough_games + Text: "还没开过足够多局判断" + - QuestionId: q08 QuestionType: 2 - Title: "\u54EA\u4E9B\u89C4\u5219\u6216\u4FE1\u606F\u6700\u5BB9\u6613\u8BA9\u4F60\u56F0\u60D1\uFF1F" - Hint: "\u53EF\u591A\u9009" + Title: "哪些规则或信息最容易让你困惑?" + Hint: "可多选" Required: 0 MaxSelectCount: 0 Options: - OptionId: damage_formula - Text: "\u653B\u51FB/\u9632\u5FA1/\u8840\u91CF\u5982\u4F55\u51B3\u5B9A\u4F24\u5BB3" + Text: "攻击/防御/血量如何决定伤害" - OptionId: action_points - Text: "\u79FB\u52A8\u3001\u653B\u51FB\u3001\u6CBB\u7597\u7B49\u884C\u52A8\u70B9\u89C4\u5219" + Text: "移动、攻击、治疗等行动点规则" - OptionId: tech_city_economy - Text: "\u79D1\u6280\u3001\u57CE\u5E02\u5347\u7EA7\u548C\u8D44\u6E90\u6536\u76CA" + Text: "科技、城市升级和资源收益" - OptionId: hero_skill_timing - Text: "\u82F1\u96C4\u6280\u80FD\u89E6\u53D1\u6761\u4EF6\u548C\u6301\u7EED\u6548\u679C" + Text: "英雄技能触发条件和持续效果" - OptionId: diplomacy_victory - Text: "\u5916\u4EA4\u3001\u5BA3\u6218\u548C\u80DC\u5229\u6761\u4EF6" + Text: "外交、宣战和胜利条件" - OptionId: terrain_building - Text: "\u9053\u8DEF\u3001\u6865\u6881\u3001\u5730\u5F62\u548C\u5EFA\u7B51\u9650\u5236" - - QuestionId: q06 + Text: "道路、桥梁、地形和建筑限制" + - QuestionId: q09 QuestionType: 1 - Title: "\u5F53\u524D\u5355\u5C40\u8282\u594F\u5BF9\u4F60\u6765\u8BF4\u5982\u4F55\uFF1F" + Title: "当前单局节奏对你来说如何?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: too_fast - Text: "\u504F\u5FEB\uFF0C\u8FD8\u6CA1\u7406\u89E3\u5C31\u88AB\u6253\u5D29" + Text: "偏快,还没理解就被打崩" - OptionId: good - Text: "\u6BD4\u8F83\u5408\u9002" + Text: "比较合适" - OptionId: slow - Text: "\u504F\u6162\uFF0C\u7B49\u5F85\u548C\u91CD\u590D\u64CD\u4F5C\u591A" + Text: "偏慢,等待和重复操作多" - OptionId: depends - Text: "\u4E0D\u540C\u9636\u6BB5\u5DEE\u522B\u5F88\u5927" - - QuestionId: q07 + Text: "不同阶段差别很大" + - QuestionId: q10 QuestionType: 1 - Title: "\u4E00\u5C40\u5230\u4E2D\u540E\u671F\u65F6\uFF0C\u7B56\u7565\u9009\u62E9\u662F\u5426\u8FD8\u6709\u8DA3\uFF1F" + Title: "一局到中后期时,策略选择是否还有趣?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: stays_interesting - Text: "\u8D8A\u5230\u540E\u9762\u8D8A\u6709\u535A\u5F08" + Text: "越到后面越有博弈" - OptionId: some_repetition - Text: "\u6709\u8DA3\uFF0C\u4F46\u6709\u4E9B\u91CD\u590D" + Text: "有趣,但有些重复" - OptionId: becomes_single_plan - Text: "\u5BB9\u6613\u53D8\u6210\u56FA\u5B9A\u5957\u8DEF" + Text: "容易变成固定套路" - OptionId: not_reached - Text: "\u8FD8\u6CA1\u73A9\u5230\u8DB3\u591F\u5224\u65AD" - - QuestionId: q08 + Text: "还没玩到足够判断" + - QuestionId: q11 QuestionType: 2 - Title: "\u4F60\u89C9\u5F97\u5F53\u524D\u6700\u6709\u7B56\u7565\u4EF7\u503C\u7684\u51B3\u7B56\u662F\u54EA\u4E9B\uFF1F" - Hint: "\u53EF\u591A\u9009" + Title: "你觉得当前最有策略价值的决策是哪些?" + Hint: "可多选" Required: 0 MaxSelectCount: 0 Options: - OptionId: expansion_timing - Text: "\u63A2\u7D22\u3001\u5360\u57CE\u548C\u6269\u5F20\u65F6\u673A" + Text: "探索、占城和扩张时机" - OptionId: tech_path - Text: "\u79D1\u6280\u987A\u5E8F\u548C\u7ECF\u6D4E\u89C4\u5212" + Text: "科技顺序和经济规划" - OptionId: hero_leveling - Text: "\u82F1\u96C4\u9009\u62E9\u3001\u5347\u7EA7\u548C\u4EFB\u52A1" + Text: "英雄选择、升级和任务" - OptionId: tactical_positioning - Text: "\u6218\u6597\u7AD9\u4F4D\u3001\u5148\u624B\u548C\u53CD\u5236" + Text: "战斗站位、先手和反制" - OptionId: faction_mechanics - Text: "\u9635\u8425\u7279\u8272\u673A\u5236" + Text: "阵营特色机制" - OptionId: diplomacy_timing - Text: "\u5916\u4EA4\u3001\u7ED3\u76DF\u548C\u5BA3\u6218\u65F6\u673A" - - QuestionId: q09 + Text: "外交、结盟和宣战时机" + - QuestionId: q12 QuestionType: 2 - Title: "\u4F60\u89C9\u5F97\u54EA\u4E9B\u4E2D\u540E\u671F\u95EE\u9898\u6700\u660E\u663E\uFF1F" - Hint: "\u53EF\u591A\u9009" + Title: "你觉得哪些中后期问题最明显?" + Hint: "可多选" Required: 0 MaxSelectCount: 0 Options: - OptionId: warship_cannon - Text: "\u6218\u8230/\u70AE\u624B\u7B49\u8FDC\u7A0B\u706B\u529B\u8FC7\u4E8E\u4E3B\u5BFC" + Text: "战舰/炮手等远程火力过于主导" - OptionId: hero_snowball - Text: "\u82F1\u96C4\u6216\u91D1\u5E01\u5347\u7EA7\u6EDA\u96EA\u7403\u592A\u5FEB" + Text: "英雄或金币升级滚雪球太快" - OptionId: tech_runs_out - Text: "\u79D1\u6280\u3001\u6587\u5316\u6216\u5185\u653F\u540E\u671F\u76EE\u6807\u4E0D\u8DB3" + Text: "科技、文化或内政后期目标不足" - OptionId: unit_spam - Text: "\u7206\u5175/\u5806\u5355\u4F4D\u540E\u64CD\u4F5C\u91CF\u592A\u5927" + Text: "爆兵/堆单位后操作量太大" - OptionId: ai_pressure - Text: "AI\u5F3A\u5EA6\u6216\u5175\u529B\u4F53\u611F\u4E0D\u5408\u7406" + Text: "AI强度或兵力体感不合理" - OptionId: not_reached - Text: "\u8FD8\u6CA1\u73A9\u5230\u4E2D\u540E\u671F" - - QuestionId: q10 + Text: "还没玩到中后期" + - QuestionId: q13 QuestionType: 1 - Title: "\u6218\u6597\u7ED3\u679C\u548C\u4F24\u5BB3\u9884\u671F\u5BF9\u4F60\u6765\u8BF4\u662F\u5426\u8DB3\u591F\u6E05\u695A\uFF1F" + Title: "战斗结果和伤害预期对你来说是否足够清楚?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: clear - Text: "\u5F88\u6E05\u695A" + Text: "很清楚" - OptionId: mostly_clear - Text: "\u5927\u81F4\u6E05\u695A\uFF0C\u4F46\u5076\u5C14\u4F1A\u610F\u5916" + Text: "大致清楚,但偶尔会意外" - OptionId: unclear - Text: "\u7ECF\u5E38\u4E0D\u77E5\u9053\u4E3A\u4EC0\u4E48\u8FD9\u4E48\u4F24" + Text: "经常不知道为什么这么伤" - OptionId: not_combat_focused - Text: "\u6211\u5F88\u5C11\u5173\u6CE8\u8FD9\u4E2A" - - QuestionId: q11 + Text: "我很少关注这个" + - QuestionId: q14 QuestionType: 2 - Title: "\u54EA\u4E9B\u6218\u6597\u4FE1\u606F\u6700\u9700\u8981\u663E\u793A\u5F97\u66F4\u6E05\u695A\uFF1F" - Hint: "\u53EF\u591A\u9009" + Title: "哪些战斗信息最需要显示得更清楚?" + Hint: "可多选" Required: 0 MaxSelectCount: 0 Options: - OptionId: combat_predict - Text: "\u653B\u51FB\u540E\u53CC\u65B9\u5269\u4F59\u8840\u91CF" + Text: "攻击后双方剩余血量" - OptionId: counterattack - Text: "\u53CD\u51FB\u3001\u8FDE\u6740\u548C\u5148\u624B\u5224\u5B9A" + Text: "反击、连杀和先手判定" - OptionId: terrain_defense - Text: "\u5730\u5F62\u548C\u57CE\u9632\u5BF9\u9632\u5FA1\u7684\u5F71\u54CD" + Text: "地形和城防对防御的影响" - OptionId: aoe_chain - Text: "\u8303\u56F4\u4F24\u5BB3\u548C\u8FDE\u9501\u6548\u679C" + Text: "范围伤害和连锁效果" - OptionId: skill_effects - Text: "\u82F1\u96C4/\u5355\u4F4D\u6280\u80FD\u5BF9\u6218\u6597\u7684\u5F71\u54CD" + Text: "英雄/单位技能对战斗的影响" - OptionId: threat_range - Text: "\u654C\u65B9\u4E0B\u56DE\u5408\u53EF\u80FD\u6253\u5230\u7684\u8303\u56F4" - - QuestionId: q12 + Text: "敌方下回合可能打到的范围" + - QuestionId: q15 QuestionType: 1 - Title: "\u4F60\u89C9\u5F97\u9635\u8425\u4E4B\u95F4\u7684\u73A9\u6CD5\u5DEE\u5F02\u662F\u5426\u8DB3\u591F\u660E\u663E\uFF1F" + Title: "你觉得阵营之间的玩法差异是否足够明显?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: very_distinct - Text: "\u5F88\u660E\u663E\uFF0C\u6BCF\u4E2A\u9635\u8425\u90FD\u6709\u81EA\u5DF1\u7684\u6253\u6CD5" + Text: "很明显,每个阵营都有自己的打法" - OptionId: some_distinct - Text: "\u6709\u5DEE\u5F02\uFF0C\u4F46\u90E8\u5206\u9635\u8425\u8FD8\u662F\u50CF" + Text: "有差异,但部分阵营还是像" - OptionId: mostly_same - Text: "\u611F\u89C9\u5927\u591A\u90FD\u662F\u76F8\u4F3C\u5957\u8DEF" + Text: "感觉大多都是相似套路" - OptionId: not_enough_factions - Text: "\u73A9\u8FC7\u7684\u9635\u8425\u8FD8\u4E0D\u591F\u591A" - - QuestionId: q13 + Text: "玩过的阵营还不够多" + - QuestionId: q16 QuestionType: 1 - Title: "\u4F60\u89C9\u5F97\u5F53\u524D\u9635\u8425/\u82F1\u96C4\u5E73\u8861\u6700\u63A5\u8FD1\u54EA\u79CD\u60C5\u51B5\uFF1F" + Title: "你觉得当前阵营/英雄平衡最接近哪种情况?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: mostly_fair - Text: "\u5927\u4F53\u516C\u5E73" + Text: "大体公平" - OptionId: new_factions_strong - Text: "\u65B0\u9635\u8425/\u65B0\u82F1\u96C4\u660E\u663E\u66F4\u5F3A" + Text: "新阵营或新英雄明显更强" - OptionId: old_factions_weak - Text: "\u65E9\u671F\u9635\u8425\u6216\u666E\u901A\u5175\u79CD\u660E\u663E\u843D\u540E" + Text: "早期阵营或普通兵种明显落后" - OptionId: specific_outliers - Text: "\u5C11\u6570\u82F1\u96C4/\u6280\u80FD\u7279\u522B\u5931\u8861" + Text: "少数英雄或技能特别失衡" - OptionId: no_judgement - Text: "\u8FD8\u4E0D\u597D\u5224\u65AD" - - QuestionId: q14 + Text: "还不好判断" + - QuestionId: q17 QuestionType: 2 - Title: "\u5982\u679C\u4F60\u89C9\u5F97\u6709\u5931\u8861\u70B9\uFF0C\u4E3B\u8981\u662F\u54EA\u4E9B\uFF1F" - Hint: "\u53EF\u591A\u9009\uFF1B\u6CA1\u611F\u89C9\u5230\u53EF\u8DF3\u8FC7" + Title: "哪些英雄最需要重新检查平衡?" + Hint: "最多选5项;如果选择“没有明显失衡英雄”或“玩过/遇到的英雄还不够多”,建议不要再选具体英雄" Required: 0 - MaxSelectCount: 0 + MaxSelectCount: 5 Options: - - OptionId: koishi_reisen - Text: "\u53E4\u660E\u5730\u604B/\u94C3\u4ED9\u7B49\u82F1\u96C4\u6E05\u573A\u6216\u53CD\u5236\u96BE" - - OptionId: remilia_scarlet - Text: "\u7EA2\u9B54\u9986\u6216\u65E7\u9635\u8425\u5F3A\u5EA6\u4E0D\u8DB3" - - OptionId: viking_new - Text: "\u7EF4\u4EAC/\u534E\u6247/\u5807\u5B50\u7B49\u65B0\u5185\u5BB9\u5F3A\u5EA6\u504F\u9AD8" - - OptionId: hero_gold_level - Text: "\u82F1\u96C4\u91D1\u5E01\u5347\u7EA7\u548C\u7ECF\u6D4E\u6EDA\u96EA\u7403" - - OptionId: warship_cannon_knight - Text: "\u6218\u8230\u3001\u70AE\u624B\u3001\u9A91\u58EB\u7B49\u5173\u952E\u5355\u4F4D" - - OptionId: ai_bonus - Text: "AI\u52A0\u6210\u6216\u9AD8\u96BE\u5EA6\u4F53\u611F" - - QuestionId: q15 + - OptionId: kaguya + Text: "辉夜" + - OptionId: reisen + Text: "铃仙" + - OptionId: tewi + Text: "帝" + - OptionId: eirin + Text: "永琳" + - OptionId: mokou + Text: "妹红" + - OptionId: remilia + Text: "蕾米莉亚" + - OptionId: patchouli + Text: "帕秋莉" + - OptionId: sakuya + Text: "咲夜" + - OptionId: flandre + Text: "芙兰朵露" + - OptionId: meiling + Text: "美铃" + - OptionId: kanako + Text: "神奈子" + - OptionId: suwako + Text: "诹访子" + - OptionId: sanae + Text: "早苗" + - OptionId: aya + Text: "文" + - OptionId: momiji + Text: "椛" + - OptionId: satori + Text: "觉" + - OptionId: koishi + Text: "恋" + - OptionId: utsuho + Text: "空" + - OptionId: yuugi + Text: "勇仪" + - OptionId: rin + Text: "燐" + - OptionId: reimu + Text: "灵梦" + - OptionId: sumireko + Text: "堇子" + - OptionId: kasen + Text: "华扇" + - OptionId: aunn + Text: "阿吽" + - OptionId: suika + Text: "萃香" + - OptionId: none + Text: "没有明显失衡英雄" + - OptionId: not_enough + Text: "玩过或遇到的英雄还不够多" + - QuestionId: q18 QuestionType: 2 - Title: "\u54EA\u4E9B\u64CD\u4F5C\u6700\u8BA9\u4F60\u611F\u5230\u8D1F\u62C5\u6216\u70E6\u8E81\uFF1F" - Hint: "\u53EF\u591A\u9009" + Title: "哪些操作最让你感到负担或烦躁?" + Hint: "可多选" Required: 0 MaxSelectCount: 0 Options: - OptionId: many_units - Text: "\u4E2D\u540E\u671F\u5355\u4F4D\u592A\u591A\uFF0C\u9010\u4E2A\u70B9\u5F88\u7D2F" + Text: "中后期单位太多,逐个点很累" - OptionId: find_idle_units - Text: "\u96BE\u4EE5\u627E\u5230\u672A\u884C\u52A8\u5355\u4F4D\u6216\u82F1\u96C4" + Text: "难以找到未行动单位或英雄" - OptionId: misclick - Text: "\u5BB9\u6613\u8BEF\u70B9\u79FB\u52A8/\u653B\u51FB/\u5360\u9886\u7B49\u4E0D\u53EF\u9006\u64CD\u4F5C" + Text: "容易误点移动、攻击、占领等不可逆操作" - OptionId: pathing_camera - Text: "\u62D6\u5730\u56FE\u3001\u9009\u4E2D\u5355\u4F4D\u6216\u81EA\u52A8\u955C\u5934\u4E0D\u987A" + Text: "拖地图、选中单位或自动镜头不顺" - OptionId: city_unit_overlap - Text: "\u57CE\u5E02\u4E0A\u6709\u5355\u4F4D\u65F6\u4EA4\u4E92\u4E0D\u65B9\u4FBF" + Text: "城市上有单位时交互不方便" - OptionId: waiting_turns - Text: "\u7B49\u5F85AI\u6216\u5176\u4ED6\u73A9\u5BB6\u56DE\u5408\u592A\u4E45" - - QuestionId: q16 + Text: "等待AI或其他玩家回合太久" + - QuestionId: q19 QuestionType: 2 - Title: "\u4F60\u6700\u5E0C\u671B\u4F18\u5148\u589E\u52A0\u54EA\u4E9B\u51CF\u8D1F\u529F\u80FD\uFF1F" - Hint: "\u6700\u591A\u90093\u9879" + Title: "哪些操作体验你最希望继续优化?" + Hint: "最多选3项" Required: 0 MaxSelectCount: 3 Options: - - OptionId: next_idle_unit - Text: "\u4E0B\u4E00\u4E2A\u672A\u884C\u52A8\u5355\u4F4D/\u672A\u5904\u7406\u5355\u4F4D\u63D0\u793A" - - OptionId: undo_safe_actions - Text: "\u672A\u63A2\u8DEF/\u672A\u6218\u6597\u65F6\u7684\u64A4\u9500" - - OptionId: confirmation - Text: "\u653B\u51FB\u53CB\u519B\u3001\u9996\u90FD\u64CD\u4F5C\u7B49\u4E8C\u6B21\u786E\u8BA4" - - OptionId: unit_sleep_skip - Text: "\u4F11\u7720/\u8DF3\u8FC7\u5355\u4F4D\u548C\u81EA\u52A8\u5FAA\u73AF" + - OptionId: idle_unit_hint + Text: "未行动单位提示仍不够明显" + - OptionId: skip_sleep + Text: "跳过或休眠单位的操作还不够顺" + - OptionId: undo_protection + Text: "撤销或误操作保护还不够可靠" - OptionId: better_tooltips - Text: "\u66F4\u8BE6\u7EC6\u7684\u6280\u80FD/\u79D1\u6280/\u5730\u5F62\u63D0\u793A" + Text: "技能、科技、地形说明不够清楚" + - OptionId: combat_prediction + Text: "战斗预测和威胁范围不够清楚" - OptionId: faster_ai - Text: "\u66F4\u5FEB\u7684AI\u56DE\u5408\u548C\u52A8\u753B\u901F\u5EA6\u9009\u9879" - - QuestionId: q17 + Text: "AI、动画、等待节奏仍然偏慢" + - OptionId: camera_selection + Text: "镜头、拖图、选中单位手感不顺" + - QuestionId: q20 QuestionType: 1 - Title: "\u76EE\u524D\u5730\u56FE\u751F\u6210\u548C\u5F00\u5C40\u516C\u5E73\u6027\u5BF9\u4F60\u6765\u8BF4\u5982\u4F55\uFF1F" + Title: "目前地图生成和开局公平性对你来说如何?" Hint: "" Required: 1 MaxSelectCount: 0 Options: - OptionId: fair - Text: "\u5927\u4F53\u516C\u5E73" + Text: "大体公平" - OptionId: sometimes_bad - Text: "\u5076\u5C14\u6709\u5F88\u5DEE\u7684\u51FA\u751F\u70B9\u6216\u5730\u5F62" + Text: "偶尔有很差的出生点或地形" - OptionId: often_bad - Text: "\u7ECF\u5E38\u56E0\u5730\u56FE\u5F71\u54CD\u5F88\u5927" + Text: "经常因地图影响很大" - OptionId: not_notice - Text: "\u6CA1\u6709\u660E\u663E\u611F\u53D7" - - QuestionId: q18 + Text: "没有明显感受" + - QuestionId: q21 QuestionType: 2 - Title: "\u5982\u679C\u4E0B\u4E2A\u7248\u672C\u4E3B\u8981\u4F18\u5316\u73A9\u6CD5\uFF0C\u4F60\u6700\u5E0C\u671B\u4F18\u5148\u505A\u54EA\u4E9B\uFF1F" - Hint: "\u6700\u591A\u90093\u9879" + Title: "如果下个版本主要优化玩法,你最希望优先做哪些?" + Hint: "最多选3项" Required: 1 MaxSelectCount: 3 Options: - OptionId: tutorial_rules - Text: "\u6559\u7A0B\u3001\u56FE\u9274\u548C\u89C4\u5219\u8BF4\u660E" + Text: "教程、图鉴和规则说明" + - OptionId: localization + Text: "翻译质量和多语言支持" - OptionId: combat_readability - Text: "\u6218\u6597\u9884\u6D4B\u3001\u4F24\u5BB3\u516C\u5F0F\u548C\u5A01\u80C1\u8303\u56F4" + Text: "战斗预测、伤害公式和威胁范围" - OptionId: faction_balance - Text: "\u9635\u8425/\u82F1\u96C4/\u5173\u952E\u5355\u4F4D\u5E73\u8861" + Text: "阵营、英雄、关键单位平衡" - OptionId: economy_tech_depth - Text: "\u7ECF\u6D4E\u3001\u79D1\u6280\u548C\u4E2D\u540E\u671F\u76EE\u6807" + Text: "经济、科技和中后期目标" - OptionId: burden_reduction - Text: "\u51CF\u5C11\u91CD\u590D\u64CD\u4F5C\u548C\u8BEF\u64CD\u4F5C" + Text: "减少重复操作和误操作" - OptionId: map_generation - Text: "\u5730\u56FE\u751F\u6210\u548C\u5F00\u5C40\u516C\u5E73" + Text: "地图生成和开局公平" - OptionId: multiplayer_stability - Text: "\u8054\u673A\u7A33\u5B9A\u6027\u548C\u591A\u4EBA\u4F53\u9A8C" - - QuestionId: q19 + Text: "联机稳定性和多人体验" + - OptionId: other + Text: "其他,请在开放反馈中补充" + - QuestionId: q22 QuestionType: 0 - Title: "\u8BF7\u5199\u4E00\u4E2A\u4F60\u89C9\u5F97\u6700\u503C\u5F97\u4FDD\u7559\u6216\u7EE7\u7EED\u5F3A\u5316\u7684\u73A9\u6CD5\u70B9\u3002" - Hint: "\u6BD4\u5982\u67D0\u4E2A\u9635\u8425\u3001\u82F1\u96C4\u6280\u80FD\u3001\u6218\u6597\u535A\u5F08\u6216\u7ECF\u6D4E\u601D\u8DEF" - Required: 0 - MaxSelectCount: 0 - Options: [] - - QuestionId: q20 - QuestionType: 0 - Title: "\u8BF7\u5199\u4E00\u4E2A\u6700\u5F71\u54CD\u4F60\u7EE7\u7EED\u73A9\u7684\u73A9\u6CD5\u95EE\u9898\u6216\u8D1F\u62C5\u3002" - Hint: "\u8D8A\u5177\u4F53\u8D8A\u597D\uFF0C\u6BD4\u5982\u54EA\u4E2A\u9635\u8425/\u82F1\u96C4/\u9636\u6BB5/\u64CD\u4F5C\u8BA9\u4F60\u60F3\u9000\u51FA" + Title: "请补充:最值得保留的玩法点,以及最影响你继续玩的玩法问题或负担。" + Hint: "可以各写一句。比如某个阵营、英雄技能、战斗博弈、经济思路,或某个让你想退出的阶段、操作、英雄、翻译问题。" Required: 0 MaxSelectCount: 0 Options: [] From 2ab6b63368dcc377688c49df906d23a12fde2339 Mon Sep 17 00:00:00 2001 From: daixiawu Date: Mon, 29 Jun 2026 18:22:46 +0800 Subject: [PATCH 3/6] chore: lock Unity MCP package --- Unity/Packages/packages-lock.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Unity/Packages/packages-lock.json b/Unity/Packages/packages-lock.json index 163e427f4..ac6493cf1 100644 --- a/Unity/Packages/packages-lock.json +++ b/Unity/Packages/packages-lock.json @@ -14,6 +14,16 @@ "dependencies": {}, "hash": "2554e81f0315ddf835026940ccc4ba8175edfda3" }, + "com.gamebooom.unity.mcp": { + "version": "https://github.com/FunplayAI/funplay-unity-mcp.git#v0.4.8", + "depth": 0, + "source": "git", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.2.1", + "com.unity.inputsystem": "1.7.0" + }, + "hash": "5cfd52f0fb31911a3c45ef995ef475095772566b" + }, "com.github-glitchenzo.nugetforunity": { "version": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity", "depth": 0, @@ -194,6 +204,15 @@ }, "url": "https://packages.unity.com" }, + "com.unity.inputsystem": { + "version": "1.11.2", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.com" + }, "com.unity.mathematics": { "version": "1.2.6", "depth": 1, @@ -201,6 +220,13 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.2.1", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.render-pipelines.core": { "version": "14.0.11", "depth": 1, From 1742d0a495764f92736ccd837ab5f038124918c8 Mon Sep 17 00:00:00 2001 From: daixiawu Date: Mon, 29 Jun 2026 18:23:10 +0800 Subject: [PATCH 4/6] Fix iPhone audio upload fallback --- Tools/Dashboard/index.html | 4 ++-- Tools/Dashboard/js/codex_threads.js | 29 +++++++++++++++++++---------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Tools/Dashboard/index.html b/Tools/Dashboard/index.html index 78c51aa00..3bc135023 100644 --- a/Tools/Dashboard/index.html +++ b/Tools/Dashboard/index.html @@ -843,10 +843,10 @@
- +
- + diff --git a/Tools/Dashboard/js/codex_threads.js b/Tools/Dashboard/js/codex_threads.js index 467051cf1..613eda859 100644 --- a/Tools/Dashboard/js/codex_threads.js +++ b/Tools/Dashboard/js/codex_threads.js @@ -40,13 +40,21 @@ function codexPreferredAudioMimeType() { function codexAudioExtension(mimeType) { const lower = (mimeType || '').toLowerCase(); - if (lower.includes('mp4')) return 'm4a'; + if (lower.includes('m4a') || lower.includes('mp4')) return 'm4a'; if (lower.includes('wav')) return 'wav'; if (lower.includes('mpeg') || lower.includes('mp3')) return 'mp3'; if (lower.includes('ogg')) return 'ogg'; return 'webm'; } +function codexLooksLikeAudioFile(file) { + const type = (file?.type || '').toLowerCase(); + if (type.startsWith('audio/')) return true; + if (type.startsWith('video/')) return false; + const name = (file?.name || '').toLowerCase(); + return /\.(m4a|mp3|wav|webm|ogg|aac|flac)$/i.test(name); +} + function codexIsSecureVoiceContext() { return window.isSecureContext || ['localhost', '127.0.0.1', '::1'].includes(window.location.hostname); } @@ -104,11 +112,11 @@ function codexOpenAudioFilePicker(message) { async function codexStartVoiceInput() { if (!codexIsSecureVoiceContext()) { - codexOpenAudioFilePicker('手机 Chrome 需要 HTTPS 才能直接录音,请改用系统录音/音频文件'); + codexSetVoiceStatus('手机局域网 HTTP 页面不能直接录音;请先用语音备忘录录音,再点“上传音频”。'); return; } if (!navigator.mediaDevices?.getUserMedia || !window.MediaRecorder) { - codexOpenAudioFilePicker('当前浏览器不能直接录音,请改用系统录音/音频文件'); + codexSetVoiceStatus('当前浏览器不能直接录音;请点“上传音频”选择录音文件。'); return; } try { @@ -158,21 +166,22 @@ async function codexToggleVoiceInput() { } } -async function codexFinishVoiceInput(blob) { +async function codexFinishVoiceInput(blob, filename = '') { if (!blob || blob.size === 0) { codexSetVoiceStatus('没有录到声音'); return; } codexSetVoiceStatus('正在转写...'); try { + const mimeType = blob.type || (filename ? '' : 'audio/webm'); const audioBase64 = await codexBlobToBase64(blob); const resp = await fetch('/api/codex/transcribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ audioBase64, - mimeType: blob.type || 'audio/webm', - filename: `codex-voice-${Date.now()}.${codexAudioExtension(blob.type)}`, + mimeType, + filename: filename || `codex-voice-${Date.now()}.${codexAudioExtension(mimeType)}`, language: 'zh', }), }); @@ -188,11 +197,11 @@ async function codexFinishVoiceInput(blob) { async function codexHandleAudioFilePicked(event) { const file = event.target?.files?.[0]; if (!file) return; - if (!file.type.startsWith('audio/')) { - codexSetVoiceStatus('请选择音频文件'); + if (!codexLooksLikeAudioFile(file)) { + codexSetVoiceStatus('请选择音频文件,不支持视频'); return; } - await codexFinishVoiceInput(file); + await codexFinishVoiceInput(file, file.name || ''); } async function codexLoadSessions(force = false) { @@ -416,7 +425,7 @@ function codexBind() { const audioPick = document.getElementById('codex-audio-pick'); if (audioPick && !audioPick.dataset.bound) { audioPick.dataset.bound = '1'; - audioPick.addEventListener('click', () => codexOpenAudioFilePicker('选择或录制一段音频后自动转写')); + audioPick.addEventListener('click', () => codexOpenAudioFilePicker('请选择语音备忘录/录音文件,选完会自动转写')); } const audioFile = document.getElementById('codex-audio-file'); if (audioFile && !audioFile.dataset.bound) { From 37b75756ff0d27879d3fd557fee70c2ba281a570 Mon Sep 17 00:00:00 2001 From: daixiawu Date: Mon, 29 Jun 2026 18:25:24 +0800 Subject: [PATCH 5/6] Build questionnaire center UI --- .../TH1_UI/View/Outside/UIOutsideMenuView.cs | 3 +- .../UIOutsideQuestionnaireOptionMono.cs | 8 + .../UIOutsideQuestionnaireQuestionMono.cs | 13 + .../Outside/UIOutsideQuestionnaireView.cs | 1043 ++++++++++++++--- 4 files changed, 922 insertions(+), 145 deletions(-) diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs index 02093a60d..83e97d314 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs @@ -142,7 +142,7 @@ namespace TH1_UI.View.Outside StoryButton.onClick.RemoveAllListeners(); StoryButton.onClick.AddListener(OnStoryClicked); BindTransReportButton(); - HideQuestionnaireButton(); + BindQuestionnaireButton(); SettingButton.onClick.RemoveAllListeners(); SettingButton.onClick.AddListener(OnSettingClicked); @@ -282,6 +282,7 @@ namespace TH1_UI.View.Outside } if (QuestionnaireButton == null) return; + QuestionnaireButton.gameObject.SetActive(true); RestoreButtonTargetAlpha(QuestionnaireButton); SetQuestionnaireButtonText(); QuestionnaireButton.onClick.RemoveAllListeners(); diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs index b21cffcb2..eb4fc4751 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs @@ -39,6 +39,14 @@ namespace TH1_UI.View.Outside Toggle?.SetIsOnWithoutNotify(value); } + public void SetInteractable(bool value) + { + if (Toggle != null) + { + Toggle.interactable = value; + } + } + private void OnToggleChanged(bool value) { _onValueChanged?.Invoke(this, value); diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs index 7506b40a4..fe081ef9b 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs @@ -103,6 +103,19 @@ namespace TH1_UI.View.Outside _onAnswerChanged?.Invoke(); } + public void SetReadOnly(bool readOnly) + { + if (OpenInput != null) + { + OpenInput.interactable = !readOnly; + } + + foreach (var item in _optionItems) + { + item.SetInteractable(!readOnly); + } + } + public bool HasAnswer() { if (QuestionInfo == null) return false; diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs index 5a218055e..a9e98c7f3 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Logic.CrashSight; @@ -37,10 +38,25 @@ namespace TH1_UI.View.Outside public ViDelegateAssisstant.Dele OnBtnCloseClick; private readonly List _questionItems = new List(); + private readonly List _listItems = new List(); + private QuestionnaireDataAssets _dataAssets; private QuestionnaireInfo _questionnaireInfo; private QuestionnaireAnswerSheet _currentSheet; private bool _isUploading; + private bool _layoutBuilt; + private bool _showExpired; + + private Transform _sidebarContent; + private Transform _rightBody; + private Transform _rightFooter; + private TextMeshProUGUI _expiredToggleText; + private TextMeshProUGUI _rightHeaderTitle; + private TextMeshProUGUI _rightHeaderSubtitle; + private Button _recordsButton; + private Button _startButton; + private TextMeshProUGUI _recordsButtonText; + private TextMeshProUGUI _startButtonText; protected override void OnInit() { @@ -53,46 +69,25 @@ namespace TH1_UI.View.Outside EnsureLayout(); LoadAssetsIfNeeded(); - _questionnaireInfo = null; - if (_dataAssets != null) - { - if (!string.IsNullOrEmpty(evt.QuestionnaireId)) - { - _dataAssets.GetQuestionnaireInfo(evt.QuestionnaireId, out _questionnaireInfo); - } - - _questionnaireInfo ??= _dataAssets.GetDefaultQuestionnaire(); - } - - if (_questionnaireInfo == null) - { - Debug.LogError("[UIOutsideQuestionnaireView] QuestionnaireDataAssets is missing questionnaire data."); - SetStatus(string.Empty); - return; - } - - TitleText.text = ResolveText(_questionnaireInfo.Title); - DescriptionText.text = ResolveText(_questionnaireInfo.Description); - SubmitButtonText.text = ResolveText(_questionnaireInfo.SubmitButtonText); - RefillButtonText.text = ResolveText(_questionnaireInfo.ResubmitButtonText); - CloseButtonText.text = ResolveText(_questionnaireInfo.CloseButtonText); - - SubmitButton.onClick.RemoveAllListeners(); - SubmitButton.onClick.AddListener(OnSubmitClicked); - RefillButton.onClick.RemoveAllListeners(); - RefillButton.onClick.AddListener(OnRefillClicked); CloseButton.onClick.RemoveAllListeners(); CloseButton.onClick.AddListener(OnCloseClicked); SetUploading(false); - BuildQuestions(); - _currentSheet = QuestionnaireAnswerStore.Instance.GetAnswerSheet(_questionnaireInfo.QuestionnaireId); - ApplyPreviousSheet(_currentSheet); + var selected = ResolveInitialQuestionnaire(evt); + if (selected != null && selected.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Expired) + { + _showExpired = true; + } - var hasSubmitted = _currentSheet != null; - RefillButton.gameObject.SetActive(hasSubmitted); - var submittedMessage = ResolveSubmittedMessage(_currentSheet); - SetStatus(hasSubmitted ? submittedMessage : string.Empty); + BuildQuestionnaireList(); + + if (selected == null) + { + ShowEmptyState("暂无可填写问卷"); + return; + } + + SelectQuestionnaire(selected); } public void OnCloseView() @@ -110,7 +105,8 @@ namespace TH1_UI.View.Outside return raw; } - public static TextMeshProUGUI CreateText(Transform parent, string objectName, string text, float fontSize, Color color, TextAlignmentOptions alignment) + public static TextMeshProUGUI CreateText(Transform parent, string objectName, string text, float fontSize, + Color color, TextAlignmentOptions alignment) { var go = new GameObject(objectName, typeof(RectTransform), typeof(TextMeshProUGUI)); go.transform.SetParent(parent, false); @@ -142,23 +138,425 @@ namespace TH1_UI.View.Outside } } - private void BuildQuestions() + private QuestionnaireInfo ResolveInitialQuestionnaire(ShowUIOutsideQuestionnaire evt) { - foreach (Transform child in QuestionListContent) + if (_dataAssets == null) { - Destroy(child.gameObject); + Debug.LogError("[UIOutsideQuestionnaireView] QuestionnaireDataAssets is missing."); + return null; } - _questionItems.Clear(); - if (_questionnaireInfo.Questions == null) return; - - for (var i = 0; i < _questionnaireInfo.Questions.Count; i++) + QuestionnaireInfo selected = null; + if (!string.IsNullOrEmpty(evt.QuestionnaireId)) { - var question = _questionnaireInfo.Questions[i]; + _dataAssets.GetQuestionnaireInfo(evt.QuestionnaireId, out selected); + } + + selected ??= _dataAssets.GetDefaultQuestionnaire(); + if (selected != null && selected.IsVisibleAt(DateTime.UtcNow)) return selected; + + return GetVisibleQuestionnaires().FirstOrDefault(); + } + + private List GetVisibleQuestionnaires() + { + return _dataAssets != null + ? _dataAssets.GetVisibleQuestionnaires(DateTime.UtcNow) + : new List(); + } + + private void SelectQuestionnaire(QuestionnaireInfo info) + { + if (info == null) + { + ShowEmptyState("暂无可填写问卷"); + return; + } + + _questionnaireInfo = info; + _currentSheet = QuestionnaireAnswerStore.Instance.GetLatestAnswerSheet(info.QuestionnaireId); + if (info.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Expired && !_showExpired) + { + _showExpired = true; + BuildQuestionnaireList(); + } + + UpdateListSelection(); + ShowDetail(); + } + + private void BuildQuestionnaireList() + { + if (_sidebarContent == null) return; + + DestroyChildren(_sidebarContent); + _listItems.Clear(); + + var items = GetVisibleQuestionnaires(); + var featured = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Featured).ToList(); + var longTerm = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.LongTerm).ToList(); + var expired = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Expired).ToList(); + + CreateSectionHeader(_sidebarContent, "当期问卷"); + CreateQuestionnaireListItems(featured, "暂无当期问卷"); + CreateDivider(_sidebarContent); + + CreateSectionHeader(_sidebarContent, "长期问卷"); + CreateQuestionnaireListItems(longTerm, "暂无长期问卷"); + CreateDivider(_sidebarContent); + + CreateExpiredHeader(expired.Count); + if (_showExpired) + { + CreateQuestionnaireListItems(expired, "暂无往期问卷"); + } + else if (expired.Count > 0) + { + var hint = CreateText(_sidebarContent, "ExpiredHiddenHint", $"{expired.Count} 份往期问卷已收起", 17f, + new Color(0.62f, 0.66f, 0.64f, 1f), TextAlignmentOptions.Left); + hint.enableWordWrapping = true; + } + + if (items.Count == 0) + { + var empty = CreateText(_sidebarContent, "EmptyList", "暂无可显示问卷", 18f, + new Color(0.74f, 0.76f, 0.72f, 1f), TextAlignmentOptions.Left); + empty.enableWordWrapping = true; + } + } + + private void CreateQuestionnaireListItems(List infos, string emptyText) + { + if (infos == null || infos.Count == 0) + { + var empty = CreateText(_sidebarContent, "Empty", emptyText, 17f, + new Color(0.62f, 0.66f, 0.64f, 1f), TextAlignmentOptions.Left); + empty.enableWordWrapping = true; + return; + } + + foreach (var info in infos) + { + CreateQuestionnaireListItem(info); + } + } + + private void CreateQuestionnaireListItem(QuestionnaireInfo info) + { + var row = new GameObject("QuestionnaireItem", typeof(RectTransform), typeof(Image), typeof(Button), + typeof(VerticalLayoutGroup), typeof(LayoutElement)); + row.transform.SetParent(_sidebarContent, false); + + var image = row.GetComponent(); + image.color = new Color(1f, 1f, 1f, 0.055f); + + var layout = row.GetComponent(); + layout.padding = new RectOffset(12, 12, 10, 10); + layout.spacing = 4f; + layout.childControlWidth = true; + layout.childControlHeight = true; + layout.childForceExpandWidth = true; + layout.childForceExpandHeight = false; + + var layoutElement = row.GetComponent(); + layoutElement.minHeight = 76f; + layoutElement.preferredHeight = 82f; + + var button = row.GetComponent