Better Sound Sequences (#190)

* add way of creating sound sequences in inspector

- actually implement GameAction preFunction
- implement sound scheduling for Jukebox and MultiSound

* Dj School: fix turntable effect being parented to root

* Pajama Party: fix sleep action type not carrying over between transitions
This commit is contained in:
minenice55 2023-01-04 23:04:31 -05:00 committed by GitHub
parent 22133a5c54
commit 87d20b8c8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 392 additions and 108 deletions

View file

@ -22473,6 +22473,145 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: c8bf5d56f80c8814489e7b35cc9421ff, type: 3} m_Script: {fileID: 11500000, guid: c8bf5d56f80c8814489e7b35cc9421ff, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
SoundSequences:
- name: arisa_hai
sequence:
game: 1
force: 0
clips:
- clip: fanClub/arisa_hai_1_jp
beat: 0
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/arisa_hai_2_jp
beat: 1
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/arisa_hai_3_jp
beat: 2
pitch: 1
volume: 1
looping: 0
offset: 0
- name: crowd_hai
sequence:
game: 1
force: 0
clips:
- clip: fanClub/crowd_hai_jp
beat: 0
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/crowd_hai_jp
beat: 1
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/crowd_hai_jp
beat: 2
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/crowd_hai_jp
beat: 3
pitch: 1
volume: 1
looping: 0
offset: 0
- name: arisa_kamone
sequence:
game: 1
force: 0
clips:
- clip: fanClub/arisa_ka_jp
beat: 0
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/arisa_mo_jp
beat: 0.5
pitch: 1
volume: 1
looping: 0
offset: 0.07407407
- clip: fanClub/arisa_ne_jp
beat: 1
pitch: 1
volume: 1
looping: 0
offset: 0.07407407
- name: arisa_kamone_fast
sequence:
game: 1
force: 0
clips:
- clip: fanClub/arisa_ka_fast_jp
beat: 0
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/arisa_mo_fast_jp
beat: 0.25
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/arisa_ne_fast_jp
beat: 0.5
pitch: 1
volume: 1
looping: 0
offset: 0
- name: crowd_kamone
sequence:
game: 1
force: 0
clips:
- clip: fanClub/crowd_ka_jp
beat: 0
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/crowd_mo_jp
beat: 1.5
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/crowd_ne_jp
beat: 2
pitch: 1
volume: 1
looping: 0
offset: 0
- clip: fanClub/crowd_hey_jp
beat: 3
pitch: 1
volume: 1
looping: 0
offset: 0
- name: crowd_big_ready
sequence:
game: 1
force: 0
clips:
- clip: fanClub/crowd_big_ready
beat: 0
pitch: 1
volume: 1
looping: 0
offset: 0
EligibleHits: [] EligibleHits: []
scheduledInputs: [] scheduledInputs: []
firstEnable: 0 firstEnable: 0

View file

@ -0,0 +1,79 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
namespace HeavenStudio.Util
{
/// <summary>
/// MultiSound that is serializable in the inspector, etc.
/// </summary>
[Serializable]
public class SoundSequence
{
[Tooltip("Should sequence use game specific-sounds?")]
[SerializeField] bool game;
[Tooltip("Should sequence force playback even if corresponding game is not loaded?")]
[SerializeField] bool force;
[Tooltip("Clips to play in the sequence")]
[SerializeField] private List<SequenceClip> clips = new List<SequenceClip>();
public SoundSequence(bool game, bool force, params SequenceClip[] clips)
{
this.game = game;
this.force = force;
this.clips = new List<SequenceClip>(clips);
}
public MultiSound Play(float startBeat)
{
List<MultiSound.Sound> sounds = new List<MultiSound.Sound>();
foreach (SequenceClip clip in clips)
{
sounds.Add(new MultiSound.Sound(clip.clip, startBeat + clip.beat, clip.pitch, clip.volume, clip.looping, clip.offset));
}
return MultiSound.Play(sounds.ToArray(), game, force);
}
[Serializable]
public struct SequenceClip
{
public SequenceClip(string clip, float beat, float pitch = 1f, float volume = 1f, bool looping = false, float offset = 0f)
{
this.clip = clip;
this.beat = beat;
this.pitch = pitch;
this.volume = volume;
this.looping = looping;
this.offset = offset;
}
[Tooltip("Filename of clip to use (will look in assetbundles before resources)")]
public string clip;
[Tooltip("Beat to play clip at relative to start of sequence")]
public float beat;
[Tooltip("Pitch to play clip at")]
[DefaultValue(1f)]
public float pitch;
[Tooltip("Volume to play clip at")]
[DefaultValue(1f)]
public float volume;
[Tooltip("Whether to loop the clip")]
public bool looping;
[Tooltip("Offset to start playing clip")]
public float offset;
}
[Serializable]
public struct SequenceKeyValue
{
[Tooltip("Name of sequence (game scripts will call sequences to play using this name")]
public string name;
[Tooltip("Sequence to play")]
public SoundSequence sequence;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 643b435927934b34599a4250da088992
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -22,16 +22,22 @@ namespace HeavenStudio
// Current song position, in seconds // Current song position, in seconds
private double songPos; // for Conductor use only private double songPos; // for Conductor use only
public float songPosition => (float) songPos; public float songPosition => (float) songPos;
public double songPositionAsDouble => songPos;
// Current song position, in beats // Current song position, in beats
private double songPosBeat; // for Conductor use only private double songPosBeat; // for Conductor use only
public float songPositionInBeats => (float) songPosBeat; public float songPositionInBeats => (float) songPosBeat;
public double songPositionInBeatsAsDouble => songPosBeat;
// Current time of the song // Current time of the song
private double time; private double time;
double lastAbsTime; double lastAbsTime;
// the dspTime we started at
private double dspStartTime;
public double dspStartTimeAsDouble => dspStartTime;
// an AudioSource attached to this GameObject that will play the music. // an AudioSource attached to this GameObject that will play the music.
public AudioSource musicSource; public AudioSource musicSource;
@ -52,6 +58,7 @@ namespace HeavenStudio
// Metronome tick sound enabled // Metronome tick sound enabled
public bool metronome = false; public bool metronome = false;
Util.Sound metronomeSound;
public float timeSinceLastTempoChange = Single.MinValue; public float timeSinceLastTempoChange = Single.MinValue;
@ -142,6 +149,7 @@ namespace HeavenStudio
} }
} }
lastAbsTime = Time.realtimeSinceStartupAsDouble; lastAbsTime = Time.realtimeSinceStartupAsDouble;
dspStartTime = AudioSettings.dspTime;
// GameManager.instance.SetCurrentEventToClosest(songPositionInBeats); // GameManager.instance.SetCurrentEventToClosest(songPositionInBeats);
} }
@ -189,13 +197,21 @@ namespace HeavenStudio
{ {
if (ReportBeat(ref lastReportedBeat)) if (ReportBeat(ref lastReportedBeat))
{ {
Util.Jukebox.PlayOneShot("metronome"); metronomeSound = Util.Jukebox.PlayOneShot("metronome", lastReportedBeat + 1f);
} }
else if (songPositionInBeats < lastReportedBeat) else if (songPositionInBeats < lastReportedBeat)
{ {
lastReportedBeat = Mathf.Round(songPositionInBeats); lastReportedBeat = Mathf.Round(songPositionInBeats);
} }
} }
else
{
if (metronomeSound != null)
{
metronomeSound.Delete();
metronomeSound = null;
}
}
} }
public bool ReportBeat(ref float lastReportedBeat, float offset = 0, bool shiftBeatToOffset = true) public bool ReportBeat(ref float lastReportedBeat, float offset = 0, bool shiftBeatToOffset = true)

