Notifications
Article
Pool Manager come gestore di coda
Published 6 months ago
89
0
Caso concreto, voice system
Carissimi, nello sviluppo del mio gioco ho avuto la necessità di implementare una “voce guida” per il mio pilota di aereo. Avrei potuto costruire, come del resto ho fatto per la musica, un manager che gestisse tutte le audioclip e fosse attivabile da qualsiasi punto del codice. Tuttavia avendo a disposizione un PoolManager che gestisce i pool limitando il numero delle istanze (http://docs.poolmanager.path-o-logical.com/) ho preferito usare questo come gestore di coda. Nel caso di una voce guida, ovviamente, le voci NON devono sovrapporsi e in questo l’allocazione di al massimo una istanza fa al caso. Ho quindi implementato una coda “per oggetti”, per prefabs. Nel pool “voices” metto tutte le voci che mi serviranno a runtime limitando ad una istanza per ogni tipo di voce. L’evento che ha bisogno di riprodurre la voce non fa altro che instanziare (spawn) il giusto prefab dal pool. Ma la voce non parte subito. Avevo infatti la necessità che le voci fossero riprodotte con un certo ritardo dopo l’evento che le richiedeva (esplosioni, danni, etc). Pertanto, una volta instanziato il prefab, la voce attende quanto indicato e poi alza un flag “CanPlay” nello script, in pratica va a mettersi “in coda” nel VoiceManager della scena. Il VoiceManager cerca tutti gli oggetti nella scena con un dato TAG, verifica che nessuno di questi sia in riproduzione (flag “PlayStarted” = false), quindi la voce guida è muta: è possibile riprodurre il prossimo clip in coda. Basta che il VoiceManager trovi nell’array un prefab che abbia nello script il flag “CanPlay”. Attualmente mi basta trovare il primo in coda, ma nulla vieta di scegliere quello con priorità più elevata nell’audiosource. Trovato il prefab-clip da riprodurre, il VoiceManager ne disattiva il flag “CanPlay” ed attiva il flag”PlayStarted A questo punto lo script del prefab-clip inizia la riproduzione ed alla fine di questa setta il flag “PlayStared” = false e si deinstanzia chiamando l’apposita funzione del PoolManager.
Il vantaggio di questo modello di voice queue è che consente di gestire le voci e gli eventi direttamente dall’editor con un semplice script di spawning, invece di dovere scrivere codice per ogni evento che richieda voce. Caso tipico: il mio aereo è colpito, quindi instanzio un prefab esplosione che instanzia, tra i vari effetti, anche la voce guida “sei stato colpito”.
Ecco gli scripts:
VOICE SYSTEM (il voice manager), uno solo nella scena.
using System.Collections; using System.Collections.Generic; using UnityEngine;
public class VoicesSystemBehaviourScript : MonoBehaviour { private GameObject[] Voices; AudioSource MyAudioSource; GameObject MyAudioSourceGO; float CounterTime = 0.1f;
void Start() { MyAudioSource = null; MyAudioSourceGO = null; }
void Update() {
CounterTime -= Time.deltaTime; if (CounterTime > 0.0f) return;
CounterTime = 0.1f; if (MyAudioSource != null) if (MyAudioSource.isPlaying) return;
MyAudioSource = null; Voices = GameObject.FindGameObjectsWithTag("AudioVoice");
for (int i = 0; i < Voices.Length; i++) if (Voices[i].GetComponent<VoiceQueueBehaviourScript>().CanPlay) {MyAudioSource = Voices[i].GetComponent<AudioSource>(); MyAudioSource.enabled = true; MyAudioSource.Play(); Voices[i].GetComponent<VoiceQueueBehaviourScript>().PlayStarted = true; Voices[i].GetComponent<VoiceQueueBehaviourScript>().CanPlay = false; MyAudioSourceGO = Voices[i]; i= Voices.Length; break; } } }

Questo è il codice che gestisce la singola voce (il prefab nel pool)
using System.Collections; using System.Collections.Generic; using UnityEngine; using PathologicalGames;
public class VoiceQueueBehaviourScript : MonoBehaviour { public float PlayAfterSecs=1.0f; public bool CanPlay=false; public bool PlayStarted = false; public bool PlayRandom = false; public float Probability = 100.0f; private float CurrentSpawnedTime; private bool Spawned = false; private AudioSource MyAudioSource; private float PlayingTime = 0.0f; private GameObject[]Voices;
void Start() { MyAudioSource = GetComponent<AudioSource>(); }
// OnSpawned called by pool manager private void OnSpawned() { if (PlayRandom) if (Random.Range(0.0f, 100.0f) < 100.0f-Probability) {PoolManager.Pools["Voices"].Despawn(this.gameObject.transform); return; } Spawned = true; CurrentSpawnedTime = Random.Range(0.0f, 0.2f); PlayStarted = false; CanPlay = false; }
// OnDespawned called by pool manager private void OnDespawned() { Spawned = false; CurrentSpawnedTime = 10000.0f; PlayStarted = false; CanPlay = false; }
void PlayVoice() { PlayStarted = true; MyAudioSource.enabled = true; MyAudioSource.Play(); PlayingTime = 0.0f; }
int PlayingVoices(GameObject[] GOs) { int PlayingVoicesNum = 0; for (int i = 0; i < GOs.Length; i++) if (GOs[i].GetComponent<AudioSource>().isPlaying) PlayingVoicesNum++; return PlayingVoicesNum; }
void Update() { if (!Spawned) return; if (PlayStarted) { PlayingTime += Time.deltaTime; if ((!MyAudioSource.isPlaying) || (PlayingTime > 10.0f)) PoolManager.Pools["Voices"].Despawn(this.gameObject.transform); }
CurrentSpawnedTime += Time.deltaTime; if (CurrentSpawnedTime < PlayAfterSecs) return;
if (!PlayStarted) CanPlay = true; } }
Questo è lo script spawner che consente, nel nostro caso, di riprodurre una voce da qualsiasi gameobject.
using System.Collections; using System.Collections.Generic; using UnityEngine; using PathologicalGames;

public class SpawnBehaviourScript : MonoBehaviour{ public GameObject PrefabToSpawn; public Vector3 SpawnPosOffset; public bool SpawnFromPool=false; public string SpawnPool; public bool SpawnAfter = false; public float SpawnAfterSecs = 0.0f; public bool DestroyAfter=false; public float DestroyAfterSecs=2.0f;
bool Spawned = false; float CurrentTimer = 0.0f;
private void SpawnGO()
{ if (PrefabToSpawn) { if (SpawnFromPool) {Transform GO = PoolManager.Pools[SpawnPool].Spawn(PrefabToSpawn, transform.position + SpawnPosOffset, Quaternion.identity); if (DestroyAfter) PoolManager.Pools[SpawnPool].Despawn(GO, DestroyAfterSecs); } else { GameObject GO = Instantiate(PrefabToSpawn, transform.position + SpawnPosOffset, Quaternion.identity); if (DestroyAfter) Destroy(GO, DestroyAfterSecs); } } Spawned = true; }
private void OnEnable() { if (SpawnAfter) return; if (Spawned) return; SpawnGO(); }
private void OnDisable() { Spawned = false; }
void Update() { if (!SpawnAfter) return; if (Spawned) return; CurrentTimer += Time.deltaTime; if (CurrentTimer < SpawnAfterSecs) return; SpawnGO(); } }
Grazie per la lettura. Se avete domande, sono a disposizione.
Gianni Schicchi
Indie developer - Programmer
11
Comments