using System.Collections; using UnityEngine; using UnityEngine.Events; using DG.Tweening; using Yarn.Unity; namespace AibisDream.MiniGame.HuoShan { /// /// 处理器旋钮控制器 - 实现旋钮的拖拽调节和失败逻辑 /// 功能: /// - 旋钮拖拽旋转(0-360度) /// - 角度映射到功率值(0-100%) /// - 降功率失败判定 /// - 信息面板显示失败结果 /// 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) }, 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 onPowerChanged; [Tooltip("功率调节失败时触发")] public UnityEvent onPowerAdjustmentFail; [Tooltip("功率调节成功时触发")] public UnityEvent 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(); 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}%"); } /// /// 功率调节失败 /// 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(); 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(); if (rectTransform != null) { Vector2 endPosition = rectTransform.anchoredPosition + Vector2.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", "下一个节点"); } /// /// Yarn命令:尝试调节功率 /// [YarnCommand("adjust_power")] public static IEnumerator AdjustPower(float targetPower) { var controller = FindObjectOfType(); if (controller != null) { controller.SetPower(targetPower); yield return new WaitForSeconds(1f); } } /// /// Yarn命令:重置旋钮 /// [YarnCommand("reset_knob")] public static void ResetKnob() { var controller = FindObjectOfType(); if (controller != null) { controller.Reset(); } } #endregion #region 公共方法 /// /// 获取当前功率 /// public float GetCurrentPower() { return _currentPower; } /// /// 获取是否失败 /// public bool HasFailed() { return _hasFailed; } /// /// 重置旋钮状态 /// 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: 重置状态"); } /// /// 设置最小可接受功率 /// public void SetMinAcceptablePower(float minPower) { minAcceptablePower = minPower; } /// /// 强制触发成功(调试用) /// public void ForceSuccess() { _hasSucceeded = true; onPowerAdjustmentSuccess?.Invoke(_currentPower); } #endregion } }