This commit is contained in:
2025-05-27 21:16:58 +08:00
parent f23affbabd
commit 31e8ae41be
12 changed files with 6021 additions and 1581 deletions
+157 -35
View File
@@ -10,7 +10,9 @@ public class RobotECGWave : MonoBehaviour
Sine,
Triangle,
Square,
HumanECG
HumanECG,
Emergency,
Noise
}
[Header("波形设置")]
@@ -25,6 +27,15 @@ public class RobotECGWave : MonoBehaviour
public int resolution = 300;
public float updateInterval = 0.02f;
[Header("发光点设置")]
public SpriteRenderer glowPoint;
public Color glowColor = Color.red;
public float glowSize = 0.1f;
[Header("噪音设置")]
public float noiseAmplitude = 0.5f;
public float targetWaveformNoiseAmplitude = 0.1f;
private LineRenderer lineRenderer;
private Vector3[] points;
private float timer = 0f;
@@ -34,6 +45,12 @@ public class RobotECGWave : MonoBehaviour
private float phaseOffset = 0f;
private void Start()
{
Init();
SetupGlowPoint();
}
void OnEnable()
{
Init();
@@ -55,10 +72,37 @@ public class RobotECGWave : MonoBehaviour
}
}
void SetupGlowPoint()
{
if (glowPoint == null)
{
GameObject glowObj = new GameObject("GlowPoint");
glowObj.transform.SetParent(transform);
glowPoint = glowObj.AddComponent<SpriteRenderer>();
// 创建一个简单的圆形精灵
Texture2D texture = new Texture2D(32, 32);
Color[] colors = new Color[32 * 32];
for (int i = 0; i < colors.Length; i++)
{
float x = (i % 32) / 31f;
float y = (i / 32) / 31f;
float distance = Vector2.Distance(new Vector2(x, y), new Vector2(0.5f, 0.5f));
colors[i] = new Color(1, 1, 1, Mathf.Clamp01(1 - distance * 2));
}
texture.SetPixels(colors);
texture.Apply();
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, 32, 32), new Vector2(0.5f, 0.5f));
glowPoint.sprite = sprite;
}
glowPoint.color = glowColor;
glowPoint.transform.localScale = Vector3.one * glowSize;
}
void Update()
{
Init();
if (Application.isPlaying)
{
timer += Time.deltaTime;
@@ -93,13 +137,19 @@ public class RobotECGWave : MonoBehaviour
if (lineRenderer != null)
lineRenderer.SetPositions(points);
// 更新发光点位置
if (glowPoint != null)
{
Vector3 lastPoint = points[resolution - 1];
glowPoint.transform.position = lastPoint;
}
}
float GetWaveValue(float time)
{
switch (waveType)
{
case WaveType.Sine:
return Mathf.Sin(time * Mathf.PI * 2f);
@@ -112,6 +162,12 @@ public class RobotECGWave : MonoBehaviour
case WaveType.HumanECG:
return GenerateHumanECGShape(time);
case WaveType.Emergency:
return GenerateEmergencyShape(time);
case WaveType.Noise:
return GenerateNoiseShape(time);
case WaveType.RobotECG:
default:
return GenerateRobotECGShape(time);
@@ -119,40 +175,39 @@ public class RobotECGWave : MonoBehaviour
}
float GenerateRobotECGShape(float time)
{
float t = time % 1.0f;
{
float t = time % 1.0f;
float baseValue = 0f;
float baseValue = 0f;
if (t < 0.10f)
{
// 上升锯齿:锯齿线性上升
baseValue = (t / 0.10f) + Mathf.PingPong(t * 80f, 0.1f);
}
else if (t < 0.20f)
{
// 高电平方波 + 高频扰动
baseValue = 1f + Mathf.PingPong(t * 120f, 0.1f);
}
else if (t < 0.30f)
{
// 快速下降:线性下降带锯齿
baseValue = Mathf.Lerp(1f, -0.8f, (t - 0.20f) / 0.10f) + Mathf.PingPong(t * 100f, 0.05f);
}
else if (t < 0.40f)
{
// 低电平方波 + 微扰动
baseValue = -0.8f + Mathf.PingPong(t * 60f, 0.05f);
}
else
{
// 平缓底噪,锯齿叠加轻扰动
baseValue = Mathf.Sin(t * 80f) * 0.05f + Mathf.PingPong(t * 10f, 0.02f);
}
return baseValue;
}
if (t < 0.10f)
{
// 上升锯齿:锯齿线性上升
baseValue = (t / 0.10f) + Mathf.PingPong(t * 80f, 0.1f);
}
else if (t < 0.20f)
{
// 高电平方波 + 高频扰动
baseValue = 1f + Mathf.PingPong(t * 120f, 0.1f);
}
else if (t < 0.30f)
{
// 快速下降:线性下降带锯齿
baseValue = Mathf.Lerp(1f, -0.8f, (t - 0.20f) / 0.10f) + Mathf.PingPong(t * 100f, 0.05f);
}
else if (t < 0.40f)
{
// 低电平方波 + 微扰动
baseValue = -0.8f + Mathf.PingPong(t * 60f, 0.05f);
}
else
{
// 平缓底噪,锯齿叠加轻扰动
baseValue = Mathf.Sin(t * 80f) * 0.05f + Mathf.PingPong(t * 10f, 0.02f);
}
return baseValue;
}
float GenerateHumanECGShape(float time)
{
@@ -186,4 +241,71 @@ public class RobotECGWave : MonoBehaviour
return 0f; // 静息段
}
float GenerateEmergencyShape(float time)
{
float t = time % 1.0f;
float baseValue = 0f;
// 使用多个正弦波叠加来创造不规则波形
float irregularity = Mathf.Sin(t * 20f) * 0.2f +
Mathf.Sin(t * 35f) * 0.15f +
Mathf.Sin(t * 50f) * 0.1f;
if (t < 0.15f)
{
// 快速上升段
baseValue = Mathf.Lerp(0f, 1.2f, t / 0.15f) + irregularity;
}
else if (t < 0.25f)
{
// 剧烈波动段
baseValue = 1.2f + Mathf.Sin(t * 100f) * 0.3f + irregularity;
}
else if (t < 0.35f)
{
// 快速下降段
baseValue = Mathf.Lerp(1.2f, -0.8f, (t - 0.25f) / 0.1f) + irregularity;
}
else if (t < 0.45f)
{
// 不规则波动段
baseValue = -0.8f + Mathf.Sin(t * 80f) * 0.4f + irregularity;
}
else if (t < 0.55f)
{
// 快速上升段
baseValue = Mathf.Lerp(-0.8f, 0.8f, (t - 0.45f) / 0.1f) + irregularity;
}
else if (t < 0.65f)
{
// 高频震荡段
baseValue = 0.8f + Mathf.Sin(t * 120f) * 0.2f + irregularity;
}
else if (t < 0.75f)
{
// 快速下降段
baseValue = Mathf.Lerp(0.8f, -0.5f, (t - 0.65f) / 0.1f) + irregularity;
}
else
{
// 不规则底噪段
baseValue = -0.5f + Mathf.Sin(t * 60f) * 0.3f + irregularity;
}
// 添加随机扰动
baseValue += Mathf.PerlinNoise(t * 100f, Time.time) * 0.1f;
return baseValue;
}
float GenerateNoiseShape(float time)
{
float t = time % 1.0f;
// 使用纯随机值生成噪音
float noise = Random.Range(-noiseAmplitude, noiseAmplitude);
// 确保波形在合理范围内
return Mathf.Clamp(noise, -1f, 1f);
}
}
@@ -0,0 +1,158 @@
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(LineRenderer))]
public class SynthWaveform : MonoBehaviour
{
[System.Serializable]
public class SynthVoice
{
public bool enabled = true; // 是否启用该波形
[Range(0f, 1f)]
public float waveMorph = 0f; // 0~1 控制波形从三角到脉冲环形渐变
public float frequency = 1f; // 基础频率
public int octave = 0; // 八度调整(+/-
[Range(0f, 1f)]
public float amplitude = 1f; // 振幅
[Range(0.1f, 0.9f)]
public float pulseWidth = 0.5f; // 脉冲宽度,仅在Pulse阶段有效
}
public SynthVoice[] voices = new SynthVoice[3];
[Header("绘图设置")]
public int resolution = 300;
public float waveWidth = 10f;
public float waveHeight = 1f;
public float speed = 2f;
[Header("周期设置")]
[Range(1, 20)]
public int cycles = 4; // 在waveWidth内显示几个波周期
[Header("跳动点")]
public Transform glowPointInstance; // 小球跳动点物体
private LineRenderer lineRenderer;
private Vector3[] points;
private float phaseOffset = 0f;
void OnEnable()
{
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.positionCount = resolution;
points = new Vector3[resolution];
}
void Update()
{
phaseOffset += Time.deltaTime * speed;
UpdateWave();
UpdateGlowPoint();
}
void UpdateWave()
{
Vector3 origin = transform.position;
Quaternion rotation = transform.rotation;
for (int i = 0; i < resolution; i++)
{
float x = ((float)i / (resolution - 1)) * waveWidth;
float time = (x / waveWidth) * cycles + phaseOffset;
float sample = 0f;
foreach (var voice in voices)
{
if (!voice.enabled) continue;
float freq = voice.frequency * Mathf.Pow(2, voice.octave);
float wavePos = (time * freq) % 1f;
float morphValue = voice.waveMorph * 20f; // 0~20两圈循环
float val = MorphWaveCircular(morphValue, wavePos, voice.pulseWidth);
sample += val * voice.amplitude;
}
sample = Mathf.Clamp(sample, -1f, 1f);
Vector3 localPos = new Vector3(x, sample * waveHeight, 0f);
points[i] = origin + rotation * localPos;
}
lineRenderer.SetPositions(points);
}
void UpdateGlowPoint()
{
if (glowPointInstance == null || points == null || points.Length == 0) return;
// phaseOffset在0~1之间循环,映射points索引
float t = phaseOffset % 1f;
int idx = Mathf.RoundToInt(t * (resolution - 1));
idx = Mathf.Clamp(idx, 0, resolution - 1);
// 小球X固定为最右端点X
float fixedX = points[resolution - 1].x;
// 小球Y跟随索引对应的波形Y
float y = points[idx].y;
glowPointInstance.position = new Vector3(fixedX, y, points[idx].z);
}
// 环形渐变波形 morphValue 0~20,循环两圈
float MorphWaveCircular(float morphValue, float time, float pulseWidth)
{
float val = morphValue % 20f;
float[] keyPoints = { 0f, 4f, 7f, 10f, 14f, 17f, 20f };
int[] waveIDs = { 0, 1, 2, 3, 3, 0, 1 };
int idx = 0;
for (int i = 0; i < keyPoints.Length - 1; i++)
{
if (val >= keyPoints[i] && val < keyPoints[i + 1])
{
idx = i;
break;
}
}
float t = (val - keyPoints[idx]) / (keyPoints[idx + 1] - keyPoints[idx]);
float waveA = GetWaveByID(waveIDs[idx], time, pulseWidth);
float waveB = GetWaveByID(waveIDs[idx + 1], time, pulseWidth);
return Mathf.Lerp(waveA, waveB, t);
}
float GetWaveByID(int id, float t, float pulseWidth)
{
switch (id)
{
case 0: return Triangle(t);
case 1: return Sawtooth(t);
case 2: return Square(t);
case 3: return Pulse(t, pulseWidth);
default: return 0f;
}
}
float Sawtooth(float t)
{
return 2f * t - 1f;
}
float Triangle(float t)
{
return 1f - 4f * Mathf.Abs(Mathf.Round(t - 0.25f) - (t - 0.25f));
}
float Square(float t)
{
return t < 0.5f ? 1f : -1f;
}
float Pulse(float t, float pulseWidth)
{
return t < pulseWidth ? 1f : -1f;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a5207af6f6b1874d84ed2369a7f6594
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: