New bark player, for playing sequences of barks from specific place
This commit is contained in:
@@ -9,26 +9,39 @@ using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Beyond
|
||||
namespace Beyond // Ensure this namespace matches your project structure
|
||||
{
|
||||
// Forward declare Player if it's in a different namespace or assembly,
|
||||
// or ensure 'using Beyond.PlayerNamespace;' if applicable.
|
||||
// Assuming Player class might be in the same 'Beyond' namespace for simplicity here.
|
||||
// If Player class is defined elsewhere, you might need:
|
||||
// using YourPlayerNamespace;
|
||||
|
||||
public class BarkManager : Saver
|
||||
{
|
||||
// --- Inner Classes (Bark, BarkEntry, SaveData) remain the same ---
|
||||
[Serializable]
|
||||
public class Bark
|
||||
{
|
||||
[Tooltip("Conversation to get bark content from.")]
|
||||
[ConversationPopup(false)]
|
||||
public string barkConversation = string.Empty;
|
||||
public string barkConversation = string.Empty;
|
||||
[Tooltip("Direct text to bark if Conversation is empty.")]
|
||||
public string barkText;
|
||||
[Tooltip("Audio clip for the bark.")]
|
||||
public AudioClip clip;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class BarkEntry
|
||||
{
|
||||
[Tooltip("If true, this entire entry can only be triggered once via PlayBark(int) or PlayBark(string). BarkPlayer ignores this for sequential playback.")]
|
||||
public bool playOnce = true;
|
||||
[Tooltip("Delay before the bark starts playing (applies to the first bark triggered externally or random barks).")]
|
||||
public float delayToStart = 0f;
|
||||
[Tooltip("Optional custom audio mixer group for barks in this entry.")]
|
||||
public AudioMixerGroup customOutput;
|
||||
[Tooltip("The list of barks for this entry.")]
|
||||
public Bark[] barks;
|
||||
}
|
||||
|
||||
@@ -38,189 +51,328 @@ namespace Beyond
|
||||
[Serializable]
|
||||
public class SaveData
|
||||
{
|
||||
public bool[] wasPlayed;
|
||||
public bool[] wasEntryPlayed;
|
||||
}
|
||||
public SaveData m_saveData = new SaveData();
|
||||
|
||||
private Queue<QueuedBarkRequest> m_barkQueue = new Queue<QueuedBarkRequest>();
|
||||
private struct QueuedBarkRequest
|
||||
{
|
||||
public int entryIndex;
|
||||
public Transform target; // Can be null if default (Player) is intended
|
||||
}
|
||||
|
||||
public SaveData m_saveData = new SaveData();
|
||||
public Queue<int> m_barkQueue = new Queue<int>();
|
||||
public AudioSource m_audioSource;
|
||||
|
||||
private Dictionary<string, int> m_conversationToEntry = new Dictionary<string, int>();
|
||||
|
||||
private static BarkManager s_instance;
|
||||
|
||||
public static BarkManager Instance => s_instance;
|
||||
|
||||
public bool IsPlaying => m_audioSource != null && m_audioSource.isPlaying;
|
||||
|
||||
// --- Core Methods ---
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
if (s_instance != null)
|
||||
if (s_instance != null && s_instance != this)
|
||||
{
|
||||
Debug.LogError("BarkManager instance already exists! destroying...");
|
||||
Destroy(this);
|
||||
Debug.LogError("BarkManager instance already exists! Destroying this duplicate.", this);
|
||||
Destroy(gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_instance = this;
|
||||
// Optional: DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
SetupAudioSource();
|
||||
}
|
||||
|
||||
public void PlayBark(int num)
|
||||
{
|
||||
m_barkQueue.Enqueue(num);
|
||||
/*
|
||||
if (num >= 0 && num < m_barks.Length)
|
||||
{
|
||||
if (Player.Instance == null)
|
||||
return;
|
||||
var bark = m_barks[num];
|
||||
if (bark.playOnce && m_saveData.wasPlayed[num])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bark.barks != null && m_barks.Length > 0)
|
||||
{
|
||||
var b = bark.barks[Random.Range(0, bark.barks.Length)];
|
||||
if (b.clip != null)
|
||||
{
|
||||
Player.Instance.AudioSource.clip = b.clip;
|
||||
Player.Instance.AudioSource.Play();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(b.barkText))
|
||||
{
|
||||
DialogueManager.BarkString(b.barkText, Player.Instance.transform);
|
||||
}
|
||||
}
|
||||
|
||||
m_saveData.wasPlayed[num] = true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void PlayBark(string conversation)
|
||||
{
|
||||
if (m_conversationToEntry.ContainsKey(conversation))
|
||||
{
|
||||
m_barkQueue.Enqueue(m_conversationToEntry[conversation]);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("BarkManager: Conversation not found: "+conversation, this);
|
||||
}
|
||||
}
|
||||
|
||||
//private AudioSource audioSourceLast;
|
||||
//private GameObject audioSource => Player.Instance.audioSource;
|
||||
|
||||
private void PlayInternal(int num)
|
||||
{
|
||||
if (num >= 0 && num < m_barks.Length)
|
||||
{
|
||||
if (Player.Instance == null)
|
||||
return;
|
||||
var bark = m_barks[num];
|
||||
if (bark.playOnce && m_saveData.wasPlayed[num])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bark.barks != null && m_barks.Length > 0)
|
||||
{
|
||||
if (bark.delayToStart > 0)
|
||||
{
|
||||
StartCoroutine(PlayBarkWithDelayCoroutine(bark));
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayBarkImmediately(bark);
|
||||
}
|
||||
}
|
||||
|
||||
m_saveData.wasPlayed[num] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator PlayBarkWithDelayCoroutine(BarkEntry bark)
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(bark.delayToStart);
|
||||
PlayBarkImmediately(bark);
|
||||
}
|
||||
|
||||
private void PlayBarkImmediately(BarkEntry bark)
|
||||
{
|
||||
var b = bark.barks[Random.Range(0, bark.barks.Length)];
|
||||
if (b.clip != null)
|
||||
{
|
||||
//GameObject audioObject = Instantiate(audioSource, Player.Instance.transform.position, transform.rotation) as GameObject;
|
||||
//audioSourceLast = audioObject.GetComponent<AudioSource>();
|
||||
m_audioSource.outputAudioMixerGroup = bark.customOutput != null ? bark.customOutput : null;
|
||||
m_audioSource.PlayOneShot(b.clip);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(b.barkConversation))
|
||||
{
|
||||
DialogueManager.Bark(b.barkConversation, Player.Instance.transform);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(b.barkText))
|
||||
{
|
||||
DialogueManager.BarkString(b.barkText, Player.Instance.transform);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanBarkBeStillPlayed(int barkNum)
|
||||
{
|
||||
return !(m_saveData.wasPlayed[barkNum] && m_barks[barkNum].playOnce);
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
private void Start()
|
||||
{
|
||||
if (m_audioSource == null)
|
||||
{
|
||||
m_audioSource = GetComponent<AudioSource>();
|
||||
}
|
||||
SetupAudioSource();
|
||||
InitializeSaveData();
|
||||
BuildConversationLookup();
|
||||
}
|
||||
|
||||
private void SetupAudioSource()
|
||||
{
|
||||
if (m_audioSource == null) m_audioSource = GetComponent<AudioSource>();
|
||||
if (m_audioSource == null)
|
||||
{
|
||||
m_audioSource = gameObject.AddComponent<AudioSource>();
|
||||
m_audioSource.playOnAwake = false;
|
||||
m_audioSource.spatialBlend = 1.0f;
|
||||
}
|
||||
|
||||
m_saveData.wasPlayed = new bool[m_barks.Length];
|
||||
for (int i = 0; i < m_saveData.wasPlayed.Length; i++)
|
||||
m_saveData.wasPlayed[i] = false;
|
||||
for (int i=0; i<m_barks.Length; i++)
|
||||
}
|
||||
|
||||
private void InitializeSaveData()
|
||||
{
|
||||
if (m_saveData.wasEntryPlayed == null || m_saveData.wasEntryPlayed.Length != m_barks.Length)
|
||||
{
|
||||
m_saveData.wasEntryPlayed = new bool[m_barks.Length];
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildConversationLookup()
|
||||
{
|
||||
m_conversationToEntry.Clear();
|
||||
for (int i = 0; i < m_barks.Length; i++)
|
||||
{
|
||||
var b = m_barks[i];
|
||||
if (b.barks.Length == 1 && !b.barks[0].barkConversation.IsNullOrWhitespace())
|
||||
var entry = m_barks[i];
|
||||
if (entry.barks != null)
|
||||
{
|
||||
m_conversationToEntry[b.barks[0].barkConversation] = i;
|
||||
foreach (var bark in entry.barks)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(bark.barkConversation))
|
||||
{
|
||||
if (!m_conversationToEntry.ContainsKey(bark.barkConversation))
|
||||
{
|
||||
m_conversationToEntry[bark.barkConversation] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a bark from the specified entry. Uses Player.Instance.transform for text barks if barkTarget is null.
|
||||
/// </summary>
|
||||
/// <param name="entryIndex">Index of the BarkEntry.</param>
|
||||
/// <param name="barkTarget">Transform for positioning Dialogue Manager barks. If null, attempts to use Player.Instance.transform.</param>
|
||||
/// <param name="specificBarkIndex">Index of the specific bark within the entry. -1 for random.</param>
|
||||
/// <returns>The AudioClip that will be played (or null if none/delayed).</returns>
|
||||
public AudioClip PlayBark(int entryIndex, Transform barkTarget = null, int specificBarkIndex = -1)
|
||||
{
|
||||
if (entryIndex < 0 || entryIndex >= m_barks.Length)
|
||||
{
|
||||
Debug.LogError($"BarkManager: Invalid entryIndex {entryIndex}.", this);
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Determine the target for text barks ---
|
||||
Transform targetForText = barkTarget;
|
||||
if (targetForText == null)
|
||||
{
|
||||
if (Player.Instance != null) // Check if Player singleton exists
|
||||
{
|
||||
targetForText = Player.Instance.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Player.Instance is null, text bark won't have a specific target unless one was provided.
|
||||
// Audio will still play. Log warning maybe?
|
||||
// Debug.LogWarning($"BarkManager: barkTarget is null and Player.Instance is null. Text bark for entry {entryIndex} will not be shown.", this);
|
||||
}
|
||||
}
|
||||
// --- End Target Determination ---
|
||||
|
||||
var barkEntry = m_barks[entryIndex];
|
||||
|
||||
// Check playOnce only for random barks
|
||||
if (specificBarkIndex == -1 && barkEntry.playOnce && m_saveData.wasEntryPlayed != null && m_saveData.wasEntryPlayed[entryIndex])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (barkEntry.barks == null || barkEntry.barks.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"BarkManager: BarkEntry {entryIndex} has no barks defined.", this);
|
||||
return null;
|
||||
}
|
||||
|
||||
AudioClip clipToPlay = null;
|
||||
if (specificBarkIndex != -1) // Specific bark (e.g., BarkPlayer) - play immediately
|
||||
{
|
||||
// Pass the determined targetForText
|
||||
clipToPlay = PlayBarkImmediately(barkEntry, targetForText, specificBarkIndex);
|
||||
}
|
||||
else // Random bark
|
||||
{
|
||||
if (barkEntry.delayToStart > 0f && !IsPlaying)
|
||||
{
|
||||
// Pass the determined targetForText to the coroutine
|
||||
StartCoroutine(PlayBarkWithDelayCoroutine(barkEntry, targetForText, specificBarkIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pass the determined targetForText
|
||||
clipToPlay = PlayBarkImmediately(barkEntry, targetForText, specificBarkIndex);
|
||||
}
|
||||
|
||||
// Mark entry as played if playOnce (only for random)
|
||||
if (barkEntry.playOnce && m_saveData.wasEntryPlayed != null)
|
||||
{
|
||||
m_saveData.wasEntryPlayed[entryIndex] = true;
|
||||
}
|
||||
}
|
||||
return clipToPlay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues a request to play a random bark from the entry associated with the conversation.
|
||||
/// Uses Player.Instance.transform for text barks if barkTarget is null.
|
||||
/// </summary>
|
||||
public void PlayBark(string conversation, Transform barkTarget = null)
|
||||
{
|
||||
if (m_conversationToEntry.TryGetValue(conversation, out int entryIndex))
|
||||
{
|
||||
// Determine target *before* queuing, store null if Player isn't available/needed
|
||||
Transform targetForQueue = barkTarget;
|
||||
if (targetForQueue == null && Player.Instance != null)
|
||||
{
|
||||
targetForQueue = Player.Instance.transform;
|
||||
}
|
||||
// Note: If Player.Instance becomes available *later* when the queue processes,
|
||||
// the Update loop logic will handle checking for it again.
|
||||
// So, we can actually queue null and let Update resolve it.
|
||||
m_barkQueue.Enqueue(new QueuedBarkRequest { entryIndex = entryIndex, target = barkTarget }); // Queue the original target (null if default)
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"BarkManager: Conversation '{conversation}' not found in any BarkEntry.", this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues a request to play a random bark from the specified entry index.
|
||||
/// Uses Player.Instance.transform for text barks if barkTarget is null.
|
||||
/// </summary>
|
||||
public void PlayBarkFromQueue(int entryIndex, Transform barkTarget = null)
|
||||
{
|
||||
// Queue the original target (null if default intended). Update loop will resolve Player.Instance if needed.
|
||||
m_barkQueue.Enqueue(new QueuedBarkRequest { entryIndex = entryIndex, target = barkTarget });
|
||||
}
|
||||
|
||||
|
||||
private IEnumerator PlayBarkWithDelayCoroutine(BarkEntry barkEntry, Transform targetForText, int specificBarkIndex)
|
||||
{
|
||||
// Note: Player.Instance could become valid/invalid during this delay.
|
||||
// Re-check target just before playing? Or rely on the target passed in?
|
||||
// Let's stick with the target determined when the coroutine was started for consistency.
|
||||
yield return new WaitForSecondsRealtime(barkEntry.delayToStart);
|
||||
PlayBarkImmediately(barkEntry, targetForText, specificBarkIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to immediately play audio and show text bark. Uses the provided targetForText.
|
||||
/// </summary>
|
||||
private AudioClip PlayBarkImmediately(BarkEntry barkEntry, Transform targetForText, int specificBarkIndex = -1)
|
||||
{
|
||||
if (barkEntry.barks == null || barkEntry.barks.Length == 0) return null;
|
||||
|
||||
Bark barkToPlay = null;
|
||||
int chosenIndex = specificBarkIndex;
|
||||
|
||||
if (chosenIndex < 0 || chosenIndex >= barkEntry.barks.Length)
|
||||
{
|
||||
if (barkEntry.barks.Length > 0) chosenIndex = Random.Range(0, barkEntry.barks.Length);
|
||||
else return null;
|
||||
}
|
||||
barkToPlay = barkEntry.barks[chosenIndex];
|
||||
|
||||
// --- Audio Playback ---
|
||||
AudioClip clipToPlay = null;
|
||||
if (barkToPlay.clip != null)
|
||||
{
|
||||
if (m_audioSource == null) Debug.LogError("BarkManager: m_audioSource is null! Cannot play audio.", this);
|
||||
else
|
||||
{
|
||||
m_audioSource.outputAudioMixerGroup = barkEntry.customOutput != null ? barkEntry.customOutput : null;
|
||||
m_audioSource.PlayOneShot(barkToPlay.clip);
|
||||
clipToPlay = barkToPlay.clip;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Text Bark ---
|
||||
// Use the resolved targetForText passed into this method
|
||||
if (targetForText != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(barkToPlay.barkConversation))
|
||||
{
|
||||
DialogueManager.Bark(barkToPlay.barkConversation, targetForText);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(barkToPlay.barkText))
|
||||
{
|
||||
DialogueManager.BarkString(barkToPlay.barkText, targetForText);
|
||||
}
|
||||
}
|
||||
// If targetForText is null here, no text bark is shown.
|
||||
|
||||
return clipToPlay;
|
||||
}
|
||||
|
||||
// --- Utility & Save/Load --- (Remain the same)
|
||||
|
||||
public bool CanBarkBeStillPlayed(int entryIndex)
|
||||
{
|
||||
if (entryIndex < 0 || entryIndex >= m_barks.Length) return false;
|
||||
if (m_saveData.wasEntryPlayed == null || entryIndex >= m_saveData.wasEntryPlayed.Length) return true;
|
||||
return !m_barks[entryIndex].playOnce || !m_saveData.wasEntryPlayed[entryIndex];
|
||||
}
|
||||
|
||||
public int GetBarkCountInEntry(int entryIndex)
|
||||
{
|
||||
if (entryIndex >= 0 && entryIndex < m_barks.Length && m_barks[entryIndex].barks != null)
|
||||
{
|
||||
return m_barks[entryIndex].barks.Length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override string RecordData()
|
||||
{
|
||||
return SaveSystem.Serialize(m_saveData);
|
||||
if (m_saveData.wasEntryPlayed == null || m_saveData.wasEntryPlayed.Length != m_barks.Length)
|
||||
{
|
||||
bool[] oldData = m_saveData.wasEntryPlayed;
|
||||
m_saveData.wasEntryPlayed = new bool[m_barks.Length];
|
||||
if (oldData != null) Array.Copy(oldData, m_saveData.wasEntryPlayed, Math.Min(oldData.Length, m_barks.Length));
|
||||
}
|
||||
return SaveSystem.Serialize(m_saveData);
|
||||
}
|
||||
|
||||
public override void ApplyData(string s)
|
||||
{
|
||||
var sd = SaveSystem.Deserialize<SaveData>(s);
|
||||
if (sd != null)
|
||||
if (string.IsNullOrEmpty(s)) return;
|
||||
var loadedData = SaveSystem.Deserialize<SaveData>(s);
|
||||
if (loadedData != null)
|
||||
{
|
||||
m_saveData = sd;
|
||||
if (loadedData.wasEntryPlayed != null && loadedData.wasEntryPlayed.Length == m_barks.Length)
|
||||
{
|
||||
m_saveData = loadedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"BarkManager: Loaded 'wasEntryPlayed' data length mismatch. Discarding loaded state.", this);
|
||||
InitializeSaveData();
|
||||
}
|
||||
}
|
||||
else Debug.LogError("BarkManager: Failed to deserialize save data.", this);
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (m_audioSource && m_audioSource.isPlaying)
|
||||
return;
|
||||
if (m_barkQueue.Count > 0)
|
||||
// Process the queue for externally triggered RANDOM barks
|
||||
if (m_barkQueue.Count > 0 && !IsPlaying)
|
||||
{
|
||||
int barkId = m_barkQueue.Dequeue();
|
||||
PlayInternal(barkId);
|
||||
QueuedBarkRequest request = m_barkQueue.Dequeue();
|
||||
|
||||
// --- Resolve target just before playing from queue ---
|
||||
Transform targetForText = request.target; // Use the target stored in the queue request first
|
||||
if (targetForText == null) // If no specific target was queued...
|
||||
{
|
||||
if (Player.Instance != null) // Check for Player *now*
|
||||
{
|
||||
targetForText = Player.Instance.transform;
|
||||
}
|
||||
// else: targetForText remains null, text bark won't show
|
||||
}
|
||||
// --- End Target Resolution ---
|
||||
|
||||
// Call the main PlayBark method, passing the resolved target and -1 for random
|
||||
PlayBark(request.entryIndex, targetForText, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
322
Assets/Scripts/Utils/BarkPlayer.cs
Normal file
322
Assets/Scripts/Utils/BarkPlayer.cs
Normal file
@@ -0,0 +1,322 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using PixelCrushers; // For Saver
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Beyond
|
||||
{
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class BarkPlayer : Saver
|
||||
{
|
||||
[Header("Bark Configuration")]
|
||||
[Tooltip("The index of the BarkEntry in BarkManager's m_barks list.")]
|
||||
public int barkManagerEntryIndex = 0;
|
||||
|
||||
[Tooltip("Delay in seconds after one bark's audio finishes before starting the next.")]
|
||||
public float delayBetweenBarks = 0.5f;
|
||||
|
||||
[Tooltip("Play barks within the entry in a random order (shuffled once on start/load).")]
|
||||
public bool shuffleOrder = false;
|
||||
|
||||
[Tooltip("Keep playing barks (looping the sequence if necessary) as long as an object on a triggering layer stays inside.")]
|
||||
public bool loopWhileInside = true;
|
||||
|
||||
[Header("Trigger Settings")]
|
||||
[Tooltip("Layers that can activate this bark trigger.")]
|
||||
public LayerMask triggeringLayers; // User configures this in Inspector
|
||||
|
||||
[Header("State (Read Only)")]
|
||||
[SerializeField, ReadOnly(true)]
|
||||
private int nextBarkSequenceIndex = 0;
|
||||
[SerializeField, ReadOnly(true)]
|
||||
private bool isTriggeringObjectInside = false; // Renamed for clarity
|
||||
|
||||
// --- Internal Vars ---
|
||||
// Removed PLAYER_LAYER_NAME constant
|
||||
private List<int> playbackOrder;
|
||||
private bool isInitialized = false;
|
||||
private Collider triggerCollider;
|
||||
private BarkManager barkManager;
|
||||
private Coroutine currentPlaybackCoroutine = null;
|
||||
private Transform currentTriggererTransform = null; // Store the transform of the object inside
|
||||
|
||||
|
||||
// --- Save Data --- (Remains the same)
|
||||
[System.Serializable]
|
||||
public class BarkPlayerData { /* ... same as before ... */
|
||||
public int savedNextBarkSequenceIndex;
|
||||
public List<int> savedPlaybackOrder;
|
||||
public bool wasShuffled;
|
||||
}
|
||||
private BarkPlayerData m_saveData = new BarkPlayerData();
|
||||
|
||||
// --- Methods ---
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
triggerCollider = GetComponent<Collider>();
|
||||
if (!triggerCollider.isTrigger)
|
||||
{
|
||||
Debug.LogWarning($"BarkPlayer on {gameObject.name}: Collider is not set to 'Is Trigger'. Forcing it.", this);
|
||||
triggerCollider.isTrigger = true;
|
||||
}
|
||||
|
||||
// --- Set Default LayerMask if Unassigned ---
|
||||
if (triggeringLayers.value == 0) // LayerMask is empty/unassigned in inspector
|
||||
{
|
||||
int playerLayer = LayerMask.NameToLayer("Player");
|
||||
if (playerLayer != -1)
|
||||
{
|
||||
triggeringLayers = LayerMask.GetMask("Player"); // Default to Player layer
|
||||
Debug.LogWarning($"BarkPlayer on {gameObject.name}: Triggering Layers not set in inspector, defaulting to 'Player' layer.", this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Layer "Player" doesn't exist, and no layers were assigned. Log an error.
|
||||
Debug.LogError($"BarkPlayer on {gameObject.name}: Triggering Layers is not set in the inspector, and the default 'Player' layer was not found. This trigger will likely not activate.", this);
|
||||
// Leave triggeringLayers as 0 (Nothing)
|
||||
}
|
||||
}
|
||||
// --- End LayerMask Default ---
|
||||
|
||||
|
||||
// Initialize save data defaults
|
||||
m_saveData.savedNextBarkSequenceIndex = 0;
|
||||
m_saveData.savedPlaybackOrder = null;
|
||||
m_saveData.wasShuffled = shuffleOrder;
|
||||
nextBarkSequenceIndex = 0;
|
||||
isTriggeringObjectInside = false;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
barkManager = BarkManager.Instance;
|
||||
if (barkManager == null)
|
||||
{
|
||||
Debug.LogError($"BarkPlayer on {gameObject.name}: Could not find BarkManager instance!", this);
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
InitializePlaybackOrder();
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
// InitializePlaybackOrder() remains the same as before
|
||||
|
||||
private void InitializePlaybackOrder()
|
||||
{
|
||||
if (barkManager == null) return;
|
||||
|
||||
int barkCount = barkManager.GetBarkCountInEntry(barkManagerEntryIndex);
|
||||
if (barkCount <= 0)
|
||||
{
|
||||
Debug.LogWarning($"BarkPlayer on {gameObject.name}: BarkEntry {barkManagerEntryIndex} has no barks. Disabling.", this);
|
||||
playbackOrder = new List<int>();
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool useLoadedOrder = m_saveData.savedPlaybackOrder != null &&
|
||||
m_saveData.wasShuffled == shuffleOrder &&
|
||||
m_saveData.savedPlaybackOrder.Count == barkCount;
|
||||
|
||||
if (useLoadedOrder)
|
||||
{
|
||||
playbackOrder = new List<int>(m_saveData.savedPlaybackOrder);
|
||||
}
|
||||
else
|
||||
{
|
||||
playbackOrder = Enumerable.Range(0, barkCount).ToList();
|
||||
if (shuffleOrder)
|
||||
{
|
||||
for (int i = playbackOrder.Count - 1; i > 0; i--) {
|
||||
int j = Random.Range(0, i + 1);
|
||||
(playbackOrder[i], playbackOrder[j]) = (playbackOrder[j], playbackOrder[i]);
|
||||
}
|
||||
m_saveData.wasShuffled = true;
|
||||
} else {
|
||||
m_saveData.wasShuffled = false;
|
||||
}
|
||||
m_saveData.savedPlaybackOrder = new List<int>(playbackOrder);
|
||||
}
|
||||
|
||||
nextBarkSequenceIndex = Mathf.Clamp(m_saveData.savedNextBarkSequenceIndex, 0, playbackOrder.Count);
|
||||
|
||||
if (nextBarkSequenceIndex >= playbackOrder.Count && !loopWhileInside)
|
||||
{
|
||||
Debug.Log($"BarkPlayer on {gameObject.name}: Sequence for entry {barkManagerEntryIndex} already completed (and not looping).", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
// --- Check Layer Mask ---
|
||||
int otherLayer = other.gameObject.layer;
|
||||
// Check if the entering object's layer is in our mask
|
||||
if ((triggeringLayers.value & (1 << otherLayer)) == 0)
|
||||
{
|
||||
// Layer not in mask, ignore this trigger event
|
||||
return;
|
||||
}
|
||||
// --- End Layer Check ---
|
||||
|
||||
// Object on a triggering layer entered
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object '{other.name}' entered.", this);
|
||||
isTriggeringObjectInside = true;
|
||||
currentTriggererTransform = other.transform; // Store the transform for barking
|
||||
|
||||
// Start playback if not already running
|
||||
if (currentPlaybackCoroutine == null)
|
||||
{
|
||||
currentPlaybackCoroutine = StartCoroutine(ContinuousPlaybackLoop());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
// --- Check Layer Mask ---
|
||||
int otherLayer = other.gameObject.layer;
|
||||
if ((triggeringLayers.value & (1 << otherLayer)) == 0)
|
||||
{
|
||||
// Ignore exit events from non-triggering layers
|
||||
return;
|
||||
}
|
||||
// --- End Layer Check ---
|
||||
|
||||
// Object on a triggering layer exited
|
||||
// Important: Only react if the exiting object is the *same one* we stored.
|
||||
// This handles cases where multiple triggering objects might enter/exit.
|
||||
if (other.transform == currentTriggererTransform)
|
||||
{
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object '{other.name}' exited.", this);
|
||||
isTriggeringObjectInside = false;
|
||||
currentTriggererTransform = null; // Clear the stored transform
|
||||
|
||||
// Stop the playback coroutine
|
||||
if (currentPlaybackCoroutine != null)
|
||||
{
|
||||
StopCoroutine(currentPlaybackCoroutine);
|
||||
currentPlaybackCoroutine = null;
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Stopping playback loop due to exit.", this);
|
||||
// Optional: Stop BarkManager audio immediately
|
||||
// if (barkManager != null && barkManager.IsPlaying) barkManager.m_audioSource.Stop();
|
||||
}
|
||||
}
|
||||
// Else: Some other object on a triggering layer exited, but the primary one is still inside. Do nothing.
|
||||
|
||||
}
|
||||
|
||||
// ContinuousPlaybackLoop now uses the stored currentTriggererTransform
|
||||
private IEnumerator ContinuousPlaybackLoop()
|
||||
{
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Starting playback loop.", this);
|
||||
|
||||
// Ensure we have a target transform before proceeding
|
||||
while (currentTriggererTransform == null && isTriggeringObjectInside)
|
||||
{
|
||||
Debug.LogWarning($"BarkPlayer ({gameObject.name}): Waiting for valid triggerer transform.", this);
|
||||
yield return null; // Wait a frame if trigger happened but transform wasn't stored yet (unlikely but safe)
|
||||
}
|
||||
|
||||
// Main loop runs while a triggering object is inside AND we have its transform
|
||||
while (isTriggeringObjectInside && currentTriggererTransform != null)
|
||||
{
|
||||
if (playbackOrder == null || playbackOrder.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"BarkPlayer ({gameObject.name}): No barks to play in entry {barkManagerEntryIndex}.", this);
|
||||
yield break;
|
||||
}
|
||||
|
||||
bool sequenceFinished = nextBarkSequenceIndex >= playbackOrder.Count;
|
||||
|
||||
if (sequenceFinished)
|
||||
{
|
||||
if (loopWhileInside)
|
||||
{
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Sequence finished, looping.", this);
|
||||
nextBarkSequenceIndex = 0;
|
||||
if (shuffleOrder) {
|
||||
InitializePlaybackOrder();
|
||||
nextBarkSequenceIndex = 0;
|
||||
if (playbackOrder.Count == 0) yield break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Sequence finished, not looping. Waiting for exit.", this);
|
||||
// Wait until trigger object leaves
|
||||
while(isTriggeringObjectInside && currentTriggererTransform != null) { yield return null; }
|
||||
yield break; // Exit coroutine
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for BarkManager's AudioSource if necessary
|
||||
if (barkManager.IsPlaying)
|
||||
{
|
||||
yield return null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Play the bark using the stored transform
|
||||
int barkIndexToPlay = playbackOrder[nextBarkSequenceIndex];
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Playing bark {nextBarkSequenceIndex} (actual index: {barkIndexToPlay}) targeting '{currentTriggererTransform.name}'.", this);
|
||||
|
||||
// Pass the stored transform
|
||||
AudioClip playedClip = barkManager.PlayBark(barkManagerEntryIndex, currentTriggererTransform, barkIndexToPlay);
|
||||
|
||||
nextBarkSequenceIndex++;
|
||||
|
||||
float waitTime = delayBetweenBarks;
|
||||
if (playedClip != null) { waitTime += Mathf.Max(0f, playedClip.length); }
|
||||
|
||||
if (waitTime > 0) { yield return new WaitForSeconds(waitTime); }
|
||||
else { yield return null; }
|
||||
}
|
||||
|
||||
Debug.Log($"BarkPlayer ({gameObject.name}): Playback loop finished (Triggering object left or transform lost).", this);
|
||||
// Reset coroutine reference *if* it was this instance that finished it
|
||||
// (OnTriggerExit might have already cleared it)
|
||||
if (currentPlaybackCoroutine != null && !isTriggeringObjectInside) {
|
||||
currentPlaybackCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- Save System Integration --- (Remains the same)
|
||||
public override string RecordData() { /* ... same as before ... */
|
||||
m_saveData.savedNextBarkSequenceIndex = this.nextBarkSequenceIndex;
|
||||
m_saveData.savedPlaybackOrder = new List<int>(this.playbackOrder ?? new List<int>());
|
||||
m_saveData.wasShuffled = this.shuffleOrder;
|
||||
return SaveSystem.Serialize(m_saveData);
|
||||
}
|
||||
public override void ApplyData(string s) { /* ... same as before ... */
|
||||
if (string.IsNullOrEmpty(s)) return;
|
||||
var loadedData = SaveSystem.Deserialize<BarkPlayerData>(s);
|
||||
if (loadedData != null) { m_saveData = loadedData; }
|
||||
else Debug.LogError($"BarkPlayer ({gameObject.name}): Failed to deserialize save data.", this);
|
||||
}
|
||||
|
||||
// --- Gizmos --- (Remain the same, uses isTriggeringObjectInside now)
|
||||
private void OnDrawGizmos() {
|
||||
Collider col = GetComponent<Collider>();
|
||||
if (col != null) {
|
||||
Gizmos.color = isTriggeringObjectInside ? new Color(1f, 0.5f, 0.5f, 0.4f) : new Color(0.5f, 1f, 0.5f, 0.3f);
|
||||
if (col is BoxCollider box) Gizmos.DrawCube(transform.TransformPoint(box.center), Vector3.Scale(box.size, transform.lossyScale));
|
||||
else if (col is SphereCollider sphere) Gizmos.DrawSphere(transform.TransformPoint(sphere.center), sphere.radius * MaxComponent(transform.lossyScale));
|
||||
}
|
||||
}
|
||||
private float MaxComponent(Vector3 v) => Mathf.Max(Mathf.Max(v.x, v.y), v.z);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Utils/BarkPlayer.cs.meta
Normal file
2
Assets/Scripts/Utils/BarkPlayer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82f8f534f4d73499ea554681a3e99a4a
|
||||
Reference in New Issue
Block a user