feat(系统): 新增销售系统交互控制器
实现销售系统核心交互组件: - CrankController: 曲柄/旋钮控制 - KnobController: 旋钮交互逻辑 - ProcessorLightController: 处理器灯光控制 - SalesYarnCommand: Yarn 命令集成 - SliderController: 滑片控制器 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user