using UnityEngine; using Shapes; using System.Collections.Generic; using UnityEngine.UI; [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, 0.3f); // 湖面高处颜色 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 Slider xOffsetSlider; public Slider zOffsetSlider; public float maxOffset = 20f; [Header("目标点设置")] public List targetPoints = new List(); 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; // 线框相对于实体的大小比例 [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 = 2f; // 恢复原来的强度 public float rippleDuration = 0.8f; // 适中的持续时间 public float rippleSpreadSpeed = 4f; // 适中的传播速度 public float rippleSharpness = 4f; // 适中的锐度 public float rippleMaxRadius = 3f; // 新增:涟漪最大半径 private Vector3[] points; // 存储每个点是否在可视区域内的标记 private bool[] validPoints; private float time; private List ripples = new List(); [System.Serializable] public class TargetPoint { public Vector2 gridPosition; // 网格位置(0到pointCount-1) public TargetType type; public Color color = Color.white; } public enum TargetType { Sphere, Cube, Torus } private struct Ripple { public Vector2 position; public float startTime; } private struct RippleResult { public float heightOffset; public float brightnessBoost; } private void Update() { // 如果是播放状态,则更新时间;否则设为0 time = Application.isPlaying ? Time.time * timeScale : 0f; HandleMouseInput(); HandleUIInput(); CleanExpiredRipples(); } private void HandleUIInput() { if (xOffsetSlider != null && zOffsetSlider != null) { fieldOffset = new Vector2( xOffsetSlider.value * maxOffset, zOffsetSlider.value * maxOffset ); } } // 处理鼠标点击,触发新的涟漪 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 RippleResult GetRippleEffect(float x, float z, float currentTime) { float rippleOffset = 0f; float brightness = 0f; // 遍历所有涟漪,计算它们对当前点的影响 foreach (var ripple in ripples) { // 计算涟漪已经存在的时间 float elapsed = currentTime - ripple.startTime; float spread = elapsed * rippleSpreadSpeed; // 涟漪扩展的距离 float dist = Vector2.Distance(new Vector2(x, z), ripple.position); // 当前点与涟漪中心的距离 // 如果距离超过最大半径,跳过这个涟漪的影响 if (dist > rippleMaxRadius) continue; // 计算涟漪的时间影响,t表示涟漪的衰减程度 float t = Mathf.Clamp01(elapsed / rippleDuration); float ease = Mathf.Sin(t * Mathf.PI); // 使用正弦函数使衰减效果平滑 // 计算涟漪对当前点的高度影响 float spike = Mathf.Exp(-Mathf.Pow(dist - spread, 2f) * rippleSharpness); rippleOffset += spike * rippleStrength * ease; // 计算涟漪对当前点的亮度提升 float bright = Mathf.Exp(-Mathf.Pow((dist - spread) * 0.5f, 2f)); brightness += bright * ease; } return new RippleResult { heightOffset = rippleOffset, // 高度偏移 brightnessBoost = brightness // 亮度提升 }; } // 计算某个点的基础高度,基于噪声函数模拟波浪 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; noise += Mathf.PerlinNoise(nx + nt, nz + nt) * 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(); // 添加目标点的绘制 } } // 计算每个点的位置和涟漪效果 private void CalculatePointPositions(float step, float halfField, float halfVisible) { // 遍历每个点,计算其位置和涟漪影响 for (int i = 0; i < pointCount; i++) { for (int j = 0; j < pointCount; j++) { // 计算当前点的位置(x,z) 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); // 计算亮度,防止超出范围 Color baseColor = Color.Lerp(lakeDarkColor, lakeLightColor, heightT); baseColor *= brightness; // 绘制当前点 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(lakeDarkColor, lakeLightColor, heightT); lineColor.a *= 0.8f; // 设置透明度 // 绘制连线 Draw.Line(current, next, lineWidth * (1f - heightT * 0.5f), lineColor); } private void DrawEdgeLine(float x, float z, float halfVisible) { // 检查点是否在可视区域内 if (Mathf.Abs(x) > halfVisible || Mathf.Abs(z) > halfVisible) { return; } // 计算湖面高度作为起始点的Y坐标 float startY = GetHeight(x, z, 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 DrawEdgeLines(float halfField, float halfVisible) { // 绘制湖面边缘的向下线条(无扰动) float step = fieldSize / (pointCount - 1); float halfEdgeLineLength = edgeLineLength * 0.5f; // 设置边缘线长度 // 只在field边界处绘制线条,且仅当边界在可视区域内时绘制 // 1. 绘制 X 轴的边缘线(左边和右边) for (float z = -halfField; z <= halfField; z += step) { // 检查边界点是否在可视区域内 if (Mathf.Abs(z) <= halfVisible) { float x = halfField; // 右边 DrawEdgeLine(x, z, halfVisible); x = -halfField; // 左边 DrawEdgeLine(x, z, halfVisible); } } // 2. 绘制 Z 轴的边缘线(上边和下边) for (float x = -halfField; x <= halfField; x += step) { // 检查边界点是否在可视区域内 if (Mathf.Abs(x) <= halfVisible) { float z = halfField; // 上边 DrawEdgeLine(x, z, halfVisible); z = -halfField; // 下边 DrawEdgeLine(x, z, halfVisible); } } } // 绘制目标点 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); // 绘制外层立方体线框 DrawWireCube(position, rot, targetSize * wireScale, targetWireColor); // 根据类型绘制内部形状 switch (target.type) { case TargetType.Sphere: Draw.Sphere(position, targetSize * 0.8f, targetFillColor); break; case TargetType.Cube: Draw.Cube(position, rot, targetSize * 0.8f, targetFillColor); break; case TargetType.Torus: Draw.Torus(position, rot, targetSize * 0.8f, targetSize * 0.3f, targetFillColor); 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); } } }