196 lines
6.8 KiB
C#
196 lines
6.8 KiB
C#
using UnityEngine;
|
|
using Shapes;
|
|
using System.Collections.Generic;
|
|
|
|
[ExecuteAlways]
|
|
public class LakeVisualizer : ImmediateModeShapeDrawer
|
|
{
|
|
[Header("网格设置")]
|
|
public float fieldSize = 20f;
|
|
public int pointCount = 30;
|
|
public float pointSize = 0.15f;
|
|
public float lineWidth = 0.03f;
|
|
|
|
[Header("波动参数")]
|
|
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 = 1.5f;
|
|
public float rippleSpreadSpeed = 6f;
|
|
public float rippleSharpness = 2f;
|
|
|
|
[Header("目标设置")]
|
|
public Vector2 targetPosition = Vector2.zero;
|
|
public float shapeChangeRadius = 4f; // 控制形状混合的范围
|
|
|
|
private Vector3[] points;
|
|
private float time;
|
|
|
|
private struct Ripple
|
|
{
|
|
public Vector2 position;
|
|
public float startTime;
|
|
}
|
|
|
|
private List<Ripple> ripples = new List<Ripple>();
|
|
|
|
private void Update()
|
|
{
|
|
time = Application.isPlaying ? Time.time * timeScale : 0f;
|
|
|
|
if (Application.isPlaying && Input.GetMouseButtonDown(0))
|
|
{
|
|
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
|
if (Physics.Raycast(ray, out RaycastHit hit))
|
|
{
|
|
ripples.Add(new Ripple
|
|
{
|
|
position = new Vector2(hit.point.x, hit.point.z),
|
|
startTime = Time.time
|
|
});
|
|
}
|
|
}
|
|
|
|
ripples.RemoveAll(r => Time.time - r.startTime > rippleDuration);
|
|
}
|
|
|
|
private float GetNoiseHeight(float x, float z, float currentTime)
|
|
{
|
|
float noise = 0f;
|
|
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 halfSize = fieldSize * 0.5f;
|
|
|
|
if (points == null || points.Length != pointCount * pointCount)
|
|
points = new Vector3[pointCount * pointCount];
|
|
|
|
for (int i = 0; i < pointCount; i++)
|
|
{
|
|
for (int j = 0; j < pointCount; j++)
|
|
{
|
|
float x = -halfSize + j * step;
|
|
float z = -halfSize + i * step;
|
|
Vector2 pos2D = new Vector2(x, z);
|
|
|
|
float baseY = GetNoiseHeight(x, z, time);
|
|
float rippleY = 0f;
|
|
float brightness = 0f;
|
|
float shapeBlend = 0f;
|
|
|
|
foreach (var ripple in ripples)
|
|
{
|
|
float elapsed = Time.time - ripple.startTime;
|
|
float spread = elapsed * rippleSpreadSpeed;
|
|
float dist = Vector2.Distance(pos2D, ripple.position);
|
|
|
|
float ease = Mathf.Sin(Mathf.Clamp01(elapsed / rippleDuration) * Mathf.PI);
|
|
float spike = Mathf.Exp(-Mathf.Pow(dist - spread, 2f) * rippleSharpness);
|
|
float bright = Mathf.Exp(-Mathf.Pow((dist - spread) * 0.5f, 2f));
|
|
|
|
rippleY += spike * rippleStrength * ease;
|
|
brightness += bright * ease;
|
|
|
|
// 目标点接近程度影响形状混合程度
|
|
float targetDist = Vector2.Distance(ripple.position, targetPosition);
|
|
float shapeFactor = Mathf.Clamp01(1f - targetDist / shapeChangeRadius);
|
|
shapeBlend = Mathf.Max(shapeBlend, shapeFactor * ease);
|
|
}
|
|
|
|
float finalY = baseY + rippleY;
|
|
Vector3 worldPos = new Vector3(x, finalY, z);
|
|
points[i * pointCount + j] = worldPos;
|
|
|
|
float depth = (finalY + waveHeight) / (waveHeight * 2f);
|
|
float brightnessScale = Mathf.Clamp01(1f + brightness);
|
|
Color pointColor = Color.Lerp(new Color(0.5f, 1f, 1f), new Color(0f, 0.5f, 1f), depth);
|
|
pointColor *= brightnessScale;
|
|
|
|
float scale = pointSize * (1f - depth * 0.5f);
|
|
|
|
// 形状混合逻辑(近似插值)
|
|
if (shapeBlend < 0.1f)
|
|
{
|
|
Draw.Sphere(worldPos, scale, pointColor);
|
|
}
|
|
else if (shapeBlend < 0.3f)
|
|
{
|
|
Draw.Torus(worldPos, scale * 0.6f, scale * 0.2f, pointColor); // 环形
|
|
}
|
|
else if (shapeBlend < 0.6f)
|
|
{
|
|
Draw.Cube(worldPos, scale * 1.4f, pointColor); // 立方体
|
|
}
|
|
else if (shapeBlend < 0.9f)
|
|
{
|
|
Draw.Cone(worldPos, scale * 1.6f, scale * 0.5f, pointColor); // 锥体
|
|
}
|
|
else
|
|
{
|
|
Vector3 a = worldPos + Vector3.up * scale;
|
|
Vector3 b = worldPos + new Vector3(scale, -scale, 0);
|
|
Vector3 c = worldPos + new Vector3(-scale, -scale, 0);
|
|
Draw.Triangle(a, b, c, pointColor); // 三角形
|
|
}
|
|
}
|
|
}
|
|
|
|
// 连接线条(可选)
|
|
for (int i = 0; i < pointCount; i++)
|
|
{
|
|
for (int j = 0; j < pointCount; j++)
|
|
{
|
|
int index = i * pointCount + j;
|
|
Vector3 current = points[index];
|
|
|
|
if (j < pointCount - 1)
|
|
{
|
|
Vector3 next = points[index + 1];
|
|
DrawLine(current, next);
|
|
}
|
|
|
|
if (i < pointCount - 1)
|
|
{
|
|
Vector3 next = points[index + pointCount];
|
|
DrawLine(current, next);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawLine(Vector3 a, Vector3 b)
|
|
{
|
|
float avgY = (a.y + b.y) * 0.5f;
|
|
float lineDepth = (avgY + waveHeight) / (waveHeight * 2f);
|
|
Color lineColor = Color.Lerp(Color.cyan, new Color(0, 0.5f, 1f, 0.3f), lineDepth);
|
|
Draw.Line(a, b, lineWidth * (1f - lineDepth * 0.5f), lineColor);
|
|
}
|
|
}
|