Files

579 lines
23 KiB
C#
Raw Permalink 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 UnityEngine;
using UnityEngine.Events;
using AibisDream;
using AibisDream.FixSystem;
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using AibisDream.MiniGame.HuoShan;
namespace AibisDream.MiniGame.Language
{
/// <summary>
/// 销售模块深入阶段(滑片进度 0~1 均分三段)
/// </summary>
/// <summary>
/// 销售系统管理器
/// 负责视图开关、相机注册、销售模块深入阶段状态管理
/// </summary>
public class SalesManager : MonoBehaviour
{
[Header("UI配置")]
[Tooltip("销售系统主视图对象(会自动查找名为'SaleView'或'SellView'的子对象)")]
[SerializeField] private GameObject saleView;
[Header("销售模块深入 - Crank 与 Slider 协调")]
[Tooltip("发条控制器(不填则从 saleView 下查找)")]
[SerializeField] private CrankController crankController;
[Tooltip("滑片控制器(不填则从 saleView 下查找)")]
[SerializeField] private SliderController sliderController;
[Tooltip("总共转动几次(发条上满+滑片到位 为一轮)")]
[SerializeField] private int totalTurns = 4;
[Tooltip("每次转动的时间(秒),发条回位与滑片滑动均用此刻度")]
[SerializeField] private float timePerTurn = 3f;
[Tooltip("每轮转动完成后调用的 Yarn 节点名,可通过 $salesTurnIndex 分支不同内容")]
[SerializeField] private string crankCompleteNode = "销售模块转动完成";
[Tooltip("滑片开始转动时调用的 Yarn 节点名,配合 ChipModule 解释正在做的步骤,可通过 $salesTurnIndex 分支")]
[SerializeField] private string crankSlideStartNode = "销售转动_开始";
// [Header("销售模块深入 - 阶段事件(滑片进度阈值)")]
// [Tooltip("语义组织阶段时调用的 Yarn 节点名(滑片进度 ≥ 1/3")]
// [SerializeField] private string phaseSemanticNode = "语义组织达到";
// [Tooltip("审查阶段时调用的 Yarn 节点名(滑片进度 ≥ 2/3")]
// [SerializeField] private string phaseReviewNode = "审查达到";
// [Tooltip("风格阶段时调用的 Yarn 节点名(滑片进度 ≥ 1)")]
// [SerializeField] private string phaseStyleNode = "风格达到";
[Header("处理器深入模式")]
[Tooltip("盖子 SpriteRenderer(深入模式下淡出)")]
[SerializeField] private SpriteRenderer processorCover;
[Tooltip("旋钮控制器(不填则从 saleView 下查找)")]
[SerializeField] private KnobController knobController;
[Tooltip("辉光管粒子效果(不填则从 saleView 下查找)")]
[SerializeField] private GlowTubeEffect glowTubeEffect;
[Header("SaleView 进入动画")]
[Tooltip("进入动画控制器(不填则从 saleView 下查找 SaleViewEnterAnimation")]
[SerializeField] private SaleViewEnterAnimation saleViewEnterAnimation;
[Tooltip("深入模式缩放倍数")]
[SerializeField] private float deepModeScale = 1.2f;
[Tooltip("深入模式 X 偏移(负方向)")]
[SerializeField] private float deepModeOffsetX = -5f;
[Tooltip("深入模式动画时长")]
[SerializeField] private float deepModeTransitionDuration = 0.6f;
[Tooltip("盖子淡出时长")]
[SerializeField] private float coverFadeDuration = 0.5f;
private bool _isProcessorDeepMode;
private Vector3 _saleViewOriginalPos;
private Vector3 _saleViewOriginalScale;
private Sequence _deepModeSequence;
private int _currentTurnIndex; // 当前已完成第几轮(1-based)
private int _lastCompletedTurnIndex;
/// <summary>
/// 当前销售模块深入阶段
/// </summary>
/// <summary>
/// 当前转动到第几轮(1-based,滑片到位时更新)
/// </summary>
public int CurrentTurnIndex => _currentTurnIndex;
/// <summary>发条控制器(供 Yarn 命令等通过 SalesManager 获取)</summary>
public CrankController CrankController => crankController;
/// <summary>滑片控制器(供 Yarn 命令等通过 SalesManager 获取)</summary>
public SliderController SliderController => sliderController;
/// <summary>旋钮控制器</summary>
public KnobController KnobController => knobController;
/// <summary>辉光管效果</summary>
public GlowTubeEffect GlowTubeEffect => glowTubeEffect;
/// <summary>是否处于处理器深入模式</summary>
public bool IsProcessorDeepMode => _isProcessorDeepMode;
/// <summary>播放 SaleView 进入动画(轨道伸张、滑片伸出等)</summary>
public void PlaySaleViewEnterAnimation() => saleViewEnterAnimation?.PlayEnterAnimation();
/// <summary>
/// 播放进入动画,并在完成后释放控制权(禁用 Animator),
/// 以便 SliderController 等代码控制滑片等物件。
/// 完成后激活 SliderController,使其接受滑片控制并显示等离子体。
/// </summary>
public IEnumerator PlaySaleViewEnterAnimationAndReleaseWhenDone()
{
// if (saleViewEnterAnimation != null)
// yield return saleViewEnterAnimation.PlayEnterAnimationAndReleaseWhenDone();
// // 进入动画完成,激活滑片控制(等离子体与 Slider 才接受控制)
yield return TimelineCenter.Instance.PlayTimelineAsync("步进模式/进入步进模式");
sliderController?.SetControlActive(true);
}
private void Start()
{
RegisterToSystem();
FindSaleView();
FindSaleViewEnterAnimation();
FindCrankController();
FindSliderController();
FindKnobController();
FindGlowTubeEffect();
FindProcessorCover();
}
private void OnDestroy()
{
_deepModeSequence?.Kill();
UnsubscribeFromCrankSlider();
}
/// <summary>
/// 查找销售视图(按名字"SaleView",递归查找子孙节点)
/// </summary>
private void FindSaleView()
{
if (saleView == null)
{
// 递归查找,支持 SaleView/SellView 不在直接子级的情况
foreach (Transform t in transform.GetComponentsInChildren<Transform>(true))
{
if (t.name == "SaleView" || t.name == "SellView")
{
saleView = t.gameObject;
Debug.Log($"[SalesManager] 找到视图: {t.name}");
break;
}
}
if (saleView == null)
Debug.LogWarning("[SalesManager] 未找到名为 'SaleView' 或 'SellView' 的子对象", this);
}
// 初始时隐藏视图
if (saleView != null)
{
saleView.SetActive(false);
}
}
/// <summary>
/// 查找 SaleView 进入动画(从 saleView 下查找)
/// </summary>
private void FindSaleViewEnterAnimation()
{
if (saleViewEnterAnimation != null) return;
var root = saleView != null ? saleView.transform : transform;
saleViewEnterAnimation = root.GetComponentInChildren<SaleViewEnterAnimation>(true);
if (saleViewEnterAnimation != null)
Debug.Log("[SalesManager] 找到 SaleViewEnterAnimation");
}
/// <summary>
/// 查找发条控制器(从 saleView 或自身下查找)
/// </summary>
private void FindCrankController()
{
if (crankController != null) return;
var root = saleView != null ? saleView.transform : transform;
crankController = root.GetComponentInChildren<CrankController>(true);
if (crankController != null)
Debug.Log("[SalesManager] 找到 CrankController");
else
Debug.LogWarning("[SalesManager] 未找到 CrankController,请在 SaleView 下添加或手动指定", this);
}
/// <summary>
/// 查找滑片控制器(从 saleView 或自身下查找)
/// </summary>
private void FindSliderController()
{
if (sliderController != null) return;
var root = saleView != null ? saleView.transform : transform;
sliderController = root.GetComponentInChildren<SliderController>(true);
if (sliderController != null)
Debug.Log("[SalesManager] 找到 SliderController");
else
Debug.LogWarning("[SalesManager] 未找到 SliderController,请在 SaleView 下添加或手动指定", this);
}
/// <summary>
/// 建立 Crank 与 Slider 的协调:回位时长、段数、事件订阅
/// 事件统一监听滑片转到位置(阶段事件)和滑片到位完成(每轮完成)
/// 每次 OpenView 时调用,确保 prefab 初始 inactive 时也能正确连接
/// </summary>
private void EnsureCrankSliderSync()
{
UnsubscribeFromCrankSlider(); // 避免重复订阅
if (crankController == null)
{
Debug.LogWarning("[SalesManager] CrankController 未找到,发条转动无法驱动滑片!", this);
}
else
{
crankController.SetReturnDuration(timePerTurn);
crankController.onCrankComplete.AddListener(OnCrankComplete);
}
if (sliderController == null)
{
Debug.LogWarning("[SalesManager] SliderController 未找到,事件无法触发!", this);
}
else
{
sliderController.SetSegmentCount(totalTurns);
// 滑片到位完成 → 每轮转动完成事件
sliderController.onSlideComplete.AddListener(OnSlideComplete);
// 阶段事件:监听滑片转到 1/3、2/3、1 位置
// sliderController.onPhaseSemanticReached.AddListener(OnPhaseSemanticReached);
// sliderController.onPhaseReviewReached.AddListener(OnPhaseReviewReached);
// sliderController.onPhaseStyleReached.AddListener(OnPhaseStyleReached);
}
}
private void UnsubscribeFromCrankSlider()
{
if (crankController != null)
crankController.onCrankComplete.RemoveListener(OnCrankComplete);
if (sliderController != null)
{
sliderController.onSlideComplete.RemoveListener(OnSlideComplete);
// sliderController.onPhaseSemanticReached.RemoveListener(OnPhaseSemanticReached);
// sliderController.onPhaseReviewReached.RemoveListener(OnPhaseReviewReached);
// sliderController.onPhaseStyleReached.RemoveListener(OnPhaseStyleReached);
}
}
// private void OnPhaseSemanticReached() => TryStartPhaseNode(phaseSemanticNode);
// private void OnPhaseReviewReached() => TryStartPhaseNode(phaseReviewNode);
// private void OnPhaseStyleReached() => TryStartPhaseNode(phaseStyleNode);
private void OnCrankComplete()
{
Debug.Log($"[SalesManager] OnCrankComplete 被调用!当前轮次: {_currentTurnIndex + 1}, sliderController: {sliderController != null}", this);
if (_currentTurnIndex >= totalTurns)
{
Debug.LogWarning("[SalesManager] 已完成全部发条轮次,忽略重复完成事件。", this);
return;
}
_currentTurnIndex++;
#if UNITY_EDITOR
Debug.Log($"[SalesManager] 发条完成,当前第 {_currentTurnIndex}/{totalTurns} 轮", this);
#endif
// 滑片开始转动前,先触发 ChipModule 解释当前步骤(Stage3 等配合)
YarnVariableStorage.Instance?.SetValue("$salesTurnIndex", (float)_currentTurnIndex);
if (!string.IsNullOrEmpty(crankSlideStartNode))
DialogController.Instance?.StartDialogNode(crankSlideStartNode);
if (sliderController != null)
{
// 滑片移动至当前轮对应的段,时长与发条回位一致
Debug.Log($"[SalesManager] 开始滑动滑片到段 {_currentTurnIndex}", this);
sliderController.SlideToSegment(_currentTurnIndex, timePerTurn);
}
else
{
Debug.LogError($"[SalesManager] SliderController 为 null,无法滑动滑片!", this);
}
}
private void OnSlideComplete()
{
if (string.IsNullOrEmpty(crankCompleteNode) || _currentTurnIndex <= 0) return;
if (_lastCompletedTurnIndex == _currentTurnIndex) return;
_lastCompletedTurnIndex = _currentTurnIndex;
YarnVariableStorage.Instance?.SetValue("$salesTurnIndex", (float)_currentTurnIndex);
DialogController.Instance?.StartDialogNode(crankCompleteNode);
}
private void TryStartPhaseNode(string nodeName)
{
if (string.IsNullOrEmpty(nodeName)) return;
DialogController.Instance?.StartDialogNode(nodeName);
}
/// <summary>
/// 注册到系统字典
/// </summary>
private void RegisterToSystem()
{
FixSystemCenter.SystemDic.Register(this);
}
/// <summary>
/// 打开销售系统界面
/// </summary>
public void OpenView()
{
gameObject.SetActive(true);
// 延迟一帧确保子物体已激活,再建立连接(避免 prefab 初始 inactive 导致 Start 未跑)
StartCoroutine(OpenViewDelayed());
}
private System.Collections.IEnumerator OpenViewDelayed()
{
yield return null; // 等待本帧结束,子物体 Awake/OnEnable 完成
Debug.Log($"[SalesManager] OpenViewDelayed 开始,子物体已激活。crankController: {crankController != null}, sliderController: {sliderController != null}", this);
EnsureCrankSliderSync(); // 每次打开时建立/重建连接
ResetCrankCycle();
if (saleView != null)
{
saleView.SetActive(true);
sliderController?.SetPlasmaActive(true); // OpenView 时等离子体激活,360° 满弧
Debug.Log("[SalesManager] 销售系统已打开");
}
else
{
Debug.LogError("[SalesManager] SaleView 未找到,无法打开销售界面", this);
}
}
/// <summary>
/// 关闭销售系统界面
/// </summary>
public void CloseView()
{
UnsubscribeFromCrankSlider();
sliderController?.SetControlActive(false);
sliderController?.SetPlasmaActive(false);
saleViewEnterAnimation?.ResetToIdle();
if (saleView != null)
{
saleView.SetActive(false);
}
gameObject.SetActive(false);
Debug.Log("[SalesManager] 销售系统已关闭");
}
/// <summary>
/// 初始化销售系统
/// </summary>
public void Initialize()
{
Debug.Log("[SalesManager] 初始化销售系统");
// 这里添加初始化逻辑
}
/// <summary>
/// 重置销售模块深入阶段(滑片重置时通常需调用)
/// </summary>
public void ResetPhase()
{
if (sliderController != null)
sliderController.ResetPhaseFlags();
}
/// <summary>
/// 重置发条-滑片循环(进入销售深入时调用)
/// </summary>
public void ResetCrankCycle()
{
_currentTurnIndex = 0;
_lastCompletedTurnIndex = 0;
crankController?.ResetCrank();
sliderController?.ResetPosition();
ResetPhase();
}
#region 处理器深入模式
private void FindKnobController()
{
if (knobController != null) return;
var root = saleView != null ? saleView.transform : transform;
knobController = root.GetComponentInChildren<KnobController>(true);
if (knobController == null)
knobController = GetComponentInChildren<KnobController>(true);
if (knobController != null)
Debug.Log("[SalesManager] Found KnobController");
}
private void FindGlowTubeEffect()
{
if (glowTubeEffect != null) return;
var root = saleView != null ? saleView.transform : transform;
glowTubeEffect = root.GetComponentInChildren<GlowTubeEffect>(true);
if (glowTubeEffect == null)
glowTubeEffect = GetComponentInChildren<GlowTubeEffect>(true);
if (glowTubeEffect != null)
Debug.Log("[SalesManager] Found GlowTubeEffect");
}
private void FindProcessorCover()
{
if (processorCover != null) return;
var root = saleView != null ? saleView.transform : transform;
foreach (var t in root.GetComponentsInChildren<Transform>(true))
{
if (t.name == "\u76d6\u5b50")
{
processorCover = t.GetComponent<SpriteRenderer>();
if (processorCover != null)
Debug.Log("[SalesManager] Found processor cover");
break;
}
}
}
/// <summary>
/// 进入处理器深入模式:面板放大右移 → 盖子淡出 → 启动旋钮和辉光管
/// </summary>
public IEnumerator EnterProcessorDeepMode()
{
if (_isProcessorDeepMode)
{
if (_deepModeSequence != null && _deepModeSequence.IsActive() && _deepModeSequence.IsPlaying())
yield return _deepModeSequence.WaitForCompletion();
yield break;
}
if (saleView == null)
{
Debug.LogError("[SalesManager] SaleView is null, cannot enter processor deep mode", this);
yield break;
}
_isProcessorDeepMode = true;
yield return EnterProcessorDeepModeRoutine();
}
private IEnumerator EnterProcessorDeepModeRoutine()
{
_deepModeSequence?.Kill();
var viewTransform = saleView.transform;
_saleViewOriginalPos = viewTransform.localPosition;
_saleViewOriginalScale = viewTransform.localScale;
// Activate cover so we can fade it out
if (processorCover != null)
{
processorCover.gameObject.SetActive(true);
var c = processorCover.color;
processorCover.color = new Color(c.r, c.g, c.b, 1f);
}
// Build animation sequence
_deepModeSequence = DOTween.Sequence();
// Step 1: Scale up + shift X
Vector3 targetPos = _saleViewOriginalPos + new Vector3(deepModeOffsetX, 0f, 0f);
Vector3 targetScale = _saleViewOriginalScale * deepModeScale;
_deepModeSequence.Append(
viewTransform.DOLocalMove(targetPos, deepModeTransitionDuration).SetEase(Ease.OutCubic)
);
_deepModeSequence.Join(
viewTransform.DOScale(targetScale, deepModeTransitionDuration).SetEase(Ease.OutCubic)
);
// Step 2: Fade out cover
if (processorCover != null)
{
_deepModeSequence.Append(
processorCover.DOFade(0f, coverFadeDuration).SetEase(Ease.InQuad)
);
_deepModeSequence.AppendCallback(() =>
{
processorCover.gameObject.SetActive(false);
});
}
// Step 3: Enable knob + glow tube
_deepModeSequence.AppendCallback(() =>
{
if (glowTubeEffect != null) glowTubeEffect.gameObject.SetActive(true);
if (knobController != null)
{
knobController.gameObject.SetActive(true);
knobController.Reset();
}
Debug.Log("[SalesManager] Processor deep mode entered");
});
yield return _deepModeSequence.WaitForCompletion();
}
/// <summary>
/// 退出处理器深入模式:恢复面板位置/缩放、恢复盖子
/// </summary>
public IEnumerator ExitProcessorDeepMode()
{
if (!_isProcessorDeepMode)
{
if (_deepModeSequence != null && _deepModeSequence.IsActive() && _deepModeSequence.IsPlaying())
yield return _deepModeSequence.WaitForCompletion();
yield break;
}
_isProcessorDeepMode = false;
yield return ExitProcessorDeepModeRoutine();
}
private IEnumerator ExitProcessorDeepModeRoutine()
{
_deepModeSequence?.Kill();
if (knobController != null) knobController.gameObject.SetActive(false);
if (glowTubeEffect != null) glowTubeEffect.gameObject.SetActive(false);
_deepModeSequence = DOTween.Sequence();
// Restore cover
if (processorCover != null)
{
processorCover.gameObject.SetActive(true);
var c = processorCover.color;
processorCover.color = new Color(c.r, c.g, c.b, 0f);
_deepModeSequence.Append(
processorCover.DOFade(1f, coverFadeDuration).SetEase(Ease.OutQuad)
);
}
// Restore position and scale
var viewTransform = saleView.transform;
_deepModeSequence.Append(
viewTransform.DOLocalMove(_saleViewOriginalPos, deepModeTransitionDuration).SetEase(Ease.InOutCubic)
);
_deepModeSequence.Join(
viewTransform.DOScale(_saleViewOriginalScale, deepModeTransitionDuration).SetEase(Ease.InOutCubic)
);
_deepModeSequence.AppendCallback(() =>
{
Debug.Log("[SalesManager] Processor deep mode exited");
});
yield return _deepModeSequence.WaitForCompletion();
}
#endregion
}
}