View file

@ -89,6 +89,23 @@ namespace HeavenStudio
} }
} }
public void CallPreEvent(DynamicBeatmap.DynamicEntity entity)
{
string[] details = entity.datamodel.Split('/');
Minigames.Minigame game = minigames.Find(c => c.name == details[0]);
try
{
currentEntity = entity;
Minigames.GameAction action = game.actions.Find(c => c.actionName == details[1]);
action.preFunction.Invoke();
}
catch (Exception ex)
{
Debug.LogWarning("Event not found! May be spelled wrong or it is not implemented.\n" + ex);
}
}
public static List<DynamicBeatmap.DynamicEntity> GetAllInGameManagerList(string gameName, string[] include) public static List<DynamicBeatmap.DynamicEntity> GetAllInGameManagerList(string gameName, string[] include)
{ {
List<DynamicBeatmap.DynamicEntity> temp1 = GameManager.instance.Beatmap.entities.FindAll(c => c.datamodel.Split('/')[0] == gameName); List<DynamicBeatmap.DynamicEntity> temp1 = GameManager.instance.Beatmap.entities.FindAll(c => c.datamodel.Split('/')[0] == gameName);

View file

@ -33,7 +33,7 @@ namespace HeavenStudio
[Header("Properties")] [Header("Properties")]
public int currentEvent, currentTempoEvent, currentVolumeEvent, currentSectionEvent, public int currentEvent, currentTempoEvent, currentVolumeEvent, currentSectionEvent,
currentPreEvent, currentPreSwitch; currentPreEvent, currentPreSwitch, currentPreSequence;
public float endBeat; public float endBeat;
public float startOffset; public float startOffset;
public bool playOnStart; public bool playOnStart;
@ -48,7 +48,7 @@ namespace HeavenStudio
return (Conductor.instance.songPositionInBeats - currentSection.beat) / (nextSection.beat - currentSection.beat); return (Conductor.instance.songPositionInBeats - currentSection.beat) / (nextSection.beat - currentSection.beat);
}} }}
public event Action<float> onBeatChanged; public event Action<float> onBeatChanged;
public event Action<DynamicBeatmap.ChartSection> onSectionChange; public event Action<DynamicBeatmap.ChartSection> onSectionChange;
public int BeatmapEntities() public int BeatmapEntities()
@ -69,6 +69,7 @@ namespace HeavenStudio
{ {
currentPreEvent= 0; currentPreEvent= 0;
currentPreSwitch = 0; currentPreSwitch = 0;
currentPreSequence = 0;
this.transform.localScale = new Vector3(30000000, 30000000); this.transform.localScale = new Vector3(30000000, 30000000);
@ -219,6 +220,24 @@ namespace HeavenStudio
} }
} }
public void SeekAheadAndDoPreEvent(float start, float seekTime = 1f)
{
List<float> entities = Beatmap.entities.Select(c => c.beat).ToList();
if (currentPreSequence < Beatmap.entities.Count && currentPreSequence >= 0)
{
if (start + seekTime >= entities[currentPreSequence])
{
float beat = Beatmap.entities[currentPreSequence].beat;
var entitiesAtSameBeat = Beatmap.entities.FindAll(c => c.beat == Beatmap.entities[currentPreSequence].beat);
foreach (DynamicBeatmap.DynamicEntity entity in entitiesAtSameBeat)
{
eventCaller.CallPreEvent(entity);
}
currentPreSequence++;
}
}
}
// LateUpdate works a bit better(?) but causes some bugs (like issues with bop animations). // LateUpdate works a bit better(?) but causes some bugs (like issues with bop animations).
private void Update() private void Update()
{ {
@ -272,6 +291,8 @@ namespace HeavenStudio
//seek ahead to preload games that have assetbundles //seek ahead to preload games that have assetbundles
SeekAheadAndPreload(Conductor.instance.songPositionInBeats, seekTime); SeekAheadAndPreload(Conductor.instance.songPositionInBeats, seekTime);
SeekAheadAndDoPreEvent(Conductor.instance.songPositionInBeats, 1f);
if (currentEvent < Beatmap.entities.Count && currentEvent >= 0) if (currentEvent < Beatmap.entities.Count && currentEvent >= 0)
{ {
if (Conductor.instance.songPositionInBeats >= entities[currentEvent] /*&& SongPosLessThanClipLength(Conductor.instance.songPositionInBeats)*/) if (Conductor.instance.songPositionInBeats >= entities[currentEvent] /*&& SongPosLessThanClipLength(Conductor.instance.songPositionInBeats)*/)
@ -390,6 +411,7 @@ namespace HeavenStudio
currentEvent = entities.IndexOf(Mathp.GetClosestInList(entities, beat)); currentEvent = entities.IndexOf(Mathp.GetClosestInList(entities, beat));
currentPreEvent = entities.IndexOf(Mathp.GetClosestInList(entities, beat)); currentPreEvent = entities.IndexOf(Mathp.GetClosestInList(entities, beat));
currentPreSequence = entities.IndexOf(Mathp.GetClosestInList(entities, beat));
var gameSwitchs = Beatmap.entities.FindAll(c => c.datamodel.Split(1) == "switchGame"); var gameSwitchs = Beatmap.entities.FindAll(c => c.datamodel.Split(1) == "switchGame");

View file

@ -160,7 +160,7 @@ namespace HeavenStudio.Games.Scripts_DJSchool
tableAnim.speed = 1; tableAnim.speed = 1;
tableAnim.Play("Student_Turntable_Swipe", 0, 0); tableAnim.Play("Student_Turntable_Swipe", 0, 0);
Instantiate(slamFX).SetActive(true); Instantiate(slamFX, this.transform.parent).SetActive(true);
mixer.audioMixer.FindSnapshot("Main").TransitionTo(.01f); mixer.audioMixer.FindSnapshot("Main").TransitionTo(.01f);
} }
else else
@ -177,7 +177,7 @@ namespace HeavenStudio.Games.Scripts_DJSchool
tableAnim.speed = 1; tableAnim.speed = 1;
tableAnim.Play("Student_Turntable_Swipe", 0, 0); tableAnim.Play("Student_Turntable_Swipe", 0, 0);
Instantiate(slamFX).SetActive(true); Instantiate(slamFX, this.transform.parent).SetActive(true);
mixer.audioMixer.FindSnapshot("Main").TransitionTo(.01f); mixer.audioMixer.FindSnapshot("Main").TransitionTo(.01f);
} }

