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: