796 lines
28 KiB
C#
796 lines
28 KiB
C#
using UnityEngine;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using DG.Tweening;
|
||
using Shapes;
|
||
using TMPro;
|
||
|
||
namespace AibisDream.MiniGame.Language
|
||
{
|
||
/// <summary>
|
||
/// 游戏完成阶段
|
||
/// </summary>
|
||
public enum CompletionPhase
|
||
{
|
||
None, // 未完成
|
||
Arranging, // 排列中
|
||
Revealing, // 揭示中
|
||
Completed // 已完成
|
||
}
|
||
|
||
/// <summary>
|
||
/// 语言粒子系统主管理器
|
||
/// </summary>
|
||
public class LanguageParticleManager : MonoBehaviour
|
||
{
|
||
[Header("Canvas设置")]
|
||
[SerializeField] private Canvas worldCanvas;
|
||
[SerializeField] private float canvasSize = 10f;
|
||
|
||
[Header("字体设置")]
|
||
[SerializeField] private TMP_FontAsset chineseFontAsset;
|
||
|
||
[Header("粒子预制体")]
|
||
[SerializeField] private GameObject floatingParticlePrefab;
|
||
[SerializeField] private GameObject candidateParticlePrefab;
|
||
|
||
[Header("粒子数量")]
|
||
[SerializeField] private int floatingCount = 100;
|
||
[SerializeField] private int candidateCount = 50;
|
||
[SerializeField] private int redParticleCount = 8;
|
||
|
||
[Header("游戏参数")]
|
||
[SerializeField] private float connectionDistance = 2f; // Unity单位
|
||
[SerializeField] private float textMargin = 1f;
|
||
|
||
[Header("目标句子")]
|
||
[SerializeField] private string targetSentence = "别过来我感觉害怕";
|
||
|
||
[Header("波形参数")]
|
||
[SerializeField] private float waveformAmplitude = 0.3f;
|
||
[SerializeField] private float interactionRadius = 0.5f;
|
||
|
||
[Header("鼠标交互参数")]
|
||
[SerializeField] private float mouseInteractionRadius = 1.5f;
|
||
[SerializeField] private float repulsionForce = 2f;
|
||
[SerializeField] private float attractionForce = 1.5f;
|
||
|
||
// 粒子列表
|
||
private List<FloatingTextParticle> floatingParticles = new List<FloatingTextParticle>();
|
||
private List<CandidateParticle> candidateParticles = new List<CandidateParticle>();
|
||
private List<CandidateParticle> redParticles = new List<CandidateParticle>();
|
||
|
||
// 效果系统
|
||
private List<EffectPropagation> effectPropagations = new List<EffectPropagation>();
|
||
private Dictionary<string, bool> previousConnections = new Dictionary<string, bool>();
|
||
private bool initializationComplete = false;
|
||
private int initializationFrames = 0;
|
||
|
||
// 游戏状态
|
||
private bool isCompleted = false;
|
||
private CompletionPhase completionPhase = CompletionPhase.None;
|
||
private float completionTimer = 0f;
|
||
private float fadeOutAlpha = 1f;
|
||
private List<CandidateParticle> orderedRedParticles = new List<CandidateParticle>();
|
||
private List<Vector3> targetPositions = new List<Vector3>();
|
||
|
||
// 波形系统
|
||
private List<WaveformPoint> waveformPoints = new List<WaveformPoint>();
|
||
private float calmProgress = 0f;
|
||
private Vector2 lastMousePos;
|
||
private float mouseDragSpeed;
|
||
|
||
// 边界
|
||
private Bounds movementBounds;
|
||
|
||
// 连接渲染(使用Shapes)
|
||
private ConnectionRenderer connectionRenderer;
|
||
private WaveformRenderer waveformRenderer;
|
||
|
||
private void Start()
|
||
{
|
||
InitializeCanvas();
|
||
InitializeParticles();
|
||
InitializeRenderers();
|
||
}
|
||
|
||
private void InitializeCanvas()
|
||
{
|
||
// 如果没有指定canvas,创建一个
|
||
if (worldCanvas == null)
|
||
{
|
||
GameObject canvasObj = new GameObject("Language Canvas");
|
||
canvasObj.transform.SetParent(transform);
|
||
canvasObj.transform.localPosition = Vector3.zero;
|
||
worldCanvas = canvasObj.AddComponent<Canvas>();
|
||
worldCanvas.renderMode = RenderMode.WorldSpace;
|
||
|
||
RectTransform rectTransform = canvasObj.GetComponent<RectTransform>();
|
||
rectTransform.sizeDelta = new Vector2(canvasSize, canvasSize);
|
||
}
|
||
|
||
// 确保 Canvas 配置正确
|
||
if (worldCanvas != null)
|
||
{
|
||
// 设置 WorldSpace Canvas 的相机
|
||
if (worldCanvas.worldCamera == null)
|
||
{
|
||
worldCanvas.worldCamera = Camera.main;
|
||
}
|
||
}
|
||
|
||
// 设置运动边界
|
||
float halfSize = canvasSize / 2f - textMargin;
|
||
movementBounds = new Bounds(
|
||
worldCanvas.transform.position,
|
||
new Vector3(halfSize * 2f, halfSize * 2f, 0f)
|
||
);
|
||
}
|
||
|
||
private void InitializeParticles()
|
||
{
|
||
// 创建背景浮动文字
|
||
for (int i = 0; i < floatingCount; i++)
|
||
{
|
||
Vector3 pos = GetRandomPositionInBounds();
|
||
GameObject obj = CreateParticleObject(pos, $"FloatingParticle_{i}");
|
||
FloatingTextParticle particle = obj.AddComponent<FloatingTextParticle>();
|
||
particle.SetFont(chineseFontAsset);
|
||
particle.SetMovementBounds(movementBounds);
|
||
floatingParticles.Add(particle);
|
||
}
|
||
|
||
// 创建候选粒子
|
||
for (int i = 0; i < candidateCount; i++)
|
||
{
|
||
Vector3 pos = GetRandomPositionInBounds();
|
||
GameObject obj = CreateParticleObject(pos, $"CandidateParticle_{i}");
|
||
CandidateParticle particle = obj.AddComponent<CandidateParticle>();
|
||
particle.SetFont(chineseFontAsset);
|
||
particle.SetMovementBounds(movementBounds);
|
||
candidateParticles.Add(particle);
|
||
}
|
||
|
||
// 选择红色粒子
|
||
SelectRedParticles();
|
||
}
|
||
|
||
private GameObject CreateParticleObject(Vector3 position, string name)
|
||
{
|
||
GameObject obj = new GameObject(name);
|
||
|
||
// 添加 RectTransform(在 Canvas 下必需)
|
||
RectTransform rectTransform = obj.AddComponent<RectTransform>();
|
||
rectTransform.SetParent(worldCanvas.transform, false);
|
||
rectTransform.position = position;
|
||
rectTransform.localScale = Vector3.one;
|
||
rectTransform.sizeDelta = Vector2.zero; // 父对象不需要大小
|
||
|
||
return obj;
|
||
}
|
||
|
||
private Vector3 GetRandomPositionInBounds()
|
||
{
|
||
float x = Random.Range(movementBounds.min.x, movementBounds.max.x);
|
||
float y = Random.Range(movementBounds.min.y, movementBounds.max.y);
|
||
return new Vector3(x, y, 0f);
|
||
}
|
||
|
||
private void InitializeRenderers()
|
||
{
|
||
// 连接线渲染器
|
||
GameObject connObj = new GameObject("ConnectionRenderer");
|
||
connObj.transform.SetParent(transform);
|
||
connObj.transform.localPosition = Vector3.zero;
|
||
connectionRenderer = connObj.AddComponent<ConnectionRenderer>();
|
||
connectionRenderer.manager = this;
|
||
connectionRenderer.connectionDistance = connectionDistance;
|
||
|
||
// 波形渲染器
|
||
GameObject waveObj = new GameObject("WaveformRenderer");
|
||
waveObj.transform.SetParent(transform);
|
||
waveObj.transform.localPosition = Vector3.zero;
|
||
waveformRenderer = waveObj.AddComponent<WaveformRenderer>();
|
||
waveformRenderer.interactionRadius = interactionRadius;
|
||
}
|
||
|
||
private void SelectRedParticles()
|
||
{
|
||
redParticles.Clear();
|
||
List<CandidateParticle> shuffled = candidateParticles.OrderBy(x => Random.value).ToList();
|
||
float minDistance = Mathf.Min(movementBounds.size.x, movementBounds.size.y) / (redParticleCount * 0.8f);
|
||
|
||
foreach (var particle in shuffled)
|
||
{
|
||
if (redParticles.Count >= redParticleCount) break;
|
||
|
||
bool tooClose = false;
|
||
foreach (var selected in redParticles)
|
||
{
|
||
if (Vector3.Distance(particle.transform.position, selected.transform.position) < minDistance)
|
||
{
|
||
tooClose = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!tooClose || redParticles.Count == 0)
|
||
{
|
||
particle.isRed = true;
|
||
particle.originalIsRed = true;
|
||
redParticles.Add(particle);
|
||
}
|
||
}
|
||
|
||
// 如果不够,强制添加
|
||
int remaining = redParticleCount - redParticles.Count;
|
||
for (int i = 0; i < remaining && i < shuffled.Count; i++)
|
||
{
|
||
if (!shuffled[i].isRed)
|
||
{
|
||
shuffled[i].isRed = true;
|
||
shuffled[i].originalIsRed = true;
|
||
redParticles.Add(shuffled[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// 初始化检查
|
||
if (!initializationComplete)
|
||
{
|
||
initializationFrames++;
|
||
if (initializationFrames >= 30)
|
||
{
|
||
initializationComplete = true;
|
||
}
|
||
else if (initializationFrames >= 20)
|
||
{
|
||
// 建立初始连接状态
|
||
BuildConnectionMap();
|
||
}
|
||
}
|
||
|
||
// 更新连接
|
||
UpdateConnections();
|
||
|
||
// 检测连接变化(触发效果传播)
|
||
if (initializationComplete && completionPhase == CompletionPhase.None)
|
||
{
|
||
DetectConnectionChanges();
|
||
}
|
||
|
||
// 更新效果传播
|
||
UpdateEffectPropagations();
|
||
|
||
// 检测完成状态
|
||
if (!isCompleted && completionPhase == CompletionPhase.None)
|
||
{
|
||
if (CheckRedParticlesConnected())
|
||
{
|
||
isCompleted = true;
|
||
StartCompletionSequence();
|
||
}
|
||
}
|
||
|
||
// 更新完成动画
|
||
if (isCompleted)
|
||
{
|
||
UpdateCompletionAnimation();
|
||
}
|
||
|
||
// 更新波形
|
||
if (completionPhase == CompletionPhase.Revealing || completionPhase == CompletionPhase.Completed)
|
||
{
|
||
UpdateWaveform();
|
||
}
|
||
|
||
// 更新输入
|
||
UpdateInput();
|
||
|
||
// 更新渲染器数据
|
||
UpdateRenderers();
|
||
}
|
||
|
||
private void UpdateRenderers()
|
||
{
|
||
// 更新连接线渲染器
|
||
if (connectionRenderer != null)
|
||
{
|
||
connectionRenderer.SetData(
|
||
candidateParticles,
|
||
effectPropagations,
|
||
completionPhase,
|
||
fadeOutAlpha
|
||
);
|
||
}
|
||
|
||
// 更新波形渲染器
|
||
if (waveformRenderer != null)
|
||
{
|
||
Vector2 mousePos = GetMouseWorldPosition();
|
||
bool mouseDown = Input.GetMouseButton(0);
|
||
waveformRenderer.SetData(
|
||
waveformPoints,
|
||
completionPhase,
|
||
calmProgress,
|
||
mousePos,
|
||
mouseDown,
|
||
mouseDragSpeed
|
||
);
|
||
}
|
||
}
|
||
|
||
private void UpdateConnections()
|
||
{
|
||
// 重置所有连接
|
||
foreach (var particle in candidateParticles)
|
||
{
|
||
particle.connections.Clear();
|
||
}
|
||
|
||
// 检测连接
|
||
for (int i = 0; i < candidateParticles.Count; i++)
|
||
{
|
||
for (int j = i + 1; j < candidateParticles.Count; j++)
|
||
{
|
||
float distance = Vector3.Distance(
|
||
candidateParticles[i].transform.position,
|
||
candidateParticles[j].transform.position
|
||
);
|
||
|
||
if (distance < connectionDistance)
|
||
{
|
||
candidateParticles[i].connections.Add(candidateParticles[j]);
|
||
candidateParticles[j].connections.Add(candidateParticles[i]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void BuildConnectionMap()
|
||
{
|
||
previousConnections.Clear();
|
||
for (int i = 0; i < candidateParticles.Count; i++)
|
||
{
|
||
for (int j = i + 1; j < candidateParticles.Count; j++)
|
||
{
|
||
float distance = Vector3.Distance(
|
||
candidateParticles[i].transform.position,
|
||
candidateParticles[j].transform.position
|
||
);
|
||
|
||
if (distance < connectionDistance)
|
||
{
|
||
string key = $"{Mathf.Min(i, j)}-{Mathf.Max(i, j)}";
|
||
previousConnections[key] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void DetectConnectionChanges()
|
||
{
|
||
Dictionary<string, bool> currentConnections = new Dictionary<string, bool>();
|
||
|
||
for (int i = 0; i < candidateParticles.Count; i++)
|
||
{
|
||
for (int j = i + 1; j < candidateParticles.Count; j++)
|
||
{
|
||
float distance = Vector3.Distance(
|
||
candidateParticles[i].transform.position,
|
||
candidateParticles[j].transform.position
|
||
);
|
||
|
||
if (distance < connectionDistance)
|
||
{
|
||
string key = $"{Mathf.Min(i, j)}-{Mathf.Max(i, j)}";
|
||
currentConnections[key] = true;
|
||
|
||
// 检测新连接
|
||
if (!previousConnections.ContainsKey(key))
|
||
{
|
||
var p1 = candidateParticles[i];
|
||
var p2 = candidateParticles[j];
|
||
|
||
// 检查是否是红色粒子与蓝色粒子的新连接
|
||
bool isRedToBlue = (p1.isRed && !p2.isRed && !p2.isOrange && p1.originalIsRed) ||
|
||
(!p1.isRed && !p1.isOrange && p2.isRed && p2.originalIsRed);
|
||
|
||
if (isRedToBlue)
|
||
{
|
||
var redParticle = p1.isRed ? p1 : p2;
|
||
var blueParticle = p1.isRed ? p2 : p1;
|
||
|
||
if (!blueParticle.isStatic && !blueParticle.hasEffect)
|
||
{
|
||
TriggerEffectPropagation(redParticle, blueParticle);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
previousConnections = currentConnections;
|
||
}
|
||
|
||
private void TriggerEffectPropagation(CandidateParticle redParticle, CandidateParticle blueParticle)
|
||
{
|
||
effectPropagations.Add(new EffectPropagation(redParticle, 1f));
|
||
}
|
||
|
||
private void UpdateEffectPropagations()
|
||
{
|
||
for (int i = effectPropagations.Count - 1; i >= 0; i--)
|
||
{
|
||
effectPropagations[i].Update(Time.deltaTime);
|
||
|
||
if (!effectPropagations[i].IsActive)
|
||
{
|
||
effectPropagations[i].Cleanup();
|
||
effectPropagations.RemoveAt(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool CheckRedParticlesConnected()
|
||
{
|
||
if (redParticles.Count == 0) return false;
|
||
if (redParticles.Count == 1) return true;
|
||
|
||
// 检查所有红色粒子是否连通
|
||
HashSet<CandidateParticle> visited = new HashSet<CandidateParticle>();
|
||
Queue<CandidateParticle> queue = new Queue<CandidateParticle>();
|
||
queue.Enqueue(redParticles[0]);
|
||
visited.Add(redParticles[0]);
|
||
|
||
while (queue.Count > 0)
|
||
{
|
||
var current = queue.Dequeue();
|
||
foreach (var connected in current.connections)
|
||
{
|
||
if (connected.isRed && !visited.Contains(connected))
|
||
{
|
||
visited.Add(connected);
|
||
queue.Enqueue(connected);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (visited.Count != redParticles.Count)
|
||
return false;
|
||
|
||
// 检查红色粒子是否与蓝色粒子分离
|
||
foreach (var red in redParticles)
|
||
{
|
||
foreach (var other in candidateParticles)
|
||
{
|
||
if (other.isRed) continue;
|
||
|
||
float dist = Vector3.Distance(red.transform.position, other.transform.position);
|
||
if (dist < connectionDistance)
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private void StartCompletionSequence()
|
||
{
|
||
completionPhase = CompletionPhase.Arranging;
|
||
completionTimer = 0f;
|
||
fadeOutAlpha = 1f;
|
||
|
||
// 重置波形
|
||
calmProgress = 0f;
|
||
waveformPoints.Clear();
|
||
|
||
// 获取排序的红色粒子
|
||
orderedRedParticles = GetOrderedRedParticles();
|
||
int displayLength = Mathf.Min(orderedRedParticles.Count, targetSentence.Length);
|
||
|
||
// 计算目标位置
|
||
float spacing = 0.8f;
|
||
float totalWidth = displayLength * spacing;
|
||
float startX = -totalWidth / 2f + spacing / 2f;
|
||
float centerY = 0f;
|
||
|
||
targetPositions.Clear();
|
||
for (int i = 0; i < displayLength; i++)
|
||
{
|
||
targetPositions.Add(new Vector3(startX + i * spacing, centerY, 0f));
|
||
}
|
||
|
||
if (orderedRedParticles.Count > displayLength)
|
||
{
|
||
orderedRedParticles = orderedRedParticles.Take(displayLength).ToList();
|
||
}
|
||
|
||
// 清理效果
|
||
effectPropagations.Clear();
|
||
|
||
// 停止非红色粒子
|
||
foreach (var p in candidateParticles)
|
||
{
|
||
if (!p.isRed)
|
||
{
|
||
p.velocity = Vector2.zero;
|
||
if (p.isOrange)
|
||
{
|
||
p.RemoveEffect();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private List<CandidateParticle> GetOrderedRedParticles()
|
||
{
|
||
if (redParticles.Count == 0) return new List<CandidateParticle>();
|
||
if (redParticles.Count == 1) return new List<CandidateParticle>(redParticles);
|
||
|
||
// 从最左边的粒子开始
|
||
var ordered = new List<CandidateParticle>();
|
||
var startParticle = redParticles.OrderBy(p => p.transform.position.x).First();
|
||
|
||
HashSet<CandidateParticle> visited = new HashSet<CandidateParticle>();
|
||
Stack<CandidateParticle> stack = new Stack<CandidateParticle>();
|
||
stack.Push(startParticle);
|
||
visited.Add(startParticle);
|
||
|
||
while (stack.Count > 0)
|
||
{
|
||
var current = stack.Pop();
|
||
ordered.Add(current);
|
||
|
||
foreach (var connected in current.connections)
|
||
{
|
||
if (connected.isRed && !visited.Contains(connected))
|
||
{
|
||
visited.Add(connected);
|
||
stack.Push(connected);
|
||
}
|
||
}
|
||
}
|
||
|
||
return ordered;
|
||
}
|
||
|
||
private void UpdateCompletionAnimation()
|
||
{
|
||
completionTimer += Time.deltaTime;
|
||
|
||
if (completionPhase == CompletionPhase.Arranging)
|
||
{
|
||
bool allArrived = true;
|
||
for (int i = 0; i < orderedRedParticles.Count; i++)
|
||
{
|
||
var particle = orderedRedParticles[i];
|
||
var target = targetPositions[i];
|
||
|
||
particle.transform.position = Vector3.Lerp(
|
||
particle.transform.position,
|
||
target,
|
||
Time.deltaTime * 5f
|
||
);
|
||
|
||
if (Vector3.Distance(particle.transform.position, target) > 0.01f)
|
||
{
|
||
allArrived = false;
|
||
}
|
||
}
|
||
|
||
fadeOutAlpha = Mathf.Max(0f, fadeOutAlpha - Time.deltaTime * 2f);
|
||
|
||
if (allArrived && completionTimer > 0.5f)
|
||
{
|
||
completionPhase = CompletionPhase.Revealing;
|
||
completionTimer = 0f;
|
||
InitializeWaveform();
|
||
|
||
// 初始化抖动效果
|
||
foreach (var p in orderedRedParticles)
|
||
{
|
||
p.InitializeAnxietyEffect();
|
||
}
|
||
}
|
||
}
|
||
else if (completionPhase == CompletionPhase.Revealing)
|
||
{
|
||
float revealDelay = 0.25f;
|
||
int currentIndex = Mathf.FloorToInt(completionTimer / revealDelay);
|
||
|
||
for (int i = 0; i < orderedRedParticles.Count; i++)
|
||
{
|
||
var p = orderedRedParticles[i];
|
||
|
||
if (i < currentIndex)
|
||
{
|
||
// 已显示
|
||
if (i < targetSentence.Length)
|
||
{
|
||
p.currentChar = targetSentence[i].ToString();
|
||
p.isStatic = false;
|
||
}
|
||
}
|
||
else if (i == currentIndex)
|
||
{
|
||
// 正在显示(闪烁)
|
||
p.changeSpeedMultiplier = 20f;
|
||
if (completionTimer % revealDelay >= revealDelay - 0.1f)
|
||
{
|
||
if (i < targetSentence.Length)
|
||
{
|
||
p.currentChar = targetSentence[i].ToString();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (currentIndex >= orderedRedParticles.Count)
|
||
{
|
||
completionPhase = CompletionPhase.Completed;
|
||
for (int i = 0; i < orderedRedParticles.Count && i < targetSentence.Length; i++)
|
||
{
|
||
orderedRedParticles[i].currentChar = targetSentence[i].ToString();
|
||
orderedRedParticles[i].isStatic = false;
|
||
orderedRedParticles[i].changeSpeedMultiplier = 1f;
|
||
}
|
||
}
|
||
}
|
||
else if (completionPhase == CompletionPhase.Completed)
|
||
{
|
||
// 检查是否所有文字都已消散
|
||
bool allDissolved = orderedRedParticles.All(p => p.dissolveProgress >= 1f);
|
||
if (allDissolved && completionTimer > 1f)
|
||
{
|
||
ResetGame();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void InitializeWaveform()
|
||
{
|
||
waveformPoints.Clear();
|
||
|
||
if (orderedRedParticles.Count == 0) return;
|
||
|
||
float baseY = 0f;
|
||
|
||
for (int i = 0; i < orderedRedParticles.Count; i++)
|
||
{
|
||
var particle = orderedRedParticles[i];
|
||
var point = new WaveformPoint(
|
||
particle.transform.position.x,
|
||
baseY,
|
||
waveformAmplitude,
|
||
i,
|
||
particle
|
||
);
|
||
waveformPoints.Add(point);
|
||
particle.waveformPoint = point;
|
||
}
|
||
|
||
lastMousePos = GetMouseWorldPosition();
|
||
}
|
||
|
||
private void UpdateWaveform()
|
||
{
|
||
if (waveformPoints.Count == 0)
|
||
{
|
||
InitializeWaveform();
|
||
return;
|
||
}
|
||
|
||
Vector2 mousePos = GetMouseWorldPosition();
|
||
mouseDragSpeed = Vector2.Distance(mousePos, lastMousePos) / Time.deltaTime;
|
||
lastMousePos = mousePos;
|
||
|
||
bool mousePressed = Input.GetMouseButton(0);
|
||
|
||
foreach (var point in waveformPoints)
|
||
{
|
||
point.Update(0f, waveformAmplitude, mousePos, mousePressed, mouseDragSpeed, interactionRadius);
|
||
}
|
||
|
||
// 更新整体平静进度
|
||
if (waveformPoints.Count > 0)
|
||
{
|
||
calmProgress = waveformPoints.Average(p => p.calmAmount);
|
||
}
|
||
}
|
||
|
||
private void UpdateInput()
|
||
{
|
||
// 只在第一阶段(未完成)时处理鼠标输入
|
||
if (completionPhase != CompletionPhase.None) return;
|
||
|
||
Vector2 mouseWorldPos = GetMouseWorldPosition();
|
||
|
||
// 左键点击:排斥附近粒子
|
||
if (Input.GetMouseButton(0))
|
||
{
|
||
ApplyForceToNearbyParticles(mouseWorldPos, mouseInteractionRadius, repulsionForce);
|
||
}
|
||
|
||
// 右键点击:吸引附近粒子
|
||
if (Input.GetMouseButton(1))
|
||
{
|
||
ApplyForceToNearbyParticles(mouseWorldPos, mouseInteractionRadius, -attractionForce);
|
||
}
|
||
}
|
||
|
||
private void ApplyForceToNearbyParticles(Vector2 center, float repulsionRadius, float repulsionForce)
|
||
{
|
||
foreach (var particle in candidateParticles)
|
||
{
|
||
Vector2 particlePos = particle.transform.position;
|
||
float distance = Vector2.Distance(center, particlePos);
|
||
|
||
if (distance < repulsionRadius && distance > 0.01f)
|
||
{
|
||
// 计算方向和力度
|
||
Vector2 direction = (particlePos - center).normalized;
|
||
float forceMagnitude = repulsionForce * (1f - distance / repulsionRadius);
|
||
|
||
// 应用力
|
||
particle.ApplyForce(direction * forceMagnitude);
|
||
}
|
||
}
|
||
}
|
||
|
||
private Vector2 GetMouseWorldPosition()
|
||
{
|
||
Vector3 mousePos = Input.mousePosition;
|
||
Camera cam = Camera.main;
|
||
if (cam != null)
|
||
{
|
||
// 对于正交相机,需要使用正确的Z距离
|
||
float zDistance = cam.transform.position.z - worldCanvas.transform.position.z;
|
||
Vector3 worldPos = cam.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, Mathf.Abs(zDistance)));
|
||
return new Vector2(worldPos.x, worldPos.y);
|
||
}
|
||
return Vector2.zero;
|
||
}
|
||
|
||
private void ResetGame()
|
||
{
|
||
isCompleted = false;
|
||
completionPhase = CompletionPhase.None;
|
||
completionTimer = 0f;
|
||
fadeOutAlpha = 1f;
|
||
orderedRedParticles.Clear();
|
||
targetPositions.Clear();
|
||
effectPropagations.Clear();
|
||
previousConnections.Clear();
|
||
initializationComplete = false;
|
||
initializationFrames = 0;
|
||
calmProgress = 0f;
|
||
waveformPoints.Clear();
|
||
|
||
// 重置所有粒子
|
||
foreach (var p in candidateParticles)
|
||
{
|
||
p.isRed = false;
|
||
p.isOrange = false;
|
||
p.hasEffect = false;
|
||
p.isStatic = false;
|
||
p.originalIsRed = false;
|
||
p.changeSpeedMultiplier = 1f;
|
||
p.vibrationOffset = Vector2.zero;
|
||
p.velocity = new Vector2(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f));
|
||
p.transform.position = GetRandomPositionInBounds();
|
||
p.calmProgress = 0f;
|
||
p.dissolveProgress = 0f;
|
||
p.isCalmed = false;
|
||
p.waveformPoint = null;
|
||
}
|
||
|
||
SelectRedParticles();
|
||
}
|
||
}
|
||
}
|
||
|