View file

@ -182,7 +182,6 @@ namespace HeavenStudio.Games
private void Awake() private void Awake()
{ {
instance = this; instance = this;
Spectators = new List<GameObject>(); Spectators = new List<GameObject>();
idolAnimator = Arisa.GetComponent<Animator>(); idolAnimator = Arisa.GetComponent<Animator>();
@ -528,12 +527,7 @@ namespace HeavenStudio.Games
public void CallHai(float beat, bool noSound = false, int type = 0) public void CallHai(float beat, bool noSound = false, int type = 0)
{ {
if (!noSound) if (!noSound)
MultiSound.Play(new MultiSound.Sound[] { PlaySoundSequence("arisa_hai", beat);
new MultiSound.Sound("fanClub/arisa_hai_1_jp", beat),
new MultiSound.Sound("fanClub/arisa_hai_2_jp", beat + 1f),
new MultiSound.Sound("fanClub/arisa_hai_3_jp", beat + 2f),
});
responseToggle = false; responseToggle = false;
DisableBop(beat, 8f); DisableBop(beat, 8f);
@ -556,12 +550,7 @@ namespace HeavenStudio.Games
new BeatAction.Action(beat + 7f, delegate { PlayOneClap(beat + 7f); DoIdolClaps();}), new BeatAction.Action(beat + 7f, delegate { PlayOneClap(beat + 7f); DoIdolClaps();}),
}); });
MultiSound.Play(new MultiSound.Sound[] { PlaySoundSequence("crowd_hai", beat + 4f);
new MultiSound.Sound("fanClub/crowd_hai_jp", beat + 4f),
new MultiSound.Sound("fanClub/crowd_hai_jp", beat + 5f),
new MultiSound.Sound("fanClub/crowd_hai_jp", beat + 6f),
new MultiSound.Sound("fanClub/crowd_hai_jp", beat + 7f),
});
} }
public static void WarnHai(float beat, bool noSound = false, int type = 0) public static void WarnHai(float beat, bool noSound = false, int type = 0)
@ -586,27 +575,12 @@ namespace HeavenStudio.Games
bool doJump = (responseType == (int) KamoneResponseType.Jump || responseType == (int) KamoneResponseType.JumpFast); bool doJump = (responseType == (int) KamoneResponseType.Jump || responseType == (int) KamoneResponseType.JumpFast);
bool isBig = (responseType == (int) KamoneResponseType.ThroughFast || responseType == (int) KamoneResponseType.JumpFast); bool isBig = (responseType == (int) KamoneResponseType.ThroughFast || responseType == (int) KamoneResponseType.JumpFast);
DisableResponse(beat, 2f); DisableResponse(beat, 2f);
if (isBig) if (!noSound)
{ {
if (!noSound) if (isBig)
{ PlaySoundSequence("arisa_kamone_fast", beat);
MultiSound.Play(new MultiSound.Sound[] { else
new MultiSound.Sound("fanClub/arisa_ka_fast_jp", beat), PlaySoundSequence("arisa_kamone", beat);
new MultiSound.Sound("fanClub/arisa_mo_fast_jp", beat + 0.25f),
new MultiSound.Sound("fanClub/arisa_ne_fast_jp", beat + 0.5f),
});
}
}
else
{
if (!noSound)
{
MultiSound.Play(new MultiSound.Sound[] {
new MultiSound.Sound("fanClub/arisa_ka_jp", beat),
new MultiSound.Sound("fanClub/arisa_mo_jp", beat + 0.5f, offset: 0.07407407f),
new MultiSound.Sound("fanClub/arisa_ne_jp", beat + 1f, offset: 0.07407407f),
});
}
} }
responseToggle = true; responseToggle = true;
@ -640,12 +614,7 @@ namespace HeavenStudio.Games
}), }),
}); });
MultiSound.Play(new MultiSound.Sound[] { PlaySoundSequence("crowd_kamone", beat + 2f);
new MultiSound.Sound("fanClub/crowd_ka_jp", beat + 2f),
new MultiSound.Sound("fanClub/crowd_mo_jp", beat + 3.5f),
new MultiSound.Sound("fanClub/crowd_ne_jp", beat + 4f),
new MultiSound.Sound("fanClub/crowd_hey_jp", beat + 5f),
});
} }
public static void WarnKamone(float beat, bool noSound = false, int type = 0, int responseType = (int) KamoneResponseType.Through) public static void WarnKamone(float beat, bool noSound = false, int type = 0, int responseType = (int) KamoneResponseType.Through)
@ -679,11 +648,11 @@ namespace HeavenStudio.Games
const float BIGCALL_LENGTH = 2.75f; const float BIGCALL_LENGTH = 2.75f;
public void CallBigReady(float beat, bool noSound = false) public void CallBigReady(float beat, bool noSound = false)
{ {
if (!noSound)
PlaySoundSequence("crowd_big_ready", beat);
Prepare(beat + 1.5f); Prepare(beat + 1.5f);
Prepare(beat + 2f); Prepare(beat + 2f);
if (!noSound)
Jukebox.PlayOneShotGame("fanClub/crowd_big_ready");
DisableSpecBop(beat, 3.75f); DisableSpecBop(beat, 3.75f);
@ -701,7 +670,7 @@ namespace HeavenStudio.Games
{ {
wantBigReady = beat; wantBigReady = beat;
if (noSound) return; if (noSound) return;
Jukebox.PlayOneShotGame("fanClub/crowd_big_ready"); Jukebox.PlayOneShotGame("fanClub/crowd_big_ready", beat);
} }
public void ContinueBigReady(float beat) public void ContinueBigReady(float beat)

View file

@ -2,11 +2,15 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using HeavenStudio.Util;
namespace HeavenStudio.Games namespace HeavenStudio.Games
{ {
public class Minigame : MonoBehaviour public class Minigame : MonoBehaviour
{ {
public static float earlyTime = 0.1f, perfectTime = 0.08f, aceEarlyTime = 0.025f, aceLateTime = 0.025f, lateTime = 0.08f, endTime = 0.1f; public static float earlyTime = 0.1f, perfectTime = 0.08f, aceEarlyTime = 0.025f, aceLateTime = 0.025f, lateTime = 0.08f, endTime = 0.1f;
[SerializeField] public SoundSequence.SequenceKeyValue[] SoundSequences;
public List<Minigame.Eligible> EligibleHits = new List<Minigame.Eligible>(); public List<Minigame.Eligible> EligibleHits = new List<Minigame.Eligible>();
[System.Serializable] [System.Serializable]
@ -211,5 +215,18 @@ namespace HeavenStudio.Games
return sameTime; return sameTime;
} }
public MultiSound PlaySoundSequence(string name, float startBeat)
{
foreach (SoundSequence.SequenceKeyValue pair in SoundSequences)
{
if (pair.name == name)
{
return pair.sequence.Play(startBeat);
}
}
Debug.LogWarning($"Sound sequence {name} not found in game {this.name} (did you build AssetBundles?)");
return null;
}
} }
} }

