层级改动

This commit is contained in:
2025-01-04 20:46:40 +08:00
parent 654e10fdb7
commit 69a3c9c93c
19 changed files with 6510 additions and 3277 deletions
@@ -11,21 +11,20 @@ namespace AibisDream.FixSystem
private const string SocketObjName = "Socket";
private const string ModulePicName = "Module Pic";
private const string IndicatorName = "Indicator";
public Transform _socketPos;
public Transform socketPos;
// public Transform _indicatorPos;
private BodyModuleSystem _bodyModuleSystem;
private Material highlightMaterial;
private Material alarmMaterial;
private Material originalMaterial; // 原材质
private Material _highlightMaterial;
private Material _alarmMaterial;
private Material _originalMaterial; // 原材质
private SpriteRenderer targetSpriteRenderer; // 目标的 SpriteRenderer
private SpriteRenderer _targetSpriteRenderer; // 目标的 SpriteRenderer
#endregion
// 模块基本数据
[SerializeField] public BodyModuleData data;
private bool available = true;
private bool _available = true;
private void Awake()
{
@@ -34,28 +33,26 @@ namespace AibisDream.FixSystem
}
private void InitReference()
{
_socketPos = transform.Find(SocketObjName);
socketPos = transform.Find(SocketObjName);
_bodyModuleSystem = transform.parent.parent.GetComponent<BodyModuleSystem>();
targetSpriteRenderer=transform.Find("Module Pic").GetComponent<SpriteRenderer>();
originalMaterial = targetSpriteRenderer.GetComponent<SpriteRenderer>().material;
highlightMaterial = Resources.Load<Material>("Materials/HighlightMaterial");
alarmMaterial=Resources.Load<Material>("Materials/AlarmMaterial");
_targetSpriteRenderer=transform.Find("Module Pic").GetComponent<SpriteRenderer>();
_originalMaterial = _targetSpriteRenderer.GetComponent<SpriteRenderer>().material;
_highlightMaterial = Resources.Load<Material>("Materials/HighlightMaterial");
_alarmMaterial=Resources.Load<Material>("Materials/AlarmMaterial");
}
#region
public bool IsAvailable()
{
return available;
return _available;
}
public void SetSocketAvailable(bool isAvailable)
{
available=isAvailable;
_available=isAvailable;
}
public void PlugIn()
{
// 插入后先播放wave
//ShowWave();
if(DialogController.Instance!=null)
// 然后触发Yarn节点
DialogController.Instance.StartDialogNode(_bodyModuleSystem.inHotErr ? "系统过热" : data.PlugInNodeName);
@@ -65,56 +62,13 @@ namespace AibisDream.FixSystem
{
_bodyModuleSystem.oscilloscopeSystem.MessageBoxSetNull();
StartCoroutine(_bodyModuleSystem.oscilloscopeSystem.DisableDeepInButton());
// 先关闭wave
//_bodyModuleSystem.oscilloscopeSystem.ShowScreenMessage("未插入任何模块", Color.white);
//CloseWave();
// TODO 触发Yarn节点(似乎目前没有拔出对话?)
}
public void DeepIn()
{
// if (!data.waveHasError&&!data.ignoreWaveCheck) return;
// // 如果有错误并且已经深入了
if(DialogController.Instance!=null)
DialogController.Instance.StartDialogNode(data.DeepInNodeName);
}
public void CloseWave()
{
_bodyModuleSystem.oscilloscopeSystem.StopWave();
}
public void ShowWave()
{
if (_bodyModuleSystem.inHotErr)
{
_bodyModuleSystem.oscilloscopeSystem.ShowScreenMessage("发热,无法检查", Color.red);
_bodyModuleSystem.oscilloscopeSystem.PlayWave(WaveformType.HotNoise, false);
}
else if(data.cantStopRunning)
{
_bodyModuleSystem.oscilloscopeSystem.ShowScreenMessage("持续运行中,无法进入", Color.red);
//_bodyModuleSystem.oscilloscopeSystem.ChangeDeepInButtonState(true);
}
else if(data.ignoreWaveCheck)
{
_bodyModuleSystem.oscilloscopeSystem.ShowScreenMessage("无需检查,允许进入", Color.yellow);
_bodyModuleSystem.oscilloscopeSystem.ChangeDeepInButtonState(true);
}
else if(data.waveHasError)
{
_bodyModuleSystem.oscilloscopeSystem.ShowScreenMessage(data.moduleName+": 发现错误,拖动滑动条寻找错误波形以深入检查", Color.red);
_bodyModuleSystem.oscilloscopeSystem.PlayWave(data.waveformType, data.waveHasError);
}
else
{
_bodyModuleSystem.oscilloscopeSystem.ShowScreenMessage(data.moduleName+": 未发现错误,无寻深入", Color.green);
}
}
public Vector3 GetSocketPos()
{
return _socketPos.position;
return socketPos.position;
}
#endregion
@@ -124,13 +78,13 @@ namespace AibisDream.FixSystem
public void Load(BodyModuleData newData)
{
var modulePic = transform.Find(ModulePicName).GetComponent<SpriteRenderer>();
var socketPos = transform.Find(SocketObjName);
var newSocketPos = transform.Find(SocketObjName);
gameObject.name = newData.moduleName;
// 初始化位置与外形
transform.localPosition = newData.ModulePos;
modulePic.sprite = newData.ModulePic;
socketPos.localPosition = newData.SocketPos;
newSocketPos.localPosition = newData.SocketPos;
// 数据同步
data = newData;
@@ -140,21 +94,15 @@ namespace AibisDream.FixSystem
public BodyModuleData Save()
{
var modulePic = transform.Find(ModulePicName).GetComponent<SpriteRenderer>();
var socketPos = transform.Find(SocketObjName);
//var indicatorPos = transform.Find(IndicatorName);
var newSocketPos = transform.Find(SocketObjName);
return new BodyModuleData
{
moduleName = data.moduleName,
ModulePic = modulePic.sprite,
ModulePos = transform.localPosition,
SocketPos = socketPos.localPosition,
waveformType = data.waveformType,
waveHasError = data.waveHasError,
ignoreWaveCheck = data.ignoreWaveCheck,
SocketPos = newSocketPos.localPosition,
cantStopRunning=data.cantStopRunning,
//IndicatorPos=indicatorPos.localPosition
//indicatorPos= indicatorPos.position
};
}
@@ -163,16 +111,16 @@ namespace AibisDream.FixSystem
#region
public void Highlight(bool enable)
{
if (targetSpriteRenderer != null && highlightMaterial != null&&targetSpriteRenderer.material!=alarmMaterial)
if (_targetSpriteRenderer != null && _highlightMaterial != null&&_targetSpriteRenderer.material!=_alarmMaterial)
{
targetSpriteRenderer.material = enable ? highlightMaterial : originalMaterial;
_targetSpriteRenderer.material = enable ? _highlightMaterial : _originalMaterial;
}
}
public void Alarm(bool enable)
{
if (targetSpriteRenderer != null && alarmMaterial != null)
if (_targetSpriteRenderer != null && _alarmMaterial != null)
{
targetSpriteRenderer.material = enable ? alarmMaterial : originalMaterial;
_targetSpriteRenderer.material = enable ? _alarmMaterial : _originalMaterial;
}
}
// public void HideModule(bool enable)
@@ -200,15 +148,6 @@ namespace AibisDream.FixSystem
public SerializableVector3 modulePos;
public SerializableVector3 socketPos;
public SerializableVector3 indicatorPos;
// 功能信息
public WaveformType waveformType;
public bool waveHasError;
public bool ignoreWaveCheck;
public bool cantStopRunning;
// 只读
@@ -1,4 +1,3 @@
using System;
using System.Linq;
using AibisDream.Framework;
using AibisDream.Kit;
@@ -13,13 +12,12 @@ namespace AibisDream.FixSystem
#region
[HideInInspector]public BodyModule[] BodyModules { get; private set; }
[HideInInspector]public CableSystem CableSystem { get; private set; }
public BodyModule[] BodyModules { get; private set; }
public CableSystem CableSystem { get; private set; }
public GameObject bodyModulePrefab;
[HideInInspector]public Transform bodyModuleGroup;
[HideInInspector]public OscilloscopeSystem oscilloscopeSystem;
[HideInInspector]public HeatMapController heatMapController;
[HideInInspector] public Transform bodyModuleGroup;
[HideInInspector] public OscilloscopeSystem oscilloscopeSystem;
[HideInInspector] public HeatMapController heatMapController;
#endregion
@@ -40,17 +38,17 @@ namespace AibisDream.FixSystem
#region
public bool inHotErr;
private int heatValue;
public int HeatValue
private int _heatValue;
private int HeatValue
{
get => heatValue;
get => _heatValue;
set
{
heatValue = value;
inHotErr = heatValue > 0;
_heatValue = value;
inHotErr = _heatValue > 0;
}
}
[HideInInspector]public BodyModule curBodyModule;
#endregion
@@ -68,10 +66,11 @@ namespace AibisDream.FixSystem
CableSystem = transform.Find("Cable System").GetComponent<CableSystem>();
bodyModuleGroup = transform.Find("Body Module Group");
oscilloscopeSystem = transform.Find("Oscilloscope System").GetComponent<OscilloscopeSystem>();
if(transform.Find("HeatMapController"))
if (transform.Find("HeatMapController"))
{
heatMapController=transform.Find("HeatMapController").GetComponent<HeatMapController>();
heatMapController = transform.Find("HeatMapController").GetComponent<HeatMapController>();
}
Debug.Log("Register");
FixSystemCenter.SystemDic.Register(this);
}
@@ -99,6 +98,7 @@ namespace AibisDream.FixSystem
{
continue; // 跳过不可用的模块
}
float tempDistance = Vector3.Distance(sourcePos, bodyModule.GetSocketPos());
if (tempDistance < closestDistance)
@@ -120,6 +120,7 @@ namespace AibisDream.FixSystem
return false;
}
}
/// <summary>
/// 寻找指定名称的BodyModule
/// </summary>
@@ -131,61 +132,53 @@ namespace AibisDream.FixSystem
}
/// <summary>
/// 深入检查
/// </summary>
public void HandleDeepIn()
{
if (CableSystem.curBodyModule)
{
CableSystem.curBodyModule.DeepIn();
}
}
/// <summary>
/// 设置死循环状态
/// </summary>
public void SetModuleState_CantStopRunning(string moduleName,bool state)
public void SetModuleState_CantStopRunning(string moduleName, bool state)
{
BodyModule result = FindBodyModuleByName(moduleName);
if (result != null)
{
Debug.Log("Found module: " + moduleName);
result.data.cantStopRunning=state;
result.data.cantStopRunning = state;
}
else
{
Debug.Log("Module not found.");
}
}
}
/// <summary>
/// 设置发热
/// </summary>
public void SetHeat(int targetHeatValue)
{
HeatValue = targetHeatValue;
HeatValue=targetHeatValue;
heatMapController.SetTemptureDirect(HeatValue);
heatMapController.SetTemptureDirect(HeatValue);
}
public void ShowHeatMap()
{
heatMapController.ShowHeatMap();
ResetPlug();
}
public void HideHeatMap()
{
heatMapController.HideHeatMap();
}
/// <summary>
/// 冷却后处理
/// </summary>
public void OnCooling()
{
if (!inHotErr) return;
if (!inHotErr) return;
// 系统冷却
heatValue = 0;
curBodyModule?.ShowWave();
_heatValue = 0;
}
/// <summary>
/// 插头重新插入初始插孔
/// </summary>
@@ -193,6 +186,7 @@ namespace AibisDream.FixSystem
{
CableSystem.ResetPlug();
}
#region
public override string[] GetDataKeyOptions()
@@ -207,7 +201,7 @@ namespace AibisDream.FixSystem
Debug.Log("配置项名称为空");
return;
}
if (!ConfigUtil.Instance.TryLoadConfig<BodyModuleSystemData>(DataPath + CurDataKey,
out var data))
{
@@ -1,8 +1,6 @@
using AibisDream.Utility;
using TMPro;
using TMPro;
using UnityEngine;
using DG.Tweening;
using Random = UnityEngine.Random;
using System.Collections;
namespace AibisDream.FixSystem
@@ -11,13 +9,8 @@ namespace AibisDream.FixSystem
{
#region
private BodyModuleSystem _bodyModuleSystem;
private TMP_Text _state; // 显示状态的TextMeshPro组件
private TMP_Text _screenText;// 显示消息的TextMeshPro组件
private WaveformLine _waveformLine;
private ScanLine _scanLine;
private SpriteRenderer _screen;
private WaveSlider _waveSlider;
private SpriteButton _deepinButton;
#endregion
@@ -38,132 +31,16 @@ namespace AibisDream.FixSystem
private void InitComponentRefs()
{
_bodyModuleSystem = GetComponentInParent<BodyModuleSystem>();
_screenText = transform.Find("Screen Text").GetComponent<TMP_Text>();
_state = transform.Find("Screen State Text").GetComponent<TMP_Text>();
_waveformLine = transform.Find("Waveform Visualizer").GetComponent<WaveformLine>();
_scanLine = transform.Find("Scan Line").GetComponent<ScanLine>();
_screen = transform.Find("Screen").GetComponent<SpriteRenderer>();
_waveSlider = transform.Find("Wave Slider").GetComponent<WaveSlider>();
_deepinButton = new SpriteButton(transform.Find("Deepin Button").gameObject, false);
_waveformLine.SetLineSize(_screen.bounds.size.x, _screen.bounds.size.y);
_scanLine.SetScreenSize(_screen.bounds.size.x, _screen.bounds.size.y);
//_screenText.text = "";
MessageBoxSetNull();
_deepinButton.OnButtonDown += HandleDeepInButtonClick;
_scanLine.ScanErrorEvent += OnScanSuccess;
}
private void Update()
{
_deepinButton.CheckButtonClick();
}
#region
/// <summary>
/// 屏幕上播放波形
/// </summary>
/// <param name="waveformType">波形类型</param>
/// <param name="hasError">是否有错误</param>
public void PlayWave(WaveformType waveformType, bool hasError)
{
_waveSlider.InitSlider();
isPlaying = true;
canScan = hasError;
_deepinButton.SetActive(canScan);
var initialRandomPhase = Mathf.PI * Random.Range(7, 13) * 0.1f;
_waveformLine.PlayWave(waveformType, initialRandomPhase, hasError);
}
/// <summary>
/// 停止播放波形
/// </summary>
public void StopWave()
{
isPlaying = false;
canScan = false;
isScanning = false;
_deepinButton.SetActive(false);
_waveSlider.InitSlider();
_waveformLine.StopWave();
_scanLine.CloseScan();
}
public void ChangeDeepInButtonState(bool state)
{
_deepinButton.SetActive(state);
}
/// <summary>
/// 调整相位
/// </summary>
public void AdjustPhaseShift(float phaseShift)
{
if (isPlaying)
{
_waveformLine.AdjustPhaseShift(phaseShift);
}
}
/// <summary>
/// 开始扫描
/// </summary>
public void StartScan()
{
if (!canScan) return;
float spikeX = _waveformLine.GetSpikeX();
StartCoroutine(_scanLine.ScanForError(spikeX));
}
public void HandleDeepInButtonClick()
{
// if(_bodyModuleSystem.CableSystem.curBodyModule.data.ignoreWaveCheck==true)
// {
// _bodyModuleSystem.HandleDeepIn();
// }
// else
// {
// StartScan();
// }
_bodyModuleSystem.HandleDeepIn();
}
/// <summary>
/// 扫描成功后
/// </summary>
public void OnScanSuccess()
{
_bodyModuleSystem.HandleDeepIn();
_ = CommonUtil.Delay(2000, StopWave);
}
/// <summary>
/// 显示消息
/// </summary>
/// <param name="message">消息</param>
public void ShowScreenMessage(string message, Color textcolor)
{
_screenText.text = message;
_screenText.color=textcolor;
}
/// <summary>
/// 清除消息
/// </summary>
public void ClearScreenMessage()
{
_screenText.text = "";
_screenText.color=Color.white;
}
#endregion
public void MessageBoxSetError()
{
@@ -220,15 +97,4 @@ namespace AibisDream.FixSystem
}
}
public enum WaveformType
{
Default,
Sine,
Square,
Triangle,
Sawtooth,
HotNoise,
Composite,
Pulse
}
}
@@ -1,123 +0,0 @@
using System;
using System.Collections;
using UnityEngine;
namespace AibisDream.FixSystem
{
public class ScanLine : MonoBehaviour
{
#region
private LineRenderer _line;
private OscilloscopeSystem _oscilloscopeSystem;
#endregion
#region screen获取
private float _screenLeftX;
private float _screenRightX;
private float _topY;
private float _bottomY;
private const float ScanSpeed = 2f;
public event Action ScanErrorEvent;
#endregion
private void Awake()
{
InitComponentRefs();
}
private void InitComponentRefs()
{
_oscilloscopeSystem = GetComponentInParent<OscilloscopeSystem>();
_line = GetComponent<LineRenderer>();
_line.enabled = false;
_line.positionCount = 2;
}
public IEnumerator ScanForError(float spikeX)
{
_line.enabled = true;
_oscilloscopeSystem.isScanning = true;
// 初始化扫描线
InitStartLine();
// 扫描线右移
var currentX = _screenLeftX;
var lastX = currentX;
while (currentX <= _screenRightX)
{
// 横坐标右移
currentX += Time.deltaTime * ScanSpeed;
SetLinePos(currentX);
// 检查是否扫到尖峰
if (CheckSpikeInRange(lastX, currentX, spikeX))
{
// 修改线条
MarkLineAsError(spikeX);
// 触发扫描事件
yield return new WaitForSeconds(1f);
ScanErrorEvent?.Invoke();
break;
}
lastX = currentX;
yield return null;
}
_oscilloscopeSystem.isScanning = false;
}
private void InitStartLine()
{
// 初始状态是一根靠左的竖线
SetLinePos(_screenLeftX);
_line.enabled = true;
_line.startColor = Color.white;
_line.endColor = Color.white;
}
private void SetLinePos(float posX)
{
var topPosition = new Vector3(posX, _topY, 0);
var bottomPosition = new Vector3(posX, _bottomY, 0);
_line.SetPosition(0, topPosition);
_line.SetPosition(1, bottomPosition);
}
private bool CheckSpikeInRange(float x1, float x2, float spikeX)
{
var left = Mathf.Min(x1, x2);
var right = Mathf.Max(x1, x2);
return spikeX >= left && spikeX <= right;
}
private void MarkLineAsError(float spikeX)
{
SetLinePos(spikeX);
_line.startColor = Color.red;
_line.endColor = Color.red;
}
public void CloseScan()
{
_line.enabled = false;
}
public void SetScreenSize(float width, float height)
{
_screenLeftX = - width / 2;
_screenRightX = width / 2;
_topY = height / 2;
_bottomY = - height / 2;
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d539f4fdddef410c9df21320f69fe38f
timeCreated: 1731586860
@@ -1,99 +0,0 @@
using UnityEngine;
namespace AibisDream.FixSystem
{
public class WaveSlider : MonoBehaviour
{
private OscilloscopeSystem _oscilloscopeSystem;
private Transform _slider; // 滑块的 Transform
private Vector2 _startPos;
private Vector2 _endPos;
private float _value;
private bool _isDragging;
private void Awake()
{
InitComponentsRef();
}
private void InitComponentsRef()
{
_oscilloscopeSystem = transform.GetComponentInParent<OscilloscopeSystem>();
_slider = transform.Find("Sliding Block");
var startTrans = transform.Find("Slider Start Pos");
var endTrans = transform.Find("Slider End Pos");
_startPos = new Vector2(startTrans.position.x, startTrans.position.y);
_endPos = new Vector2(endTrans.position.x, endTrans.position.y);
InitSlider();
}
public void InitSlider()
{
_slider.position = new Vector3(_startPos.x, _startPos.y, _slider.position.z);
}
private void Update()
{
if (_oscilloscopeSystem.isScanning) return;
if (Input.GetMouseButtonDown(0))
{
OnMouseButtonDown();
}
if (Input.GetMouseButtonUp(0))
{
OnMouseButtonUp();
}
if (_isDragging)
{
OnDragging();
}
}
private void OnMouseButtonDown()
{
// 检测鼠标是否点击在滑块上
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Collider2D hitCollider = Physics2D.OverlapPoint(mousePos);
if (hitCollider && hitCollider.transform == _slider)
{
_isDragging = true;
}
}
private void OnMouseButtonUp()
{
_isDragging = false;
}
private void OnDragging()
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
float clampedX = Mathf.Clamp(mousePos.x, _startPos.x, _endPos.x);
_slider.position = new Vector3(clampedX, _slider.position.y, _slider.position.z);
// 计算滑块在轨道上的比例
var curValue = (clampedX - _startPos.x) / (_endPos.x - _startPos.x);
UpdateValue(curValue);
}
private void UpdateValue(float curValue)
{
if (!Mathf.Approximately(curValue, _value))
{
// 如果相位有更新,就同步给波形
_oscilloscopeSystem.AdjustPhaseShift(curValue);
_value = curValue;
}
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5c6494bd9d5943cf8e875ae582481cf1
timeCreated: 1732863766
@@ -1,378 +0,0 @@
using System;
using System.Linq;
using UnityEngine;
using Random = UnityEngine.Random;
namespace AibisDream.FixSystem
{
public class WaveformLine : MonoBehaviour
{
#region
private LineRenderer _waveformLine;
#endregion
private float _screenWidth;
private float _screenHeight;
#region
private bool _isWavePlaying;
#endregion
private WaveParam _waveParam;
#region Line数据
private float[] _waveformData;
private float[] _noiseData;
private float[] _spikeData;
private float[] _displayData; // 最后展示数据
#endregion
[SerializeField] private WaveformConfig config;
private void Awake()
{
InitComponentRefs();
InitValues();
}
private void InitValues()
{
_waveformData = new float[config.samples];
_noiseData = new float[config.samples];
_spikeData = new float[config.samples];
_displayData = new float[config.samples];
_waveformLine.positionCount = config.samples;
_waveformLine.enabled = false;
}
private void InitComponentRefs()
{
_waveformLine = GetComponent<LineRenderer>();
}
private void Update()
{
if (_isWavePlaying)
{
// 绘制线条
DrawWaveform();
}
}
private void DrawWaveform()
{
// 计算当前线条的点数据
CalcLinePointsData();
// 计算结束,绘制线条
DrawLine();
}
#region
private void CalcLinePointsData()
{
// 分组计算:
var waveformData = _waveParam.waveformType == WaveformType.Default
? GenerateNoiseData()
: GenerateWaveformData(); // 波形数据
// 计算错误数据
var spikeData = GenerateSpikeData(); // 错误尖峰数据
// 合成最终结果
for (int i = 0; i < config.samples; i++)
{
// 正常线条加错误尖峰
var lineValue = waveformData[i] + spikeData[i];
_displayData[i] = Mathf.Clamp(lineValue, -_screenHeight / 1.8f, _screenHeight / 1.8f);
}
}
private float[] GenerateNoiseData()
{
for (var i = 0; i < config.samples; i++)
{
_noiseData[i] = Random.Range(-config.AdjustedNoiseAmplitude, config.AdjustedNoiseAmplitude);
}
return _noiseData;
}
private float[] GenerateWaveformData()
{
// 计算波形
for (int i = 0; i < config.samples; i++)
{
float basicAngle = TransIndexToAngle(i);
// 正波形
float wavePoint = CalcLineValueByAngle(basicAngle);
// 如果有相位偏移
if (_waveParam.PhaseShift > 0)
{
wavePoint -= CalcLineValueByAngle(basicAngle + _waveParam.PhaseShift);
}
_waveformData[i] = wavePoint;
}
return _waveformData;
}
private float TransIndexToAngle(int idx)
{
return (float)idx / config.samples * Mathf.PI * 2f * config.spatialFrequency;
}
private float CalcLineValueByAngle(float angle)
{
// 计算波形值
var value = _waveParam.waveformType switch
{
WaveformType.Sine => Mathf.Sin(angle),
WaveformType.Square => Mathf.Sign(Mathf.Sin(angle)),
WaveformType.Triangle => Mathf.PingPong(angle / Mathf.PI, 1f) * 2f - 1f,
WaveformType.Sawtooth => angle / (Mathf.PI * 2f) % 1f * 2f - 1f, // 统一周期为 2π
WaveformType.Pulse => Mathf.PingPong(angle / (Mathf.PI * 2f), 1f) > 0.5f ? 1f : -1f, // 统一周期为 2π
WaveformType.HotNoise => Mathf.PerlinNoise(angle / (Mathf.PI * 2f), Time.time * config.temporalFrequency) *
2f - 1f, // 统一周期为 2π
WaveformType.Composite => Mathf.Sin(angle) + Mathf.Sin(angle * 2f) * 0.5f, // 组合波形也统一周期
_ => 0f
};
return value * config.amplitude; // 最终返回角度对应波形
}
private float[] GenerateSpikeData()
{
if (CheckErrorShow())
{
// 错误点可以显示
for (var i = 0; i < config.samples; i++)
{
_spikeData[i] = CalcSpikeValueByIdx(i);
}
}
else
{
// 错误点尚不可显示,全部置为0
Array.Clear(_spikeData, 0, _spikeData.Length);
}
return _spikeData;
}
/// <summary>
/// 错误点是否已经可以定位
/// </summary>
/// <returns></returns>
private bool CheckErrorShow()
{
// 没有错误当然就不显示尖峰点
if (_waveParam.spikeIdx == -1) return false;
// 尖峰点小于1/3时显示
return _displayData[_waveParam.spikeIdx] < config.amplitude / 3f;
}
private float CalcSpikeValueByIdx(int idx)
{
var distanceToSpike = Mathf.Abs(_waveParam.spikeIdx - idx);
// 尖峰范围外直接数据为0
if (distanceToSpike > config.spikeRange) return 0f;
// 尖峰范围内计算
// 计算随机范围
var attenuation = (1f - distanceToSpike / 20f) * 0.9f;
var baseAmplitude = _displayData[idx] * config.AmplitudeScale * (_screenHeight / 2f);
var randomRange = (Mathf.Abs(baseAmplitude) - config.amplitude / 3f) * attenuation;
// 得出结果
if (Mathf.Abs(baseAmplitude) < config.amplitude / 3)
{
return Random.Range(-randomRange, randomRange); // 对 Y 值进行随机扰动
}
return 0;
}
#endregion
/// <summary>
/// 根据计算出的点数绘制线条
/// </summary>
private void DrawLine()
{
for (int i = 0; i < config.samples; i++)
{
// 计算线上每个点的位置
float x = (float)i / (config.samples - 1) * _screenWidth - _screenWidth / 2f; // x轴数据
float y = _displayData[i] * config.AmplitudeScale * (_screenHeight / 2f);
Vector3 position = new Vector3(x, y, 0);
// 设置
_waveformLine.SetPosition(i, position);
}
// 设置颜色和宽度
_waveformLine.startColor = config.LineColor;
_waveformLine.endColor = config.LineColor;
_waveformLine.startWidth = config.LineWidth;
_waveformLine.endWidth = config.LineWidth;
}
private int GeneSpikeIdx()
{
float[] waveData = GenerateWaveformData();
int resIdx = GetRandomIndexFromTop20Percent(waveData);
Array.Clear(waveData, 0, waveData.Length);
return resIdx;
}
int GetRandomIndexFromTop20Percent(float[] array)
{
int n = array.Length;
int topCount = (int)Math.Ceiling(n * 0.2);
var indexedArray = array
.Select((value, index) => new { Value = Math.Abs(value), Index = index })
.OrderByDescending(item => item.Value)
.Take(topCount)
.Select(item => item.Index)
.ToArray();
int randomIndex = indexedArray[Random.Range(0, indexedArray.Length)];
return randomIndex;
}
#region
/// <summary>
/// 设置屏幕尺寸作为参数
/// </summary>
/// <param name="width">宽</param>
/// <param name="height">高</param>
public void SetLineSize(float width, float height)
{
_screenHeight = height;
_screenWidth = width;
}
/// <summary>
/// 显示波形
/// </summary>
public void PlayWave(WaveformType waveformType, float initialPhase, bool hasError)
{
// 配置波形数据
config.waveformType = waveformType;
_waveParam = new WaveParam
{
waveformType = waveformType,
spikeIdx = -1,
initPhaseShift = initialPhase,
varPhaseShift = 0
};
_waveParam.spikeIdx = hasError ? GeneSpikeIdx() : -1;
// 进行播放
_waveformLine.enabled = true;
_isWavePlaying = true;
}
/// <summary>
/// 停止显示波形
/// </summary>
public void StopWave()
{
// 清除数据
config.waveformType = WaveformType.Default;
_waveParam = new WaveParam
{
waveformType = WaveformType.Default,
spikeIdx = -1,
initPhaseShift = 0,
varPhaseShift = 0
};
// 停止
_isWavePlaying = false;
_waveformLine.enabled = false;
}
/// <summary>
/// 获取尖峰点坐标
/// </summary>
/// <returns></returns>
public float GetSpikeX()
{
if (CheckErrorShow())
{
return _waveParam.spikeIdx / (float)config.samples * _screenWidth - _screenWidth / 2;
}
return float.NegativeInfinity;
}
/// <summary>
/// 调整相位
/// </summary>
/// <param name="phaseShift">相位</param>
public void AdjustPhaseShift(float phaseShift)
{
_waveParam.varPhaseShift = phaseShift * 2 * Mathf.PI;
}
#endregion
}
[Serializable]
public class WaveformConfig
{
public int samples = 1024;
public float amplitude = 0.6f;
public int spikeRange = 20;
public float spatialFrequency = 1f;
public float temporalFrequency = 0.1f;
public float targetWaveformNoiseAmplitude = 0.1f;
public float noiseAmplitude = 0.5f;
public Color noiseColor = Color.gray;
public Color clearColor = Color.green;
public Color hotColor = Color.red;
public WaveformType waveformType;
public Color LineColor
{
get
{
if (waveformType == WaveformType.Default)
{
return noiseColor;
}
return waveformType == WaveformType.HotNoise ? hotColor : clearColor;
}
}
public float LineWidth => waveformType != WaveformType.Default ? 0.05f : 0.02f;
public float AmplitudeScale => waveformType != WaveformType.Default ? 1f : 0.2f;
public float AdjustedNoiseAmplitude =>
waveformType != WaveformType.Default ? targetWaveformNoiseAmplitude : noiseAmplitude;
}
public struct WaveParam
{
public WaveformType waveformType;
public float initPhaseShift;
public int spikeIdx;
public float varPhaseShift;
public float PhaseShift => initPhaseShift + varPhaseShift;
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: fd4bf97f1c7d4f559fe7ce23a64bff87
timeCreated: 1731495781