Files
2025-05-08 12:23:51 +08:00

263 lines
8.2 KiB
C#
Raw Permalink 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;
[ExecuteAlways]
public class EmoMazeDrawer : 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;
public float rippleDelay = 0.25f;
[Header("迷宫设置")]
public Vector2Int playerPos = new Vector2Int(1, 1);
public float moveCooldown = 0.2f;
public float playerBounceHeight = 1.5f;
public List<Vector2Int> obstacles = new List<Vector2Int>();
private float lastMoveTime = -999f;
private Vector3[] points;
private float time;
// 涟漪记录结构体
private struct Ripple
{
public Vector2 position;
public float startTime;
public float delay;
}
private struct RippleResult
{
public float heightOffset;
public float brightnessBoost;
}
private List<Ripple> ripples = new List<Ripple>();
private void Update()
{
// 时间更新(Play模式下使用 Time.time
time = Application.isPlaying ? Time.time * timeScale : Time.realtimeSinceStartup * timeScale;
if (Application.isPlaying)
{
HandleInput();
ripples.RemoveAll(r => Time.time - r.startTime > rippleDuration + rippleDelay);
}
}
private void HandleInput()
{
if (Time.time - lastMoveTime < moveCooldown) return;
Vector2Int dir = Vector2Int.zero;
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) dir = Vector2Int.up;
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) dir = Vector2Int.down;
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) dir = Vector2Int.left;
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) dir = Vector2Int.right;
if (dir != Vector2Int.zero)
{
Vector2Int newPos = playerPos + dir;
if (IsInBounds(newPos) && !IsBlocked(newPos))
{
playerPos = newPos;
lastMoveTime = Time.time;
}
else
{
Vector3 world = GridToWorld(newPos);
AddRipple(world.x, world.z); // 产生“碰撞弹开”的涟漪
}
}
}
private bool IsInBounds(Vector2Int pos) =>
pos.x >= 0 && pos.y >= 0 && pos.x < pointCount && pos.y < pointCount;
private bool IsBlocked(Vector2Int pos)
{
foreach (var obs in obstacles)
{
if (Vector2Int.Distance(obs, pos) <= 1f) return true;
}
return false;
}
private void AddRipple(float x, float z)
{
ripples.Add(new Ripple
{
position = new Vector2(x, z),
startTime = Time.time,
delay = rippleDelay
});
}
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;
if (elapsed < ripple.delay)
{
float dist = Vector2.Distance(new Vector2(x, z), ripple.position);
float spike = Mathf.Exp(-dist * rippleSharpness);
float ease = Mathf.Sin((elapsed / ripple.delay) * Mathf.PI * 0.5f);
rippleOffset += spike * rippleStrength * ease;
continue;
}
float spreadElapsed = elapsed - ripple.delay;
float spread = spreadElapsed * rippleSpreadSpeed;
float dist2 = Vector2.Distance(new Vector2(x, z), ripple.position);
float t = Mathf.Clamp01(spreadElapsed / rippleDuration);
float ease2 = Mathf.Sin(t * Mathf.PI);
float spike2 = Mathf.Exp(-Mathf.Pow(dist2 - spread, 2f) * rippleSharpness);
rippleOffset += spike2 * rippleStrength * ease2;
float bright = Mathf.Exp(-Mathf.Pow((dist2 - spread) * 0.5f, 2f));
brightness += bright * ease2;
}
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;
}
private Vector3 GridToWorld(Vector2Int pos)
{
float step = fieldSize / (pointCount - 1);
float half = fieldSize * 0.5f;
float x = -half + pos.x * step;
float z = -half + pos.y * step;
return new Vector3(x, 0, z);
}
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;
float baseHeight = GetHeight(x, z, time);
if (IsNearObstacle(new Vector2Int(j, i)))
baseHeight += playerBounceHeight;
RippleResult ripple = GetRippleEffect(x, z, Time.time);
float finalY = baseHeight + ripple.heightOffset;
Vector3 pos = new Vector3(x, finalY, z);
points[i * pointCount + j] = pos;
float depth = (finalY + waveHeight) / (waveHeight * 2f);
float brightness = Mathf.Clamp01(1f + ripple.brightnessBoost);
float pointScale = pointSize * (1f - depth * 0.5f);
Color baseColor = Color.Lerp(new Color(0.5f, 1f, 1f), new Color(0f, 0.5f, 1f), depth);
baseColor *= brightness;
Draw.Sphere(pos, pointScale, baseColor);
}
}
// 绘制线连接点
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)
DrawLine(current, points[index + 1]);
if (i < pointCount - 1)
DrawLine(current, points[index + pointCount]);
}
}
// 玩家显示
Vector3 playerWorld = GridToWorld(playerPos);
playerWorld.y = points[playerPos.y * pointCount + playerPos.x].y + 0.3f;
Draw.Sphere(playerWorld, pointSize * 1.5f, Color.yellow);
}
}
private 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);
}
private bool IsNearObstacle(Vector2Int pos)
{
foreach (var o in obstacles)
{
if (Vector2Int.Distance(o, pos) <= 1f)
return true;
}
return false;
}
}