View file

@ -37,7 +37,7 @@ namespace HeavenStudio.Games.Loaders
new Param("type", PajamaParty.SleepType.Normal, "Sleep Type", "Type of sleep action to use"), new Param("type", PajamaParty.SleepType.Normal, "Sleep Type", "Type of sleep action to use"),
new Param("toggle", false, "Alt. Animation", "Use an alternate animation for Mako") new Param("toggle", false, "Alt. Animation", "Use an alternate animation for Mako")
}, },
inactiveFunction = delegate {var e = eventCaller.currentEntity; PajamaParty.WarnSleepSequence(e.beat, e["toggle"]);} inactiveFunction = delegate {var e = eventCaller.currentEntity; PajamaParty.WarnSleepSequence(e.beat, e["toggle"], e["type"]);}
}, },
new GameAction("throw", "Throw Pillows") new GameAction("throw", "Throw Pillows")
{ {
@ -280,7 +280,7 @@ namespace HeavenStudio.Games
}); });
} }
public static void WarnSleepSequence(float beat, bool alt = false) public static void WarnSleepSequence(float beat, bool alt = false, int action = (int) PajamaParty.SleepType.Normal)
{ {
MultiSound.Play(new MultiSound.Sound[] { MultiSound.Play(new MultiSound.Sound[] {
new MultiSound.Sound("pajamaParty/siesta1", beat), new MultiSound.Sound("pajamaParty/siesta1", beat),
@ -291,6 +291,7 @@ namespace HeavenStudio.Games
}, forcePlay: true); }, forcePlay: true);
WantSleepSequence = beat; WantSleepSequence = beat;
WantSleepType = alt; WantSleepType = alt;
WantSleepAction = action;
} }
public void DoBedImpact() public void DoBedImpact()

