提交
This commit is contained in:
@@ -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:
|
||||
Reference in New Issue
Block a user