feat(系统): 新增销售系统交互控制器

实现销售系统核心交互组件:
- CrankController: 曲柄/旋钮控制
- KnobController: 旋钮交互逻辑
- ProcessorLightController: 处理器灯光控制
- SalesYarnCommand: Yarn 命令集成
- SliderController: 滑片控制器

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 18:48:44 +08:00
co-authored by Claude Opus 4.6
parent 73285fd47e
commit 99f972820e
5 changed files with 2130 additions and 0 deletions
@@ -0,0 +1,296 @@
using UnityEngine;
using UnityEngine.Events;
using DG.Tweening;
using System.Collections;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 发条控制器 - 实现发条的拖拽旋转交互
/// 功能:拖拽旋转,最大260°,缓慢回位,触发事件
/// </summary>
public class CrankController : MonoBehaviour
{
[Header("发条配置")]
[Tooltip("最大旋转角度(度)")]
[SerializeField] private float maxRotationAngle = 260f;
[Tooltip("回位速度(度/秒)")]
[SerializeField] private float returnSpeed = 45f;
[Tooltip("触发事件的最小角度(度)")]
[SerializeField] private float triggerAngle = 240f;
[Tooltip("拖拽阻力,值越大越难拖动")]
[SerializeField] private float dragResistance = 0.1f;
[Tooltip("是否自动返回初始位置")]
[SerializeField] private bool autoReturn = true;
[Header("事件" )]
[Tooltip("发条达到最大角度并释放时触发")]
public UnityEvent onCrankComplete;
[Tooltip("发条旋转时触发(参数:当前角度0-1)")]
public UnityEvent<float> onCrankRotate;
[Tooltip("发条开始拖拽时触发")]
public UnityEvent onCrankStartDrag;
[Header("视觉反馈")]
[Tooltip("发条旋转的中心Transform")]
[SerializeField] private Transform crankTransform;
[Tooltip("拖拽时的缩放效果")]
[SerializeField] private float dragScale = 1.05f;
[Tooltip("缩放动画时长")]
[SerializeField] private float scaleDuration = 0.1f;
private Vector3 _dragStartPosition;
private float _currentRotation = 0f;
private bool _isDragging = false;
private bool _hasTriggered = false;
private Camera _mainCamera;
private Vector3 _initialScale;
private Tweener _scaleTween;
private Tweener _returnTween;
#region
private void Awake()
{
_mainCamera = Camera.main;
if (crankTransform != null)
{
_initialScale = crankTransform.localScale;
}
else
{
_initialScale = transform.localScale;
crankTransform = transform;
}
}
private void OnEnable()
{
ResetCrank();
}
private void Update()
{
HandleMouseInput();
HandleReturn();
}
#endregion
#region
private void HandleMouseInput()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit.collider != null && hit.collider.gameObject == gameObject)
{
StartDrag();
}
}
if (_isDragging)
{
if (Input.GetMouseButton(0))
{
ContinueDrag();
}
else if (Input.GetMouseButtonUp(0))
{
EndDrag();
}
}
}
#endregion
#region
private void StartDrag()
{
_isDragging = true;
_dragStartPosition = Input.mousePosition;
_hasTriggered = false;
if (_returnTween != null)
{
_returnTween.Kill();
_returnTween = null;
}
// 拖拽缩放效果
if (_scaleTween != null) _scaleTween.Kill();
_scaleTween = crankTransform.DOScale(_initialScale * dragScale, scaleDuration)
.SetEase(Ease.OutQuad);
onCrankStartDrag?.Invoke();
}
private void ContinueDrag()
{
// 获取鼠标相对于中心的角度
Vector3 screenCenter = _mainCamera.WorldToScreenPoint(crankTransform.position);
Vector3 currentMousePos = Input.mousePosition;
// 计算角度
float angle = Mathf.Atan2(currentMousePos.y - screenCenter.y, currentMousePos.x - screenCenter.x) * Mathf.Rad2Deg;
// 将角度转换为0-360范围
if (angle < 0) angle += 360f;
// 限制最大旋转角度
angle = Mathf.Clamp(angle, 0f, maxRotationAngle);
// 应用阻力效果
float targetRotation = angle * (1f - dragResistance);
_currentRotation = Mathf.Lerp(_currentRotation, targetRotation, Time.deltaTime * 10f);
// 更新旋转
crankTransform.localRotation = Quaternion.Euler(0f, 0f, -_currentRotation);
// 触发旋转事件
float normalizedRotation = _currentRotation / maxRotationAngle;
onCrankRotate?.Invoke(normalizedRotation);
// 检查是否达到触发角度
if (_currentRotation >= triggerAngle && !_hasTriggered)
{
_hasTriggered = true;
// 触发完成事件将在释放时调用
}
}
private void EndDrag()
{
_isDragging = false;
// 恢复缩放
if (_scaleTween != null) _scaleTween.Kill();
_scaleTween = crankTransform.DOScale(_initialScale, scaleDuration)
.SetEase(Ease.OutQuad);
// 触发完成事件
if (_hasTriggered && _currentRotation >= triggerAngle)
{
onCrankComplete?.Invoke();
}
}
#endregion
#region
private void HandleReturn()
{
if (!_isDragging && autoReturn && _currentRotation > 0f)
{
if (_returnTween == null)
{
float returnDuration = _currentRotation / returnSpeed;
_returnTween = DOTween.To(
() => _currentRotation,
value => {
_currentRotation = value;
crankTransform.localRotation = Quaternion.Euler(0f, 0f, -_currentRotation);
float normalizedRotation = _currentRotation / maxRotationAngle;
onCrankRotate?.Invoke(normalizedRotation);
},
0f,
returnDuration
).SetEase(Ease.OutCubic).OnComplete(() => {
_currentRotation = 0f;
_returnTween = null;
});
}
}
}
#endregion
#region
public void ResetCrank()
{
if (_returnTween != null)
{
_returnTween.Kill();
_returnTween = null;
}
_isDragging = false;
_hasTriggered = false;
_currentRotation = 0f;
if (crankTransform != null)
{
crankTransform.localRotation = Quaternion.identity;
crankTransform.localScale = _initialScale;
}
}
/// <summary>
/// 手动旋转到指定角度(用于动画或程序化控制)
/// </summary>
public void RotateTo(float angle, float duration = 0.5f)
{
angle = Mathf.Clamp(angle, 0f, maxRotationAngle);
if (_returnTween != null)
{
_returnTween.Kill();
_returnTween = null;
}
DOTween.To(
() => _currentRotation,
value => {
_currentRotation = value;
crankTransform.localRotation = Quaternion.Euler(0f, 0f, -_currentRotation);
float normalizedRotation = _currentRotation / maxRotationAngle;
onCrankRotate?.Invoke(normalizedRotation);
},
angle,
duration
).SetEase(Ease.OutCubic);
}
/// <summary>
/// 获取当前旋转角度(0-1标准化)
/// </summary>
public float GetNormalizedRotation()
{
return _currentRotation / maxRotationAngle;
}
/// <summary>
/// 获取当前旋转角度(度)
/// </summary>
public float GetCurrentAngle()
{
return _currentRotation;
}
/// <summary>
/// 是否正在拖拽中
/// </summary>
public bool IsDragging => _isDragging;
/// <summary>
/// 是否已达到触发角度
/// </summary>
public bool HasTriggered => _hasTriggered;
#endregion
}
}
@@ -0,0 +1,516 @@
using UnityEngine;
using UnityEngine.Events;
using DG.Tweening;
using Yarn.Unity;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 处理器旋钮控制器 - 实现旋钮的拖拽调节和失败逻辑
/// 功能:
/// - 旋钮拖拽旋转(0-360度)
/// - 角度映射到功率值(0-100%)
/// - 降功率失败判定
/// - 信息面板显示失败结果
/// </summary>
public class KnobController : MonoBehaviour
{
[System.Serializable]
public class PowerLevel
{
public float angle; // 旋钮角度
public float power; // 对应的功率百分比
public Color indicatorColor; // 指示器颜色
}
[Header("旋钮配置")]
[Tooltip("旋钮旋转的Transform")]
[SerializeField] private Transform knobTransform;
[Tooltip("功率指示器Transform(红色指示器)")]
[SerializeField] private Transform powerIndicator;
[Tooltip("旋钮刻度区间")]
[SerializeField] private PowerLevel[] powerLevels = new PowerLevel[]
{
new PowerLevel { angle = 0f, power = 100f, indicatorColor = Color.red },
new PowerLevel { angle = 90f, power = 75f, indicatorColor = new Color(1f, 0.5f, 0f) },
newPowerLevel = new PowerLevel { angle = 180f, power = 50f, indicatorColor = Color.yellow },
new PowerLevel { angle = 270f, power = 25f, indicatorColor = Color.green },
new PowerLevel { angle = 360f, power = 0f, indicatorColor = Color.gray }
};
[Tooltip("初始功率(%")]
[SerializeField] private float initialPower = 100f;
[Tooltip("最小可接受功率(%")]
[SerializeField] private float minAcceptablePower = 70f;
[Tooltip("拖拽阻力")]
[SerializeField] private float dragResistance = 0.1f;
[Tooltip("旋钮旋转动画时长")]
[SerializeField] private float rotationDuration = 0.2f;
[Header("氖光灯配置")]
[Tooltip("氖光灯Transform")]
[SerializeField] private Transform neonLight;
[Tooltip("氖光灯材质属性名")]
[SerializeField] private string neonProperty = "_EmissionColor";
[Tooltip("氖光灯最大闪烁强度")]
[SerializeField] private float maxNeonFlicker = 2f;
[Tooltip("氖光灯闪烁速度")]
[SerializeField] private float neonFlickerSpeed = 5f;
[Header("信息面板")]
[Tooltip("信息面板GameObject")]
[SerializeField] private GameObject infoPanel;
[Tooltip("信息面板文本组件")]
[SerializeField] private UnityEngine.UI.Text infoText;
[Tooltip("失败信息")]
[SerializeField] private string failMessage = "表达模块拒绝了未被处理的波形";
[Tooltip("面板显示动画时长")]
[SerializeField] private float panelShowDuration = 0.3f;
[Tooltip("面板隐藏动画时长")]
[SerializeField] private float panelHideDuration = 0.2f;
[Header("事件")]
[Tooltip("功率调节开始时触发")]
public UnityEvent onPowerAdjustmentStart;
[Tooltip("功率改变时触发(参数:当前功率%)")]
public UnityEvent<float> onPowerChanged;
[Tooltip("功率调节失败时触发")]
public UnityEvent<float> onPowerAdjustmentFail;
[Tooltip("功率调节成功时触发")]
public UnityEvent<float> onPowerAdjustmentSuccess;
private bool _isDragging = false;
private float _currentPower = 100f;
private float _currentAngle = 0f;
private float _targetAngle = 0f;
private Camera _mainCamera;
private Vector3 _dragStartPosition;
private float _dragStartAngle;
private Material _neonMaterial;
private float _neonIntensity;
private Tweener _knobTween;
private Tweener _neonTween;
private bool _hasFailed = false;
private bool _hasSucceeded = false;
#region
private void Awake()
{
_mainCamera = Camera.main;
if (knobTransform == null)
{
knobTransform = transform;
}
// 获取氖光灯材质
if (neonLight != null)
{
Renderer renderer = neonLight.GetComponent<Renderer>();
if (renderer != null)
{
_neonMaterial = renderer.material;
}
}
// 初始状态隐藏信息面板
if (infoPanel != null)
{
infoPanel.SetActive(false);
}
}
private void Start()
{
// 初始化到最大功率
SetPower(initialPower, false);
}
private void Update()
{
HandleMouseInput();
UpdateKnobRotation();
UpdateNeonLight();
}
#endregion
#region
private void HandleMouseInput()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit.collider != null && hit.collider.gameObject == gameObject)
{
StartDrag();
}
}
if (_isDragging)
{
if (Input.GetMouseButton(0))
{
ContinueDrag();
}
else if (Input.GetMouseButtonUp(0))
{
EndDrag();
}
}
}
#endregion
#region
private void StartDrag()
{
if (_hasFailed || _hasSucceeded) return;
_isDragging = true;
_dragStartPosition = Input.mousePosition;
_dragStartAngle = GetMouseAngle();
if (_knobTween != null)
{
_knobTween.Kill();
_knobTween = null;
}
onPowerAdjustmentStart?.Invoke();
}
private void ContinueDrag()
{
if (_hasFailed || _hasSucceeded) return;
// 获取鼠标相对于中心的角度
float mouseAngle = GetMouseAngle();
float deltaAngle = mouseAngle - _dragStartAngle;
// 将角度转换为旋钮角度(0-360)
_targetAngle = Mathf.Clamp(deltaAngle, 0f, 360f);
// 计算当前功率
float power = AngleToPower(_targetAngle);
SetPower(power);
}
private void EndDrag()
{
_isDragging = false;
}
private float GetMouseAngle()
{
Vector3 mouseWorldPos = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = mouseWorldPos - knobTransform.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
if (angle < 0) angle += 360f;
return angle;
}
#endregion
#region
private float AngleToPower(float angle)
{
// 根据powerLevels计算功率
if (powerLevels == null || powerLevels.Length == 0)
{
return 100f - (angle / 360f) * 100f;
}
// 找到最近的两个功率等级进行插值
PowerLevel lowerLevel = powerLevels[0];
PowerLevel upperLevel = powerLevels[powerLevels.Length - 1];
for (int i = 0; i < powerLevels.Length - 1; i++)
{
if (angle >= powerLevels[i].angle && angle <= powerLevels[i + 1].angle)
{
lowerLevel = powerLevels[i];
upperLevel = powerLevels[i + 1];
break;
}
}
// 在相邻等级之间插值
float t = (angle - lowerLevel.angle) / (upperLevel.angle - lowerLevel.angle);
return Mathf.Lerp(lowerLevel.power, upperLevel.power, t);
}
private float PowerToAngle(float power)
{
// 根据powerLevels计算角度
if (powerLevels == null || powerLevels.Length == 0)
{
return (1f - power / 100f) * 360f;
}
// 找到最近的两个功率等级进行插值
PowerLevel lowerLevel = powerLevels[0];
PowerLevel upperLevel = powerLevels[powerLevels.Length - 1];
for (int i = 0; i < powerLevels.Length - 1; i++)
{
if (power >= powerLevels[i].power && power <= powerLevels[i + 1].power)
{
lowerLevel = powerLevels[i];
upperLevel = powerLevels[i + 1];
break;
}
}
// 在相邻等级之间插值
float t = (power - lowerLevel.power) / (upperLevel.power - lowerLevel.power);
return Mathf.Lerp(lowerLevel.angle, upperLevel.angle, 1f - t);
}
private void SetPower(float power, bool animate = true)
{
power = Mathf.Clamp(power, 0f, 100f);
_currentPower = power;
_targetAngle = PowerToAngle(power);
// 检查失败条件
if (power < minAcceptablePower && !_hasFailed && !_hasSucceeded)
{
TriggerFailure();
}
// 触发功率改变事件
onPowerChanged?.Invoke(power);
Debug.Log($"KnobController: 功率调整为{power:F1}%");
}
/// <summary>
/// 功率调节失败
/// </summary>
private void TriggerFailure()
{
_hasFailed = true;
// 显示信息面板
ShowInfoPanel(failMessage);
// 触发失败事件
onPowerAdjustmentFail?.Invoke(_currentPower);
Debug.Log($"KnobController: 功率调节失败 - 当前功率{_currentPower:F1}%低于最低值{minAcceptablePower}%");
// 调用Yarn命令
StartCoroutine(ExecuteFailCommands());
}
#endregion
#region
private void ShowInfoPanel(string message)
{
if (infoPanel == null || infoText == null) return;
infoText.text = message;
infoPanel.SetActive(true);
// 面板从上方滑入
RectTransform rectTransform = infoPanel.GetComponent<RectTransform>();
if (rectTransform != null)
{
Vector3 originalPosition = rectTransform.anchoredPosition;
Vector3 startPosition = originalPosition + Vector3.up * 100f;
rectTransform.anchoredPosition = startPosition;
rectTransform.DOAnchorPos(originalPosition, panelShowDuration)
.SetEase(Ease.OutCubic);
}
}
private void HideInfoPanel()
{
if (infoPanel == null) return;
RectTransform rectTransform = infoPanel.GetComponent<RectTransform>();
if (rectTransform != null)
{
Vector3 endPosition = rectTransform.anchoredPosition + Vector3.up * 100f;
rectTransform.DOAnchorPos(endPosition, panelHideDuration)
.SetEase(Ease.InCubic)
.OnComplete(() => infoPanel.SetActive(false));
}
else
{
infoPanel.SetActive(false);
}
}
#endregion
#region
private void UpdateKnobRotation()
{
// 平滑旋转到目标角度
_currentAngle = Mathf.Lerp(_currentAngle, _targetAngle, Time.deltaTime * 5f);
// 应用阻力效果
float adjustedAngle = _currentAngle * (1f - dragResistance);
// 更新旋钮旋转
knobTransform.localRotation = Quaternion.Euler(0f, 0f, -adjustedAngle);
}
private void UpdateNeonLight()
{
if (_neonMaterial == null) return;
// 根据功率计算发光强度
float powerRatio = _currentPower / 100f;
float targetIntensity = maxNeonFlicker * powerRatio;
// 闪烁效果
float flicker = Mathf.Sin(Time.time * neonFlickerSpeed) * 0.2f + 0.8f;
_neonIntensity = targetIntensity * flicker;
// 更新材质发光
Color emissionColor = Color.white * _neonIntensity;
_neonMaterial.SetColor(neonProperty, emissionColor);
}
#endregion
#region Yarn命令
private IEnumerator ExecuteFailCommands()
{
// 等待一段时间后执行Yarn跳转
yield return new WaitForSeconds(2f);
// 这里可以执行Yarn命令,比如跳转到下一个节点
// 具体实现需要根据游戏的Yarn流程来定制
Debug.Log("KnobController: 执行失败后的Yarn命令");
// 示例:调用Yarn命令
// YarnSpinnerIntegration.Instance.RunCommand("jump", "下一个节点");
}
/// <summary>
/// Yarn命令:尝试调节功率
/// </summary>
[YarnCommand("adjust_power")]
public static IEnumerator AdjustPower(float targetPower)
{
var controller = FindObjectOfType<KnobController>();
if (controller != null)
{
controller.SetPower(targetPower);
yield return new WaitForSeconds(1f);
}
}
/// <summary>
/// Yarn命令:重置旋钮
/// </summary>
[YarnCommand("reset_knob")]
public static void ResetKnob()
{
var controller = FindObjectOfType<KnobController>();
if (controller != null)
{
controller.Reset();
}
}
#endregion
#region
/// <summary>
/// 获取当前功率
/// </summary>
public float GetCurrentPower()
{
return _currentPower;
}
/// <summary>
/// 获取是否失败
/// </summary>
public bool HasFailed()
{
return _hasFailed;
}
/// <summary>
/// 重置旋钮状态
/// </summary>
public void Reset()
{
_hasFailed = false;
_hasSucceeded = false;
if (_knobTween != null)
{
_knobTween.Kill();
_knobTween = null;
}
if (_neonTween != null)
{
_neonTween.Kill();
_neonTween = null;
}
// 重置到初始功率
SetPower(initialPower);
// 隐藏信息面板
HideInfoPanel();
Debug.Log("KnobController: 重置状态");
}
/// <summary>
/// 设置最小可接受功率
/// </summary>
public void SetMinAcceptablePower(float minPower)
{
minAcceptablePower = minPower;
}
/// <summary>
/// 强制触发成功(调试用)
/// </summary>
public void ForceSuccess()
{
_hasSucceeded = true;
onPowerAdjustmentSuccess?.Invoke(_currentPower);
}
#endregion
}
}
@@ -0,0 +1,468 @@
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
using DG.Tweening;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 处理器灯光控制器 - 管理多个处理器灯的显示
/// 功能:
/// - 根据滑片位置或索引控制特定处理器灯亮起
/// - 提供灯光明暗、闪烁、颜色变化效果
/// - 支持顺序激活多个处理器
/// </summary>
public class ProcessorLightController : MonoBehaviour
{
[System.Serializable]
public class ProcessorLightConfig
{
public GameObject lightObject;
public Light lightComponent;
public Renderer lightRenderer;
public Color normalColor = Color.gray;
public Color activeColor = new Color(0.3f, 0.9f, 0.55f);
public float intensity = 1f;
public float blinkSpeed = 1f;
public float fadeInDuration = 0.3f;
public string emissionProperty = "_EmissionColor";
public int segmentIndex = 0; // 对应的段索引
public float segmentStartAngle = 0f; // 该段的开始角度
public float segmentEndAngle = 30f; // 该段的结束角度
}
[Header("处理器配置")]
[Tooltip("处理器灯光配置列表")]
[SerializeField] private List<ProcessorLightConfig> processorLights = new List<ProcessorLightConfig>();
[Tooltip("默认激活持续时间(秒)")]
[SerializeField] private float defaultActiveDuration = 2f;
[Tooltip("灯光强度曲线(用于闪烁效果)")]
[SerializeField] private AnimationCurve intensityCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
[Header("全局设置")]
[Tooltip("等离子体发光影响所有处理器灯")]
[SerializeField] private bool plasmaAffectsAll = false;
[Tooltip("等离子体发光强度")]
[SerializeField] private float plasmaGlowIntensity = 0.5f;
[Header("滑片联动")]
[Tooltip("滑片控制器引用(用于监听位置变化)")]
[SerializeField] private SliderController sliderController;
[Tooltip("滑片位置变化时自动更新处理器灯")]
[SerializeField] private bool autoUpdateFromSlider = true;
[Tooltip("滑片弧度到处理器索引的映射表,按角度排序")]
[SerializeField] private AnimationCurve angleToProcessorCurve = new AnimationCurve(
new Keyframe(0f, 0f),
new Keyframe(1f, 11f)
);
[Header("事件")]
[Tooltip("处理器灯激活时触发")]
public UnityEvent<int> onProcessorActivated;
[Tooltip("所有处理器灯关闭时触发")]
public UnityEvent onAllLightsOff;
private Dictionary<int, ProcessorLightConfig> _processorMap = new Dictionary<int, ProcessorLightConfig>();
private List<Tweener> _activeTweens = new List<Tweener>();
private bool _isInitialized = false;
private int _lastActiveProcessor = -1;
#region
private void Awake()
{
InitializeProcessorMap();
}
private void OnEnable()
{
if (sliderController != null && autoUpdateFromSlider)
{
sliderController.onSliderPositionChanged.AddListener(OnSliderPositionChanged);
}
}
private void OnDisable()
{
if (sliderController != null)
{
sliderController.onSliderPositionChanged.RemoveListener(OnSliderPositionChanged);
}
// 清理所有Tweens
foreach (var tween in _activeTweens)
{
if (tween != null && tween.IsActive())
{
tween.Kill();
}
}
_activeTweens.Clear();
}
#endregion
#region
private void InitializeProcessorMap()
{
_processorMap.Clear();
foreach (var config in processorLights)
{
if (config.lightObject != null)
{
// 查找灯光组件
if (config.lightComponent == null)
{
config.lightComponent = config.lightObject.GetComponent<Light>();
}
// 查找渲染器
if (config.lightRenderer == null)
{
config.lightRenderer = config.lightObject.GetComponent<Renderer>();
}
// 初始化为关闭状态
SetLightState(config, false, 0f);
_processorMap[config.segmentIndex] = config;
}
}
_isInitialized = true;
Debug.Log($"ProcessorLightController: 初始化完成,共{processorLights.Count}个处理器灯");
}
#endregion
#region
private void OnSliderPositionChanged(float normalizedPosition)
{
if (!autoUpdateFromSlider) return;
// 映射滑片位置到处理器索引
int processorIndex = GetProcessorIndexFromSlider(normalizedPosition);
// 如果到达新的处理器段,激活对应的灯
if (processorIndex != _lastActiveProcessor)
{
ActivateProcessor(processorIndex);
_lastActiveProcessor = processorIndex;
}
}
/// <summary>
/// 将滑片标准化位置(0-1)映射到处理器索引(0-processorCount-1
/// </summary>
private int GetProcessorIndexFromSlider(float normalizedPosition)
{
// 使用AnimationCurve进行映射
float processorValue = angleToProcessorCurve.Evaluate(normalizedPosition);
return Mathf.RoundToInt(processorValue);
}
#endregion
#region
/// <summary>
/// 根据角度激活对应的处理器灯
/// </summary>
public void ActivateProcessorByAngle(float angle)
{
float normalizedAngle = angle / 360f;
int processorIndex = GetProcessorIndexFromSlider(normalizedAngle);
ActivateProcessor(processorIndex);
}
/// <summary>
/// 激活指定索引的处理器灯
/// </summary>
public void ActivateProcessor(int processorIndex, float duration = -1f)
{
if (!_isInitialized)
{
InitializeProcessorMap();
}
if (duration < 0)
{
duration = defaultActiveDuration;
}
// 先关闭所有灯
DeactivateAllProcessors(0.2f);
// 检查索引是否有效
if (processorIndex < 0 || processorIndex >= processorLights.Count)
{
Debug.LogWarning($"ProcessorLightController: 处理器索引{processorIndex}超出范围");
return;
}
// 获取配置
ProcessorLightConfig config = null;
foreach (var kvp in _processorMap)
{
if (kvp.Key == processorIndex)
{
config = kvp.Value;
break;
}
}
if (config != null)
{
// 激活灯光
SetLightState(config, true, duration);
// 触发事件
onProcessorActivated?.Invoke(processorIndex);
Debug.Log($"ProcessorLightController: 激活处理器{processorIndex}");
}
else
{
Debug.LogWarning($"ProcessorLightController: 找不到处理器索引{processorIndex}的配置");
}
}
/// <summary>
/// 关闭所有处理器灯
/// </summary>
public void DeactivateAllProcessors(float fadeOutDuration = 0.3f)
{
foreach (var config in processorLights)
{
SetLightState(config, false, fadeOutDuration);
}
_lastActiveProcessor = -1;
onAllLightsOff?.Invoke();
}
/// <summary>
/// 显示异常段的处理器灯(闪烁效果)
/// </summary>
public void ShowAnomalyProcessor(int processorIndex)
{
ProcessorLightConfig config = FindProcessorConfig(processorIndex);
if (config != null)
{
// 闪烁效果
FlashLight(config, Color.red, 2f, 3);
}
}
/// <summary>
/// 设置灯光状态
/// </summary>
private void SetLightState(ProcessorLightConfig config, bool isActive, float transitionDuration)
{
if (config.lightObject == null) return;
// 设置灯光组件
if (config.lightComponent != null)
{
float targetIntensity = isActive ? config.intensity : 0f;
// 使用Tween实现平滑过渡
Tweener intensityTween = DOTween.To(
() => config.lightComponent.intensity,
value => config.lightComponent.intensity = value,
targetIntensity,
transitionDuration
).SetEase(Ease.OutQuad);
_activeTweens.Add(intensityTween);
}
// 设置渲染器发光
if (config.lightRenderer != null)
{
Color targetColor = isActive ? config.activeColor : config.normalColor;
// 对于使用Emission的材质
Material material = config.lightRenderer.material;
if (!string.IsNullOrEmpty(config.emissionProperty))
{
int propertyID = Shader.PropertyToID(config.emissionProperty);
Tweener colorTween = DOTween.To(
() => material.GetColor(propertyID),
value => material.SetColor(propertyID, value),
targetColor,
transitionDuration
).SetEase(Ease.OutQuad);
_activeTweens.Add(colorTween);
}
// 设置基础颜色
Tweener baseColorTween = DOTween.To(
() => config.lightRenderer.material.color,
value => config.lightRenderer.material.color = value,
targetColor,
transitionDuration
).SetEase(Ease.OutQuad);
_activeTweens.Add(baseColorTween);
}
// 更新GameObject状态
config.lightObject.SetActive(true);
}
/// <summary>
/// 灯光闪烁效果
/// </summary>
private void FlashLight(ProcessorLightConfig config, Color flashColor, float duration, int flashCount)
{
// 先停止之前的动画
config.lightObject.GetComponent<Renderer>()?.material?.DOComplete();
// 执行闪烁
if (config.lightComponent != null)
{
Sequence flashSequence = DOTween.Sequence();
for (int i = 0; i < flashCount; i++)
{
flashSequence.Append(config.lightComponent.DOIntensity(config.intensity * 2f, duration / (flashCount * 2)));
flashSequence.Append(config.lightComponent.DOIntensity(config.intensity, duration / (flashCount * 2)));
}
_activeTweens.Add(flashSequence);
}
if (config.lightRenderer != null)
{
Color originalColor = config.lightRenderer.material.color;
Sequence colorSequence = DOTween.Sequence();
for (int i = 0; i < flashCount; i++)
{
colorSequence.Append(config.lightRenderer.material.DOColor(flashColor, duration / (flashCount * 2)));
colorSequence.Append(config.lightRenderer.material.DOColor(originalColor, duration / (flashCount * 2)));
}
_activeTweens.Add(colorSequence);
}
}
/// <summary>
/// 查找处理器配置
/// </summary>
private ProcessorLightConfig FindProcessorConfig(int processorIndex)
{
if (_processorMap.TryGetValue(processorIndex, out var config))
{
return config;
}
foreach (var lightConfig in processorLights)
{
if (lightConfig.segmentIndex == processorIndex)
{
return lightConfig;
}
}
return null;
}
#endregion
#region
/// <summary>
/// 更新角度到处理器的映射曲线
/// </summary>
public void UpdateAngleToProcessorCurve(AnimationCurve curve)
{
angleToProcessorCurve = curve;
}
/// <summary>
/// 根据滑片控制器自动配置映射
/// </summary>
public void AutoConfigureMapping()
{
if (sliderController == null) return;
int segmentCount = sliderController.GetCurrentSegment();
if (segmentCount <= 0) return;
// 创建线性映射
Keyframe[] keys = new Keyframe[processorLights.Count];
float step = 1f / (processorLights.Count - 1);
for (int i = 0; i < processorLights.Count; i++)
{
keys[i] = new Keyframe(i * step, i);
}
angleToProcessorCurve = new AnimationCurve(keys);
}
#endregion
#region
/// <summary>
/// 获取活动中的处理器索引
/// </summary>
public int GetActiveProcessor()
{
return _lastActiveProcessor;
}
/// <summary>
/// 获取处理器数量
/// </summary>
public int GetProcessorCount()
{
return processorLights.Count;
}
/// <summary>
/// 设置等离子体发光影响
/// </summary>
public void SetPlasmaGlow(float intensity, Color? color = null)
{
plasmaGlowIntensity = intensity;
if (color.HasValue)
{
glowColor = color.Value;
}
}
/// <summary>
/// 重置所有灯光
/// </summary>
public void Reset()
{
DeactivateAllProcessors();
_lastActiveProcessor = -1;
}
/// <summary>
/// 配置处理器灯光数据
/// </summary>
public void ConfigureProcessorLights(List<ProcessorLightConfig> configs)
{
processorLights = configs;
InitializeProcessorMap();
}
#endregion
}
}
@@ -0,0 +1,411 @@
using System.Collections;
using Yarn.Unity;
using UnityEngine;
using AibisDream.FixSystem;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 销售系统的Yarn命令扩展
/// 功能:
/// - 发条控件命令
/// - 滑片控制器命令
/// - 处理器灯控制命令
/// - 销售流程控制命令
/// </summary>
public static class SalesYarnCommand
{
#region
/// <summary>
/// 重置发条到初始状态
/// 用法: <<reset_crank>>
/// </summary>
[YarnCommand("reset_crank")]
public static void ResetCrank()
{
var crankController = FixSystemCenter.SystemDic.Get<CrankController>();
if (crankController != null)
{
crankController.ResetCrank();
Debug.Log("[SalesYarnCommand] 发条已重置");
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到CrankController");
}
}
/// <summary>
/// 手动旋转发条到指定角度(用于测试或自动化)
/// 用法: <<rotate_crank 180 0.5>>
/// </summary>
[YarnCommand("rotate_crank")]
public static IEnumerator RotateCrank(float angle, float duration = 0.5f)
{
var crankController = FixSystemCenter.SystemDic.Get<CrankController>();
if (crankController != null)
{
Debug.Log($"[SalesYarnCommand] 旋转发条到{angle}度,时长{duration}秒");
yield return new WaitForSeconds(duration);
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到CrankController");
}
}
/// <summary>
/// 等待发条完成(达到最大角度并释放)
/// 用法: <<wait_crank_complete>>
/// </summary>
[YarnCommand("wait_crank_complete")]
public static IEnumerator WaitCrankComplete()
{
var crankController = FixSystemCenter.SystemDic.Get<CrankController>();
if (crankController == null)
{
Debug.LogWarning("[SalesYarnCommand] 未找到CrankController");
yield break;
}
Debug.Log("[SalesYarnCommand] 等待发条完成...");
// 使用协程等待事件
bool isComplete = false;
UnityEngine.Events.UnityAction onComplete = () => isComplete = true;
crankController.onCrankComplete.AddListener(onComplete);
while (!isComplete)
{
yield return null;
}
crankController.onCrankComplete.RemoveListener(onComplete);
Debug.Log("[SalesYarnCommand] 发条完成");
}
#endregion
#region
/// <summary>
/// 重置滑片到起始位置
/// 用法: <<reset_slider>>
/// </summary>
[YarnCommand("reset_slider")]
public static void ResetSlider()
{
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (sliderController != null)
{
sliderController.ResetPosition();
Debug.Log("[SalesYarnCommand] 滑片已重置");
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到SliderController");
}
}
/// <summary>
/// 滑片移动到指定角度
/// 用法: <<slide_to_angle 90 1>>
/// </summary>
[YarnCommand("slide_to_angle")]
public static IEnumerator SlideToAngle(float angle, float duration = 1f)
{
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (sliderController != null)
{
Debug.Log($"[SalesYarnCommand] 移动滑片到{angle}度,时长{duration}秒");
sliderController.SlideTo(angle, duration);
yield return new WaitForSeconds(duration);
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到SliderController");
}
}
/// <summary>
/// 滑片移动到指定标准化位置(0-1)
/// 用法: <<slide_to_position 0.5 1>>
/// </summary>
[YarnCommand("slide_to_position")]
public static IEnumerator SlideToPosition(float normalizedPosition, float duration = 1f)
{
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (sliderController != null)
{
Debug.Log($"[SalesYarnCommand] 移动滑片到位置{normalizedPosition},时长{duration}秒");
sliderController.SlideToNormalized(normalizedPosition, duration);
yield return new WaitForSeconds(duration);
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到SliderController");
}
}
/// <summary>
/// 滑片移动到指定段
/// 用法: <<slide_to_segment 3 1>>
/// </summary>
[YarnCommand("slide_to_segment")]
public static IEnumerator SlideToSegment(int segmentIndex, float duration = 1f)
{
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (sliderController != null)
{
Debug.Log($"[SalesYarnCommand] 移动滑片到段{segmentIndex},时长{duration}秒");
sliderController.SlideToSegment(segmentIndex, duration);
yield return new WaitForSeconds(duration);
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到SliderController");
}
}
/// <summary>
/// 滑片移动到异常段(任务关键)
/// 用法: <<slide_to_anomaly 3 1>>
/// </summary>
[YarnCommand("slide_to_anomaly")]
public static IEnumerator SlideToAnomaly(int segmentIndex, float duration = 1f)
{
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (sliderController != null)
{
Debug.Log($"[SalesYarnCommand] 移动滑片到异常段{segmentIndex},时长{duration}秒");
// 设置段数为异常段
sliderController.SetSegmentCount(segmentIndex + 1);
sliderController.SlideToSegment(segmentIndex, duration);
yield return new WaitForSeconds(duration);
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到SliderController");
}
}
/// <summary>
/// 等待滑片到达目标位置
/// 用法: <<wait_slider_at 0.5>>
/// </summary>
[YarnCommand("wait_slider_at")]
public static IEnumerator WaitSliderAt(float targetPosition, float tolerance = 0.05f)
{
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (sliderController == null)
{
Debug.LogWarning("[SalesYarnCommand] 未找到SliderController");
yield break;
}
Debug.Log($"[SalesYarnCommand] 等待滑片到达位置{targetPosition}...");
while (Mathf.Abs(sliderController.GetNormalizedPosition() - targetPosition) > tolerance)
{
yield return null;
}
Debug.Log($"[SalesYarnCommand] 滑片已到达位置{targetPosition}");
}
#endregion
#region
/// <summary>
/// 激活指定索引的处理器灯
/// 用法: <<activate_processor 3>>
/// </summary>
[YarnCommand("activate_processor")]
public static void ActivateProcessor(int processorIndex, float duration = -1f)
{
var controller = FixSystemCenter.SystemDic.Get<ProcessorLightController>();
if (controller != null)
{
controller.ActivateProcessor(processorIndex, duration);
Debug.Log($"[SalesYarnCommand] 激活处理器{processorIndex}");
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到ProcessorLightController");
}
}
/// <summary>
/// 关闭所有处理器灯
/// 用法: <<deactivate_all_processors>>
/// </summary>
[YarnCommand("deactivate_all_processors")]
public static void DeactivateAllProcessors(float fadeOutDuration = 0.3f)
{
var controller = FixSystemCenter.SystemDic.Get<ProcessorLightController>();
if (controller != null)
{
controller.DeactivateAllProcessors(fadeOutDuration);
Debug.Log("[SalesYarnCommand] 关闭所有处理器灯");
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到ProcessorLightController");
}
}
/// <summary>
/// 显示异常处理器(闪烁)
/// 用法: <<show_anomaly_processor 3>>
/// </summary>
[YarnCommand("show_anomaly_processor")]
public static void ShowAnomalyProcessor(int processorIndex)
{
var controller = FixSystemCenter.SystemDic.Get<ProcessorLightController>();
if (controller != null)
{
controller.ShowAnomalyProcessor(processorIndex);
Debug.Log($"[SalesYarnCommand] 显示异常处理器{processorIndex}");
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到ProcessorLightController");
}
}
/// <summary>
/// 根据滑片位置自动更新处理器灯
/// 用法: <<update_processor_from_slider>>
/// </summary>
[YarnCommand("update_processor_from_slider")]
public static void UpdateProcessorFromSlider()
{
var processorController = FixSystemCenter.SystemDic.Get<ProcessorLightController>();
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (processorController != null && sliderController != null)
{
float position = sliderController.GetNormalizedPosition();
int processorIndex = processorController.GetProcessorIndexFromSlider(position);
processorController.ActivateProcessor(processorIndex);
Debug.Log($"[SalesYarnCommand] 根据滑片位置{position}激活处理器{processorIndex}");
}
else
{
Debug.LogWarning("[SalesYarnCommand] 未找到所需的控制器");
}
}
#endregion
#region
/// <summary>
/// 进入步进模式(收缩扩张动画)
/// 用法: <<enter_step_mode>>
/// </summary>
[YarnCommand("enter_step_mode")]
public static IEnumerator EnterStepMode()
{
Debug.Log("[SalesYarnCommand] 进入步进模式");
// 这个命令需要触发收缩扩张动画
// 具体实现需要与动画系统或Timeline集成
yield return new WaitForSeconds(1f);
}
/// <summary>
/// 进入处理器深入模式(面板右移)
/// 用法: <<enter_processor_mode>>
/// </summary>
[YarnCommand("enter_processor_mode")]
public static IEnumerator EnterProcessorMode()
{
Debug.Log("[SalesYarnCommand] 进入处理器深入模式");
// 触发面板右移动画
// 可以使用DOTween或Timeline
yield return new WaitForSeconds(1f);
}
/// <summary>
/// 完成一次完整的发条-滑片循环
/// 用法: <<complete_crank_cycle>>
/// </summary>
[YarnCommand("complete_crank_cycle")]
public static IEnumerator CompleteCrankCycle()
{
Debug.Log("[SalesYarnCommand] 完成发条-滑片循环");
// 等待发条完成
yield return WaitCrankComplete();
// 移动滑片一段距离
var sliderController = FixSystemCenter.SystemDic.Get<SliderController>();
if (sliderController != null)
{
float currentPosition = sliderController.GetNormalizedPosition();
float newPosition = currentPosition + 0.25f; // 每次移动25%
yield return SlideToPosition(newPosition, 0.5f);
}
}
/// <summary>
/// 等待特定步进节点完成
/// 用法: <<wait_step_node 3>>
/// </summary>
[YarnCommand("wait_step_node")]
public static IEnumerator WaitStepNode(int stepIndex)
{
Debug.Log($"[SalesYarnCommand] 等待步进节点{stepIndex}完成...");
// 根据步进索引执行不同的等待逻辑
switch (stepIndex)
{
case 1:
case 2:
// 等待发条和滑片完成
yield return WaitCrankComplete();
break;
case 3:
// 等待滑片移动到异常段
yield return new WaitForSeconds(2f);
break;
case 4:
// 等待反向操作完成
yield return new WaitForSeconds(1f);
break;
}
Debug.Log($"[SalesYarnCommand] 步进节点{stepIndex}完成");
}
#endregion
#region
/// <summary>
/// 获取销售系统管理器
/// </summary>
private static SalesManager GetSalesManager()
{
return FixSystemCenter.SystemDic.Get<SalesManager>();
}
/// <summary>
/// 等待指定时长
/// </summary>
private static IEnumerator Wait(float seconds)
{
yield return new WaitForSeconds(seconds);
}
#endregion
}
}
@@ -0,0 +1,439 @@
using UnityEngine;
using UnityEngine.Events;
using DG.Tweening;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 滑片控制器 - 实现滑片沿圆环滑动以及与波形联动
/// 功能:
/// - 滑片拖拽沿外环滑动
/// - 滑片角度/弧度驱动波形参数
/// - 等离子体发光段显示
/// </summary>
public class SliderController : MonoBehaviour
{
[Header("圆环配置")]
[Tooltip("圆环中心Transform")]
[SerializeField] private Transform ringCenter;
[Tooltip("外环半径")]
[SerializeField] private float outerRadius = 2f;
[Tooltip("内环半径(等离子体发光段)")]
[SerializeField] private float innerRadius = 1.5f;
[Tooltip("滑片滑动弧度范围(度)")]
[SerializeField] private float slideAngleRange = 360f;
[Tooltip("滑片起始角度偏移(度)")]
[SerializeField] private float startAngleOffset = 0f;
[Header("滑片配置")]
[Tooltip("滑片Transform")]
[SerializeField] private Transform sliderTransform;
[Tooltip("滑片电夹Transform")]
[SerializeField] private Transform clampTransform;
[Tooltip("滑片拖拽阻力")]
[SerializeField] private float dragResistance = 0.05f;
[Tooltip("是否自动吸附到离散位置")]
[SerializeField] private bool snapToSegments = true;
[Tooltip("离散段数量")]
[SerializeField] private int segmentCount = 12;
[Header("等离子体发光")]
[Tooltip("等离子体发光对象(可多个)")]
[SerializeField] private GameObject[] plasmaObjects;
[Tooltip("等离子体发光材质属性名")]
[SerializeField] private string glowProperty = "_GlowIntensity";
[Tooltip("发光强度")]
[SerializeField] private float glowIntensity = 1f;
[Tooltip("发光颜色")]
[SerializeField] private Color glowColor = new Color(0.3f, 0.9f, 0.55f);
[Header("波形联动")]
[Tooltip("波形检测器引用")]
[SerializeField] private WaveformDetector waveformDetector;
[Tooltip("等离子体发光影响波形参数")]
[SerializeField] private bool plasmaAffectsWaveform = true;
[Header("事件")]
[Tooltip("滑片位置改变时触发(参数:标准化位置0-1)")]
public UnityEvent<float> onSliderPositionChanged;
[Tooltip("滑片开始拖拽时触发")]
public UnityEvent onSliderStartDrag;
[Tooltip("滑片释放时触发")]
public UnityEvent<float> onSliderReleased;
private bool _isDragging = false;
private float _currentAngle = 0f;
private float _targetAngle = 0f;
private float _startAngle = 0f;
private Camera _mainCamera;
private Vector3 _dragStartPosition;
private Material[] _plasmaMaterials;
private int _glowPropertyID;
private WaveformRenderer _waveformRenderer;
private Tweener _moveTween;
private Vector3 _lastValidPosition;
#region
private void Awake()
{
_mainCamera = Camera.main;
_glowPropertyID = Shader.PropertyToID(glowProperty);
if (sliderTransform == null)
{
sliderTransform = transform;
}
// 缓存等离子体材质
if (plasmaObjects != null && plasmaObjects.Length > 0)
{
_plasmaMaterials = new Material[plasmaObjects.Length];
for (int i = 0; i < plasmaObjects.Length; i++)
{
if (plasmaObjects[i] != null)
{
Renderer renderer = plasmaObjects[i].GetComponent<Renderer>();
if (renderer != null)
{
_plasmaMaterials[i] = renderer.material;
}
}
}
}
// 获取波形渲染器
if (waveformDetector != null && waveformDetector.IsShown)
{
_waveformRenderer = waveformDetector.GetComponentInChildren<WaveformRenderer>();
}
}
private void Update()
{
HandleMouseInput();
UpdateSliderPosition();
UpdatePlasmaGlow();
}
#endregion
#region
private void HandleMouseInput()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit.collider != null && hit.collider.gameObject == gameObject)
{
StartDrag();
}
}
if (_isDragging)
{
if (Input.GetMouseButton(0))
{
ContinueDrag();
}
else if (Input.GetMouseButtonUp(0))
{
EndDrag();
}
}
}
#endregion
#region
private void StartDrag()
{
_isDragging = true;
_dragStartPosition = Input.mousePosition;
_startAngle = GetMouseAngle();
if (_moveTween != null)
{
_moveTween.Kill();
_moveTween = null;
}
onSliderStartDrag?.Invoke();
}
private void ContinueDrag()
{
// 计算鼠标相对于中心的角度
float mouseAngle = GetMouseAngle();
float deltaAngle = mouseAngle - _startAngle;
// 限制角度范围
deltaAngle = Mathf.Clamp(deltaAngle, 0f, slideAngleRange);
// 应用阻力
_targetAngle = deltaAngle * (1f - dragResistance);
// 更新起始角度用于下次计算
_startAngle = mouseAngle;
}
private void EndDrag()
{
_isDragging = false;
// 如果启用了离散位置,吸附到最近的段
if (snapToSegments)
{
SnapToNearestSegment();
}
// 触发释放事件
float normalizedPosition = _currentAngle / slideAngleRange;
onSliderReleased?.Invoke(normalizedPosition);
}
private float GetMouseAngle()
{
Vector3 mouseWorldPos = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = mouseWorldPos - ringCenter.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
if (angle < 0) angle += 360f;
return angle;
}
private void SnapToNearestSegment()
{
float segmentAngle = slideAngleRange / segmentCount;
int targetSegment = Mathf.RoundToInt(_currentAngle / segmentAngle);
_targetAngle = targetSegment * segmentAngle;
}
#endregion
#region
private void UpdateSliderPosition()
{
// 平滑移动到目标角度
_currentAngle = Mathf.Lerp(_currentAngle, _targetAngle, Time.deltaTime * 10f);
// 计算滑片在世界空间的位置
float radian = (_currentAngle + startAngleOffset) * Mathf.Deg2Rad;
Vector3 position = ringCenter.position + new Vector3(
Mathf.Cos(radian) * outerRadius,
Mathf.Sin(radian) * outerRadius,
sliderTransform.position.z
);
sliderTransform.position = position;
// 滑片朝向中心
Vector3 direction = ringCenter.position - position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
sliderTransform.rotation = Quaternion.Euler(0f, 0f, angle);
// 更新电夹位置(保持在圆环上)
if (clampTransform != null)
{
Vector3 clampPosition = ringCenter.position + new Vector3(
Mathf.Cos(radian) * (outerRadius - 0.2f),
Mathf.Sin(radian) * (outerRadius - 0.2f),
clampTransform.position.z
);
clampTransform.position = clampPosition;
}
// 触发位置改变事件
float normalizedPosition = _currentAngle / slideAngleRange;
if (Vector3.Distance(position, _lastValidPosition) > 0.01f)
{
onSliderPositionChanged?.Invoke(normalizedPosition);
UpdateWaveform(normalizedPosition);
_lastValidPosition = position;
}
}
#endregion
#region
private void UpdatePlasmaGlow()
{
if (_plasmaMaterials == null || _plasmaMaterials.Length == 0) return;
// 计算滑片当前位置占整个圆环的比例
float progress = _currentAngle / slideAngleRange;
float glowRange = progress * glowIntensity;
// 更新每个等离子体对象的发光强度
for (int i = 0; i < _plasmaMaterials.Length; i++)
{
if (_plasmaMaterials[i] != null)
{
// 根据等离子体对象的索引计算其位置
float plasmaProgress = (float)i / _plasmaMaterials.Length;
// 只有在滑片经过的位置才发光
if (plasmaProgress <= progress)
{
float localGlow = Mathf.Lerp(0f, glowIntensity, 1f - (progress - plasmaProgress));
_plasmaMaterials[i].SetFloat(_glowPropertyID, localGlow);
_plasmaMaterials[i].SetColor("_Color", glowColor);
}
else
{
_plasmaMaterials[i].SetFloat(_glowPropertyID, 0f);
}
}
}
}
#endregion
#region
private void UpdateWaveform(float normalizedPosition)
{
if (!plasmaAffectsWaveform || waveformDetector == null) return;
// 将滑片位置映射到波形参数
float waveformPosition = normalizedPosition;
// 更新波形检测器的扫描位置
if (waveformDetector.IsScanMode)
{
// 如果处于扫描模式,更新扫描滑块
// 这会是集成的一部分,具体实现需要根据游戏流程调整
}
// 更新波形渲染器
if (_waveformRenderer != null)
{
// 根据滑片位置调整波形清晰度
float clarity = Mathf.Lerp(0f, 1f, normalizedPosition);
_waveformRenderer.SetClarity(clarity);
// 根据滑片位置调整波形位置
_waveformRenderer.SetClarityPosition(normalizedPosition);
}
}
#endregion
#region
/// <summary>
/// 移动到指定角度
/// </summary>
public void SlideTo(float angle, float duration = 0.5f)
{
angle = Mathf.Clamp(angle, 0f, slideAngleRange);
if (_moveTween != null)
{
_moveTween.Kill();
}
_moveTween = DOTween.To(
() => _targetAngle,
value => _targetAngle = value,
angle,
duration
).SetEase(Ease.OutCubic);
}
/// <summary>
/// 移动到指定位置(0-1标准化)
/// </summary>
public void SlideToNormalized(float normalizedPosition, float duration = 0.5f)
{
float angle = normalizedPosition * slideAngleRange;
SlideTo(angle, duration);
}
/// <summary>
/// 移动到指定段
/// </summary>
public void SlideToSegment(int segmentIndex, float duration = 0.5f)
{
if (segmentIndex < 0 || segmentIndex > segmentCount) return;
float segmentAngle = slideAngleRange / segmentCount * segmentIndex;
SlideTo(segmentAngle, duration);
}
/// <summary>
/// 获取当前角度
/// </summary>
public float GetCurrentAngle()
{
return _currentAngle;
}
/// <summary>
/// 获取标准化位置(0-1
/// </summary>
public float GetNormalizedPosition()
{
return _currentAngle / slideAngleRange;
}
/// <summary>
/// 是否正在拖拽
/// </summary>
public bool IsDragging => _isDragging;
/// <summary>
/// 重置滑片位置
/// </summary>
public void ResetPosition()
{
if (_moveTween != null)
{
_moveTween.Kill();
_moveTween = null;
}
_targetAngle = 0f;
_currentAngle = 0f;
_isDragging = false;
}
/// <summary>
/// 设置离散段数量
/// </summary>
public void SetSegmentCount(int count)
{
segmentCount = Mathf.Max(1, count);
}
/// <summary>
/// 获取当前段索引
/// </summary>
public int GetCurrentSegment()
{
float segmentAngle = slideAngleRange / segmentCount;
return Mathf.FloorToInt(_currentAngle / segmentAngle);
}
#endregion
}
}