Files
MRFishingMaster/Assets/_FishingMaster/Scripts/Statue/DriftBottleManager.cs

508 lines
14 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;
using System.Collections.Generic;
using DragonLi.Core;
using FluffyUnderware.Curvy;
using UnityEngine;
using Random = UnityEngine.Random;
/// <summary>
/// 雕像数据
/// </summary>
[Serializable]
public class StatueData
{
public int statueIndex; // 雕像序号0-2
public string statueName; // 雕像名称
public int[] requiredFragments; // 所需碎片序号数组
public bool isCompleted; // 是否已完成
public StatueAssembler assembler; // 石像拼凑器引用
}
/// <summary>
/// 漂流瓶管理器 - 管理海底雕像事件
/// 每60秒生成3个漂流瓶玩家收集碎片集齐3个雕像
/// </summary>
public class DriftBottleManager : MonoBehaviour
{
public static DriftBottleManager Instance { get; private set; }
[Header("生成配置")]
[Tooltip("生成间隔(秒)")]
public float spawnInterval = 60f;
[Tooltip("每次生成数量")]
public int spawnCountPerWave = 3;
[Tooltip("漂流瓶预制体")]
public GameObject driftBottlePrefab;
[Header("碎片飞行效果")]
[Tooltip("碎片飞行特效预制体")]
public GameObject fragmentFlyPrefab;
[Header("雕像配置")]
[Tooltip("雕像总数")]
public int totalStatues = 3;
[Tooltip("每个雕像所需碎片数")]
public int fragmentsPerStatue = 10;
[Tooltip("雕像名称列表")]
public string[] statueNames = { "龙王雕像", "美人鱼雕像", "三叉戟雕像" };
[Header("石像位置")]
[Tooltip("3个石像的位置Transform")]
public StatueAssembler[] statueTransforms;
[Tooltip("石像显示位置偏移")]
public Vector3 statueShowOffset = new Vector3(0, 0, 5f);
[Header("调试")]
public bool debugMode = false;
// 私有变量
private float spawnTimer = 0f;
private bool isEventActive = false;
private int currentFragmentIndex = 0; // 当前碎片序号0-29
private HashSet<int> collectedFragments = new HashSet<int>(); // 已收集的碎片
private List<StatueData> statues = new List<StatueData>(); // 雕像数据列表
private HashSet<DriftBottle> activeBottles = new HashSet<DriftBottle>(); // 活跃漂流瓶
private bool allStatuesCompleted = false;
// 事件
public event Action<int, int> OnFragmentCollected; // 参数:碎片序号,雕像序号
public event Action<int> OnStatueCompleted; // 参数:雕像序号
public event Action OnAllStatuesCompleted; // 所有雕像完成
public event Action<int, int, int> OnSpawnWave; // 参数:当前波次,总波次,生成数量
private void Awake()
{
Instance = this;
}
private void Start()
{
InitializeStatues();
}
private void Update()
{
if (!isEventActive || allStatuesCompleted) return;
spawnTimer += Time.deltaTime;
if (spawnTimer >= spawnInterval)
{
spawnTimer = 0f;
TrySpawnBottles();
}
// 调试快捷键
if (debugMode && Input.GetKeyDown(KeyCode.B))
{
TrySpawnBottles();
}
}
/// <summary>
/// 初始化雕像数据
/// </summary>
private void InitializeStatues()
{
statues.Clear();
int totalFragments = totalStatues * fragmentsPerStatue;
for (int i = 0; i < totalStatues; i++)
{
StatueData statue = new StatueData
{
statueIndex = i,
statueName = statueNames.Length > i ? statueNames[i] : $"雕像{i + 1}",
requiredFragments = new int[fragmentsPerStatue],
isCompleted = false
};
// 填充所需碎片序号
for (int j = 0; j < fragmentsPerStatue; j++)
{
statue.requiredFragments[j] = i * fragmentsPerStatue + j;
}
statues.Add(statue);
}
// 注册石像拼凑器
if (statueTransforms != null)
{
foreach (StatueAssembler assembler in statueTransforms)
{
if (assembler != null)
{
SetStatueAssembler(assembler.statueIndex, assembler);
}
}
}
Debug.Log($"[DriftBottleManager] 初始化完成,共{totalStatues}个雕像,{totalFragments}个碎片");
}
/// <summary>
/// 设置石像拼凑器
/// </summary>
public void SetStatueAssembler(int statueIndex, StatueAssembler assembler)
{
if (statueIndex >= 0 && statueIndex < statues.Count)
{
statues[statueIndex].assembler = assembler;
// 注册完成事件
assembler.OnStatueCompleted += OnStatueAssembleCompleted;
}
}
/// <summary>
/// 启动雕像事件
/// </summary>
public void StartStatueEvent()
{
if (allStatuesCompleted)
{
Debug.Log("[DriftBottleManager] 所有雕像已集齐,事件不再启动");
return;
}
isEventActive = true;
spawnTimer = 0f;
Debug.Log("[DriftBottleManager] 海底雕像事件已启动!");
// 立即生成第一波
//TrySpawnBottles();
}
/// <summary>
/// 停止雕像事件
/// </summary>
public void StopStatueEvent()
{
isEventActive = false;
Debug.Log("[DriftBottleManager] 海底雕像事件已停止");
}
/// <summary>
/// 尝试生成漂流瓶
/// </summary>
private void TrySpawnBottles()
{
if (driftBottlePrefab == null)
{
Debug.LogError("[DriftBottleManager] 漂流瓶预制体未设置!");
return;
}
// 计算还需要生成多少碎片
int totalFragments = totalStatues * fragmentsPerStatue;
int remainingFragments = totalFragments - currentFragmentIndex;
if (remainingFragments <= 0)
{
Debug.Log("[DriftBottleManager] 所有碎片已生成完毕");
return;
}
// 计算本次生成数量
int spawnCount = Mathf.Min(spawnCountPerWave, remainingFragments);
// 获取路径
CurvySpline[] availablePaths = GameInit.Ins.GetIndexPath(3);
if (availablePaths.Length == 0)
{
Debug.LogError("[DriftBottleManager] 没有可用的路径!");
return;
}
// 生成漂流瓶,分散在不同路径
for (int i = 0; i < spawnCount; i++)
{
int fragIndex = currentFragmentIndex++;
int statueIdx = fragIndex / fragmentsPerStatue;
// 选择路径(分散在不同路径)
CurvySpline path = availablePaths[i % availablePaths.Length];
SpawnBottle(path, fragIndex, statueIdx);
}
// 触发事件
int currentWave = (currentFragmentIndex - 1) / spawnCountPerWave + 1;
int totalWaves = Mathf.CeilToInt((float)totalFragments / spawnCountPerWave);
OnSpawnWave?.Invoke(currentWave, totalWaves, spawnCount);
}
/// <summary>
/// 生成单个漂流瓶
/// </summary>
private void SpawnBottle(CurvySpline path, int fragIndex, int statueIdx)
{
if (driftBottlePrefab == null) return;
if (path == null)
{
Debug.LogError("[DriftBottleManager] SpawnBottle 失败path 为 null");
return;
}
// 使用路径对象的位置作为生成点(路径生成器已将路径对象设置在正确位置)
Vector3 spawnPos = path.transform.position;
// 在路径起点生成漂流瓶
GameObject bottleGO = Instantiate(driftBottlePrefab, spawnPos, Quaternion.identity);
DriftBottle bottle = bottleGO.GetComponent<DriftBottle>();
if (bottle != null)
{
bottle.Init(path, fragIndex, statueIdx);
}
}
/// <summary>
/// 获取可用路径列表 - 每次生成都是全新的随机路径
/// </summary>
/// <summary>
/// 漂流瓶被捕获 - 创建飞行碎片效果
/// </summary>
public void OnBottleCaptured(DriftBottle bottle, int fragIndex, int statueIdx)
{
if (collectedFragments.Contains(fragIndex))
{
return;
}
// 记录收集
collectedFragments.Add(fragIndex);
// 获取漂流瓶位置
Vector3 bottlePosition = bottle != null ? bottle.transform.position : Vector3.zero;
// 创建碎片飞行效果
CreateFragmentFlyEffect(bottlePosition, fragIndex, statueIdx);
// 触发事件
OnFragmentCollected?.Invoke(fragIndex, statueIdx);
}
/// <summary>
/// 创建碎片飞行效果
/// </summary>
private void CreateFragmentFlyEffect(Vector3 startPos, int fragIndex, int statueIdx)
{
if (fragmentFlyPrefab == null)
{
// 如果没有飞行特效预制体,直接添加到石像
AddFragmentToStatue(fragIndex, statueIdx);
return;
}
// 获取目标石像
Transform targetStatue = GetStatueTransform(statueIdx);
if (targetStatue == null)
{
return;
}
// 实例化飞行碎片
GameObject flyObj = Instantiate(fragmentFlyPrefab, startPos, Quaternion.identity);
FragmentFlyEffect flyEffect = flyObj.GetComponent<FragmentFlyEffect>();
if (flyEffect != null)
{
flyEffect.Initialize(startPos, targetStatue, fragIndex, statueIdx, OnFragmentArrived);
}
else
{
// 如果没有FragmentFlyEffect组件直接添加
AddFragmentToStatue(fragIndex, statueIdx);
Destroy(flyObj);
}
}
/// <summary>
/// 碎片到达回调
/// </summary>
private void OnFragmentArrived(int fragIndex, int statueIdx)
{
AddFragmentToStatue(fragIndex, statueIdx);
}
/// <summary>
/// 添加碎片到石像
/// </summary>
private void AddFragmentToStatue(int fragIndex, int statueIdx)
{
if (statueIdx >= statues.Count) return;
StatueData statue = statues[statueIdx];
if (statue.assembler != null)
{
statue.assembler.AddFragment(fragIndex);
}
else
{
Debug.LogWarning($"[DriftBottleManager] 石像{statueIdx}的拼凑器未设置!");
}
}
/// <summary>
/// 获取石像位置
/// </summary>
private Transform GetStatueTransform(int statueIdx)
{
if (statueTransforms != null && statueIdx < statueTransforms.Length && statueTransforms[statueIdx] != null)
{
return statueTransforms[statueIdx].transform;
}
// 如果没有设置,使用默认位置
if (GameInit.Ins?.playerCam != null)
{
Vector3 playerPos = GameInit.Ins.playerCam.transform.position;
Vector3 playerForward = GameInit.Ins.playerCam.transform.forward;
// 在玩家前方显示石像
float offsetX = (statueIdx - 1) * 5f; // 三个石像水平分布
Vector3 statuePos = playerPos + playerForward * statueShowOffset.z + Vector3.right * offsetX;
statuePos.y += statueShowOffset.y;
GameObject tempObj = new GameObject($"StatueTarget_{statueIdx}");
tempObj.transform.position = statuePos;
return tempObj.transform;
}
return null;
}
/// <summary>
/// 石像拼凑完成回调
/// </summary>
private void OnStatueAssembleCompleted(int statueIdx)
{
if (statueIdx >= statues.Count) return;
statues[statueIdx].isCompleted = true;
Debug.Log($"[DriftBottleManager] {statues[statueIdx].statueName} 拼凑完成!");
// 触发事件
OnStatueCompleted?.Invoke(statueIdx);
// 检查是否所有雕像都完成了
CheckAllStatuesCompleted();
}
/// <summary>
/// 检查是否所有雕像都完成
/// </summary>
private void CheckAllStatuesCompleted()
{
foreach (var statue in statues)
{
if (!statue.isCompleted)
return;
}
// 所有雕像完成
allStatuesCompleted = true;
isEventActive = false;
Debug.Log("[DriftBottleManager] 所有雕像已集齐!海底雕像事件结束!");
OnAllStatuesCompleted?.Invoke();
// 播放完成音效
GameManager.Ins?.PlaySound2D("雕像事件完成");
}
#region
public void RegisterBottle(DriftBottle bottle)
{
if (bottle != null)
activeBottles.Add(bottle);
}
public void UnregisterBottle(DriftBottle bottle)
{
if (bottle != null)
activeBottles.Remove(bottle);
}
#endregion
#region
/// <summary>
/// 获取已收集碎片数量
/// </summary>
public int GetCollectedFragmentCount()
{
return collectedFragments.Count;
}
/// <summary>
/// 获取指定雕像的收集进度
/// </summary>
public int GetStatueProgress(int statueIdx)
{
if (statueIdx >= statues.Count) return 0;
if (statues[statueIdx].assembler != null)
{
return statues[statueIdx].assembler.GetProgress();
}
return 0;
}
/// <summary>
/// 获取指定雕像是否完成
/// </summary>
public bool IsStatueCompleted(int statueIdx)
{
if (statueIdx >= statues.Count) return false;
return statues[statueIdx].isCompleted;
}
/// <summary>
/// 获取当前目标雕像序号
/// </summary>
public int GetCurrentTargetStatueIndex()
{
for (int i = 0; i < statues.Count; i++)
{
if (!statues[i].isCompleted)
return i;
}
return -1; // 全部完成
}
/// <summary>
/// 获取雕像数据列表
/// </summary>
public List<StatueData> GetStatues() => statues;
/// <summary>
/// 是否所有雕像都完成
/// </summary>
public bool IsAllStatuesCompleted() => allStatuesCompleted;
/// <summary>
/// 获取下一次生成倒计时
/// </summary>
public float GetNextSpawnCountdown()
{
if (!isEventActive || allStatuesCompleted) return -1;
return Mathf.Max(0, spawnInterval - spawnTimer);
}
#endregion
}