Files
MRFishingMaster/Assets/_FishingMaster/Scripts/Fish/Base/FishSpawner.cs

303 lines
7.0 KiB
C#

using System.Collections;
using System.Collections.Generic;
using DragonLi.Core;
using UnityEngine;
using FluffyUnderware.Curvy;
using Unity.VisualScripting;
public class FishSpawner : MonoBehaviour
{
public static FishSpawner Instance;
public readonly HashSet<Fish> AliveFishes = new HashSet<Fish>();
[Header("Spawn Rule")]
public float checkInterval = 1f;
[Header("Prefabs")]
public GameObject fishGroupPrefab;
public float fishSpeed;
public int maxFishCount;
private Dictionary<FishType, FishData> fishDataMap = new();
private Dictionary<FishType, FishSpawnState> spawnStates = new();
private float timer;
// 高价值互斥锁
private bool highFishAlive;
private bool angelHorseAlive;
private const float MIX_MIDDLE_BASE_RATE = 0.3f;
void Awake()
{
Instance = this;
}
#region Fish Register
public void RegisterFish(Fish fish)
{
if (fish == null) return;
AliveFishes.Add(fish);
}
public void UnregisterFish(Fish fish)
{
if (fish == null) return;
AliveFishes.Remove(fish);
}
#endregion
void Start()
{
fishSpeed = 1;
maxFishCount = 200;
}
void Update()
{
timer += Time.deltaTime;
if (timer >= checkInterval)
{
timer = 0f;
TrySpawnFish();
}
}
public void LoadFishData()
{
// ⚠️ 实际项目中从配置表加载
foreach (var item in GameManager.Ins.FishDataDic)
{
fishDataMap.Add((FishType)item.Key, item.Value);
}
foreach (var kv in fishDataMap)
spawnStates[kv.Key] = new FishSpawnState();
}
void TrySpawnFish()
{
if(AliveFishes.Count>=maxFishCount)
return;
FishData data = GetFishByProbability();
if (data == null) return;
FishType type = (FishType)data.FishId;
FishValueLevel level = FishValueHelper.GetValueLevel(data);
// ⛔ 特高价值唯一
if (level == FishValueLevel.UltraHigh && angelHorseAlive)
return;
// ⛔ 高价值互斥
if (level == FishValueLevel.High && highFishAlive)
return;
FishSpawnState state = spawnStates[type];
if (Time.time < state.CooldownEndTime)
return;
int count = GetFishCount(data);
CurvySpline spline = GetSplineByType();
// ====== 生成 ======
if (count > 1)
{
if (level == FishValueLevel.Low)
SpawnLowValueGroup(data, count, spline, state);
else
SpawnNormalGroup(data, count, spline);
}
else
{
SpawnSingleFish(data, spline);
}
state.CooldownEndTime = Time.time + data.IntervalTime;
if (level == FishValueLevel.High)
highFishAlive = true;
if (level == FishValueLevel.UltraHigh)
angelHorseAlive = true;
}
#region Spawn Logic
void SpawnLowValueGroup(
FishData data,
int count,
CurvySpline spline,
FishSpawnState state)
{
List<FishData> list = new();
for (int i = 0; i < count; i++)
list.Add(data);
float rate = MIX_MIDDLE_BASE_RATE;
if (state.NoMiddleFishCount >= 3)
rate *= 2f;
bool hasMiddle = false;
if (Random.value < rate)
{
FishData middle = GetRandomMiddleValueFish();
if (middle != null)
{
list.Add(middle);
hasMiddle = true;
}
}
state.NoMiddleFishCount = hasMiddle ? 0 : state.NoMiddleFishCount + 1;
SpawnGroup(list, spline);
}
void SpawnNormalGroup(FishData data, int count, CurvySpline spline)
{
List<FishData> list = new();
for (int i = 0; i < count; i++)
list.Add(data);
SpawnGroup(list, spline);
}
void SpawnGroup(List<FishData> list, CurvySpline spline)
{
GameObject go = Instantiate(fishGroupPrefab);
go.GetComponent<FishGroup>().Init(list, spline);
}
void SpawnSingleFish(FishData data, CurvySpline spline)
{
GameObject go = Instantiate(GameManager.Ins.GetFishPre(data.FishId-1));
go.GetComponent<Fish>().Init(data, spline);
}
public void CreateTypeFish(FishType fishType)
{
FishData data= fishDataMap[fishType];
CurvySpline spline = GetSplineByType();
SpawnSingleFish(data,spline);
FishValueLevel level = FishValueHelper.GetValueLevel(data);
if (level == FishValueLevel.High)
highFishAlive = true;
if (level == FishValueLevel.UltraHigh)
angelHorseAlive = true;
}
#endregion
#region Utils
FishData GetFishByProbability()
{
float total = 0;
foreach (var d in fishDataMap.Values)
total += d.Odds;
float r = Random.Range(0, total);
float cur = 0;
foreach (var d in fishDataMap.Values)
{
cur += d.Odds;
if (r <= cur)
return d;
}
return null;
}
int GetFishCount(FishData data)
{
if (data.FishCount == null || data.FishCount.Length < 2)
return 1;
return Random.Range(data.FishCount[0], data.FishCount[1] + 1);
}
CurvySpline GetSplineByType()
{
return GameInit.Ins.GetFishPath();
}
FishData GetRandomMiddleValueFish()
{
List<FishData> list = new();
foreach (var d in fishDataMap.Values)
{
if (FishValueHelper.GetValueLevel(d) == FishValueLevel.Middle)
list.Add(d);
}
return list.Count == 0 ? null : list[Random.Range(0, list.Count)];
}
public void OnHighFishRemoved(FishValueLevel level)
{
if (level == FishValueLevel.High)
highFishAlive = false;
if (level == FishValueLevel.UltraHigh)
angelHorseAlive = false;
}
#endregion
public int GetFishCount(FishType fishType)
{
int count = 0;
foreach (var item in AliveFishes)
{
if((FishType)item.Data.FishId==fishType)
count++;
}
return count;
}
public void SetFishSpeed(float curTime)
{
fishSpeed = 2;
foreach (var item in AliveFishes)
{
item.SetSpeed();
}
CoroutineTaskManager.Instance.WaitSecondTodo(() =>
{
fishSpeed = 1;
foreach (var item in AliveFishes)
{
item.SetSpeed();
}
}, curTime);
}
public IEnumerator GetAllFishes()
{
List<Fish> allFish = new List<Fish>();
foreach (var item in AliveFishes)
{
allFish.Add(item);
}
foreach (var fish in allFish)
{
fish.DieFish();
yield return new WaitForSeconds(0.01f);
}
}
}