Files
bansonic_beta_main/Assets/scripts/gamePlay_gameplay/NotePool.cs
T

398 lines
16 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.Generic;
using UnityEngine;
using GamePlay; // for PoolItem
using System.Collections;
public class NotePool : MonoBehaviour
{
public static NotePool Instance { get; private set; }
public GameObject[] notePrefabs; // Ԥɫ
public GameObject[] holdNotePrefabs; // ƬԤ
public GameObject[] holdNoteEndPrefabs; // βרóԤѡ
public GameObject[] startNotePrefabs; // startƬԤ
private int poolSize = 24; // ÿ͵شС (increased)
private int maxPoolSize = 120; // ص (increased)
// ɫĿӳÿ switch
private Dictionary<string, int> colorIndexMap;
private Dictionary<int, Stack<GameObject>> notePools; //
private Dictionary<int, Stack<GameObject>> holdNotePools; // Ƭζ
private Dictionary<int, Stack<GameObject>> holdNoteEndPools; // β
private Dictionary<int, Stack<GameObject>> startNotePools; // startƬζ
// ڲ㼶֯еĶ󣬱ڵԺ͹
private Transform notePoolContainer;
private Transform holdPoolContainer;
private Transform startPoolContainer;
// ƵĬϹر
public bool verboseLogging = false;
[Header("Prewarm Settings")]
[Tooltip("If true, the pool will be prewarmed across multiple frames to avoid a large GC/instantiation spike at scene load.")]
public bool prewarmOnStart = true;
[Tooltip("Number of instantiated pool items to create per frame during prewarming.")]
// Raised default so heavy pools finish prewarming faster and reduce hitch at first spawn.
public int prewarmPerFrame = 200;
// internal prewarm coroutine handle
private Coroutine prewarmCoroutine = null;
// Expose whether prewarm has completed
public bool IsPrewarmed => prewarmCoroutine == null;
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
return;
}
// ʼɫӳ
colorIndexMap = new Dictionary<string, int>
{
{ "red", 0 },
{ "green", 1 },
{ "yellow", 2 },
{ "purple", 3 },
{ "blue", 4 }
};
//
notePoolContainer = new GameObject("_NotePool_Notes").transform;
notePoolContainer.SetParent(transform, false);
holdPoolContainer = new GameObject("_NotePool_HoldSegments").transform;
holdPoolContainer.SetParent(transform, false);
startPoolContainer = new GameObject("_NotePool_StartSegments").transform;
startPoolContainer.SetParent(transform, false);
notePools = new Dictionary<int, Stack<GameObject>>();
holdNotePools = new Dictionary<int, Stack<GameObject>>();
holdNoteEndPools = new Dictionary<int, Stack<GameObject>>();
startNotePools = new Dictionary<int, Stack<GameObject>>();
int colorCount = Mathf.Max(1, notePrefabs != null ? notePrefabs.Length : 0);
for (int i = 0; i < colorCount; i++)
{
notePools[i] = new Stack<GameObject>();
holdNotePools[i] = new Stack<GameObject>();
holdNoteEndPools[i] = new Stack<GameObject>();
startNotePools[i] = new Stack<GameObject>();
}
// minimal immediate entries to ensure safety (one each)
for (int i = 0; i < colorCount; i++)
{
if (notePrefabs != null && i < notePrefabs.Length && notePrefabs[i] != null)
AddToPool(notePools[i], notePrefabs[i], notePoolContainer);
if (startNotePrefabs != null && i < startNotePrefabs.Length && startNotePrefabs[i] != null)
AddToPool(startNotePools[i], startNotePrefabs[i], startPoolContainer);
if (holdNotePrefabs != null && i < holdNotePrefabs.Length && holdNotePrefabs[i] != null)
AddToPool(holdNotePools[i], holdNotePrefabs[i], holdPoolContainer);
if (holdNoteEndPrefabs != null && i < holdNoteEndPrefabs.Length && holdNoteEndPrefabs[i] != null)
AddToPool(holdNoteEndPools[i], holdNoteEndPrefabs[i], holdPoolContainer);
}
if (prewarmOnStart)
{
prewarmCoroutine = StartCoroutine(PrewarmCoroutine(colorCount));
}
// Also ensure particle and judge prefab warm-up if AnimationController exists in scene.
var anim = AnimationController.Global ?? FindObjectOfType<AnimationController>();
if (anim != null)
{
anim.PrewarmParticles(2);
if (verboseLogging) Debug.Log("NotePool: requested AnimationController prewarm");
}
}
private IEnumerator PrewarmCoroutine(int colorCount)
{
// Calculate targets
int noteTarget = poolSize; // per color
int startTarget = poolSize;
int holdTarget = poolSize * 5;
int endTarget = poolSize * 5;
while (true)
{
int createdThisFrame = 0;
bool allReached = true;
for (int i = 0; i < colorCount; i++)
{
// note pool
while (notePools[i].Count < noteTarget && createdThisFrame < prewarmPerFrame)
{
if (notePrefabs != null && i < notePrefabs.Length && notePrefabs[i] != null)
AddToPool(notePools[i], notePrefabs[i], notePoolContainer);
createdThisFrame++;
}
if (notePools[i].Count < noteTarget) allReached = false;
// start pool
while (startNotePools[i].Count < startTarget && createdThisFrame < prewarmPerFrame)
{
if (startNotePrefabs != null && i < startNotePrefabs.Length && startNotePrefabs[i] != null)
AddToPool(startNotePools[i], startNotePrefabs[i], startPoolContainer);
createdThisFrame++;
}
if (startNotePools[i].Count < startTarget) allReached = false;
// hold middle pool
while (holdNotePools[i].Count < holdTarget && createdThisFrame < prewarmPerFrame)
{
if (holdNotePrefabs != null && i < holdNotePrefabs.Length && holdNotePrefabs[i] != null)
AddToPool(holdNotePools[i], holdNotePrefabs[i], holdPoolContainer);
createdThisFrame++;
}
if (holdNotePools[i].Count < holdTarget) allReached = false;
// hold end pool
while (holdNoteEndPools[i].Count < endTarget && createdThisFrame < prewarmPerFrame)
{
if (holdNoteEndPrefabs != null && i < holdNoteEndPrefabs.Length && holdNoteEndPrefabs[i] != null)
AddToPool(holdNoteEndPools[i], holdNoteEndPrefabs[i], holdPoolContainer);
else if (holdNotePrefabs != null && i < holdNotePrefabs.Length && holdNotePrefabs[i] != null)
AddToPool(holdNoteEndPools[i], holdNotePrefabs[i], holdPoolContainer);
createdThisFrame++;
}
if (holdNoteEndPools[i].Count < endTarget) allReached = false;
if (createdThisFrame >= prewarmPerFrame)
break;
}
if (allReached) break;
yield return null;
}
prewarmCoroutine = null;
}
private GameObject GetObjectFromPool(Stack<GameObject> pool, GameObject prefab, Transform container)
{
if (pool == null)
return InstantiateAndPrepare(prefab);
if (pool.Count > 0)
{
GameObject obj = pool.Pop();
if (obj == null)
{
// жѱ٣
obj = InstantiateAndPrepare(prefab);
}
else
{
// Żʹ PoolItem ʶǷͬһ prefab
GamePlay.PoolItem pi = obj.GetComponent<GamePlay.PoolItem>();
if (pi == null || prefab == null || pi.prefabName != prefab.name)
{
// ٴ͵Ķ滻Ϊȷ prefab ʵ
if (pi == null && verboseLogging) Debug.LogWarning("PoolItem missing on pooled object, replacing.");
Destroy(obj);
obj = InstantiateAndPrepare(prefab);
}
}
obj.transform.SetParent(null);
obj.SetActive(true);
// mark as taken from pool
GamePlay.PoolItem takenPi = obj.GetComponent<GamePlay.PoolItem>();
if (takenPi != null) takenPi.inPool = false;
return obj;
}
else
{
// ؿʱֱʵ
GameObject obj = InstantiateAndPrepare(prefab);
obj.SetActive(true);
return obj;
}
}
private GameObject InstantiateAndPrepare(GameObject prefab)
{
if (prefab == null) return null;
GameObject obj = Instantiate(prefab);
GamePlay.PoolItem pi = obj.GetComponent<GamePlay.PoolItem>();
if (pi == null) pi = obj.AddComponent<GamePlay.PoolItem>();
pi.prefabName = prefab.name;
pi.inPool = false;
return obj;
}
private void ReturnObjectToPool(Stack<GameObject> pool, GameObject obj, Transform container)
{
if (obj == null || pool == null) return; // ֹ
// ظڳڣֱӺ
GamePlay.PoolItem pi = obj.GetComponent<GamePlay.PoolItem>();
if (pi != null && pi.inPool) return;
// ƶñ任
obj.transform.SetParent(container, false);
obj.transform.localPosition = Vector3.zero;
obj.transform.localRotation = Quaternion.identity;
obj.transform.localScale = Vector3.one;
// ΪǼ״̬Ա㸴
obj.SetActive(false);
// Ϊڳ
if (pi != null) pi.inPool = true;
if (pool.Count < maxPoolSize)
{
pool.Push(obj);
}
else
{
// ٶԽʡڴ
Destroy(obj);
}
}
private void AddToPool(Stack<GameObject> pool, GameObject prefab, Transform container)
{
if (prefab == null) return;
if (pool.Count < maxPoolSize)
{
GameObject obj = Instantiate(prefab, container);
// ensure PoolItem exists and is marked in pool
GamePlay.PoolItem pi = obj.GetComponent<GamePlay.PoolItem>() ?? obj.AddComponent<GamePlay.PoolItem>();
pi.prefabName = prefab.name;
pi.inPool = true;
obj.SetActive(false);
pool.Push(obj);
}
}
public GameObject GetNote(string color)
{
int colorIndex = GetColorIndexFromName(color);
return GetObjectFromPool(notePools[colorIndex], notePrefabs[colorIndex], notePoolContainer);
}
public GameObject GetStartNote(string color)
{
int colorIndex = GetColorIndexFromName(color);
return GetObjectFromPool(startNotePools[colorIndex], startNotePrefabs[colorIndex], startPoolContainer);
}
public GameObject GetHoldNoteSegment(string color)
{
int colorIndex = GetColorIndexFromName(color);
return GetObjectFromPool(holdNotePools[colorIndex], holdNotePrefabs[colorIndex], holdPoolContainer);
}
// βרóػȡβƬΣδβԤ򽵼ΪжΣ
public GameObject GetHoldNoteEndSegment(string color)
{
int colorIndex = GetColorIndexFromName(color);
GameObject prefab = (holdNoteEndPrefabs != null && holdNoteEndPrefabs.Length > colorIndex && holdNoteEndPrefabs[colorIndex] != null)
? holdNoteEndPrefabs[colorIndex]
: holdNotePrefabs[colorIndex];
return GetObjectFromPool(holdNoteEndPools[colorIndex], prefab, holdPoolContainer);
}
public void ReturnNote(GameObject note, string color)
{
if (note == null) return;
NoteController noteScript = note.GetComponent<NoteController>();
if (noteScript != null)
{
noteScript.ResetState();
}
int idx = GetColorIndexFromName(color);
ReturnObjectToPool(notePools[idx], note, notePoolContainer);
}
public void ReturnStartNote(GameObject startNote, string color)
{
if (startNote == null) return;
if (string.IsNullOrEmpty(color))
{
if (verboseLogging) Debug.LogWarning($" 黹 StartNote ʱ color Ϊգ: {startNote.name}");
Destroy(startNote);
return;
}
HoldNote holdNote = startNote.GetComponent<HoldNote>();
if (holdNote != null)
{
holdNote.ResetState(); // ȷ״̬
}
int idx = GetColorIndexFromName(color);
ReturnObjectToPool(startNotePools[idx], startNote, startPoolContainer);
}
// 黹βƬ
public void ReturnHoldNoteEndSegment(GameObject holdNoteEnd, string color)
{
if (holdNoteEnd == null) return;
if (string.IsNullOrEmpty(color))
{
if (verboseLogging) Debug.LogWarning($" 黹 HoldNoteEndSegment ʱ color Ϊգ: {holdNoteEnd.name}");
Destroy(holdNoteEnd);
return;
}
HoldNote holdNoteScript = holdNoteEnd.GetComponent<HoldNote>();
if (holdNoteScript != null)
{
holdNoteScript.ResetState(); // ȷ״̬
}
int idx = GetColorIndexFromName(color);
ReturnObjectToPool(holdNoteEndPools[idx], holdNoteEnd, holdPoolContainer);
}
public void ReturnHoldNoteSegment(GameObject holdNote, string color)
{
if (holdNote == null) return;
if (string.IsNullOrEmpty(color))
{
if (verboseLogging) Debug.LogWarning($" 黹 HoldNoteSegment ʱ color Ϊգ: {holdNote.name}");
Destroy(holdNote);
return;
}
HoldNote holdNoteScript = holdNote.GetComponent<HoldNote>();
if (holdNoteScript != null)
{
holdNoteScript.ResetState(); // ȷ״̬
}
int idx = GetColorIndexFromName(color);
ReturnObjectToPool(holdNotePools[idx], holdNote, holdPoolContainer);
}
private int GetColorIndexFromName(string colorName)
{
if (string.IsNullOrEmpty(colorName)) return 0;
if (colorIndexMap != null && colorIndexMap.TryGetValue(colorName, out int idx)) return idx;
if (verboseLogging) Debug.LogError($"δʶɫ: {colorName}Ĭʹúɫ");
return 0;
}
}
public class PoolItem : MonoBehaviour
{
public string prefabName;
public bool inPool;
}