933 lines
34 KiB
C#
933 lines
34 KiB
C#
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using DG.Tweening;
|
||
|
||
namespace AibisDream.MiniGame.Language
|
||
{
|
||
/// <summary>
|
||
/// 情绪类型
|
||
/// </summary>
|
||
public enum EmotionType
|
||
{
|
||
Joy, // 快乐 - 15Hz
|
||
Sadness, // 悲伤 - 40Hz
|
||
Anger, // 愤怒 - 65Hz
|
||
Fear, // 恐惧 - 90Hz
|
||
Neutral // 中性/噪音
|
||
}
|
||
|
||
/// <summary>
|
||
/// 情绪配置
|
||
/// </summary>
|
||
[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"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 网格单元格数据
|
||
/// </summary>
|
||
[System.Serializable]
|
||
public class GridCell
|
||
{
|
||
public string word; // 真实词语
|
||
public string cover; // 笑话掩盖词
|
||
public EmotionType emotion;
|
||
public bool isTarget; // 是否是目标词
|
||
public int row;
|
||
public int col;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析目标配置
|
||
/// </summary>
|
||
[System.Serializable]
|
||
public class AnalysisTarget
|
||
{
|
||
public string name;
|
||
public EmotionType emotion;
|
||
public List<TargetWord> words = new List<TargetWord>();
|
||
public Vector2Int stripPosition; // 解码器条纹正确位置(行,列)
|
||
public bool isCorrectAnswer; // 是否是正确答案
|
||
}
|
||
|
||
/// <summary>
|
||
/// 目标词配置
|
||
/// </summary>
|
||
[System.Serializable]
|
||
public class TargetWord
|
||
{
|
||
public Vector2Int position; // 在网格中的位置(行,列)
|
||
public string word; // 真实词语
|
||
public string cover; // 笑话掩盖词
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析模式管理器
|
||
/// </summary>
|
||
public class AnalysisModeManager : MonoBehaviour
|
||
{
|
||
[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;
|
||
// [SerializeField] private TMP_Text logText;
|
||
// [SerializeField] private ScrollRect logScrollRect;
|
||
|
||
[Header("网格配置")]
|
||
[SerializeField] private RectTransform gridContainer; // 可选,用于定位网格中心
|
||
// [SerializeField] private GameObject gridCellPrefab; // 不需要了,直接使用粒子
|
||
[SerializeField] private int gridWidth = 20;
|
||
[SerializeField] private int gridHeight = 20;
|
||
[SerializeField] private float cellSize = 0.4f; // Unity 单位(与 LanguageParticleManager 匹配)
|
||
[SerializeField] private float cellGap = 0.05f; // Unity 单位
|
||
[SerializeField] private float cellFontSize = 2.5f; // 文字大小(与 LanguageParticleManager 匹配)
|
||
[SerializeField] private Vector3 gridCenterOffset = Vector3.zero; // 网格中心偏移量
|
||
|
||
[Header("解码器条纹")]
|
||
[SerializeField] private RectTransform decoderStrip;
|
||
// [SerializeField] private GameObject stripHolePrefab; // 暂时不用,条纹作为独立 prefab
|
||
[SerializeField] private int stripWidth = 5; // 条纹宽度(列数)
|
||
[SerializeField] private int stripHeight = 3; // 条纹高度(行数)
|
||
|
||
[Header("字体配置")]
|
||
[SerializeField] private TMP_FontAsset chineseFontAsset; // 中文字体资源
|
||
|
||
[Header("动画配置")]
|
||
[SerializeField] private float panelSlideDuration = 0.5f;
|
||
[SerializeField] private float dataLoadDuration = 1.5f;
|
||
[SerializeField] private Ease slideEase = Ease.OutCubic;
|
||
|
||
[Header("焦虑短句配置")]
|
||
[SerializeField] private List<string> anxietyPhrases = new List<string>
|
||
{
|
||
"临检要来了大家都很紧张只有销售员说终于可以检查业绩了",
|
||
"不合格英里被骂了因为销售业绩太差了",
|
||
"我担心业绩会下滑结果真的下滑了我的担心是对的",
|
||
"销售员总是混淆客户和老板因为都要讨好",
|
||
"一个销售员走进酒吧问两张桌子要不要买保险"
|
||
};
|
||
|
||
[Header("目标配置")]
|
||
[SerializeField] private string targetSentence = "[临检]要来了,不合格[英里]被骂了,这让我很[担心]";
|
||
|
||
// 解析目标:只有一个正确答案
|
||
private List<AnalysisTarget> analysisTargets = new List<AnalysisTarget>();
|
||
|
||
// 状态
|
||
private bool isAnalysisModeActive = false;
|
||
private bool isFilteringActive = false;
|
||
private float currentFrequency = 50f;
|
||
private GridCell[,] gridData;
|
||
private Dictionary<Vector2Int, TextParticle> gridParticles = new Dictionary<Vector2Int, TextParticle>(); // 使用粒子代替 UI
|
||
// private List<Vector2Int> stripHolePositions = new List<Vector2Int>(); // 暂时不用,条纹作为独立 prefab 配置
|
||
private bool isInitialized = false;
|
||
private string completionNodeName = "";
|
||
|
||
private void Awake()
|
||
{
|
||
SetupUI();
|
||
SetupTargets();
|
||
}
|
||
|
||
private void SetupUI()
|
||
{
|
||
if (analysisPanel != null)
|
||
{
|
||
analysisPanel.gameObject.SetActive(false);
|
||
}
|
||
|
||
if (initializeButton != null)
|
||
{
|
||
initializeButton.onClick.AddListener(OnInitializeClicked);
|
||
// 初始化按钮现在作为"重置网格"使用,网格会自动创建
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
if (decoderStrip != null)
|
||
{
|
||
decoderStrip.gameObject.SetActive(false);
|
||
// SetupDecoderStripDragging(); // 暂时注释,条纹作为独立 prefab 配置
|
||
}
|
||
}
|
||
|
||
private void SetupTargets()
|
||
{
|
||
// 清空目标列表
|
||
analysisTargets.Clear();
|
||
|
||
// 创建正确答案目标
|
||
var correctTarget = new AnalysisTarget
|
||
{
|
||
name = "Fear Solution 1",
|
||
emotion = EmotionType.Fear,
|
||
stripPosition = new Vector2Int(3, 5), // 条纹正确位置
|
||
isCorrectAnswer = true
|
||
};
|
||
|
||
// 添加目标词:临检、英里、担心(相对于条纹的孔洞位置)
|
||
correctTarget.words.Add(new TargetWord
|
||
{
|
||
position = new Vector2Int(3, 5), // 绝对位置 = 条纹位置 + 孔洞相对位置
|
||
word = "临检",
|
||
cover = "检查"
|
||
});
|
||
|
||
correctTarget.words.Add(new TargetWord
|
||
{
|
||
position = new Vector2Int(4, 6),
|
||
word = "英里",
|
||
cover = "背后"
|
||
});
|
||
|
||
correctTarget.words.Add(new TargetWord
|
||
{
|
||
position = new Vector2Int(5, 9),
|
||
word = "担心",
|
||
cover = "对的"
|
||
});
|
||
|
||
analysisTargets.Add(correctTarget);
|
||
|
||
// 可以添加更多诱饵目标(与正确答案接近但不完全匹配)
|
||
// 这里暂时只有一个正确答案
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动分析模式(由 ExpressionManager 调用)
|
||
/// </summary>
|
||
public void StartAnalysisMode(List<string> phrases = null, string completionNode = null)
|
||
{
|
||
if (isAnalysisModeActive) return;
|
||
|
||
// 更新配置
|
||
if (phrases != null && phrases.Count > 0)
|
||
{
|
||
anxietyPhrases = phrases;
|
||
}
|
||
completionNodeName = completionNode ?? "";
|
||
|
||
// 显示分析控制面板
|
||
isAnalysisModeActive = true;
|
||
if (analysisPanel != null)
|
||
{
|
||
analysisPanel.gameObject.SetActive(true);
|
||
}
|
||
|
||
// 自动初始化网格(不需要等待用户点击 Initialize)
|
||
if (!isInitialized)
|
||
{
|
||
InitializeGrid();
|
||
}
|
||
|
||
// Log("> 分析模式已启动");
|
||
// Log("> 点击 [初始化] 导入数据...");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭分析模式
|
||
/// </summary>
|
||
public void CloseAnalysisMode()
|
||
{
|
||
if (!isAnalysisModeActive) return;
|
||
|
||
// 滑出 UI 面板
|
||
if (analysisPanel != null)
|
||
{
|
||
Vector2 endPos = new Vector2(Screen.width, 0);
|
||
analysisPanel.DOAnchorPos(endPos, panelSlideDuration)
|
||
.SetEase(slideEase)
|
||
.OnComplete(() =>
|
||
{
|
||
analysisPanel.gameObject.SetActive(false);
|
||
isAnalysisModeActive = false;
|
||
});
|
||
}
|
||
else
|
||
{
|
||
isAnalysisModeActive = false;
|
||
}
|
||
|
||
// 重置状态
|
||
isFilteringActive = false;
|
||
isInitialized = false;
|
||
currentFrequency = 50f;
|
||
|
||
ClearGrid();
|
||
|
||
if (decoderStrip != null)
|
||
{
|
||
decoderStrip.gameObject.SetActive(false);
|
||
}
|
||
}
|
||
|
||
private void OnInitializeClicked()
|
||
{
|
||
// 现在网格会在 StartAnalysisMode 时自动初始化
|
||
// 这个按钮可以用来重新初始化网格(重置)
|
||
if (isInitialized)
|
||
{
|
||
// 重新初始化(重置网格)
|
||
isInitialized = false;
|
||
ClearGrid();
|
||
}
|
||
|
||
// Log("> 开始导入焦虑词语数据...");
|
||
InitializeGrid();
|
||
}
|
||
|
||
private void InitializeGrid()
|
||
{
|
||
// 创建网格数据
|
||
gridData = new GridCell[gridHeight, gridWidth];
|
||
|
||
// 将焦虑短句分割成2字符片段
|
||
List<string> allFragments = new List<string>();
|
||
foreach (var phrase in anxietyPhrases)
|
||
{
|
||
var fragments = SplitIntoTwoCharChunks(phrase);
|
||
allFragments.AddRange(fragments);
|
||
}
|
||
|
||
// 如果片段不够填满网格,重复使用
|
||
int totalCells = gridWidth * gridHeight;
|
||
while (allFragments.Count < totalCells)
|
||
{
|
||
allFragments.AddRange(allFragments.Take(totalCells - allFragments.Count));
|
||
}
|
||
|
||
// 填充网格数据
|
||
int fragmentIndex = 0;
|
||
for (int row = 0; row < gridHeight; row++)
|
||
{
|
||
for (int col = 0; col < gridWidth; col++)
|
||
{
|
||
GridCell cell = new GridCell
|
||
{
|
||
row = row,
|
||
col = col,
|
||
word = allFragments[fragmentIndex % allFragments.Count],
|
||
cover = allFragments[fragmentIndex % allFragments.Count],
|
||
emotion = EmotionType.Neutral,
|
||
isTarget = false
|
||
};
|
||
|
||
// 检查是否是目标位置
|
||
foreach (var target in analysisTargets)
|
||
{
|
||
foreach (var targetWord in target.words)
|
||
{
|
||
if (targetWord.position.x == row && targetWord.position.y == col)
|
||
{
|
||
cell.word = targetWord.word;
|
||
cell.cover = targetWord.cover;
|
||
cell.emotion = target.emotion;
|
||
cell.isTarget = true;
|
||
break;
|
||
}
|
||
}
|
||
if (cell.isTarget) break;
|
||
}
|
||
|
||
// 为非目标单元格随机分配一些情绪(作为诱饵)
|
||
if (!cell.isTarget && Random.value > 0.88f)
|
||
{
|
||
EmotionType[] emotions = { EmotionType.Fear, EmotionType.Sadness, EmotionType.Joy, EmotionType.Anger };
|
||
cell.emotion = emotions[Random.Range(0, emotions.Length)];
|
||
}
|
||
|
||
gridData[row, col] = cell;
|
||
fragmentIndex++;
|
||
}
|
||
}
|
||
|
||
// 创建网格 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;
|
||
|
||
// 计算起始位置(左上角)
|
||
Vector3 gridStartPos = gridCenter - new Vector3(totalWidth / 2f, totalHeight / 2f, 0f);
|
||
|
||
// 随机顺序创建单元格(Matrix 风格)
|
||
List<Vector2Int> cellPositions = new List<Vector2Int>();
|
||
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);
|
||
|
||
// Log("> 数据导入完成");
|
||
// Log($"> 共 {gridWidth}x{gridHeight} 个数据节点");
|
||
// Log("> 点击 [启动过滤分析] 开始...");
|
||
|
||
isInitialized = true;
|
||
|
||
if (startFilterButton != null)
|
||
{
|
||
startFilterButton.interactable = true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建网格粒子(直接使用 TextParticle,与 LanguageParticleManager 一致)
|
||
/// </summary>
|
||
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>();
|
||
rectTransform.SetParent(sharedCanvas.transform, false);
|
||
rectTransform.position = position;
|
||
rectTransform.localScale = Vector3.one;
|
||
rectTransform.sizeDelta = Vector2.zero;
|
||
|
||
// 添加 TextParticle 组件
|
||
TextParticle particle = particleObj.AddComponent<TextParticle>();
|
||
particle.SetFont(chineseFontAsset);
|
||
|
||
// 设置为静态(不移动)
|
||
particle.isStatic = true;
|
||
particle.isCalmed = false;
|
||
particle.velocity = Vector2.zero;
|
||
|
||
// 重要:设置透明度,否则粒子不可见!
|
||
particle.alpha = 1f;
|
||
particle.baseAlpha = 1f;
|
||
|
||
// 设置文字内容
|
||
if (gridData != null)
|
||
{
|
||
GridCell cellData = gridData[row, col];
|
||
particle.ForceSetCharacter(cellData.cover); // 初始显示掩盖词
|
||
|
||
// 设置字体大小和颜色
|
||
if (particle.textMesh != null)
|
||
{
|
||
particle.textMesh.fontSize = cellFontSize;
|
||
particle.textMesh.color = Color.white;
|
||
}
|
||
}
|
||
|
||
gridParticles[new Vector2Int(row, col)] = particle;
|
||
}
|
||
|
||
private void ClearGrid()
|
||
{
|
||
foreach (var particle in gridParticles.Values)
|
||
{
|
||
if (particle != null)
|
||
{
|
||
Destroy(particle.gameObject);
|
||
}
|
||
}
|
||
gridParticles.Clear();
|
||
}
|
||
|
||
private List<string> SplitIntoTwoCharChunks(string text)
|
||
{
|
||
List<string> chunks = new List<string>();
|
||
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;
|
||
|
||
// Log("> 过滤分析已启动");
|
||
// Log("> 解码器条纹已加载");
|
||
// Log("> 调整情绪频率以隔离信号模式...");
|
||
|
||
// 显示解码器条纹(暂时注释,作为独立 prefab 后续配置)
|
||
// if (decoderStrip != null)
|
||
// {
|
||
// decoderStrip.gameObject.SetActive(true);
|
||
//
|
||
// // 初始位置设置到正确答案位置(玩家可以拖动)
|
||
// var correctTarget = analysisTargets.FirstOrDefault(t => t.isCorrectAnswer);
|
||
// if (correctTarget != null)
|
||
// {
|
||
// PositionDecoderStrip(correctTarget.stripPosition.x, correctTarget.stripPosition.y);
|
||
// }
|
||
//
|
||
// // 淡入动画
|
||
// CanvasGroup canvasGroup = decoderStrip.GetComponent<CanvasGroup>();
|
||
// if (canvasGroup == null)
|
||
// {
|
||
// canvasGroup = decoderStrip.gameObject.AddComponent<CanvasGroup>();
|
||
// }
|
||
// canvasGroup.alpha = 0f;
|
||
// canvasGroup.DOFade(1f, 0.8f);
|
||
// }
|
||
|
||
// 启用滑动条和验证按钮
|
||
if (emotionSlider != null)
|
||
{
|
||
emotionSlider.interactable = true;
|
||
}
|
||
|
||
if (verifyButton != null)
|
||
{
|
||
verifyButton.interactable = true;
|
||
}
|
||
|
||
if (startFilterButton != null)
|
||
{
|
||
startFilterButton.interactable = false;
|
||
}
|
||
|
||
// 应用初始过滤效果
|
||
UpdateGridVisualsWithFilter();
|
||
}
|
||
|
||
private void PositionDecoderStrip(int row, int col)
|
||
{
|
||
if (decoderStrip == null || gridContainer == null) return;
|
||
|
||
// 使用解码器组件设置位置
|
||
AnalysisDecoderStrip stripComponent = decoderStrip.GetComponent<AnalysisDecoderStrip>();
|
||
if (stripComponent != null)
|
||
{
|
||
stripComponent.SetGridPosition(row, col, cellSize, cellGap);
|
||
}
|
||
else
|
||
{
|
||
// 备用设置方式
|
||
float xPos = col * (cellSize + cellGap);
|
||
float yPos = -row * (cellSize + cellGap);
|
||
decoderStrip.anchoredPosition = new Vector2(xPos, yPos);
|
||
}
|
||
|
||
// 设置条纹大小
|
||
float stripWidth = this.stripWidth * cellSize + (this.stripWidth - 1) * cellGap;
|
||
float stripHeight = this.stripHeight * cellSize + (this.stripHeight - 1) * cellGap;
|
||
decoderStrip.sizeDelta = new Vector2(stripWidth, stripHeight);
|
||
}
|
||
|
||
// 解码器条纹拖拽逻辑 - 暂时注释,条纹作为独立 prefab 配置
|
||
// private void SetupDecoderStripDragging()
|
||
// {
|
||
// if (decoderStrip == null) return;
|
||
//
|
||
// // 添加拖拽组件
|
||
// var draggable = decoderStrip.gameObject.AddComponent<UnityEngine.EventSystems.EventTrigger>();
|
||
//
|
||
// // 这里需要实现拖拽逻辑,但由于 EventTrigger 比较复杂,
|
||
// // 暂时先让条纹固定在初始位置,玩家可以通过调整频率来验证
|
||
// }
|
||
|
||
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 UpdateGridVisualsWithFilter()
|
||
{
|
||
if (gridData == null) return;
|
||
|
||
float maxSignal = 0f;
|
||
|
||
for (int row = 0; row < gridHeight; row++)
|
||
{
|
||
for (int col = 0; col < gridWidth; col++)
|
||
{
|
||
GridCell cellData = gridData[row, col];
|
||
Vector2Int pos = new Vector2Int(row, col);
|
||
|
||
if (!gridParticles.ContainsKey(pos)) continue;
|
||
|
||
TextParticle particle = gridParticles[pos];
|
||
if (particle == null || particle.textMesh == null) continue;
|
||
|
||
// 计算与当前频率的距离
|
||
EmotionConfig emotion = GetEmotionConfig(cellData.emotion);
|
||
float distance = 100f;
|
||
|
||
if (emotion.type != EmotionType.Neutral)
|
||
{
|
||
distance = Mathf.Abs(currentFrequency - emotion.frequency);
|
||
}
|
||
|
||
// 根据距离更新显示
|
||
if (cellData.emotion != EmotionType.Neutral)
|
||
{
|
||
bool isTargetWord = cellData.word != cellData.cover;
|
||
|
||
if (distance < 5f)
|
||
{
|
||
// 锁定:显示真实词语,高亮显示
|
||
particle.ForceSetCharacter(cellData.word);
|
||
particle.textMesh.color = emotion.color;
|
||
particle.textMesh.fontStyle = FontStyles.Bold;
|
||
particle.alpha = 1f;
|
||
|
||
// 调整光晕效果
|
||
if (particle.glowSprite != null)
|
||
{
|
||
Color glowColor = emotion.color;
|
||
glowColor.a = 0.5f;
|
||
particle.glowSprite.color = glowColor;
|
||
}
|
||
|
||
maxSignal = Mathf.Max(maxSignal, 100f - distance * 20f);
|
||
}
|
||
else if (distance < 20f)
|
||
{
|
||
// 闪烁区:在真实词和掩盖词之间闪烁
|
||
if (isTargetWord)
|
||
{
|
||
float glitchChance = 1f - (distance / 20f);
|
||
if (Random.value < glitchChance)
|
||
{
|
||
particle.ForceSetCharacter(cellData.word);
|
||
particle.textMesh.color = emotion.color;
|
||
}
|
||
else
|
||
{
|
||
particle.ForceSetCharacter(cellData.cover);
|
||
particle.textMesh.color = Color.white;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
particle.ForceSetCharacter(cellData.word);
|
||
particle.textMesh.color = emotion.color;
|
||
}
|
||
|
||
particle.textMesh.fontStyle = FontStyles.Normal;
|
||
particle.alpha = 0.9f;
|
||
|
||
if (particle.glowSprite != null)
|
||
{
|
||
Color glowColor = emotion.color;
|
||
glowColor.a = 0.3f;
|
||
particle.glowSprite.color = glowColor;
|
||
}
|
||
}
|
||
else if (distance < 35f)
|
||
{
|
||
// 淡出区:显示掩盖词,逐渐淡化
|
||
particle.ForceSetCharacter(isTargetWord ? cellData.cover : cellData.word);
|
||
float alpha = 0.6f - ((distance - 20f) / 30f);
|
||
Color color = Color.white;
|
||
color.a = alpha;
|
||
particle.textMesh.color = color;
|
||
particle.textMesh.fontStyle = FontStyles.Normal;
|
||
particle.alpha = alpha;
|
||
|
||
if (particle.glowSprite != null)
|
||
{
|
||
particle.glowSprite.color = new Color(1f, 1f, 1f, 0f);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 消失区:几乎不可见
|
||
particle.ForceSetCharacter(isTargetWord ? cellData.cover : cellData.word);
|
||
Color color = Color.white;
|
||
color.a = 0.05f;
|
||
particle.textMesh.color = color;
|
||
particle.textMesh.fontStyle = FontStyles.Normal;
|
||
particle.alpha = 0.05f;
|
||
|
||
if (particle.glowSprite != null)
|
||
{
|
||
particle.glowSprite.color = new Color(0f, 0f, 0f, 0f);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 中性/噪音:始终淡化显示
|
||
particle.ForceSetCharacter(cellData.word);
|
||
Color color = new Color(0.27f, 0.33f, 0.27f, 0.3f);
|
||
particle.textMesh.color = color;
|
||
particle.textMesh.fontStyle = FontStyles.Normal;
|
||
particle.alpha = 0.3f;
|
||
|
||
if (particle.glowSprite != null)
|
||
{
|
||
particle.glowSprite.color = new Color(0f, 0f, 0f, 0f);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新信号强度显示
|
||
if (signalStrengthText != null)
|
||
{
|
||
signalStrengthText.text = $"信号强度: {Mathf.RoundToInt(maxSignal)}%";
|
||
}
|
||
}
|
||
|
||
private void OnVerifyClicked()
|
||
{
|
||
if (!isFilteringActive) return;
|
||
|
||
// Log($"> 正在扫描扇区...");
|
||
// Log($"> 当前频率: {currentFrequency:F1} Hz");
|
||
|
||
// 获取解码器条纹当前位置
|
||
Vector2Int stripPos = Vector2Int.zero;
|
||
if (decoderStrip != null)
|
||
{
|
||
// 使用解码器组件获取位置
|
||
AnalysisDecoderStrip stripComponent = decoderStrip.GetComponent<AnalysisDecoderStrip>();
|
||
if (stripComponent != null)
|
||
{
|
||
stripPos = stripComponent.GetGridPosition(cellSize, cellGap);
|
||
}
|
||
else
|
||
{
|
||
// 备用计算方式
|
||
Vector2 anchoredPos = decoderStrip.anchoredPosition;
|
||
int col = Mathf.RoundToInt(anchoredPos.x / (cellSize + cellGap));
|
||
int row = Mathf.RoundToInt(-anchoredPos.y / (cellSize + cellGap));
|
||
stripPos = new Vector2Int(row, col);
|
||
}
|
||
}
|
||
|
||
// Log($"> 条纹位置: [{stripPos.x}, {stripPos.y}]");
|
||
|
||
// 检查是否匹配任何目标
|
||
bool foundMatch = false;
|
||
AnalysisTarget matchedTarget = null;
|
||
|
||
foreach (var target in analysisTargets)
|
||
{
|
||
if (target.stripPosition == stripPos)
|
||
{
|
||
EmotionConfig emotion = GetEmotionConfig(target.emotion);
|
||
float distance = Mathf.Abs(currentFrequency - emotion.frequency);
|
||
|
||
if (distance < 8f)
|
||
{
|
||
// 匹配成功
|
||
foundMatch = true;
|
||
matchedTarget = target;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
// Log("> 发现模式但信号微弱,请调整频率");
|
||
// Log("> 似乎要说的不是这个...");
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (foundMatch && matchedTarget != null)
|
||
{
|
||
// 成功匹配
|
||
// Log($"> <color={ColorUtility.ToHtmlStringRGB(GetEmotionConfig(matchedTarget.emotion).color)}>解密成功!</color>");
|
||
// Log($"> <color={ColorUtility.ToHtmlStringRGB(GetEmotionConfig(matchedTarget.emotion).color)}>完整句子: {targetSentence}</color>");
|
||
// Log("> 寻找完成");
|
||
|
||
// 触发成功回调
|
||
OnAnalysisComplete(true);
|
||
}
|
||
else
|
||
{
|
||
// 未找到匹配
|
||
// Log("> 未检测到数据对齐");
|
||
// Log("> 似乎要说的不是这个...");
|
||
|
||
// 触发失败回调
|
||
OnAnalysisComplete(false);
|
||
}
|
||
}
|
||
|
||
private void OnAnalysisComplete(bool success)
|
||
{
|
||
// 通过 Yarn 对话系统反馈结果
|
||
if (!string.IsNullOrEmpty(completionNodeName))
|
||
{
|
||
if (success)
|
||
{
|
||
// 成功节点
|
||
DialogController.Instance?.StartDialogNode(completionNodeName + "_Success");
|
||
}
|
||
else
|
||
{
|
||
// 失败节点
|
||
DialogController.Instance?.StartDialogNode(completionNodeName + "_Fail");
|
||
}
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
// private void Log(string message)
|
||
// {
|
||
// if (logText != null)
|
||
// {
|
||
// logText.text += message + "\n";
|
||
|
||
// // 自动滚动到底部
|
||
// if (logScrollRect != null)
|
||
// {
|
||
// Canvas.ForceUpdateCanvases();
|
||
// logScrollRect.verticalNormalizedPosition = 0f;
|
||
// }
|
||
// }
|
||
|
||
// Debug.Log($"[AnalysisMode] {message}");
|
||
// }
|
||
}
|
||
}
|
||
|