Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/Emo/LakeLineDrawer.cs
T
2025-05-09 13:42:17 +08:00

1230 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using Shapes;
using System.Collections.Generic;
using UnityEngine.UI;
using TMPro;
[ExecuteAlways]
public class LakeLineDrawer : ImmediateModeShapeDrawer
{
[Header("场设置")]
public float fieldSize = 80f;
public Vector2 fieldOffset = Vector2.zero;
public int pointCount = 80;
public float pointSize = 0.1f;
public float lineWidth = 0.03f;
[Header("颜色设置")]
public Color lakeDarkColor = new Color(0f, 0.5f, 1f); // 湖面低处颜色
public Color lakeLightColor = new Color(0.5f, 1f, 1f); // 湖面高处颜色
public Color edgeDarkColor = new Color(0f, 0.5f, 1f); // 边缘低处颜色
public Color edgeLightColor = new Color(0.5f, 1f, 1f, 0.3f); // 边缘高处颜色
public float edgeLineLength = 1f; // 边缘线的长度
[Header("UI控制")]
public Button upButton;
public Button downButton;
public Button leftButton;
public Button rightButton;
public float moveStep = 1f; // 每次移动的步长
public Transform targetButtonsParent; // 目标按钮的父物体(带有Layout组件的容器)
public GameObject targetButtonPrefab; // 目标按钮预制体
public float buttonSpacing = 10f; // 按钮之间的间距
[Header("目标点设置")]
public List<TargetPoint> targetPoints = new List<TargetPoint>();
public float targetSize = 0.5f;
public float rotationSpeed = 30f; // 旋转速度(度/秒)
public float floatAmplitude = 0.2f; // 起伏幅度
public float floatFrequency = 2f; // 起伏频率
public float targetLineWidth = 0.02f; // 目标点线框的宽度
public float targetHeightOffset = 0.5f; // 目标点距离湖面的高度偏移
public Color targetFillColor = Color.red; // 目标点实体颜色
public Color targetWireColor = Color.white; // 目标点线框颜色
public float wireScale = 1.05f; // 线框相对于实体的大小比例
public float targetColorInfluenceRange = 5f; // 目标点颜色影响范围
public float targetColorInfluenceStrength = 1f; // 目标点颜色影响强度
[Header("可视区域")]
public float visibleAreaSize = 20f;
// 波动参数,用于生成波动效果
public float noiseScale = 0.3f;
public float waveHeight = 1.5f;
public float waveSpeed = 1f;
public float timeScale = 1f;
[Header("涟漪参数")]
public float rippleStrength = 4f; // 增加涟漪强度
public float rippleDuration = 0.8f; // 缩短持续时间使效果更活跃
public float rippleSpreadSpeed = 6f; // 增加传播速度
public float rippleSharpness = 3f; // 增加锐度
public float rippleMaxRadius = 4f; // 增加最大半径
public float rippleFadeStartTime = 0.3f; // 提前开始淡出的时间点
public float rippleFadeCurve = 2f; // 增加淡出曲线的指数,使消失更快
[Header("障碍物设置")]
public List<Obstacle> obstacles = new List<Obstacle>();
public float obstacleHeight = 2f; // 障碍物高度
public float obstacleWidth = 0.5f; // 障碍物宽度
public Color obstacleColor = Color.white; // 障碍物颜色
public float collisionEffectDuration = 0.5f; // 碰撞效果持续时间
public float collisionEffectSize = 1f; // 碰撞效果大小
public Color collisionEffectColor = Color.white; // 碰撞效果颜色
public int collisionEffectRingCount = 3; // 涟漪环的数量
public int collisionEffectDashCount = 12; // 每个环的虚线数量
public int maxObstacleCount = 20; // 最大障碍物数量
public float minDistanceToTarget = 3f; // 障碍物到目标的最小距离
public float maxDistanceToTarget = 10f; // 障碍物到目标的最大距离
[Header("目标触发效果")]
public float targetTriggerDuration = 1f; // 目标触发效果的持续时间
public float targetTriggerStrength = 3f; // 目标触发效果的强度
public float targetTriggerSpreadSpeed = 6f; // 目标触发效果的传播速度
public Color targetTriggerColor = Color.red; // 目标触发效果的颜色
public float targetFadeInSpeed = 2f; // 目标淡入速度
public float targetScaleSpeed = 2f; // 目标大小变化速度
public float targetMaxScale = 1.5f; // 目标最大大小
[Header("文字效果设置")]
public GameObject textPrefab; // TMP文本预制体
public Canvas worldSpaceCanvas; // 世界空间Canvas
public float textFloatSpeed = 2f; // 文字上浮速度
public float textFadeInDuration = 0.3f; // 文字淡入时间
public float textFadeOutDuration = 0.5f; // 文字淡出时间
public float textScaleDuration = 0.5f; // 文字缩放时间
public float textMaxScale = 1.5f; // 文字最大缩放
public float textLifeTime = 1.5f; // 文字总生命周期
private Vector3[] points;
// 存储每个点是否在可视区域内的标记
private bool[] validPoints;
private float time;
private List<Ripple> ripples = new List<Ripple>();
private List<Ripple> targetTriggerRipples = new List<Ripple>(); // 存储目标触发的涟漪
private Dictionary<TargetPoint, Button> targetButtons = new Dictionary<TargetPoint, Button>();
private List<FloatingText> floatingTexts = new List<FloatingText>();
private class FloatingText
{
public TextMeshProUGUI textComponent;
public float startTime;
public Vector3 startPosition;
public Color targetColor;
public float currentScale = 1f;
public float currentAlpha = 0f;
}
[System.Serializable]
public class TargetPoint
{
public string name = "未命名目标"; // 目标的名字
public Vector2 gridPosition; // 网格位置(0到pointCount-1
public TargetType type;
public Color color = Color.white;
[HideInInspector]
public float visibility = 0f; // 可见度
[HideInInspector]
public float scale = 1f; // 当前大小
[HideInInspector]
public float lastTriggerTime = -1f; // 最后一次被触发的时间
[HideInInspector]
public bool isFound = false; // 是否已被找到
}
public enum TargetType
{
Sphere,
Cube,
Torus
}
private struct Ripple
{
public Vector2 position;
public float startTime;
}
private struct RippleResult
{
public float heightOffset;
public float brightnessBoost;
public Color targetColor;
public float targetInfluence;
public float shapeDistortion;
}
[System.Serializable]
public class Obstacle
{
public Vector2 gridPosition; // 网格位置(0到pointCount-1
public string displayText = "障碍"; // 碰撞时显示的文字
[HideInInspector]public float visibility = 0f; // 可见度
[HideInInspector]public float lastRippleTime = -1f; // 最后一次被涟漪影响的时间
[HideInInspector]public float riseProgress = 0f; // 升起进度(0-1
[HideInInspector]public float colorFadeProgress = 0f; // 颜色淡入进度(0-1
[HideInInspector]public float lastTextTime = -1f; // 最后一次生成文字的时间
}
private struct CollisionEffect
{
public Vector2 position;
public float startTime;
}
private List<CollisionEffect> collisionEffects = new List<CollisionEffect>();
// 涟漪效果相关常量
private const float RIPPLE_SPREAD_FACTOR = 0.3f;
private const float RIPPLE_DISTANCE_FACTOR = 2f;
private const float RIPPLE_BRIGHTNESS_MULTIPLIER = 1.5f;
private const float RIPPLE_TIME_FACTOR = 0.5f;
private const float RIPPLE_EASE_FACTOR = 0.5f;
private void Start()
{
// 添加按钮点击事件监听
if (upButton != null) upButton.onClick.AddListener(MoveUp);
if (downButton != null) downButton.onClick.AddListener(MoveDown);
if (leftButton != null) leftButton.onClick.AddListener(MoveLeft);
if (rightButton != null) rightButton.onClick.AddListener(MoveRight);
// 生成随机障碍物
GenerateRandomObstacles();
// 初始化目标按钮
InitializeTargetButtons();
}
private void InitializeTargetButtons()
{
if (targetButtonPrefab == null || targetButtonsParent == null) return;
// 清除现有的按钮
ClearTargetButtons();
}
private void CreateTargetButton(TargetPoint target)
{
if (targetButtonPrefab == null || targetButtonsParent == null) return;
// 如果按钮已经存在,直接显示
if (targetButtons.ContainsKey(target))
{
targetButtons[target].gameObject.SetActive(true);
return;
}
// 创建新按钮
var buttonObj = Instantiate(targetButtonPrefab, targetButtonsParent);
var button = buttonObj.GetComponent<Button>();
// 设置按钮文本
var text = buttonObj.GetComponentInChildren<Text>();
if (text != null)
{
text.text = string.IsNullOrEmpty(target.name) ? "目标" : target.name;
}
// 设置按钮颜色
var image = buttonObj.GetComponent<Image>();
if (image != null)
{
image.color = target.color;
}
// 添加点击事件
button.onClick.AddListener(() => MoveToTarget(target));
// 存储按钮引用
targetButtons[target] = button;
}
private void MoveToTarget(TargetPoint target)
{
float step = fieldSize / (pointCount - 1);
float halfField = fieldSize * 0.5f;
float halfVisible = visibleAreaSize * 0.5f;
// 计算目标的世界坐标
float targetX = -halfField + target.gridPosition.x * step;
float targetZ = -halfField + target.gridPosition.y * step;
// 设置相机位置,使目标在视野中心
fieldOffset.x = -targetX;
fieldOffset.y = -targetZ;
// 确保不会超出边界
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f;
fieldOffset.x = Mathf.Clamp(fieldOffset.x, -maxOffset, maxOffset);
fieldOffset.y = Mathf.Clamp(fieldOffset.y, -maxOffset, maxOffset);
}
private void ClearTargetButtons()
{
// 清除所有目标按钮
foreach (var button in targetButtons.Values)
{
if (button != null)
{
button.onClick.RemoveAllListeners();
Destroy(button.gameObject);
}
}
targetButtons.Clear();
// 清除目标按钮父物体下的所有子物体
if (targetButtonsParent != null)
{
foreach (Transform child in targetButtonsParent)
{
Destroy(child.gameObject);
}
}
// 重置所有目标的状态
foreach (var target in targetPoints)
{
target.isFound = false;
target.visibility = 0f;
target.scale = 1f;
target.lastTriggerTime = -1f;
}
}
private void OnDestroy()
{
// 移除按钮点击事件监听
if (upButton != null) upButton.onClick.RemoveListener(MoveUp);
if (downButton != null) downButton.onClick.RemoveListener(MoveDown);
if (leftButton != null) leftButton.onClick.RemoveListener(MoveLeft);
if (rightButton != null) rightButton.onClick.RemoveListener(MoveRight);
// 清除所有按钮
ClearTargetButtons();
// 清除所有漂浮文字
ClearFloatingTexts();
}
private void ClearFloatingTexts()
{
foreach (var text in floatingTexts)
{
if (text.textComponent != null)
{
Destroy(text.textComponent.gameObject);
}
}
floatingTexts.Clear();
}
private void MoveUp()
{
float halfVisible = visibleAreaSize * 0.5f;
float maxOffset = fieldSize * 0.5f - halfVisible * 0.6f; // 允许超出20%
if (fieldOffset.y < maxOffset)
{
fieldOffset.y += moveStep;
fieldOffset.y = Mathf.Clamp(fieldOffset.y, -maxOffset, maxOffset);
}
}
private void MoveDown()
{
float halfVisible = visibleAreaSize * 0.5f;
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f; // 允许超出20%
if (fieldOffset.y > -maxOffset)
{
fieldOffset.y -= moveStep;
fieldOffset.y = Mathf.Clamp(fieldOffset.y, -maxOffset, maxOffset);
}
}
private void MoveLeft()
{
float halfVisible = visibleAreaSize * 0.5f;
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f; // 允许超出20%
if (fieldOffset.x > -maxOffset)
{
fieldOffset.x -= moveStep;
fieldOffset.x = Mathf.Clamp(fieldOffset.x, -maxOffset, maxOffset);
}
}
private void MoveRight()
{
float halfVisible = visibleAreaSize * 0.5f;
float maxOffset = fieldSize * 0.5f - halfVisible * 0.8f; // 允许超出20%
if (fieldOffset.x < maxOffset)
{
fieldOffset.x += moveStep;
fieldOffset.x = Mathf.Clamp(fieldOffset.x, -maxOffset, maxOffset);
}
}
private void HandleUIInput()
{
// 由于现在使用按钮事件,这个方法可以留空或删除
}
private void Update()
{
// 如果是播放状态,则更新时间;否则设为0
time = Application.isPlaying ? Time.time * timeScale : 0f;
HandleMouseInput();
HandleUIInput();
CleanExpiredRipples();
CleanExpiredCollisionEffects();
UpdateObstacleVisibility();
UpdateTargetVisibility();
CleanExpiredTargetTriggerRipples();
UpdateFloatingTexts(); // 添加更新文字效果
// 检查所有涟漪的碰撞
if (Application.isPlaying)
{
float currentTime = Time.time;
foreach (var ripple in ripples)
{
float elapsed = currentTime - ripple.startTime;
float spread = elapsed * rippleSpreadSpeed;
CheckRippleCollisions(ripple.position, spread, currentTime);
}
}
}
private void HandleMouseInput()
{
if (Application.isPlaying && Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
Vector3 hitPos = hit.point;
ripples.Add(new Ripple
{
position = new Vector2(hitPos.x, hitPos.z),
startTime = Time.time
});
}
}
}
private void CleanExpiredRipples()
{
ripples.RemoveAll(r => Time.time - r.startTime > rippleDuration);
}
private void CleanExpiredCollisionEffects()
{
collisionEffects.RemoveAll(e => Time.time - e.startTime > collisionEffectDuration);
}
private void CleanExpiredTargetTriggerRipples()
{
targetTriggerRipples.RemoveAll(r => Time.time - r.startTime > targetTriggerDuration);
}
// 计算涟漪对某个点的影响,返回该点的偏移量和亮度提升
private RippleResult GetRippleEffect(float x, float z, float currentTime)
{
float rippleOffset = 0f;
float brightness = 0f;
Color targetColor = Color.white;
float maxTargetInfluence = 0f;
float shapeDistortion = 0f;
// 处理普通涟漪
foreach (var ripple in ripples)
{
ProcessRippleEffect(ripple, x, z, currentTime, ref rippleOffset, ref brightness, ref targetColor, ref maxTargetInfluence, ref shapeDistortion);
}
// 处理目标触发的涟漪
foreach (var ripple in targetTriggerRipples)
{
ProcessRippleEffect(ripple, x, z, currentTime, ref rippleOffset, ref brightness, ref targetColor, ref maxTargetInfluence, ref shapeDistortion, true);
}
return new RippleResult
{
heightOffset = rippleOffset,
brightnessBoost = brightness,
targetColor = targetColor,
targetInfluence = maxTargetInfluence,
shapeDistortion = shapeDistortion
};
}
private void ProcessRippleEffect(Ripple ripple, float x, float z, float currentTime,
ref float rippleOffset, ref float brightness, ref Color targetColor,
ref float maxTargetInfluence, ref float shapeDistortion, bool isTargetTrigger = false)
{
// 检查涟漪是否有效
if (currentTime - ripple.startTime > (isTargetTrigger ? targetTriggerDuration : rippleDuration))
return;
// 计算基础参数
float elapsed = currentTime - ripple.startTime;
float spreadSpeed = isTargetTrigger ? targetTriggerSpreadSpeed : rippleSpreadSpeed;
float spread = elapsed * spreadSpeed;
float dist = Vector2.Distance(new Vector2(x, z), ripple.position);
// 计算时间相关参数
float duration = isTargetTrigger ? targetTriggerDuration : rippleDuration;
float t = Mathf.Clamp01(elapsed / duration);
float fadeT = Mathf.Clamp01((t - rippleFadeStartTime) / (1f - rippleFadeStartTime));
float fadeCurve = Mathf.Pow(1f - fadeT, rippleFadeCurve);
// 计算缓动效果
float ease = CalculateEase(t, fadeCurve);
// 计算效果因子
float finalFactor = CalculateEffectFactor(dist, spread, t);
// 应用涟漪效果
float strength = isTargetTrigger ? targetTriggerStrength : rippleStrength;
ApplyRippleEffect(finalFactor, ease, strength, ref rippleOffset, ref brightness);
}
private float CalculateEase(float t, float fadeCurve)
{
return Mathf.SmoothStep(0, 1, Mathf.Sin(t * Mathf.PI * RIPPLE_EASE_FACTOR)) * fadeCurve;
}
private float CalculateEffectFactor(float dist, float spread, float t)
{
float maxRadius = rippleMaxRadius;
float distRatio = dist / maxRadius;
float spreadRatio = (dist - spread) / (maxRadius * RIPPLE_SPREAD_FACTOR);
float timeFactor = 1f - t * RIPPLE_TIME_FACTOR;
// 计算距离衰减
float distanceFactor = Mathf.Exp(-distRatio * distRatio * RIPPLE_DISTANCE_FACTOR);
// 计算扩散衰减
float spreadFactor = Mathf.Exp(-spreadRatio * spreadRatio * rippleSharpness * timeFactor);
return distanceFactor * spreadFactor;
}
private void ApplyRippleEffect(float finalFactor, float ease, float strength,
ref float rippleOffset, ref float brightness)
{
rippleOffset += finalFactor * strength * ease;
brightness += finalFactor * ease * RIPPLE_BRIGHTNESS_MULTIPLIER;
}
private void CheckRippleCollisions(Vector2 ripplePos, float spread, float currentTime)
{
float step = fieldSize / (pointCount - 1);
float halfField = fieldSize * 0.5f;
// 检查障碍物碰撞
foreach (var obstacle in obstacles)
{
float x = -halfField + obstacle.gridPosition.x * step + fieldOffset.x;
float z = -halfField + obstacle.gridPosition.y * step + fieldOffset.y;
Vector2 worldPos = new Vector2(x, z);
float dist = Vector2.Distance(ripplePos, worldPos);
if (dist <= rippleMaxRadius && Mathf.Abs(dist - spread) < obstacleWidth * 0.5f)
{
obstacle.lastRippleTime = currentTime;
if (obstacle.visibility <= 0f)
{
obstacle.visibility = 0.2f;
obstacle.riseProgress = 0f;
obstacle.colorFadeProgress = 0f;
}
else if (obstacle.visibility < 1f)
{
obstacle.riseProgress = Mathf.Max(obstacle.riseProgress, obstacle.visibility);
}
collisionEffects.Add(new CollisionEffect
{
position = worldPos,
startTime = currentTime
});
// 检查是否可以生成新的文字(冷却时间为2秒)
if (currentTime - obstacle.lastTextTime > 2f)
{
Vector3 textPosition = new Vector3(x, GetHeight(x - fieldOffset.x, z - fieldOffset.y, time) + obstacleHeight * 0.5f, z);
CreateFloatingText(textPosition, obstacle.displayText, obstacleColor);
obstacle.lastTextTime = currentTime;
}
}
}
// 检查目标碰撞
foreach (var target in targetPoints)
{
float x = -halfField + target.gridPosition.x * step + fieldOffset.x;
float z = -halfField + target.gridPosition.y * step + fieldOffset.y;
Vector2 worldPos = new Vector2(x, z);
float dist = Vector2.Distance(ripplePos, worldPos);
if (dist <= rippleMaxRadius && Mathf.Abs(dist - spread) < 0.5f && target.lastTriggerTime < currentTime - targetTriggerDuration)
{
// 确保目标点之前没有被触发过
if (target.lastTriggerTime <= 0)
{
target.lastTriggerTime = currentTime;
target.isFound = true;
target.visibility = 0.2f; // 初始可见度
targetTriggerRipples.Add(new Ripple
{
position = worldPos,
startTime = currentTime
});
// 创建目标按钮
CreateTargetButton(target);
Vector3 textPosition = new Vector3(x, GetHeight(x - fieldOffset.x, z - fieldOffset.y, time) + targetHeightOffset, z);
CreateFloatingText(textPosition, target.name, target.color);
}
}
}
}
// 修改GetHeight方法,使波浪更自然
private float GetHeight(float x, float z, float currentTime)
{
float noise = 0;
float amp = 1f;
float freq = 1f;
// 生成多个频率的噪声,模拟更复杂的波动
for (int i = 0; i < 3; i++)
{
// 基于当前时间和位置生成噪声值
float nx = x * freq * noiseScale;
float nz = z * freq * noiseScale;
float nt = currentTime * waveSpeed * freq;
// 使用平滑的插值
float noise1 = Mathf.PerlinNoise(nx + nt, nz + nt);
float noise2 = Mathf.PerlinNoise(nx + nt + 0.1f, nz + nt + 0.1f);
noise += Mathf.Lerp(noise1, noise2, Mathf.SmoothStep(0, 1, (Mathf.Sin(currentTime * 0.5f) + 1) * 0.5f)) * amp;
amp *= 0.5f; // 每次衰减幅度
freq *= 2f; // 每次增加频率
}
return noise * waveHeight;
}
// 绘制湖面
public override void DrawShapes(Camera cam)
{
using (Draw.Command(cam))
{
Draw.ResetAllDrawStates();
Draw.LineGeometry = LineGeometry.Volumetric3D;
Draw.Thickness = lineWidth;
float step = fieldSize / (pointCount - 1);
float halfField = fieldSize * 0.5f;
float halfVisible = visibleAreaSize * 0.5f;
if (points == null || points.Length != pointCount * pointCount)
points = new Vector3[pointCount * pointCount];
if (validPoints == null || validPoints.Length != pointCount * pointCount)
validPoints = new bool[pointCount * pointCount];
CalculatePointPositions(step, halfField, halfVisible);
DrawPointsAndLines(step, halfField, halfVisible);
DrawEdgeLines(halfField, halfVisible);
DrawTargetPoints();
DrawObstacles(); // 绘制障碍物
DrawCollisionEffects(); // 绘制碰撞效果
}
}
// 计算每个点的位置和涟漪效果
private void CalculatePointPositions(float step, float halfField, float halfVisible)
{
// 遍历每个点,计算其位置和涟漪影响
for (int i = 0; i < pointCount; i++)
{
for (int j = 0; j < pointCount; j++)
{
// 计算当前点的位置(xz
float x = -halfField + j * step + fieldOffset.x;
float z = -halfField + i * step + fieldOffset.y;
// 判断该点是否在可视区域内
bool inside = Mathf.Abs(x) <= halfVisible && Mathf.Abs(z) <= halfVisible;
int index = i * pointCount + j; // 计算当前点的索引
validPoints[index] = inside; // 设置该点是否有效
if (!inside)
continue;
// 获取基础波浪高度,并计算涟漪影响
float baseHeight = GetHeight(x, z, time);
RippleResult ripple = GetRippleEffect(x, z, Time.time);
float finalY = baseHeight + ripple.heightOffset;
// 创建点的坐标
Vector3 pos = new Vector3(x, finalY, z);
points[index] = pos; // 存储计算得到的点
// 计算亮度:基于高度,越高越亮
float heightT = Mathf.InverseLerp(-waveHeight, waveHeight, finalY);
float brightness = Mathf.Clamp01(1f + ripple.brightnessBoost); // 计算亮度,防止超出范围
// 使用与 LakeVisualizer 相同的颜色渐变
Color baseColor = Color.Lerp(new Color(0.5f, 1f, 1f), new Color(0f, 0.5f, 1f), heightT);
baseColor *= brightness;
// 如果受到目标影响,混合目标颜色
if (ripple.targetInfluence > 0)
{
baseColor = Color.Lerp(baseColor, ripple.targetColor, ripple.targetInfluence);
}
// 绘制当前点
Draw.Sphere(pos, pointSize * (1f - heightT * 0.5f), baseColor);
}
}
}
// 绘制点与点之间的连线
private void DrawPointsAndLines(float step, float halfField, float halfVisible)
{
for (int i = 0; i < pointCount; i++)
{
for (int j = 0; j < pointCount; j++)
{
int index = i * pointCount + j;
if (!validPoints[index])
continue;
Vector3 current = points[index];
if (j < pointCount - 1 && validPoints[index + 1])
{
Vector3 next = points[index + 1];
DrawLineBetweenPoints(current, next, halfField, halfVisible);
}
// 绘制与下边点的连线
if (i < pointCount - 1 && validPoints[index + pointCount])
{
Vector3 next = points[index + pointCount];
DrawLineBetweenPoints(current, next, halfField, halfVisible);
}
}
}
}
// 绘制两点之间的连线,加入深度和颜色的渐变
private void DrawLineBetweenPoints(Vector3 current, Vector3 next, float halfField, float halfVisible)
{
// 计算连线的平均高度
float avgY = (current.y + next.y) * 0.5f;
float heightT = Mathf.InverseLerp(-waveHeight, waveHeight, avgY);
// 计算连线的颜色,依赖于高度
Color lineColor = Color.Lerp(Color.cyan, new Color(0, 0.5f, 1f, 0.3f), heightT);
// 绘制连线
Draw.Line(current, next, lineWidth * (1f - heightT * 0.5f), lineColor);
}
private void DrawEdgeLines(float halfField, float halfVisible)
{
// 绘制湖面边缘的向下线条(无扰动)
float step = fieldSize / (pointCount - 1);
float halfEdgeLineLength = edgeLineLength * 0.5f; // 设置边缘线长度
// 1. 绘制 X 轴的边缘线(左边和右边)
for (float z = -halfField; z <= halfField; z += step)
{
float worldZ = z + fieldOffset.y;
float x = halfField + fieldOffset.x; // 右边
DrawEdgeLine(x, worldZ, halfVisible);
x = -halfField + fieldOffset.x; // 左边
DrawEdgeLine(x, worldZ, halfVisible);
}
// 2. 绘制 Z 轴的边缘线(上边和下边)
for (float x = -halfField; x <= halfField; x += step)
{
float worldX = x + fieldOffset.x;
float z = halfField + fieldOffset.y; // 上边
DrawEdgeLine(worldX, z, halfVisible);
z = -halfField + fieldOffset.y; // 下边
DrawEdgeLine(worldX, z, halfVisible);
}
}
private void DrawEdgeLine(float x, float z, float halfVisible)
{
// 检查点是否在可视区域内
if (Mathf.Abs(x) > halfVisible || Mathf.Abs(z) > halfVisible)
{
return;
}
// 计算湖面高度作为起始点的Y坐标
float startY = GetHeight(x - fieldOffset.x, z - fieldOffset.y, time); // 获取湖面高度
// 设置边缘线的起始点
Vector3 start = new Vector3(x, startY, z);
// 设置边缘线的终止点,向下延伸一定的长度
Vector3 end = new Vector3(x, startY - edgeLineLength, z); // 向下延伸的线条
// 设置颜色,基于高度
float heightT = Mathf.InverseLerp(-waveHeight, waveHeight, startY);
Color lineColor = Color.Lerp(edgeDarkColor, edgeLightColor, heightT);
lineColor.a *= 0.8f; // 设置透明度
// 绘制边缘线
Draw.Line(start, end, lineWidth * 0.5f, lineColor);
}
// 绘制目标点
private void DrawTargetPoints()
{
float step = fieldSize / (pointCount - 1);
float halfField = fieldSize * 0.5f;
float halfVisible = visibleAreaSize * 0.5f;
foreach (var target in targetPoints)
{
// 将网格位置转换为世界坐标
float x = -halfField + target.gridPosition.x * step + fieldOffset.x;
float z = -halfField + target.gridPosition.y * step + fieldOffset.y;
// 检查是否在可视区域内
if (Mathf.Abs(x) > halfVisible || Mathf.Abs(z) > halfVisible)
{
continue;
}
// 获取该点的高度
float height = GetHeight(x, z, time);
// 添加额外的起伏效果和高度偏移
float floatOffset = Mathf.Sin(time * floatFrequency) * floatAmplitude;
Vector3 position = new Vector3(x, height + floatOffset + targetHeightOffset, z);
// 计算旋转
float rotation = time * rotationSpeed;
Quaternion rot = Quaternion.Euler(0, rotation, 0);
// 计算当前颜色和大小
Color currentColor = Color.Lerp(Color.clear, target.color, target.visibility);
float currentSize = targetSize * target.scale;
// 绘制外层立方体线框
DrawWireCube(position, rot, currentSize * wireScale, currentColor);
// 根据类型绘制内部形状
switch (target.type)
{
case TargetType.Sphere:
Draw.Sphere(position, currentSize * 0.8f, currentColor);
break;
case TargetType.Cube:
Draw.Cube(position, rot, currentSize * 0.8f, currentColor);
break;
case TargetType.Torus:
Draw.Torus(position, rot, currentSize * 0.8f, currentSize * 0.3f, currentColor);
break;
}
}
}
private void DrawWireCube(Vector3 position, Quaternion rotation, float size, Color color)
{
Vector3[] vertices = new Vector3[]
{
new Vector3(-size, -size, -size),
new Vector3(size, -size, -size),
new Vector3(size, size, -size),
new Vector3(-size, size, -size),
new Vector3(-size, -size, size),
new Vector3(size, -size, size),
new Vector3(size, size, size),
new Vector3(-size, size, size)
};
// 应用旋转
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = rotation * vertices[i] + position;
}
// 绘制边
int[] edges = new int[]
{
0,1, 1,2, 2,3, 3,0, // 底面
4,5, 5,6, 6,7, 7,4, // 顶面
0,4, 1,5, 2,6, 3,7 // 连接线
};
for (int i = 0; i < edges.Length; i += 2)
{
Draw.Line(vertices[edges[i]], vertices[edges[i+1]], targetLineWidth, color);
}
}
private void DrawObstacles()
{
float step = fieldSize / (pointCount - 1);
float halfField = fieldSize * 0.5f;
float halfVisible = visibleAreaSize * 0.5f;
foreach (var obstacle in obstacles)
{
// 在编辑器中始终显示
if (Application.isPlaying && obstacle.visibility <= 0f)
continue;
// 将网格位置转换为世界坐标
float x = -halfField + obstacle.gridPosition.x * step + fieldOffset.x;
float z = -halfField + obstacle.gridPosition.y * step + fieldOffset.y;
// 检查是否在可视区域内
if (Mathf.Abs(x) > halfVisible || Mathf.Abs(z) > halfVisible)
continue;
// 获取该点的高度
float height = GetHeight(x, z, time);
// 计算颜色和升起效果
Color currentColor = obstacleColor;
if (Application.isPlaying)
{
currentColor.a *= obstacle.visibility * obstacle.colorFadeProgress;
}
float riseHeight = obstacleHeight * (Application.isPlaying ? obstacle.riseProgress : 1f);
// 绘制障碍物
Vector3 basePos = new Vector3(x, height, z);
float halfWidth = obstacleWidth * 0.5f;
// 绘制底部
Draw.Line(basePos + new Vector3(-halfWidth, 0, -halfWidth), basePos + new Vector3(halfWidth, 0, -halfWidth), lineWidth, currentColor);
Draw.Line(basePos + new Vector3(halfWidth, 0, -halfWidth), basePos + new Vector3(halfWidth, 0, halfWidth), lineWidth, currentColor);
Draw.Line(basePos + new Vector3(halfWidth, 0, halfWidth), basePos + new Vector3(-halfWidth, 0, halfWidth), lineWidth, currentColor);
Draw.Line(basePos + new Vector3(-halfWidth, 0, halfWidth), basePos + new Vector3(-halfWidth, 0, -halfWidth), lineWidth, currentColor);
// 绘制顶部(考虑升起效果)
Vector3 topPos = basePos + Vector3.up * riseHeight;
Draw.Line(topPos + new Vector3(-halfWidth, 0, -halfWidth), topPos + new Vector3(halfWidth, 0, -halfWidth), lineWidth, currentColor);
Draw.Line(topPos + new Vector3(halfWidth, 0, -halfWidth), topPos + new Vector3(halfWidth, 0, halfWidth), lineWidth, currentColor);
Draw.Line(topPos + new Vector3(halfWidth, 0, halfWidth), topPos + new Vector3(-halfWidth, 0, halfWidth), lineWidth, currentColor);
Draw.Line(topPos + new Vector3(-halfWidth, 0, halfWidth), topPos + new Vector3(-halfWidth, 0, -halfWidth), lineWidth, currentColor);
// 绘制连接线
Draw.Line(basePos + new Vector3(-halfWidth, 0, -halfWidth), topPos + new Vector3(-halfWidth, 0, -halfWidth), lineWidth, currentColor);
Draw.Line(basePos + new Vector3(halfWidth, 0, -halfWidth), topPos + new Vector3(halfWidth, 0, -halfWidth), lineWidth, currentColor);
Draw.Line(basePos + new Vector3(halfWidth, 0, halfWidth), topPos + new Vector3(halfWidth, 0, halfWidth), lineWidth, currentColor);
Draw.Line(basePos + new Vector3(-halfWidth, 0, halfWidth), topPos + new Vector3(-halfWidth, 0, halfWidth), lineWidth, currentColor);
}
}
private void DrawCollisionEffects()
{
foreach (var effect in collisionEffects)
{
float elapsed = Time.time - effect.startTime;
float t = elapsed / collisionEffectDuration;
// 计算当前效果的大小和透明度
float scale = Mathf.Lerp(0f, collisionEffectSize, t);
float alpha = Mathf.Lerp(1f, 0f, t);
Color effectColor = collisionEffectColor;
effectColor.a *= alpha;
// 计算效果位置
Vector3 effectPos = new Vector3(effect.position.x,
GetHeight(effect.position.x - fieldOffset.x, effect.position.y - fieldOffset.y, time),
effect.position.y);
// 设置虚线样式
Draw.UseDashes = true;
Draw.DashStyle = DashStyle.RelativeDashes(DashType.Basic, 0.5f, 0.5f, DashSnapping.Tiling, 0f, 0f);
// 绘制同心圆涟漪
for (int i = 0; i < collisionEffectRingCount; i++)
{
float ringScale = scale * (i + 1) / collisionEffectRingCount;
float ringAlpha = alpha * (1f - (float)i / collisionEffectRingCount);
Color ringColor = effectColor;
ringColor.a *= ringAlpha;
// 使用Shapes的Ring方法绘制虚线圆环
Draw.Ring(effectPos, Vector3.up, ringScale, lineWidth * (1f - t), ringColor);
}
// 恢复默认样式
Draw.UseDashes = false;
}
}
private void UpdateObstacleVisibility()
{
float currentTime = Application.isPlaying ? Time.time : 0f;
foreach (var obstacle in obstacles)
{
// 在编辑器中,如果没有被涟漪影响过,保持可见
if (!Application.isPlaying)
{
// obstacle.visibility = 1f;
// obstacle.riseProgress = 1f;
// obstacle.colorFadeProgress = 1f;
continue;
}
// 在游戏模式下,如果从未被涟漪影响过,保持不可见
if (obstacle.lastRippleTime <= 0f)
{
obstacle.visibility = 0f;
obstacle.riseProgress = 0f;
obstacle.colorFadeProgress = 0f;
continue;
}
// 如果超过6秒没有被涟漪影响,开始逐渐消失
if (currentTime - obstacle.lastRippleTime > 6f)
{
float fadeSpeed = Time.deltaTime;
obstacle.visibility = Mathf.Max(0f, obstacle.visibility - fadeSpeed);
// 同时降低升起高度
obstacle.riseProgress = Mathf.Max(0f, obstacle.riseProgress - fadeSpeed);
}
else if (obstacle.visibility > 0f)
{
// 如果正在显现,继续增加可见度
obstacle.visibility = Mathf.Min(1f, obstacle.visibility + Time.deltaTime * 0.5f);
// 同时增加升起高度
obstacle.riseProgress = Mathf.Min(1f, obstacle.riseProgress + Time.deltaTime * 2f);
}
// 更新颜色淡入进度
if (obstacle.visibility > 0f)
{
obstacle.colorFadeProgress = Mathf.Min(1f, obstacle.colorFadeProgress + Time.deltaTime * 3f);
}
else
{
obstacle.colorFadeProgress = 0f;
}
}
}
private void UpdateTargetVisibility()
{
float currentTime = Time.time;
foreach (var target in targetPoints)
{
// 如果目标已经被触发过
if (target.lastTriggerTime > 0)
{
// 更新可见度
target.visibility = Mathf.Min(1f, target.visibility + Time.deltaTime * targetFadeInSpeed);
// 更新大小
float targetScale = 1f + (targetMaxScale - 1f) * Mathf.Sin(currentTime * targetScaleSpeed);
target.scale = Mathf.Lerp(target.scale, targetScale, Time.deltaTime * targetScaleSpeed);
}
else
{
// 如果从未被触发过,确保保持不可见
target.visibility = 0f;
target.scale = 1f;
}
}
}
private void GenerateRandomObstacles()
{
obstacles.Clear();
float step = fieldSize / (pointCount - 1);
float halfField = fieldSize * 0.5f;
// 为每个目标生成周围的障碍物
foreach (var target in targetPoints)
{
// 计算目标的世界坐标
float targetX = -halfField + target.gridPosition.x * step;
float targetZ = -halfField + target.gridPosition.y * step;
Vector2 targetWorldPos = new Vector2(targetX, targetZ);
// 计算这个目标周围应该生成的障碍物数量
int obstacleCount = Mathf.CeilToInt(maxObstacleCount / targetPoints.Count);
for (int i = 0; i < obstacleCount; i++)
{
// 生成随机角度和距离
float angle = Random.Range(0f, 360f) * Mathf.Deg2Rad;
float distance = Random.Range(minDistanceToTarget, maxDistanceToTarget);
// 计算障碍物的世界坐标
float obstacleX = targetX + Mathf.Cos(angle) * distance;
float obstacleZ = targetZ + Mathf.Sin(angle) * distance;
// 转换为网格坐标
int gridX = Mathf.RoundToInt((obstacleX + halfField) / step);
int gridZ = Mathf.RoundToInt((obstacleZ + halfField) / step);
// 确保坐标在有效范围内
if (gridX >= 0 && gridX < pointCount && gridZ >= 0 && gridZ < pointCount)
{
// 检查是否与现有障碍物重叠
bool isOverlapping = false;
foreach (var existingObstacle in obstacles)
{
float dist = Vector2.Distance(
new Vector2(gridX, gridZ),
existingObstacle.gridPosition
);
if (dist < 2f) // 最小间距
{
isOverlapping = true;
break;
}
}
if (!isOverlapping)
{
obstacles.Add(new Obstacle
{
gridPosition = new Vector2(gridX, gridZ),
visibility = 0f,
lastRippleTime = -1f,
riseProgress = 0f,
colorFadeProgress = 0f
});
}
}
}
}
}
private void OnDrawGizmos()
{
if (!Application.isPlaying)
{
float step = fieldSize / (pointCount - 1);
float halfField = fieldSize * 0.5f;
foreach (var target in targetPoints)
{
float x = -halfField + target.gridPosition.x * step + fieldOffset.x;
float z = -halfField + target.gridPosition.y * step + fieldOffset.y;
float height = GetHeight(x, z, time);
// 绘制目标点
Gizmos.color = target.color;
Gizmos.DrawSphere(new Vector3(x, height + targetHeightOffset, z), targetSize * 0.5f);
}
}
}
// 添加一个公共方法,可以在需要时手动清除按钮
public void ResetGame()
{
ClearTargetButtons();
ClearFloatingTexts();
// 重新生成障碍物
GenerateRandomObstacles();
// 重新初始化按钮
InitializeTargetButtons();
}
private void UpdateFloatingTexts()
{
float currentTime = Time.time;
for (int i = floatingTexts.Count - 1; i >= 0; i--)
{
var text = floatingTexts[i];
float elapsed = currentTime - text.startTime;
if (elapsed >= textLifeTime)
{
if (text.textComponent != null)
{
Destroy(text.textComponent.gameObject);
}
floatingTexts.RemoveAt(i);
continue;
}
// 更新位置
float floatOffset = elapsed * textFloatSpeed;
text.textComponent.transform.position = text.startPosition + Vector3.up * floatOffset;
// 更新透明度
if (elapsed < textFadeInDuration)
{
text.currentAlpha = elapsed / textFadeInDuration;
}
else if (elapsed > textLifeTime - textFadeOutDuration)
{
text.currentAlpha = (textLifeTime - elapsed) / textFadeOutDuration;
}
else
{
text.currentAlpha = 1f;
}
// 更新缩放
if (elapsed < textScaleDuration)
{
text.currentScale = Mathf.Lerp(1f, textMaxScale, elapsed / textScaleDuration);
}
else
{
text.currentScale = textMaxScale;
}
// 应用颜色和透明度
Color currentColor = text.targetColor;
currentColor.a = text.currentAlpha;
text.textComponent.color = currentColor;
text.textComponent.transform.localScale = Vector3.one * text.currentScale;
}
}
private void CreateFloatingText(Vector3 position, string text, Color color)
{
if (textPrefab == null || worldSpaceCanvas == null) return;
GameObject textObj = Instantiate(textPrefab, worldSpaceCanvas.transform);
TextMeshProUGUI tmp = textObj.GetComponent<TextMeshProUGUI>();
if (tmp != null)
{
tmp.text = text;
tmp.color = new Color(color.r, color.g, color.b, 0f); // 初始透明
// 设置世界空间位置
textObj.transform.position = position;
floatingTexts.Add(new FloatingText
{
textComponent = tmp,
startTime = Time.time,
startPosition = position,
targetColor = color
});
}
}
}