Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/SalesSystem/KnobController.cs
T

518 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections;
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) },
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)
{
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", "下一个节点");
}
/// <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
}
}