View file

@ -20,6 +20,7 @@ namespace HeavenStudio
{ {
public class Minigame public class Minigame
{ {
public string name; public string name;
public string displayName; public string displayName;
public string color; public string color;

View file

@ -151,8 +151,6 @@ namespace HeavenStudio.Util
return null; return null;
} }
//TODO: playing sounds from assetbundles
public static void KillLoop(Sound source, float fadeTime) public static void KillLoop(Sound source, float fadeTime)
{ {
// Safeguard against previously-destroyed sounds. // Safeguard against previously-destroyed sounds.

View file

@ -8,10 +8,10 @@ namespace HeavenStudio.Util
public class MultiSound : MonoBehaviour public class MultiSound : MonoBehaviour
{ {
private float startBeat; private float startBeat;
private int index;
private bool game; private bool game;
private bool forcePlay; private bool forcePlay;
public List<Sound> sounds = new List<Sound>(); public List<Sound> sounds = new List<Sound>();
public List<Util.Sound> playingSounds = new List<Util.Sound>();
public class Sound public class Sound
{ {
@ -37,39 +37,35 @@ namespace HeavenStudio.Util
public static MultiSound Play(Sound[] snds, bool game = true, bool forcePlay = false) public static MultiSound Play(Sound[] snds, bool game = true, bool forcePlay = false)
{ {
List<Sound> sounds = snds.ToList(); List<Sound> sounds = snds.ToList();
GameObject gameObj = new GameObject(); GameObject go = new GameObject("MultiSound");
MultiSound ms = gameObj.AddComponent<MultiSound>(); MultiSound ms = go.AddComponent<MultiSound>();
ms.sounds = sounds; ms.sounds = sounds;
ms.startBeat = sounds[0].beat; ms.startBeat = sounds[0].beat;
ms.game = game; ms.game = game;
ms.forcePlay = forcePlay; ms.forcePlay = forcePlay;
gameObj.name = "MultiSound";
GameManager.instance.SoundObjects.Add(gameObj); for (int i = 0; i < sounds.Count; i++)
{
Util.Sound s;
if (game)
s = Jukebox.PlayOneShotGame(sounds[i].name, sounds[i].beat - Conductor.instance.GetRestFromRealTime(sounds[i].offset), sounds[i].pitch, sounds[i].volume, sounds[i].looping, forcePlay);
else
s = Jukebox.PlayOneShot(sounds[i].name, sounds[i].beat - Conductor.instance.GetRestFromRealTime(sounds[i].offset), sounds[i].pitch, sounds[i].volume, sounds[i].looping);
ms.playingSounds.Add(s);
}
GameManager.instance.SoundObjects.Add(go);
return ms; return ms;
} }
private void Update() private void Update()
{ {
float songPositionInBeats = Conductor.instance.songPositionInBeats; foreach (Util.Sound sound in playingSounds)
for (int i = 0; i < sounds.Count; i++)
{ {
if (songPositionInBeats >= sounds[i].beat - Conductor.instance.GetRestFromRealTime(sounds[i].offset) && index == i) if (sound != null)
{ return;
if (game)
Jukebox.PlayOneShotGame(sounds[i].name, sounds[i].beat - Conductor.instance.GetRestFromRealTime(sounds[i].offset), sounds[i].pitch, sounds[i].volume, sounds[i].looping, forcePlay);
else
Jukebox.PlayOneShot(sounds[i].name, sounds[i].beat - Conductor.instance.GetRestFromRealTime(sounds[i].offset), sounds[i].pitch, sounds[i].volume, sounds[i].looping);
index++;
}
}
if (songPositionInBeats >= (sounds[sounds.Count - 1].beat - Conductor.instance.GetRestFromRealTime(sounds[sounds.Count - 1].offset)))
{
Delete();
} }
Delete();
} }
public void Delete() public void Delete()

View file

@ -23,12 +23,13 @@ namespace HeavenStudio.Util
private int pauseTimes = 0; private int pauseTimes = 0;
private float startTime; private double startTime;
public float beat; public float beat;
public float scheduledPitch = 1f;
bool playInstant = false; bool playInstant = false;
int playIndex = 0; bool played = false;
private void Start() private void Start()
{ {
@ -40,39 +41,50 @@ namespace HeavenStudio.Util
if (beat == -1 && !scheduled) if (beat == -1 && !scheduled)
{ {
audioSource.PlayScheduled(Time.time); audioSource.PlayScheduled(AudioSettings.dspTime);
playInstant = true; playInstant = true;
playIndex++; played = true;
startTime = Conductor.instance.songPositionAsDouble;
StartCoroutine(NotRelyOnBeatSound());
} }
else else
{ {
playInstant = false; playInstant = false;
scheduledPitch = Conductor.instance.musicSource.pitch;
startTime = (AudioSettings.dspTime + (Conductor.instance.GetSongPosFromBeat(beat) - Conductor.instance.songPositionAsDouble)/(double)scheduledPitch);
audioSource.PlayScheduled(startTime);
Debug.Log($"Scheduling future sound {clip.name} for beat {beat} (scheduled: {startTime}, current time: {AudioSettings.dspTime})");
} }
startTime = Conductor.instance.songPosition;
if (!scheduled && !looping)
StartCoroutine(NotRelyOnBeatSound());
} }
private void Update() private void Update()
{ {
if (playIndex < 1) if (!played)
{ {
if (scheduled) if (scheduled)
{ {
if (AudioSettings.dspTime > scheduledTime) if (AudioSettings.dspTime > scheduledTime)
{ {
StartCoroutine(NotRelyOnBeatSound()); StartCoroutine(NotRelyOnBeatSound());
playIndex++; played = true;
} }
} }
else if (!playInstant) else if (!playInstant)
{ {
if (Conductor.instance.songPositionInBeats > beat) if (AudioSettings.dspTime > startTime)
{ {
audioSource.PlayScheduled(Time.time); played = true;
playIndex++; StartCoroutine(NotRelyOnBeatSound());
}
else
{
if (!played && scheduledPitch != Conductor.instance.musicSource.pitch)
{
scheduledPitch = Conductor.instance.musicSource.pitch;
startTime = (AudioSettings.dspTime + (Conductor.instance.GetSongPosFromBeat(beat) - Conductor.instance.songPositionAsDouble)/(double)scheduledPitch);
audioSource.SetScheduledStartTime(startTime);
Debug.Log($"Rescheduling future sound {clip.name} for beat {beat} (scheduled: {startTime}, current time: {AudioSettings.dspTime})");
}
} }
} }
} }
@ -94,7 +106,7 @@ namespace HeavenStudio.Util
{ {
if (!looping) // Looping sounds are destroyed manually. if (!looping) // Looping sounds are destroyed manually.
{ {
yield return new WaitForSeconds(clip.length); yield return new WaitForSeconds(clip.length / pitch);
Delete(); Delete();
} }
} }

View file

@ -1,5 +1,5 @@
ManifestFileVersion: 0 ManifestFileVersion: 0
CRC: 2362860034 CRC: 1243838037
AssetBundleManifest: AssetBundleManifest:
AssetBundleInfos: AssetBundleInfos:
Info_0: Info_0:

View file

@ -1,12 +1,12 @@
ManifestFileVersion: 0 ManifestFileVersion: 0
CRC: 4200721013 CRC: 1167382196
Hashes: Hashes:
AssetFileHash: AssetFileHash:
serializedVersion: 2 serializedVersion: 2
Hash: 996e27defaede200a0288a246ceb7785 Hash: 91ca0253f29ae5f7a1df107a25dc7c75
TypeTreeHash: TypeTreeHash:
serializedVersion: 2 serializedVersion: 2
Hash: 77ce82dcd01e7c9c803f77daf119d967 Hash: 222d3ac4260743916f7d5e044ddd31d4
HashAppended: 0 HashAppended: 0
ClassTypes: ClassTypes:
- Class: 1 - Class: 1

Binary file not shown.

View file

@ -1,12 +1,12 @@
ManifestFileVersion: 0 ManifestFileVersion: 0
CRC: 1499416464 CRC: 149759227
Hashes: Hashes:
AssetFileHash: AssetFileHash:
serializedVersion: 2 serializedVersion: 2
Hash: 7dbbb737179d1cc7db1edb0a31c7e1ab Hash: 462778359784eea47ee51c3c63402505
TypeTreeHash: TypeTreeHash:
serializedVersion: 2 serializedVersion: 2
Hash: 2b92e6f111bbb24dabe3a2c56ea02b28 Hash: fc2a2e95963d1b3faf439c84ecb440b4
HashAppended: 0 HashAppended: 0
ClassTypes: ClassTypes:
- Class: 1 - Class: 1

View file

@ -1,12 +1,12 @@
ManifestFileVersion: 0 ManifestFileVersion: 0
CRC: 3719062968 CRC: 1663830856
Hashes: Hashes:
AssetFileHash: AssetFileHash:
serializedVersion: 2 serializedVersion: 2
Hash: eee37f302cb3e8cbe079bb5500cb45ed Hash: 688c0aa50fbd25fe17346b36c6bf2176
TypeTreeHash: TypeTreeHash:
serializedVersion: 2 serializedVersion: 2
Hash: 1341d321cd8444a4f78a51a8a0c6daff Hash: 51d10f004f46f35e758498b711eedb2f
HashAppended: 0 HashAppended: 0
ClassTypes: ClassTypes:
- Class: 1 - Class: 1

Binary file not shown.

View file

@ -1,12 +1,12 @@
ManifestFileVersion: 0 ManifestFileVersion: 0
CRC: 1942609421 CRC: 3480558300
Hashes: Hashes:
AssetFileHash: AssetFileHash:
serializedVersion: 2 serializedVersion: 2
Hash: 82fbc7ccb21aa83d8663117da7a24cbc Hash: cfaae0e9f9c81b2d255e8da0db667aae
TypeTreeHash: TypeTreeHash:
serializedVersion: 2 serializedVersion: 2
Hash: 558e305ab690efbcd3eff9455cc9f4dc Hash: a669441aae6ee39a06c1090efd6d25bc
HashAppended: 0 HashAppended: 0
ClassTypes: ClassTypes:
- Class: 1 - Class: 1

View file

@ -1,12 +1,12 @@
ManifestFileVersion: 0 ManifestFileVersion: 0
CRC: 2386252366 CRC: 1706991399
Hashes: Hashes:
AssetFileHash: AssetFileHash:
serializedVersion: 2 serializedVersion: 2
Hash: 6f57f7727dc4903488ea4956307caac1 Hash: ae519aaff83042895009b1d4dbae0a79
TypeTreeHash: TypeTreeHash:
serializedVersion: 2 serializedVersion: 2
Hash: 6f0c5ebd30d7d3be8aad1c86837b5cc9 Hash: 994ea96351ef9c039cf2db2caf2c1169
HashAppended: 0 HashAppended: 0
ClassTypes: ClassTypes:
- Class: 1 - Class: 1
@ -50,6 +50,12 @@ ClassTypes:
SerializeReferenceClassIdentifiers: SerializeReferenceClassIdentifiers:
- AssemblyName: Assembly-CSharp - AssemblyName: Assembly-CSharp
ClassName: HeavenStudio.Games.Minigame/Eligible ClassName: HeavenStudio.Games.Minigame/Eligible
- AssemblyName: Assembly-CSharp
ClassName: HeavenStudio.Util.SoundSequence
- AssemblyName: Assembly-CSharp
ClassName: HeavenStudio.Util.SoundSequence/SequenceClip
- AssemblyName: Assembly-CSharp
ClassName: HeavenStudio.Util.SoundSequence/SequenceKeyValue
Assets: Assets:
- Assets/Resources/Sprites/Games/FanClub/Animations/Arisa/Arrange/IdolBigCall1Arrange.anim - Assets/Resources/Sprites/Games/FanClub/Animations/Arisa/Arrange/IdolBigCall1Arrange.anim
- Assets/Resources/Sprites/Games/FanClub/Animations/Fan/Head/FanHead.controller - Assets/Resources/Sprites/Games/FanClub/Animations/Fan/Head/FanHead.controller

View file

@ -1,12 +1,12 @@
ManifestFileVersion: 0 ManifestFileVersion: 0
CRC: 1695511499 CRC: 3961443211
Hashes: Hashes:
AssetFileHash: AssetFileHash:
serializedVersion: 2 serializedVersion: 2
Hash: 24892cdb3bebaf86064204efdc197727 Hash: c56eae7afab3c51aa0d877ecb5fa6484
TypeTreeHash: TypeTreeHash:
serializedVersion: 2 serializedVersion: 2
Hash: 788d5843115df613774b9a4b273d2334 Hash: 9a9bdfe5fc84d62897a4e311b61519be
HashAppended: 0 HashAppended: 0
ClassTypes: ClassTypes:
- Class: 1 - Class: 1