diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisGridCell.cs b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisGridCell.cs deleted file mode 100644 index af4e3dca9..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisGridCell.cs +++ /dev/null @@ -1,125 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; -using TMPro; - -namespace AibisDream.MiniGame.Language -{ - /// - /// 分析模式网格单元格组件 - /// - [RequireComponent(typeof(RectTransform))] - public class AnalysisGridCell : MonoBehaviour - { - [Header("UI 组件")] - [SerializeField] private Image backgroundImage; - [SerializeField] private TMP_Text contentText; - - [Header("样式配置")] - [SerializeField] private Color normalBackgroundColor = new Color(0f, 0.04f, 0f, 0.2f); - [SerializeField] private Color highlightBackgroundColor = new Color(0f, 0f, 0f, 0.8f); - - private void Awake() - { - // 自动查找组件 - if (backgroundImage == null) - { - backgroundImage = GetComponent(); - } - - if (contentText == null) - { - contentText = GetComponentInChildren(); - } - - // 设置默认样式 - if (backgroundImage != null) - { - backgroundImage.color = normalBackgroundColor; - } - - if (contentText != null) - { - contentText.color = Color.white; - contentText.fontSize = 13f; - contentText.alignment = TextAlignmentOptions.Center; - contentText.enableWordWrapping = false; - contentText.overflowMode = TextOverflowModes.Overflow; - } - } - - /// - /// 设置单元格文本 - /// - public void SetText(string text) - { - if (contentText != null) - { - contentText.text = text; - } - } - - /// - /// 设置文本颜色 - /// - public void SetTextColor(Color color) - { - if (contentText != null) - { - contentText.color = color; - } - } - - /// - /// 设置背景颜色 - /// - public void SetBackgroundColor(Color color) - { - if (backgroundImage != null) - { - backgroundImage.color = color; - } - } - - /// - /// 设置字体样式 - /// - public void SetFontStyle(FontStyles style) - { - if (contentText != null) - { - contentText.fontStyle = style; - } - } - - /// - /// 设置透明度 - /// - public void SetAlpha(float alpha) - { - if (contentText != null) - { - Color color = contentText.color; - color.a = alpha; - contentText.color = color; - } - } - - /// - /// 重置为默认样式 - /// - public void ResetStyle() - { - if (backgroundImage != null) - { - backgroundImage.color = normalBackgroundColor; - } - - if (contentText != null) - { - contentText.color = Color.white; - contentText.fontStyle = FontStyles.Normal; - } - } - } -} - diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisGridCell.cs.meta b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisGridCell.cs.meta deleted file mode 100644 index 87763442f..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisGridCell.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9547756a801380143846ebc10ce8274e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeData.cs b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeData.cs deleted file mode 100644 index 6e4fbed8c..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeData.cs +++ /dev/null @@ -1,237 +0,0 @@ -using UnityEngine; -using System.Collections.Generic; - -namespace AibisDream.MiniGame.Language -{ - /// - /// 分析模式关卡配置(ScriptableObject) - /// 用于存储每个分析谜题的数据 - /// - [CreateAssetMenu(fileName = "AnalysisLevel", menuName = "AIBIS/Language/Analysis Level")] - public class AnalysisModeData : ScriptableObject - { - [Header("关卡基础信息")] - [Tooltip("关卡名称")] - public string levelName = "Level 1"; - - [Tooltip("关卡描述")] - [TextArea(2, 4)] - public string description = "找出隐藏在笑话中的真实情绪"; - - [Header("解码器配置")] - [Tooltip("解码器宽度(列数)")] - public int decoderWidth = 5; - - [Tooltip("解码器高度(行数)")] - public int decoderHeight = 3; - - [Tooltip("孔洞位置(相对于解码器左上角)")] - public List holePositions = new List - { - new Vector2Int(0, 0), // 第1行第1列 - new Vector2Int(1, 1), // 第2行第2列 - new Vector2Int(2, 4) // 第3行第5列 - }; - - [Tooltip("解码器文字(按行从上到下、列从左到右的顺序)\n每个格子显示2字,孔洞位置自动跳过\n例如:要来了,不合格被骂了,这让我很\n需要字符数 = (宽×高 - 孔洞数) × 2")] - [TextArea(2, 5)] - public string decoderText = "要来了,不合格被骂了,这让我很"; - - [Header("答案配置")] - [Tooltip("答案所属情绪")] - public EmotionType answerEmotion = EmotionType.Fear; - - [Tooltip("答案词语(按孔洞顺序)")] - public List answerWords = new List { "临检", "英里", "担心" }; - - [Tooltip("解码器在网格中的正确位置(行,列)")] - public Vector2Int correctDecoderPosition = new Vector2Int(3, 5); - - [Tooltip("完整的答案句子(用于提示)")] - public string completeSentence = "[临检]要来了,不合格[英里]被骂了,这让我很[担心]"; - - [Header("词池配置")] - [Tooltip("每种情绪的词库(用于填充网格)")] - public EmotionWordPool wordPool = new EmotionWordPool(); - - [Header("笑话配置(可选)")] - [Tooltip("笑话短句(用于初始演出,如果为空则使用词池)")] - public List jokePhrases = new List(); - - [Header("难度配置")] - [Tooltip("诱饵数量:每种情绪的非答案词数量")] - [Range(5, 50)] - public int decoyCountPerEmotion = 15; - - [Tooltip("频率容差:验证时允许的频率偏差")] - [Range(1f, 15f)] - public float frequencyTolerance = 8f; - - [Header("完成回调")] - [Tooltip("成功完成时跳转的Yarn节点")] - public string successNodeName = ""; - - [Tooltip("失败时跳转的Yarn节点")] - public string failNodeName = ""; - - /// - /// 验证配置是否有效 - /// - public bool Validate(out string errorMessage) - { - // 检查答案词数量与孔洞数量是否匹配 - if (answerWords.Count != holePositions.Count) - { - errorMessage = $"答案词数量({answerWords.Count})与孔洞数量({holePositions.Count})不匹配"; - return false; - } - - // 检查答案词是否都是2字符 - foreach (var word in answerWords) - { - if (string.IsNullOrEmpty(word) || word.Length != 2) - { - errorMessage = $"答案词'{word}'必须是2个字符"; - return false; - } - } - - // 检查孔洞位置是否在解码器范围内 - foreach (var hole in holePositions) - { - if (hole.x < 0 || hole.x >= decoderHeight || hole.y < 0 || hole.y >= decoderWidth) - { - errorMessage = $"孔洞位置({hole.x}, {hole.y})超出解码器范围({decoderHeight}x{decoderWidth})"; - return false; - } - } - - // 检查词池是否有足够的词 - if (!wordPool.HasEnoughWords(answerEmotion, decoyCountPerEmotion + answerWords.Count)) - { - errorMessage = $"情绪{answerEmotion}的词池不足,需要至少{decoyCountPerEmotion + answerWords.Count}个词"; - return false; - } - - errorMessage = ""; - return true; - } - - /// - /// 获取网格尺寸建议(根据词数量自动计算) - /// - public Vector2Int GetRecommendedGridSize() - { - // 计算总词数:答案词 + 诱饵词(所有情绪) - int totalWords = answerWords.Count + decoyCountPerEmotion * 4; // 4种情绪 - - // 添加一些中性词作为填充 - int neutralWords = totalWords / 2; - totalWords += neutralWords; - - // 推荐网格尺寸(接近正方形) - int size = Mathf.CeilToInt(Mathf.Sqrt(totalWords)); - return new Vector2Int(size, size); - } - } - - /// - /// 情绪词池 - /// - [System.Serializable] - public class EmotionWordPool - { - [Header("快乐词语")] - [Tooltip("Joy情绪的词语库(2字词)")] - public List joyWords = new List - { - "开心", "快乐", "高兴", "喜悦", "欢笑", "愉快", "兴奋", "幸福", - "满足", "得意", "舒畅", "轻松", "欣慰", "庆幸", "欢喜", "喜庆" - }; - - [Header("悲伤词语")] - [Tooltip("Sadness情绪的词语库(2字词)")] - public List sadnessWords = new List - { - "悲伤", "难过", "伤心", "痛苦", "失望", "沮丧", "忧伤", "哀愁", - "失落", "绝望", "悲痛", "心碎", "凄凉", "孤独", "寂寞", "落寞" - }; - - [Header("愤怒词语")] - [Tooltip("Anger情绪的词语库(2字词)")] - public List angerWords = new List - { - "愤怒", "生气", "恼怒", "暴怒", "气愤", "愤恨", "恨意", "怒火", - "恼火", "火大", "抓狂", "烦躁", "憋屈", "不爽", "愤慨", "恼恨" - }; - - [Header("恐惧词语")] - [Tooltip("Fear情绪的词语库(2字词)")] - public List fearWords = new List - { - "恐惧", "害怕", "惊恐", "畏惧", "担心", "忧虑", "焦虑", "紧张", - "不安", "慌张", "惊慌", "胆怯", "恐慌", "惊吓", "惧怕", "临检", - "英里", "担忧", "忐忑", "惶恐" - }; - - [Header("中性词语")] - [Tooltip("Neutral情绪的词语库(用于填充,2字词)")] - public List neutralWords = new List - { - "工作", "学习", "生活", "时间", "地方", "事情", "人们", "世界", - "问题", "方法", "结果", "过程", "开始", "结束", "继续", "停止", - "前进", "后退", "上下", "左右", "这里", "那里", "现在", "以后" - }; - - /// - /// 获取指定情绪的词语列表 - /// - public List GetWords(EmotionType emotion) - { - switch (emotion) - { - case EmotionType.Joy: return joyWords; - case EmotionType.Sadness: return sadnessWords; - case EmotionType.Anger: return angerWords; - case EmotionType.Fear: return fearWords; - case EmotionType.Neutral: return neutralWords; - default: return neutralWords; - } - } - - /// - /// 检查是否有足够的词 - /// - public bool HasEnoughWords(EmotionType emotion, int requiredCount) - { - return GetWords(emotion).Count >= requiredCount; - } - - /// - /// 获取随机词(不重复) - /// - public List GetRandomWords(EmotionType emotion, int count, List exclude = null) - { - List pool = new List(GetWords(emotion)); - - // 排除指定词语 - if (exclude != null) - { - pool.RemoveAll(w => exclude.Contains(w)); - } - - // 随机打乱 - for (int i = 0; i < pool.Count; i++) - { - int randomIndex = Random.Range(i, pool.Count); - string temp = pool[i]; - pool[i] = pool[randomIndex]; - pool[randomIndex] = temp; - } - - // 返回指定数量 - return pool.GetRange(0, Mathf.Min(count, pool.Count)); - } - } -} - diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeData.cs.meta b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeData.cs.meta deleted file mode 100644 index 773236d30..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeData.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2d8de6401e07b534584449fb63ed5bd8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeGenerator.cs b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeGenerator.cs deleted file mode 100644 index b8e1e3578..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeGenerator.cs +++ /dev/null @@ -1,358 +0,0 @@ -using UnityEngine; -using System.Collections.Generic; -using System.Linq; - -namespace AibisDream.MiniGame.Language -{ - /// - /// 分析模式网格生成器 - /// 根据配置数据智能生成网格布局,确保唯一解 - /// - public class AnalysisModeGenerator - { - private AnalysisModeData config; - private System.Random random; - - public AnalysisModeGenerator(AnalysisModeData config, int seed = -1) - { - this.config = config; - this.random = seed >= 0 ? new System.Random(seed) : new System.Random(); - } - - /// - /// 生成完整的网格数据 - /// - public GridCell[,] GenerateGrid(int gridWidth, int gridHeight) - { - GridCell[,] grid = new GridCell[gridHeight, gridWidth]; - - // 第一步:放置答案词(在正确的解码器位置) - PlaceAnswerWords(grid, gridWidth, gridHeight); - - // 第二步:放置诱饵词(相同情绪,但位置不匹配解码器) - PlaceDecoyWords(grid, gridWidth, gridHeight); - - // 第三步:填充中性词 - FillNeutralWords(grid, gridWidth, gridHeight); - - // 第四步:验证唯一解 - if (!ValidateUniqueSolution(grid, gridWidth, gridHeight)) - { - Debug.LogWarning("[AnalysisModeGenerator] 生成的网格可能没有唯一解,建议调整配置"); - } - - return grid; - } - - /// - /// 放置答案词 - /// - private void PlaceAnswerWords(GridCell[,] grid, int gridWidth, int gridHeight) - { - Vector2Int decoderPos = config.correctDecoderPosition; - - for (int i = 0; i < config.answerWords.Count; i++) - { - // 计算答案词的绝对位置 = 解码器位置 + 孔洞相对位置 - Vector2Int holeOffset = config.holePositions[i]; - int row = decoderPos.x + holeOffset.x; - int col = decoderPos.y + holeOffset.y; - - // 确保位置在网格范围内 - if (row < 0 || row >= gridHeight || col < 0 || col >= gridWidth) - { - Debug.LogError($"[AnalysisModeGenerator] 答案词位置({row}, {col})超出网格范围"); - continue; - } - - // 放置答案词 - grid[row, col] = new GridCell - { - row = row, - col = col, - word = config.answerWords[i], - emotion = config.answerEmotion, - isTarget = true - }; - } - } - - /// - /// 放置诱饵词(相同情绪,但不匹配解码器) - /// - private void PlaceDecoyWords(GridCell[,] grid, int gridWidth, int gridHeight) - { - // 为每种情绪生成诱饵词 - EmotionType[] emotions = { EmotionType.Joy, EmotionType.Sadness, EmotionType.Anger, EmotionType.Fear }; - - foreach (var emotion in emotions) - { - // 获取该情绪的诱饵词数量 - int decoyCount = config.decoyCountPerEmotion; - - // 如果是答案情绪,减去答案词数量 - if (emotion == config.answerEmotion) - { - decoyCount = Mathf.Max(0, decoyCount - config.answerWords.Count); - } - - // 获取随机词语(排除答案词) - List decoyWords = config.wordPool.GetRandomWords( - emotion, - decoyCount, - emotion == config.answerEmotion ? config.answerWords : null - ); - - // 放置诱饵词 - PlaceWordsRandomly(grid, gridWidth, gridHeight, decoyWords, emotion, false); - } - } - - /// - /// 填充中性词 - /// - private void FillNeutralWords(GridCell[,] grid, int gridWidth, int gridHeight) - { - List neutralWords = new List(config.wordPool.neutralWords); - - // 随机打乱 - neutralWords = neutralWords.OrderBy(x => random.Next()).ToList(); - - int wordIndex = 0; - for (int row = 0; row < gridHeight; row++) - { - for (int col = 0; col < gridWidth; col++) - { - if (grid[row, col] == null) - { - // 如果词库用完了,重新开始 - if (wordIndex >= neutralWords.Count) - { - wordIndex = 0; - } - - grid[row, col] = new GridCell - { - row = row, - col = col, - word = neutralWords[wordIndex], - emotion = EmotionType.Neutral, - isTarget = false - }; - - wordIndex++; - } - } - } - } - - /// - /// 随机放置词语 - /// - private void PlaceWordsRandomly(GridCell[,] grid, int gridWidth, int gridHeight, - List words, EmotionType emotion, bool isTarget) - { - List availablePositions = new List(); - - // 收集所有可用位置 - for (int row = 0; row < gridHeight; row++) - { - for (int col = 0; col < gridWidth; col++) - { - if (grid[row, col] == null) - { - availablePositions.Add(new Vector2Int(row, col)); - } - } - } - - // 随机打乱 - availablePositions = availablePositions.OrderBy(x => random.Next()).ToList(); - - // 放置词语 - for (int i = 0; i < words.Count && i < availablePositions.Count; i++) - { - Vector2Int pos = availablePositions[i]; - grid[pos.x, pos.y] = new GridCell - { - row = pos.x, - col = pos.y, - word = words[i], - emotion = emotion, - isTarget = isTarget - }; - } - } - - /// - /// 验证唯一解:检查是否只有一个位置和情绪组合能匹配所有答案词 - /// - private bool ValidateUniqueSolution(GridCell[,] grid, int gridWidth, int gridHeight) - { - int matchCount = 0; - Vector2Int correctPos = config.correctDecoderPosition; - - // 遍历所有可能的解码器位置 - for (int row = 0; row <= gridHeight - config.decoderHeight; row++) - { - for (int col = 0; col <= gridWidth - config.decoderWidth; col++) - { - // 遍历所有情绪 - foreach (EmotionType emotion in System.Enum.GetValues(typeof(EmotionType))) - { - if (emotion == EmotionType.Neutral) continue; - - // 检查是否所有孔洞位置都匹配该情绪 - bool allMatch = true; - List matchedWords = new List(); - - foreach (var hole in config.holePositions) - { - int checkRow = row + hole.x; - int checkCol = col + hole.y; - - if (checkRow >= gridHeight || checkCol >= gridWidth) - { - allMatch = false; - break; - } - - GridCell cell = grid[checkRow, checkCol]; - if (cell == null || cell.emotion != emotion) - { - allMatch = false; - break; - } - - matchedWords.Add(cell.word); - } - - // 如果所有孔洞都匹配 - if (allMatch) - { - matchCount++; - - // 检查是否是正确答案 - bool isCorrectAnswer = (row == correctPos.x && col == correctPos.y && - emotion == config.answerEmotion); - - if (isCorrectAnswer) - { - // 验证词语是否完全匹配 - bool wordsMatch = true; - for (int i = 0; i < config.answerWords.Count; i++) - { - if (matchedWords[i] != config.answerWords[i]) - { - wordsMatch = false; - break; - } - } - - if (!wordsMatch) - { - Debug.LogError("[AnalysisModeGenerator] 正确位置的词语不匹配答案!"); - return false; - } - } - else - { - // 诱饵解 - Debug.Log($"[AnalysisModeGenerator] 发现诱饵解: 位置({row},{col}), 情绪{emotion}, 词语[{string.Join(",", matchedWords)}]"); - } - } - } - } - } - - // 应该恰好有一个解 - if (matchCount == 0) - { - Debug.LogError("[AnalysisModeGenerator] 没有找到任何解!"); - return false; - } - else if (matchCount > 1) - { - Debug.LogWarning($"[AnalysisModeGenerator] 找到 {matchCount} 个可能的解(包括诱饵),这可能增加难度"); - } - else - { - Debug.Log("[AnalysisModeGenerator] 验证通过:唯一解"); - } - - return true; - } - - /// - /// 生成笑话演出数据(可选) - /// 将网格数据转换为笑话段落,用于初始演出 - /// - public List GenerateJokePhrases(GridCell[,] grid, int gridWidth, int gridHeight) - { - // 如果配置中有笑话,直接使用 - if (config.jokePhrases != null && config.jokePhrases.Count > 0) - { - return new List(config.jokePhrases); - } - - // 否则从网格词语生成简单的笑话段落 - List phrases = new List(); - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - - for (int row = 0; row < gridHeight; row++) - { - for (int col = 0; col < gridWidth; col += 10) // 每10个词组成一句 - { - sb.Clear(); - for (int i = 0; i < 10 && col + i < gridWidth; i++) - { - if (grid[row, col + i] != null) - { - sb.Append(grid[row, col + i].word); - } - } - if (sb.Length > 0) - { - phrases.Add(sb.ToString()); - } - } - } - - return phrases; - } - - /// - /// 生成解析目标配置(用于 AnalysisModeManager) - /// - public AnalysisTarget GenerateAnalysisTarget() - { - AnalysisTarget target = new AnalysisTarget - { - name = config.levelName, - emotion = config.answerEmotion, - stripPosition = config.correctDecoderPosition, - isCorrectAnswer = true, - words = new List() - }; - - // 添加目标词 - for (int i = 0; i < config.answerWords.Count; i++) - { - Vector2Int holeOffset = config.holePositions[i]; - Vector2Int absolutePos = new Vector2Int( - config.correctDecoderPosition.x + holeOffset.x, - config.correctDecoderPosition.y + holeOffset.y - ); - - target.words.Add(new TargetWord - { - position = absolutePos, - word = config.answerWords[i] - }); - } - - return target; - } - } -} - diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeGenerator.cs.meta b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeGenerator.cs.meta deleted file mode 100644 index 3bea34162..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeGenerator.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 641e33af9f9410841b81fa65933f357d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeManager.cs b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeManager.cs deleted file mode 100644 index a784a6753..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeManager.cs +++ /dev/null @@ -1,1763 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; -using TMPro; -using System.Collections.Generic; -using System.Linq; -using DG.Tweening; -using Shapes; -using AibisDream.FixSystem; -using AibisDream.Framework; -using Cinemachine; - -namespace AibisDream.MiniGame.Language -{ - - /// - /// 情绪类型 - /// - public enum EmotionType - { - Joy, // 快乐 - 15Hz - Sadness, // 悲伤 - 40Hz - Anger, // 愤怒 - 65Hz - Fear, // 恐惧 - 90Hz - Neutral // 中性/噪音 - } - - /// - /// 情绪配置 - /// - [System.Serializable] - public class EmotionConfig - { - public EmotionType type; - public float frequency; // 0-100 的频率值 - public Color color; - public string displayName; - - public static EmotionConfig Joy => new EmotionConfig - { - type = EmotionType.Joy, - frequency = 15f, - color = new Color(1f, 1f, 0f, 1f), - displayName = "JOY" - }; - - public static EmotionConfig Sadness => new EmotionConfig - { - type = EmotionType.Sadness, - frequency = 40f, - color = new Color(0f, 0.67f, 1f, 1f), - displayName = "SADNESS" - }; - - public static EmotionConfig Anger => new EmotionConfig - { - type = EmotionType.Anger, - frequency = 65f, - color = new Color(1f, 0.2f, 0.2f, 1f), - displayName = "ANGER" - }; - - public static EmotionConfig Fear => new EmotionConfig - { - type = EmotionType.Fear, - frequency = 90f, - color = new Color(0.67f, 0f, 1f, 1f), - displayName = "FEAR" - }; - - public static EmotionConfig Neutral => new EmotionConfig - { - type = EmotionType.Neutral, - frequency = -1f, - color = new Color(0.5f, 0.5f, 0.5f, 1f), - displayName = "NOISE" - }; - } - - /// - /// 网格单元格数据 - /// - [System.Serializable] - public class GridCell - { - public string word; // 词语内容 - public EmotionType emotion; - public bool isTarget; // 是否是目标词 - public int row; - public int col; - } - - /// - /// 解析目标配置 - /// - [System.Serializable] - public class AnalysisTarget - { - public string name; - public EmotionType emotion; - public List words = new List(); - public Vector2Int stripPosition; // 解码器条纹正确位置(行,列) - public bool isCorrectAnswer; // 是否是正确答案 - } - - /// - /// 目标词配置 - /// - [System.Serializable] - public class TargetWord - { - public Vector2Int position; // 在网格中的位置(行,列) - public string word; // 词语内容 - } - - /// - /// 粒子视觉状态(用于平滑过渡) - /// - public class ParticleVisualState - { - public float targetAlpha = 1f; - public float currentAlpha = 1f; - public Color targetColor = Color.white; - public Color currentColor = Color.white; - public float targetScale = 1f; - public float currentScale = 1f; - public Vector3 basePosition; - public Vector3 jitterOffset = Vector3.zero; - public bool isShowingWord = false; // true = word, false = cover - public Tweener alphaTween; - public Tweener colorTween; - public Tweener scaleTween; - public Tweener jitterTween; - } - - /// - /// 分析模式管理器 - /// - public class AnalysisModeManager : MonoBehaviour - { - public BubbleSlotGroup bubbleSlotGroup; - [Header("Canvas 引用(共享)")] - [SerializeField] private Canvas sharedCanvas; // 共享 LanguageParticleManager 的 canvas - - [Header("UI 引用")] - [SerializeField] private RectTransform analysisPanel; - [SerializeField] private Button initializeButton; - [SerializeField] private Slider emotionSlider; - [SerializeField] private TMP_Text frequencyText; - [SerializeField] private TMP_Text signalStrengthText; - [SerializeField] private Button startFilterButton; - [SerializeField] private Button verifyButton; - - [Header("网格配置(舞台布局)")] - [Tooltip("可选:用于定位网格中心的容器")] - [SerializeField] private RectTransform gridContainer; - [Tooltip("网格宽度(列数)")] - [SerializeField] private int gridWidth = 20; - [Tooltip("网格高度(行数)")] - [SerializeField] private int gridHeight = 20; - [Tooltip("单元格大小(Unity单位)")] - [SerializeField] private float cellSize = 0.4f; - [Tooltip("单元格间距(Unity单位)")] - [SerializeField] private float cellGap = 0.05f; - [Tooltip("文字大小")] - [SerializeField] private float cellFontSize = 2.5f; - [Tooltip("网格中心偏移量")] - [SerializeField] private Vector3 gridCenterOffset = Vector3.zero; - - - [Header("字体配置")] - [Tooltip("中文字体资源(用于网格文字)")] - [SerializeField] private TMP_FontAsset chineseFontAsset; - - [Header("动画配置")] - - [Tooltip("视觉效果过渡时长(颜色、透明度等)")] - [SerializeField] private float visualTransitionDuration = 0.15f; - [Tooltip("闪烁效果间隔")] - [SerializeField] private float glitchInterval = 0.1f; - [Tooltip("抖动强度")] - [SerializeField] private float jitterIntensity = 0.002f; - - [Header("网格样式")] - [SerializeField] private Color gridLineColor = new Color(1f, 1f, 1f, 0.1f); - [SerializeField] private float gridThickness = 0.02f; - - [Header("关卡配置(数据驱动)")] - [Tooltip("默认关卡配置数据(ScriptableObject)\n也可以通过代码动态加载其他关卡")] - [SerializeField] private AnalysisModeData currentLevelData; - - // 运行时数据 - private List analysisTargets = new List(); - private AnalysisModeGenerator generator; - private int stripWidth; // 从关卡数据动态获取 - private int stripHeight; // 从关卡数据动态获取 - - // 状态 - private bool isAnalysisModeActive = false; - private bool isFilteringActive = false; - private float currentFrequency = 50f; - private GridCell[,] gridData; - private Dictionary gridParticles = new Dictionary(); - private bool isInitialized = false; - private string completionNodeName = ""; - - // 平滑过渡状态存储 - private Dictionary particleStates = new Dictionary(); - - // 闪烁计时器(用于平滑闪烁) - private Dictionary glitchTimers = new Dictionary(); - - // Shapes 格子绘制器 - private GameObject mainGridDrawerObj; - private GameObject decoderGridDrawerObj; - - private Vector3 gridStartPos; // 网格起始位置(左上角) - - // 解码器相关 - private Dictionary decoderParticles = new Dictionary(); - private Vector2Int currentDecoderGridPos; // 解码器在主网格中的位置(行,列) - private bool isDraggingDecoder = false; - private Vector3 dragStartMouseWorldPos; // 拖拽开始时的鼠标世界坐标 - private Vector3 dragStartDecoderWorldPos; // 拖拽开始时的解码器世界坐标 - private Vector2Int dragStartGridPos; // 拖拽开始时的解码器网格坐标 - - private void Start() - { - // 注册到系统字典 - FixSystemCenter.SystemDic.Register(this); - - // 注册相机 - var virtualCam = transform.Find("Analysis Camera")?.GetComponent(); - if (virtualCam != null) - { - CameraKit.Instance.RegisterCamera(CameraEnum.AnalysisMode, virtualCam); - } - - SetupUI(); - } - - private void OnDestroy() - { - CameraKit.Instance?.UnRegisterCamera(CameraEnum.AnalysisMode); - CameraKit.Instance?.UnRegisterCamera(CameraEnum.AnalysisModeDeep); - } - - /// - /// 进入分析模式(由状态机调用) - /// - public void OnEnter() - { - // 开启分析模式视图 - // if (!isAnalysisModeActive) - // { - // OpenView(); - // } - } - - /// - /// 退出分析模式(由状态机调用) - /// - public void OnExit() - { - // 关闭分析模式视图 - if (isAnalysisModeActive) - { - CloseView(); - } - } - - private void SetupUI() - { - if (analysisPanel != null) - { - analysisPanel.gameObject.SetActive(false); - } - - if (initializeButton != null) - { - initializeButton.onClick.AddListener(OnInitializeClicked); - // 初始化按钮默认可用 - initializeButton.interactable = true; - } - - if (startFilterButton != null) - { - startFilterButton.onClick.AddListener(OnStartFilterClicked); - // 过滤按钮在初始化前不可用 - startFilterButton.interactable = false; - } - - if (verifyButton != null) - { - verifyButton.onClick.AddListener(OnVerifyClicked); - verifyButton.interactable = false; - } - - if (emotionSlider != null) - { - emotionSlider.minValue = 0f; - emotionSlider.maxValue = 100f; - emotionSlider.value = 50f; - emotionSlider.onValueChanged.AddListener(OnEmotionSliderChanged); - // 滑动条在过滤模式开启前不可用 - emotionSlider.interactable = false; - } - - } - - /// - /// 加载关卡数据(数据驱动) - /// - private void LoadLevelData(AnalysisModeData levelData) - { - if (levelData == null) - { - Debug.LogError("[AnalysisModeManager] 关卡数据为空!"); - return; - } - - // 验证配置 - if (!levelData.Validate(out string errorMessage)) - { - Debug.LogError($"[AnalysisModeManager] 关卡配置无效: {errorMessage}"); - return; - } - - currentLevelData = levelData; - - // 创建生成器 - generator = new AnalysisModeGenerator(levelData); - - // 清空目标列表 - analysisTargets.Clear(); - - // 使用生成器创建目标 - AnalysisTarget target = generator.GenerateAnalysisTarget(); - analysisTargets.Add(target); - - // 更新解码器尺寸 - stripWidth = levelData.decoderWidth; - stripHeight = levelData.decoderHeight; - - Debug.Log($"[AnalysisModeManager] 已加载关卡: {levelData.levelName}"); - } - - /// - /// 启动分析模式(独立系统入口) - /// - /// 关卡配置数据 - /// 完成回调节点(可选,会覆盖配置中的节点) - public void OpenView(AnalysisModeData levelData = null, string completionNode = null) - { - if (isAnalysisModeActive) return; - - // 加载关卡数据 - if (levelData != null) - { - LoadLevelData(levelData); - } - else if (currentLevelData != null) - { - // 使用默认配置 - LoadLevelData(currentLevelData); - } - else - { - Debug.LogError("[AnalysisModeManager] 没有可用的关卡数据!"); - return; - } - - // 设置完成回调节点(优先使用参数,其次使用配置) - completionNodeName = !string.IsNullOrEmpty(completionNode) - ? completionNode - : currentLevelData.successNodeName; - - // 显示分析控制面板 - isAnalysisModeActive = true; - if (analysisPanel != null) - { - analysisPanel.gameObject.SetActive(true); - } - - // 自动初始化网格 - if (!isInitialized) - { - InitializeGrid(); - } - } - - /// - /// 关闭分析模式 - /// - public void CloseView() - { - if (!isAnalysisModeActive) return; - - isAnalysisModeActive = false; - - // 重置状态 - isFilteringActive = false; - isInitialized = false; - currentFrequency = 50f; - ClearGrid(); - - } - - private void OnInitializeClicked() - { - // 只能初始化一次 - if (isInitialized) - { - return; - } - - // 禁用初始化按钮 - if (initializeButton != null) - { - initializeButton.interactable = false; - } - - InitializeGrid(); - } - - private void InitializeGrid() - { - if (currentLevelData == null || generator == null) - { - Debug.LogError("[AnalysisModeManager] 未加载关卡数据或生成器未初始化!"); - return; - } - - // 使用生成器创建网格数据(数据驱动,智能生成) - gridData = generator.GenerateGrid(gridWidth, gridHeight); - - // 创建网格 UI(带动画) - StartCoroutine(AnimateGridCreation()); - } - - private System.Collections.IEnumerator AnimateGridCreation() - { - ClearGrid(); - - // 计算网格中心位置 - Vector3 gridCenter = Vector3.zero; - if (sharedCanvas != null) - { - gridCenter = sharedCanvas.transform.position + gridCenterOffset; - } - - // 计算网格总大小 - float totalWidth = gridWidth * cellSize + (gridWidth - 1) * cellGap; - float totalHeight = gridHeight * cellSize + (gridHeight - 1) * cellGap; - - // 计算起始位置(左上角) - gridStartPos = gridCenter - new Vector3(totalWidth / 2f, totalHeight / 2f, 0f); - - // 创建主矩阵 Shapes 格子绘制器 - CreateMainGridDrawer(); - - // 随机顺序创建单元格(Matrix 风格) - List cellPositions = new List(); - for (int row = 0; row < gridHeight; row++) - { - for (int col = 0; col < gridWidth; col++) - { - cellPositions.Add(new Vector2Int(row, col)); - } - } - cellPositions = cellPositions.OrderBy(x => Random.value).ToList(); - - int batchSize = 10; - for (int i = 0; i < cellPositions.Count; i++) - { - var pos = cellPositions[i]; - CreateGridParticle(pos.x, pos.y, gridStartPos); - - if (i % batchSize == 0) - { - yield return new WaitForSeconds(0.01f); - } - } - - yield return new WaitForSeconds(0.5f); - - isInitialized = true; - - if (startFilterButton != null) - { - startFilterButton.interactable = true; - } - } - - /// - /// 创建主矩阵 Shapes 格子绘制器 - /// - private void CreateMainGridDrawer() - { - if (sharedCanvas == null) return; - - // 清理旧的绘制器 - if (mainGridDrawerObj != null) - { - Destroy(mainGridDrawerObj); - } - - // 创建绘制器对象 - mainGridDrawerObj = new GameObject("MainGridDrawer"); - // 保持在 Canvas 下 - mainGridDrawerObj.transform.SetParent(sharedCanvas.transform, false); - - // 设置位置到网格起始点(左上角),并保持旋转一致 - mainGridDrawerObj.transform.position = gridStartPos; - mainGridDrawerObj.transform.rotation = sharedCanvas.transform.rotation; - mainGridDrawerObj.transform.localScale = Vector3.one; - - // 添加 RectTransform(Canvas 下通常需要,虽然 Shapes 主要用 Transform) - RectTransform rect = mainGridDrawerObj.AddComponent(); - rect.anchorMin = new Vector2(0.5f, 0.5f); - rect.anchorMax = new Vector2(0.5f, 0.5f); - rect.sizeDelta = Vector2.zero; - - // 计算步长 - float stride = cellSize + cellGap; - float halfStride = stride / 2f; - - // 创建网格容器(避免 Hierarchy 太乱) - GameObject linesContainer = new GameObject("Lines"); - linesContainer.transform.SetParent(mainGridDrawerObj.transform, false); - linesContainer.transform.localPosition = Vector3.zero; - - // 绘制水平线 (Rows + 1) - // Y轴向下为负,Row 0 中心在 0,上方边界在 +halfStride - float startX = -halfStride; - float endX = (gridWidth * stride) - halfStride; - - for (int row = 0; row <= gridHeight; row++) - { - float yPos = -(row * stride) + halfStride; - CreateGridLine(linesContainer.transform, new Vector3(startX, yPos, 0), new Vector3(endX, yPos, 0)); - } - - // 绘制垂直线 (Cols + 1) - // X轴向右为正,Col 0 中心在 0,左方边界在 -halfStride - float startY = halfStride; - float endY = -(gridHeight * stride) + halfStride; - - for (int col = 0; col <= gridWidth; col++) - { - float xPos = (col * stride) - halfStride; - CreateGridLine(linesContainer.transform, new Vector3(xPos, startY, 0), new Vector3(xPos, endY, 0)); - } - } - - private void CreateGridLine(Transform parent, Vector3 start, Vector3 end) - { - GameObject lineObj = new GameObject("GridLine"); - lineObj.transform.SetParent(parent, false); - - Line line = lineObj.AddComponent(); - line.Start = start; - line.End = end; - line.Color = gridLineColor; - line.Thickness = gridThickness; - line.ThicknessSpace = ThicknessSpace.Meters; // 或者 Pixels,取决于需求,通常 World Space 用 Meters - line.SortingOrder = 5; - - // 确保渲染顺序正确(如果是在 Overlay Canvas 下,Z轴可能无效,需要 SortingOrder) - // Shapes 通常会自动处理,但在 UI 中可能需要调整 Z - line.transform.localPosition = Vector3.zero; - } - - /// - /// 创建解码器 - /// - private void CreateDecoder() - { - if (currentLevelData == null || sharedCanvas == null) return; - - // 清理旧的解码器 - ClearDecoder(); - - // 初始化解码器位置(网格坐标,默认在中心附近) - currentDecoderGridPos = new Vector2Int( - gridHeight / 2 - stripHeight / 2, - gridWidth / 2 - stripWidth / 2 - ); - - // 创建解码器网格绘制器 - CreateDecoderGridDrawer(); - - // 创建解码器文字粒子 - CreateDecoderParticles(); - - // 更新解码器位置 - UpdateDecoderPosition(); - } - - /// - /// 创建解码器网格绘制器(类似MainGridDrawer) - /// - private void CreateDecoderGridDrawer() - { - if (sharedCanvas == null) return; - - // 创建绘制器对象 - decoderGridDrawerObj = new GameObject("DecoderGridDrawer"); - decoderGridDrawerObj.transform.SetParent(sharedCanvas.transform, false); - - // 设置初始位置 - decoderGridDrawerObj.transform.rotation = sharedCanvas.transform.rotation; - decoderGridDrawerObj.transform.localScale = Vector3.one; - - // 添加 RectTransform - RectTransform rect = decoderGridDrawerObj.AddComponent(); - rect.anchorMin = new Vector2(0.5f, 0.5f); - rect.anchorMax = new Vector2(0.5f, 0.5f); - rect.sizeDelta = Vector2.zero; - - // 计算步长(与主网格一致) - float stride = cellSize + cellGap; - float halfStride = stride / 2f; - - // 创建网格容器 - GameObject linesContainer = new GameObject("Lines"); - linesContainer.transform.SetParent(decoderGridDrawerObj.transform, false); - linesContainer.transform.localPosition = Vector3.zero; - - // 绘制水平线 (stripHeight + 1 条) - float startX = -halfStride; - float endX = (stripWidth * stride) - halfStride; - - for (int row = 0; row <= stripHeight; row++) - { - float yPos = -(row * stride) + halfStride; - CreateDecoderGridLine(linesContainer.transform, new Vector3(startX, yPos, 0), new Vector3(endX, yPos, 0)); - } - - // 绘制垂直线 (stripWidth + 1 条) - float startY = halfStride; - float endY = -(stripHeight * stride) + halfStride; - - for (int col = 0; col <= stripWidth; col++) - { - float xPos = (col * stride) - halfStride; - CreateDecoderGridLine(linesContainer.transform, new Vector3(xPos, startY, 0), new Vector3(xPos, endY, 0)); - } - - // 添加拖拽功能 - AddDecoderDragHandler(); - } - - /// - /// 创建解码器网格线(与主网格不同的颜色和渲染顺序) - /// - private void CreateDecoderGridLine(Transform parent, Vector3 start, Vector3 end) - { - GameObject lineObj = new GameObject("DecoderGridLine"); - lineObj.transform.SetParent(parent, false); - - Line line = lineObj.AddComponent(); - line.Start = start; - line.End = end; - // 使用 cyan 颜色(参考 web 版本的 border: 1px solid cyan) - line.Color = new Color(0f, 1f, 1f, 0.8f); // Cyan 青色,较高不透明度 - line.Thickness = gridThickness * 1.5f; // 稍微粗一些 - line.ThicknessSpace = ThicknessSpace.Meters; - line.SortingOrder = 10; // 更高的渲染顺序,显示在主网格之上 - - line.transform.localPosition = Vector3.zero; - } - - /// - /// 添加解码器拖拽处理器 - /// - private void AddDecoderDragHandler() - { - if (decoderGridDrawerObj == null) return; - - // 创建一个透明的Image作为拖拽区域 - GameObject dragAreaObj = new GameObject("DragArea"); - dragAreaObj.transform.SetParent(decoderGridDrawerObj.transform, false); - - RectTransform dragRect = dragAreaObj.AddComponent(); - dragRect.anchorMin = Vector2.zero; - dragRect.anchorMax = Vector2.one; - dragRect.offsetMin = Vector2.zero; - dragRect.offsetMax = Vector2.zero; - - float stride = cellSize + cellGap; - dragRect.sizeDelta = new Vector2(stripWidth * stride, stripHeight * stride); - dragRect.anchoredPosition = new Vector2( - (stripWidth * stride) / 2f, - -(stripHeight * stride) / 2f - ); - - // 添加半透明背景,让解码器区域更明显,同时也是拖拽响应区域 - Image dragImage = dragAreaObj.AddComponent(); - dragImage.color = new Color(0f, 0f, 0f, 0.1f); // 极淡的半透明背景,主要用于拖拽响应 - dragImage.raycastTarget = true; - - // 添加 EventTrigger 组件用于处理鼠标事件 - UnityEngine.EventSystems.EventTrigger eventTrigger = dragAreaObj.AddComponent(); - - // 开始拖拽 - UnityEngine.EventSystems.EventTrigger.Entry beginDragEntry = new UnityEngine.EventSystems.EventTrigger.Entry(); - beginDragEntry.eventID = UnityEngine.EventSystems.EventTriggerType.BeginDrag; - beginDragEntry.callback.AddListener((data) => { OnDecoderBeginDrag((UnityEngine.EventSystems.PointerEventData)data); }); - eventTrigger.triggers.Add(beginDragEntry); - - // 拖拽中 - UnityEngine.EventSystems.EventTrigger.Entry dragEntry = new UnityEngine.EventSystems.EventTrigger.Entry(); - dragEntry.eventID = UnityEngine.EventSystems.EventTriggerType.Drag; - dragEntry.callback.AddListener((data) => { OnDecoderDrag((UnityEngine.EventSystems.PointerEventData)data); }); - eventTrigger.triggers.Add(dragEntry); - - // 结束拖拽 - UnityEngine.EventSystems.EventTrigger.Entry endDragEntry = new UnityEngine.EventSystems.EventTrigger.Entry(); - endDragEntry.eventID = UnityEngine.EventSystems.EventTriggerType.EndDrag; - endDragEntry.callback.AddListener((data) => { OnDecoderEndDrag((UnityEngine.EventSystems.PointerEventData)data); }); - eventTrigger.triggers.Add(endDragEntry); - } - - /// - /// 创建解码器文字粒子 - /// - private void CreateDecoderParticles() - { - if (currentLevelData == null || sharedCanvas == null) return; - - // 解析解码器文字 - string decoderText = currentLevelData.decoderText; - List words = SplitIntoTwoCharChunks(decoderText); - int wordIndex = 0; - - // 创建HashSet用于快速查找孔洞 - HashSet holes = new HashSet(currentLevelData.holePositions); - - for (int row = 0; row < stripHeight; row++) - { - for (int col = 0; col < stripWidth; col++) - { - Vector2Int localPos = new Vector2Int(row, col); - - // 如果是孔洞,跳过 - if (holes.Contains(localPos)) - { - continue; - } - - // 如果还有词可以显示 - if (wordIndex < words.Count) - { - CreateDecoderParticle(row, col, words[wordIndex]); - wordIndex++; - } - } - } - } - - /// - /// 创建单个解码器粒子 - /// - private void CreateDecoderParticle(int row, int col, string word) - { - if (sharedCanvas == null) return; - - // 先创建背景方块(使用 Shapes) - CreateDecoderCellBackground(row, col); - - // 创建粒子对象 - GameObject particleObj = new GameObject($"DecoderParticle_{row}_{col}"); - - // 添加 RectTransform - RectTransform rectTransform = particleObj.AddComponent(); - rectTransform.SetParent(decoderGridDrawerObj.transform, false); - rectTransform.localScale = Vector3.one; - rectTransform.sizeDelta = Vector2.zero; - - // 计算局部位置(相对于解码器左上角) - float stride = cellSize + cellGap; - float xPos = col * stride; - float yPos = -row * stride; - rectTransform.localPosition = new Vector3(xPos, yPos, -0.15f); // 在背景之上 - - // 添加 TextParticle 组件 - TextParticle particle = particleObj.AddComponent(); - particle.SetFont(chineseFontAsset); - - // 设置为完全静态 - particle.isStatic = true; - particle.isCalmed = true; - particle.velocity = Vector2.zero; - - // 设置透明度 - particle.alpha = 1f; - particle.baseAlpha = 1f; - - // 设置文字内容 - particle.ForceSetCharacter(word); - - // 设置字体大小和颜色(参考 web 版本的 cyan 颜色) - if (particle.textMesh != null) - { - particle.textMesh.fontSize = cellFontSize; - // 使用 cyan 颜色(0, 255, 255)参考 web 版本 - particle.textMesh.color = new Color(0f, 1f, 1f, 1f); // Cyan 青色 - particle.textMesh.fontStyle = FontStyles.Bold; - } - - // 添加发光效果(参考 web 版本的 text-shadow: 0 0 8px cyan) - if (particle.glowSprite != null) - { - particle.glowSprite.color = new Color(0f, 1f, 1f, 0.6f); // Cyan 发光 - } - - Vector2Int pos = new Vector2Int(row, col); - decoderParticles[pos] = particle; - } - - /// - /// 创建解码器单元格背景(遮挡主网格文字) - /// - private void CreateDecoderCellBackground(int row, int col) - { - if (decoderGridDrawerObj == null) return; - - // 创建背景对象 - GameObject bgObj = new GameObject($"DecoderBg_{row}_{col}"); - bgObj.transform.SetParent(decoderGridDrawerObj.transform, false); - - // 计算局部位置(相对于解码器左上角) - float stride = cellSize + cellGap; - float xPos = col * stride; - float yPos = -row * stride; - bgObj.transform.localPosition = new Vector3(xPos, yPos, -0.1f); // 更靠前的 Z 轴,确保遮挡主网格 - - // 添加 Shapes Rectangle 组件 - Shapes.Rectangle rect = bgObj.AddComponent(); - // 背景大小应该填充整个格子区域(包括间隙),确保完全遮挡下方文字 - rect.Width = stride; - rect.Height = stride; - // 使用深青色背景,80% 不透明度 - rect.Color = new Color(0f, 0.12f, 0.12f, 0.8f); - rect.SortingOrder = 8; // 在主网格之上,在解码器网格线之下 - rect.CornerRadii = Vector4.one * 0.02f; // 轻微圆角 - // 设置为不透明渲染模式 - rect.BlendMode = Shapes.ShapesBlendMode.Opaque; - } - - /// - /// 更新解码器位置(对齐到网格) - /// - private void UpdateDecoderPosition() - { - if (decoderGridDrawerObj == null) return; - - // 计算解码器的世界位置 - float stride = cellSize + cellGap; - Vector3 decoderWorldPos = gridStartPos + new Vector3( - currentDecoderGridPos.y * stride, - -currentDecoderGridPos.x * stride, - -0.1f // Z轴稍微往前,确保显示在主网格之上 - ); - - decoderGridDrawerObj.transform.position = decoderWorldPos; - } - - /// - /// 开始拖拽解码器 - /// - private void OnDecoderBeginDrag(UnityEngine.EventSystems.PointerEventData eventData) - { - isDraggingDecoder = true; - dragStartGridPos = currentDecoderGridPos; - - // 记录鼠标的世界坐标 - if (RectTransformUtility.ScreenPointToWorldPointInRectangle( - sharedCanvas.transform as RectTransform, - eventData.position, - eventData.pressEventCamera, - out dragStartMouseWorldPos)) - { - // 记录解码器的世界坐标 - if (decoderGridDrawerObj != null) - { - dragStartDecoderWorldPos = decoderGridDrawerObj.transform.position; - } - } - } - - /// - /// 拖拽解码器中 - /// - private void OnDecoderDrag(UnityEngine.EventSystems.PointerEventData eventData) - { - if (!isDraggingDecoder || decoderGridDrawerObj == null) return; - - // 获取当前鼠标的世界坐标 - Vector3 currentMouseWorldPos; - if (RectTransformUtility.ScreenPointToWorldPointInRectangle( - sharedCanvas.transform as RectTransform, - eventData.position, - eventData.pressEventCamera, - out currentMouseWorldPos)) - { - // 计算鼠标移动的增量(世界坐标) - Vector3 mouseDelta = currentMouseWorldPos - dragStartMouseWorldPos; - - // 计算解码器应该在的世界坐标(保持鼠标和解码器的相对位置) - Vector3 targetDecoderWorldPos = dragStartDecoderWorldPos + mouseDelta; - - // 将目标世界坐标转换为网格坐标 - Vector3 offsetFromGridStart = targetDecoderWorldPos - gridStartPos; - float stride = cellSize + cellGap; - - int targetGridRow = Mathf.RoundToInt(-offsetFromGridStart.y / stride); // Y轴反向 - int targetGridCol = Mathf.RoundToInt(offsetFromGridStart.x / stride); - - // 限制在主网格范围内 - targetGridRow = Mathf.Clamp(targetGridRow, 0, gridHeight - stripHeight); - targetGridCol = Mathf.Clamp(targetGridCol, 0, gridWidth - stripWidth); - - Vector2Int newGridPos = new Vector2Int(targetGridRow, targetGridCol); - - // 如果位置改变,更新解码器 - if (newGridPos != currentDecoderGridPos) - { - currentDecoderGridPos = newGridPos; - UpdateDecoderPosition(); - } - } - } - - /// - /// 结束拖拽解码器 - /// - private void OnDecoderEndDrag(UnityEngine.EventSystems.PointerEventData eventData) - { - isDraggingDecoder = false; - - // 确保最终位置对齐到网格 - UpdateDecoderPosition(); - } - - /// - /// 清理解码器 - /// - private void ClearDecoder() - { - // 清理解码器粒子 - foreach (var particle in decoderParticles.Values) - { - if (particle != null) - { - Destroy(particle.gameObject); - } - } - decoderParticles.Clear(); - - // 清理解码器绘制器 - if (decoderGridDrawerObj != null) - { - Destroy(decoderGridDrawerObj); - decoderGridDrawerObj = null; - } - } - - /// - /// 创建网格粒子(直接使用 TextParticle,与 LanguageParticleManager 一致) - /// - private void CreateGridParticle(int row, int col, Vector3 gridStartPos) - { - if (sharedCanvas == null) return; - - // 计算粒子位置(从左上角开始) - float xPos = col * (cellSize + cellGap); - float yPos = -row * (cellSize + cellGap); // Y 轴向下 - Vector3 position = gridStartPos + new Vector3(xPos, yPos, 0f); - - // 创建粒子对象(参考 LanguageParticleManager.CreateParticleObject) - GameObject particleObj = new GameObject($"GridParticle_{row}_{col}"); - - // 添加 RectTransform(在 Canvas 下必需) - RectTransform rectTransform = particleObj.AddComponent(); - rectTransform.SetParent(sharedCanvas.transform, false); - rectTransform.position = position; - rectTransform.localScale = Vector3.one; - rectTransform.sizeDelta = Vector2.zero; - - // 添加 TextParticle 组件 - TextParticle particle = particleObj.AddComponent(); - particle.SetFont(chineseFontAsset); - - // 设置为完全静态(固定在格子里,不飘动) - particle.isStatic = true; // 静态粒子 - particle.isCalmed = true; // 完全平静,不受力影响 - particle.velocity = Vector2.zero; // 速度为0 - - // 重要:设置透明度,否则粒子不可见! - particle.alpha = 1f; - particle.baseAlpha = 1f; - - // 设置文字内容 - if (gridData != null) - { - GridCell cellData = gridData[row, col]; - particle.ForceSetCharacter(cellData.word); // 显示词语 - - // 设置字体大小和颜色 - if (particle.textMesh != null) - { - particle.textMesh.fontSize = cellFontSize; - particle.textMesh.color = Color.white; - } - } - - Vector2Int pos = new Vector2Int(row, col); - gridParticles[pos] = particle; - - // 初始化视觉状态 - if (!particleStates.ContainsKey(pos)) - { - particleStates[pos] = new ParticleVisualState - { - basePosition = position, - currentAlpha = 1f, - targetAlpha = 1f, - currentColor = Color.white, - targetColor = Color.white, - currentScale = 1f, - targetScale = 1f, - isShowingWord = true - }; - } - - // 初始化闪烁计时器 - if (!glitchTimers.ContainsKey(pos)) - { - glitchTimers[pos] = Random.Range(0f, glitchInterval); - } - } - - private void ClearGrid() - { - // 清理所有Tween - foreach (var state in particleStates.Values) - { - if (state.alphaTween != null) state.alphaTween.Kill(); - if (state.colorTween != null) state.colorTween.Kill(); - if (state.scaleTween != null) state.scaleTween.Kill(); - if (state.jitterTween != null) state.jitterTween.Kill(); - } - - foreach (var particle in gridParticles.Values) - { - if (particle != null) - { - Destroy(particle.gameObject); - } - } - gridParticles.Clear(); - particleStates.Clear(); - glitchTimers.Clear(); - - if (mainGridDrawerObj != null) - { - Destroy(mainGridDrawerObj); - mainGridDrawerObj = null; - } - - // 同时清理解码器 - ClearDecoder(); - } - - private List SplitIntoTwoCharChunks(string text) - { - List chunks = new List(); - for (int i = 0; i < text.Length; i += 2) - { - if (i + 1 < text.Length) - { - chunks.Add(text.Substring(i, 2)); - } - else - { - chunks.Add(text[i] + " "); // 奇数长度时补空格 - } - } - return chunks; - } - - private void OnStartFilterClicked() - { - if (!isInitialized || isFilteringActive) return; - - isFilteringActive = true; - - // 显示解码器条纹 - CreateDecoder(); - - // 启用滑动条和验证按钮 - if (emotionSlider != null) - { - emotionSlider.interactable = true; - } - - if (verifyButton != null) - { - verifyButton.interactable = true; - } - - if (startFilterButton != null) - { - startFilterButton.interactable = false; - } - - // 应用初始过滤效果 - UpdateGridVisualsWithFilter(); - } - - - private void OnEmotionSliderChanged(float value) - { - currentFrequency = value; - - if (frequencyText != null) - { - frequencyText.text = $"{value:F1} Hz"; - - // 确保频率文本使用正确的字体和大小 - if (chineseFontAsset != null && frequencyText.font != chineseFontAsset) - { - frequencyText.font = chineseFontAsset; - } - } - - // 更新网格显示 - if (isFilteringActive) - { - UpdateGridVisualsWithFilter(); - } - } - - private void Update() - { - // 在过滤模式下,持续更新视觉效果以确保闪烁效果平滑 - // 注意:只在闪烁区域需要频繁更新,其他区域DOTween会自动处理过渡 - if (isFilteringActive && isInitialized) - { - // 只更新处于闪烁状态的粒子(距离在5-20之间) - // 这样可以减少性能开销 - bool needsUpdate = false; - foreach (var kvp in gridParticles) - { - Vector2Int pos = kvp.Key; - if (gridData == null || pos.x >= gridHeight || pos.y >= gridWidth) continue; - - GridCell cellData = gridData[pos.x, pos.y]; - if (cellData.emotion == EmotionType.Neutral) continue; - - EmotionConfig emotion = GetEmotionConfig(cellData.emotion); - float distance = CalculateFrequencyDistance(emotion); - - // 如果处于闪烁区域,需要持续更新 - if (distance >= 5f && distance < 20f) - { - needsUpdate = true; - break; - } - } - - // 如果有粒子处于闪烁状态,每帧更新(但限制更新频率) - if (needsUpdate) - { - UpdateGridVisualsWithFilter(); - } - } - } - - private void UpdateGridVisualsWithFilter() - { - if (gridData == null) return; - - float maxSignal = 0f; - - for (int row = 0; row < gridHeight; row++) - { - for (int col = 0; col < gridWidth; col++) - { - float cellSignal = UpdateCellVisual(row, col); - maxSignal = Mathf.Max(maxSignal, cellSignal); - } - } - - UpdateSignalStrengthDisplay(maxSignal); - } - - /// - /// 更新单个单元格的视觉效果 - /// - private float UpdateCellVisual(int row, int col) - { - Vector2Int pos = new Vector2Int(row, col); - if (!gridParticles.ContainsKey(pos)) return 0f; - - TextParticle particle = gridParticles[pos]; - if (particle == null || particle.textMesh == null) return 0f; - - GridCell cellData = gridData[row, col]; - EmotionConfig emotion = GetEmotionConfig(cellData.emotion); - - // 计算频率距离 - float distance = CalculateFrequencyDistance(emotion); - - - // 根据情绪类型应用不同的视觉效果 - if (cellData.emotion == EmotionType.Neutral) - { - ApplyNeutralState(particle, cellData); - return 0f; - } - else - { - return ApplyEmotionState(particle, cellData, emotion, distance); - } - } - - - /// - /// 计算与当前频率的距离 - /// - private float CalculateFrequencyDistance(EmotionConfig emotion) - { - if (emotion.type == EmotionType.Neutral) - { - return 100f; - } - return Mathf.Abs(currentFrequency - emotion.frequency); - } - - /// - /// 应用情绪相关的视觉状态(参考 HTML 原型的视觉效果) - /// - private float ApplyEmotionState(TextParticle particle, GridCell cellData, EmotionConfig emotion, float distance) - { - bool isTargetWord = cellData.isTarget; - Vector2Int pos = new Vector2Int(cellData.row, cellData.col); - - if (!particleStates.ContainsKey(pos)) - { - particleStates[pos] = new ParticleVisualState - { - basePosition = particle.transform.position, - currentAlpha = particle.alpha, - targetAlpha = particle.alpha, - currentColor = particle.textMesh.color, - targetColor = particle.textMesh.color, - currentScale = 1f, - targetScale = 1f, - isShowingWord = true - }; - } - - ParticleVisualState state = particleStates[pos]; - state.basePosition = particle.transform.position; - - // 确保显示词语 - if (particle.currentChar != cellData.word) - { - particle.ForceSetCharacter(cellData.word); - state.isShowingWord = true; - } - - // 根据距离应用不同的视觉效果(参考 HTML) - if (distance < 5f) - { - // 锁定状态:高亮显示 - return ApplyLockedStateSimple(particle, cellData, emotion, distance, state); - } - else if (distance < 20f) - { - // 闪烁状态 - return ApplyGlitchingStateSimple(particle, cellData, emotion, distance, state); - } - else if (distance < 35f) - { - // 淡出状态 - return ApplyFadingStateSimple(particle, cellData, distance, state); - } - else - { - // 隐藏状态 - return ApplyHiddenStateSimple(particle, cellData, state); - } - } - - /// - /// 锁定状态:频率匹配,高亮显示(参考 HTML) - /// - private float ApplyLockedStateSimple(TextParticle particle, GridCell cellData, EmotionConfig emotion, float distance, ParticleVisualState state) - { - // 计算信号强度 - float signalStrength = 100f - distance * 20f; - signalStrength = Mathf.Clamp01(signalStrength / 100f); - - // 目标值:高亮、放大、加粗 - state.targetAlpha = 1f; - state.targetColor = emotion.color; - state.targetScale = 1.15f; - - // 平滑过渡 - SmoothTransition(state, particle, visualTransitionDuration); - - // 应用样式 - if (particle.textMesh != null) - { - particle.textMesh.fontStyle = FontStyles.Bold; - } - - // 设置光晕 - SetParticleGlow(particle, emotion.color, 0.3f + signalStrength * 0.4f); - - // 停止抖动 - if (state.jitterTween != null) - { - state.jitterTween.Kill(); - state.jitterTween = null; - } - particle.transform.position = state.basePosition; - - return signalStrength * 100f; - } - - /// - /// 闪烁状态:颜色和光晕闪烁(参考 HTML) - /// - private float ApplyGlitchingStateSimple(TextParticle particle, GridCell cellData, EmotionConfig emotion, float distance, ParticleVisualState state) - { - Vector2Int pos = new Vector2Int(cellData.row, cellData.col); - - // 计算闪烁强度 - float glitchChance = 1f - (distance / 20f); - glitchChance = Mathf.Clamp01(glitchChance); - - // 使用 Time.time 作为基础时间 - if (!glitchTimers.ContainsKey(pos)) - { - glitchTimers[pos] = Time.time + Random.Range(0f, glitchInterval * 2f); - } - - // 颜色在情绪色和白色之间过渡(闪烁效果) - float timePhase = (Time.time - glitchTimers[pos]) * (10f + glitchChance * 20f); - float glitchPhase = Mathf.Sin(timePhase) * 0.5f + 0.5f; - float colorLerp = glitchChance * (0.5f + glitchPhase * 0.5f); - - state.targetColor = Color.Lerp(Color.white, emotion.color, colorLerp); - - // 目标值:轻微模糊、中等透明度 - float blurFactor = (distance - 5f) / 15f; - state.targetAlpha = 0.9f - blurFactor * 0.2f; - state.targetScale = 1f; - - // 平滑过渡 - SmoothTransition(state, particle, visualTransitionDuration * 0.5f); - - // 应用样式 - if (particle.textMesh != null) - { - particle.textMesh.fontStyle = FontStyles.Normal; - } - - // 设置光晕(闪烁效果) - float glowAlpha = 0.2f + glitchChance * 0.2f + glitchPhase * 0.1f; - SetParticleGlow(particle, emotion.color, glowAlpha); - - // 添加抖动效果 - float jitterAmount = glitchChance * jitterIntensity; - Vector3 jitterOffset = new Vector3( - Random.Range(-jitterAmount, jitterAmount), - Random.Range(-jitterAmount, jitterAmount), - 0f - ); - particle.transform.position = state.basePosition + jitterOffset; - - return 0f; - } - - /// - /// 淡出状态:逐渐淡化(参考 HTML) - /// - private float ApplyFadingStateSimple(TextParticle particle, GridCell cellData, float distance, ParticleVisualState state) - { - // 计算透明度 - float alpha = 0.6f - ((distance - 20f) / 30f); - alpha = Mathf.Clamp01(alpha); - - state.targetAlpha = alpha; - state.targetColor = Color.white; - state.targetScale = 1f; - - // 平滑过渡 - SmoothTransition(state, particle, visualTransitionDuration); - - // 应用样式 - if (particle.textMesh != null) - { - particle.textMesh.fontStyle = FontStyles.Normal; - } - - // 清除光晕 - SetParticleGlow(particle, Color.clear, 0f); - - // 停止抖动 - if (state.jitterTween != null) - { - state.jitterTween.Kill(); - state.jitterTween = null; - } - particle.transform.position = state.basePosition; - - return 0f; - } - - /// - /// 隐藏状态:几乎不可见(参考 HTML) - /// - private float ApplyHiddenStateSimple(TextParticle particle, GridCell cellData, ParticleVisualState state) - { - state.targetAlpha = 0.05f; - state.targetColor = Color.white; - state.targetScale = 1f; - - // 平滑过渡 - SmoothTransition(state, particle, visualTransitionDuration); - - // 应用样式 - if (particle.textMesh != null) - { - particle.textMesh.fontStyle = FontStyles.Normal; - } - - // 清除光晕 - SetParticleGlow(particle, Color.clear, 0f); - - // 停止抖动 - if (state.jitterTween != null) - { - state.jitterTween.Kill(); - state.jitterTween = null; - } - particle.transform.position = state.basePosition; - - return 0f; - } - - /// - /// 中性状态:噪音/普通内容(参考 HTML) - /// - private void ApplyNeutralState(TextParticle particle, GridCell cellData) - { - Vector2Int pos = new Vector2Int(cellData.row, cellData.col); - - if (!particleStates.ContainsKey(pos)) - { - particleStates[pos] = new ParticleVisualState - { - basePosition = particle.transform.position, - currentAlpha = particle.alpha, - targetAlpha = particle.alpha, - currentColor = particle.textMesh.color, - targetColor = particle.textMesh.color, - currentScale = 1f, - targetScale = 1f, - isShowingWord = true - }; - } - - ParticleVisualState state = particleStates[pos]; - - // 确保显示词语 - if (particle.currentChar != cellData.word) - { - particle.ForceSetCharacter(cellData.word); - state.isShowingWord = true; - } - - // 中性颜色:灰色,淡化显示 - Color neutralColor = new Color(0.4f, 0.5f, 0.4f, 1f); - state.targetAlpha = 0.3f; - state.targetColor = neutralColor; - state.targetScale = 1f; - - // 平滑过渡 - SmoothTransition(state, particle, visualTransitionDuration); - - // 应用样式 - if (particle.textMesh != null) - { - particle.textMesh.fontStyle = FontStyles.Normal; - } - - // 清除光晕 - SetParticleGlow(particle, Color.clear, 0f); - - // 停止抖动 - if (state.jitterTween != null) - { - state.jitterTween.Kill(); - state.jitterTween = null; - } - particle.transform.position = state.basePosition; - } - - /// - /// 平滑过渡到目标视觉状态 - /// - private void SmoothTransition(ParticleVisualState state, TextParticle particle, float duration) - { - // Alpha 过渡 - if (Mathf.Abs(state.currentAlpha - state.targetAlpha) > 0.01f) - { - if (state.alphaTween != null && state.alphaTween.IsActive()) - { - state.alphaTween.Kill(); - } - state.alphaTween = DOTween.To(() => state.currentAlpha, x => { - state.currentAlpha = x; - particle.alpha = x; - }, state.targetAlpha, duration).SetEase(Ease.OutQuad); - } - - // Color 过渡 - if (ColorDistance(state.currentColor, state.targetColor) > 0.01f) - { - if (state.colorTween != null && state.colorTween.IsActive()) - { - state.colorTween.Kill(); - } - state.colorTween = DOTween.To(() => state.currentColor, x => { - state.currentColor = x; - if (particle.textMesh != null) - { - Color finalColor = x; - finalColor.a = state.currentAlpha; // 保持alpha同步 - particle.textMesh.color = finalColor; - } - }, state.targetColor, duration).SetEase(Ease.OutQuad); - } - - // Scale 过渡(通过transform.localScale) - if (Mathf.Abs(state.currentScale - state.targetScale) > 0.01f) - { - if (state.scaleTween != null && state.scaleTween.IsActive()) - { - state.scaleTween.Kill(); - } - Vector3 targetScale = Vector3.one * state.targetScale; - state.scaleTween = particle.transform.DOScale(targetScale, duration).SetEase(Ease.OutQuad); - state.currentScale = state.targetScale; - } - } - - /// - /// 计算两个颜色的距离(用于判断是否需要过渡) - /// - private float ColorDistance(Color a, Color b) - { - return Mathf.Abs(a.r - b.r) + Mathf.Abs(a.g - b.g) + Mathf.Abs(a.b - b.b); - } - - /// - /// 设置粒子光晕效果(平滑过渡) - /// - private void SetParticleGlow(TextParticle particle, Color color, float alpha) - { - if (particle.glowSprite != null) - { - Color glowColor = color; - glowColor.a = alpha; - // 使用DOTween平滑过渡光晕 - particle.glowSprite.DOColor(glowColor, visualTransitionDuration).SetEase(Ease.OutQuad); - } - } - - /// - /// 更新信号强度显示 - /// - private void UpdateSignalStrengthDisplay(float maxSignal) - { - if (signalStrengthText != null) - { - signalStrengthText.text = $"信号强度: {Mathf.RoundToInt(maxSignal)}%"; - } - } - - private void OnVerifyClicked() - { - if (!isFilteringActive || currentLevelData == null) return; - - // 检查解码器位置是否正确 - bool isPositionCorrect = (currentDecoderGridPos == currentLevelData.correctDecoderPosition); - - // 检查频率是否正确(匹配答案情绪) - EmotionConfig answerEmotion = GetEmotionConfig(currentLevelData.answerEmotion); - float frequencyDistance = Mathf.Abs(currentFrequency - answerEmotion.frequency); - bool isFrequencyCorrect = frequencyDistance <= currentLevelData.frequencyTolerance; - - // 验证答案 - bool isCorrect = isPositionCorrect && isFrequencyCorrect; - - if (isCorrect) - { - Debug.Log($"[AnalysisModeManager] 验证成功!位置: {currentDecoderGridPos}, 频率: {currentFrequency:F1} Hz"); - - // 显示成功反馈 - ShowDecoderFeedback(true); - - // 延迟后触发完成回调 - StartCoroutine(DelayedComplete(true)); - } - else - { - Debug.Log($"[AnalysisModeManager] 验证失败。位置正确: {isPositionCorrect}, 频率正确: {isFrequencyCorrect}"); - - // 显示失败反馈 - ShowDecoderFeedback(false); - } - } - - /// - /// 显示解码器验证反馈 - /// - private void ShowDecoderFeedback(bool isCorrect) - { - if (decoderGridDrawerObj == null) return; - - // 改变解码器边框颜色 - Color feedbackColor = isCorrect ? Color.green : Color.red; - Color originalColor = new Color(0f, 1f, 1f, 0.8f); // Cyan 原始颜色 - - // 找到所有网格线并改变颜色 - Transform linesContainer = decoderGridDrawerObj.transform.Find("Lines"); - if (linesContainer != null) - { - Line[] lines = linesContainer.GetComponentsInChildren(); - foreach (Line line in lines) - { - Color startColor = line.Color; - - // 使用DOTween.To来动画化颜色变化 - DOTween.To(() => startColor, x => line.Color = x, feedbackColor, 0.3f) - .SetEase(Ease.OutQuad); - - if (!isCorrect) - { - // 失败时闪烁后恢复 - DOTween.To(() => feedbackColor, x => line.Color = x, originalColor, 0.3f) - .SetDelay(0.6f) - .SetEase(Ease.OutQuad); - } - } - } - } - - /// - /// 延迟完成(用于播放动画) - /// - private System.Collections.IEnumerator DelayedComplete(bool success) - { - yield return new WaitForSeconds(1f); - OnAnalysisComplete(success); - - // 关闭分析模式 - if (analysisPanel != null) - { - analysisPanel.gameObject.SetActive(false); - } - } - - private void OnAnalysisComplete(bool success) - { - Debug.Log($"[AnalysisModeManager] OnAnalysisComplete 被调用,结果: {(success ? "成功" : "失败")}"); - Debug.Log($"[AnalysisModeManager] completionNodeName: {(string.IsNullOrEmpty(completionNodeName) ? "空" : completionNodeName)}"); - Debug.Log($"[AnalysisModeManager] currentLevelData: {(currentLevelData == null ? "null" : currentLevelData.levelName)}"); - - // 通过 Yarn 对话系统反馈结果 - if (success) - { - // 成功节点:优先使用 completionNodeName,其次使用配置中的节点 - string successNode = !string.IsNullOrEmpty(completionNodeName) - ? completionNodeName - : (currentLevelData != null && !string.IsNullOrEmpty(currentLevelData.successNodeName)) - ? currentLevelData.successNodeName - : ""; - - Debug.Log($"[AnalysisModeManager] 成功节点选择: {(string.IsNullOrEmpty(successNode) ? "未找到节点" : successNode)}"); - - if (!string.IsNullOrEmpty(successNode)) - { - Debug.Log($"[AnalysisModeManager] 准备启动对话节点: {successNode}"); - if (DialogController.Instance == null) - { - Debug.LogError("[AnalysisModeManager] DialogController.Instance 为 null,无法启动对话节点!"); - } - else - { - DialogController.Instance.StartDialogNode(successNode); - Debug.Log($"[AnalysisModeManager] 已调用 StartDialogNode: {successNode}"); - } - } - else - { - Debug.LogWarning("[AnalysisModeManager] 成功时未找到任何对话节点!"); - } - } - else - { - // 失败节点 - string failNode = !string.IsNullOrEmpty(completionNodeName) - ? completionNodeName + "_Fail" - : (currentLevelData != null && !string.IsNullOrEmpty(currentLevelData.failNodeName)) - ? currentLevelData.failNodeName - : ""; - - Debug.Log($"[AnalysisModeManager] 失败节点选择: {(string.IsNullOrEmpty(failNode) ? "未找到节点" : failNode)}"); - - if (!string.IsNullOrEmpty(failNode)) - { - Debug.Log($"[AnalysisModeManager] 准备启动对话节点: {failNode}"); - if (DialogController.Instance == null) - { - Debug.LogError("[AnalysisModeManager] DialogController.Instance 为 null,无法启动对话节点!"); - } - else - { - DialogController.Instance.StartDialogNode(failNode); - Debug.Log($"[AnalysisModeManager] 已调用 StartDialogNode: {failNode}"); - } - } - else - { - Debug.LogWarning("[AnalysisModeManager] 失败时未找到任何对话节点!"); - } - } - } - - private EmotionConfig GetEmotionConfig(EmotionType type) - { - switch (type) - { - case EmotionType.Joy: return EmotionConfig.Joy; - case EmotionType.Sadness: return EmotionConfig.Sadness; - case EmotionType.Anger: return EmotionConfig.Anger; - case EmotionType.Fear: return EmotionConfig.Fear; - default: return EmotionConfig.Neutral; - } - } - - - } -} - diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeManager.cs.meta b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeManager.cs.meta deleted file mode 100644 index 805ede67e..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3cf72a773ae1e5442aea05dc7691d4e0 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeSetup.txt b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeSetup.txt deleted file mode 100644 index 3911243d2..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeSetup.txt +++ /dev/null @@ -1,223 +0,0 @@ -# 分析模式设置指南 - -## 一、创建关卡配置 - -### 1. 创建 ScriptableObject - -1. 在 Project 窗口中,找到 `Assets/Resources/AnalysisLevels/` 文件夹(如果没有请创建) -2. 右键 → `Create > AIBIS > Language > Analysis Level` -3. 命名为 `AnalysisLevel_Fear_01` - -### 2. 配置关卡数据 - -在 Inspector 中填写: - -**关卡基础信息:** -- Level Name: "恐惧关卡1" -- Description: "找出隐藏在笑话中的焦虑" - -**解码器配置:** -- Decoder Width: 5 -- Decoder Height: 3 -- Hole Positions: (点击 + 添加3个位置) - - Element 0: X=0, Y=0 - - Element 1: X=1, Y=1 - - Element 2: X=2, Y=4 - -**答案配置:** -- Answer Emotion: Fear -- Answer Words: (点击 + 添加3个词) - - Element 0: "临检" - - Element 1: "英里" - - Element 2: "担心" -- Correct Decoder Position: X=3, Y=5 -- Complete Sentence: "[临检]要来了,不合格[英里]被骂了,这让我很[担心]" - -**词池配置:** -展开 Word Pool,确保: -- Fear Words 列表中包含 "临检", "英里", "担心" -- 每种情绪至少有 15 个词 - -**难度配置:** -- Decoy Count Per Emotion: 15 -- Frequency Tolerance: 8.0 - -**完成回调:** -- Success Node Name: "Analysis_Fear_01_Success" -- Fail Node Name: "Analysis_Fear_01_Fail" - ---- - -## 二、设置 AnalysisModeManager - -### 1. 在场景中找到或创建 AnalysisModeManager - -1. 在 Hierarchy 中找到包含 `AnalysisModeManager` 的物体 -2. 如果没有,创建一个空物体,添加 `AnalysisModeManager` 组件 - -### 2. 配置 Inspector - -**Canvas 引用:** -- Shared Canvas: 拖入共享的 Canvas(与 LanguageParticleManager 使用同一个) - -**UI 引用:** -- Analysis Panel: 分析面板 RectTransform -- Initialize Button: "初始化" 按钮 -- Emotion Slider: 情绪频率滑动条 -- Frequency Text: 频率显示文本 -- Signal Strength Text: 信号强度文本 -- Start Filter Button: "启动过滤" 按钮 -- Verify Button: "验证" 按钮 - -**网格配置:** -- Grid Container: 可选 -- Grid Width: 20 -- Grid Height: 20 -- Cell Size: 0.4 -- Cell Gap: 0.05 -- Cell Font Size: 2.5 -- Grid Center Offset: (0, 0, 0) - -**解码器条纹:** -- Decoder Strip: 拖入解码器条纹 Prefab - -**字体配置:** -- Chinese Font Asset: 拖入中文字体 - -**关卡配置:** -- Current Level Data: 拖入刚创建的 `AnalysisLevel_Fear_01` - ---- - -## 三、创建解码器条纹 Prefab - -### 1. 创建 UI 对象 - -1. 在 Canvas 下创建空物体 -2. 命名为 "DecoderStrip" -3. 添加组件: - - RectTransform (自动添加) - - Image - - Canvas Group - - AnalysisDecoderStrip - -### 2. 配置组件 - -**RectTransform:** -- Anchor: Center -- Pivot: (0.5, 0.5) -- Size Delta: 会被代码动态设置 - -**Image:** -- Color: R=0, G=30, B=30, A=242 (半透明深青色) - -**AnalysisDecoderStrip:** -- Is Draggable: ✓ -- Drag Alpha: 0.7 -- Strip Color: (0, 0.12, 0.12, 0.95) -- Border Color: Cyan -- Border Width: 2 - -### 3. 添加孔洞标记(可选) - -在 DecoderStrip 下创建子物体标记孔洞位置: - -1. 创建3个 Image 子物体,命名为 "Hole_1", "Hole_2", "Hole_3" -2. 设置为小圆圈或方框 -3. 位置对应配置中的 holePositions - -### 4. 保存为 Prefab - -将 DecoderStrip 拖到 Project 窗口,保存为 Prefab - ---- - -## 四、在 Yarn 脚本中使用 - -创建或编辑 .yarn 文件: - -```yarn -title: TestAnalysisMode ---- -艾比斯: 让我看看这些记忆背后藏着什么... - -<> - -// 玩家进行分析游戏,成功后自动跳转到下一个节点 - -=== - -title: Analysis_Fear_01_Success ---- -艾比斯: 我明白了...这些笑话背后藏着焦虑... -艾比斯: [临检]要来了,不合格[英里]被骂了,这让我很[担心]... --> 继续 - <> -=== - -title: Analysis_Fear_01_Fail ---- -艾比斯: 似乎不对...让我再试试... -=== -``` - ---- - -## 五、测试 - -1. 运行场景 -2. 触发包含 `start_analysis` 命令的对话 -3. 观察分析模式是否正确启动 -4. 调整频率滑动条,拖动解码器条纹 -5. 点击验证按钮测试 - ---- - -## 六、调试 - -### 查看日志 - -生成器会输出详细日志: - -``` -[AnalysisModeGenerator] 验证通过:唯一解 -[AnalysisModeManager] 已加载关卡: 恐惧关卡1 -``` - -### 常见问题 - -**Q: 提示"找不到关卡配置"** -A: 确保配置文件在 `Assets/Resources/AnalysisLevels/` 目录下 - -**Q: 提示"类型找不到"** -A: 在 Unity 中等待编译完成,或重启 Unity - -**Q: 网格没有生成** -A: 检查 Console 是否有错误日志,确认配置数据有效 - -**Q: 解码器条纹不能拖动** -A: 检查 Canvas 的 Raycast Target 设置,确保 EventSystem 存在 - ---- - -## 七、进阶配置 - -### 创建更多关卡 - -复制 `AnalysisLevel_Fear_01`,修改: -- 答案词 -- 孔洞位置 -- 解码器正确位置 -- 诱饵词数量 - -### 增加难度 - -- 增加 `decoyCountPerEmotion` (诱饵词数量) -- 减小 `frequencyTolerance` (频率容差) -- 增加网格尺寸 -- 使用更多孔洞 - ---- - -完成!现在可以创建和配置分析模式关卡了。 - diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeSetup.txt.meta b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeSetup.txt.meta deleted file mode 100644 index 3e191a447..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeSetup.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 60c809b2091567c4ba8ad39cde46bdf2 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeYarnCommand.cs b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeYarnCommand.cs deleted file mode 100644 index a20d72fa2..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeYarnCommand.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections; -using AibisDream.FixSystem; -using UnityEngine; -using Yarn.Unity; - -namespace AibisDream.MiniGame.Language -{ - /// - /// 分析模式的 Yarn 命令接口 - /// - public static class AnalysisModeYarnCommand - { - private static AnalysisModeManager AnalysisModeManager => FixSystemCenter.SystemDic.Get(); - - /// - /// 启动分析模式(使用 ScriptableObject 配置 - 推荐) - /// <> - /// 或 - /// <> - /// - /// 关卡配置名称(Resources/AnalysisLevels/ 下的文件名) - /// 完成时触发的对话节点(可选,会覆盖配置中的节点) - [YarnCommand("start_analysis")] - public static void StartAnalysis(string levelName, string completionNode = "") - { - if (AnalysisModeManager == null) - { - Debug.LogError("AnalysisModeManager 未找到!请确保场景中有 AnalysisModeManager 组件。"); - return; - } - - // 从 Resources 加载关卡配置 - AnalysisModeData levelData = Resources.Load($"AnalysisLevels/{levelName}"); - - if (levelData == null) - { - Debug.LogError($"找不到关卡配置: Resources/AnalysisLevels/{levelName}.asset"); - return; - } - - // 启动分析模式 - AnalysisModeManager.OpenView(levelData, completionNode); - } - - /// - /// 关闭分析模式 - /// <> - /// - [YarnCommand("close_analysis")] - public static void CloseAnalysis() - { - if (AnalysisModeManager == null) - { - Debug.LogWarning("AnalysisModeManager 未找到!"); - return; - } - - AnalysisModeManager.CloseView(); - } - } -} - diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeYarnCommand.cs.meta b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeYarnCommand.cs.meta deleted file mode 100644 index 337b7504c..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/AnalysisModeYarnCommand.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ba015d54b185f1948b0250e112daaf93 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/Text (TMP) (3).prefab b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/Text (TMP) (3).prefab deleted file mode 100644 index 6dbc7bcd8..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/Text (TMP) (3).prefab +++ /dev/null @@ -1,136 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &5030332322852799317 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 3682520096596469954} - - component: {fileID: 7208676086374186826} - - component: {fileID: 8166701485391072747} - m_Layer: 5 - m_Name: Text (TMP) (3) - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &3682520096596469954 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5030332322852799317} - 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: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 1.2, y: -1.98} - m_SizeDelta: {x: 1, y: 1} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &7208676086374186826 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5030332322852799317} - m_CullTransparentMesh: 1 ---- !u!114 &8166701485391072747 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5030332322852799317} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, 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_text: New Text - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} - m_sharedMaterial: {fileID: -6659093157667844055, guid: f242fb3dde1933640859f1c54591c9c8, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4294040693 - m_fontColor: {r: 0.45745817, g: 0.86362934, b: 0.9433962, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 0.3 - m_fontSizeBase: 0.3 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 1 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 1 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0.41540456, z: -0.14499044, w: -0.5056629} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - m_hasFontAssetChanged: 0 - m_baseMaterial: {fileID: 0} - m_maskOffset: {x: 0, y: 0, z: 0, w: 0} diff --git a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/Text (TMP) (3).prefab.meta b/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/Text (TMP) (3).prefab.meta deleted file mode 100644 index adfad4a86..000000000 --- a/Assets/Scripts/MiniGame/HuoShan/AnalysisSystem/Text (TMP) (3).prefab.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: fccb02126db337b4a831aaa31a396414 -PrefabImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: