diff --git a/Assets/Resources/Music/HS_Title_screen_WIP.mp3 b/Assets/Resources/Music/HS_Title_screen_WIP.mp3 deleted file mode 100644 index 65e6acdbd..000000000 Binary files a/Assets/Resources/Music/HS_Title_screen_WIP.mp3 and /dev/null differ diff --git a/Assets/Resources/Music/Title Theme Finished.wav b/Assets/Resources/Music/Title Theme Finished.wav new file mode 100644 index 000000000..c3bb00169 Binary files /dev/null and b/Assets/Resources/Music/Title Theme Finished.wav differ diff --git a/Assets/Resources/Music/HS_Title_screen_WIP.mp3.meta b/Assets/Resources/Music/Title Theme Finished.wav.meta similarity index 91% rename from Assets/Resources/Music/HS_Title_screen_WIP.mp3.meta rename to Assets/Resources/Music/Title Theme Finished.wav.meta index d39bb50f0..9722e3e47 100644 --- a/Assets/Resources/Music/HS_Title_screen_WIP.mp3.meta +++ b/Assets/Resources/Music/Title Theme Finished.wav.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5599aff283b14614a9c2aac420a4ab7e +guid: 44ffe30fcab40d049881a73909d21a8e AudioImporter: externalObjects: {} serializedVersion: 6 diff --git a/Assets/Resources/Sprites/Menu/HS_Menu_Mockup_BG.png b/Assets/Resources/Sprites/Menu/HS_Menu_Mockup_BG.png new file mode 100644 index 000000000..da34af411 Binary files /dev/null and b/Assets/Resources/Sprites/Menu/HS_Menu_Mockup_BG.png differ diff --git a/Assets/Resources/Sprites/Menu/placeholder.jpg.meta b/Assets/Resources/Sprites/Menu/HS_Menu_Mockup_BG.png.meta similarity index 98% rename from Assets/Resources/Sprites/Menu/placeholder.jpg.meta rename to Assets/Resources/Sprites/Menu/HS_Menu_Mockup_BG.png.meta index b8af3da0d..8c900eae0 100644 --- a/Assets/Resources/Sprites/Menu/placeholder.jpg.meta +++ b/Assets/Resources/Sprites/Menu/HS_Menu_Mockup_BG.png.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5aa9095149b92d34ca59802672e4f0cf +guid: 81664847397e71e4ab2b5fccc793e56c TextureImporter: internalIDToNameTable: [] externalObjects: {} diff --git a/Assets/Resources/Sprites/Menu/anims/Logo.controller b/Assets/Resources/Sprites/Menu/anims/Logo.controller index 618684013..60c83d2bf 100644 --- a/Assets/Resources/Sprites/Menu/anims/Logo.controller +++ b/Assets/Resources/Sprites/Menu/anims/Logo.controller @@ -1,5 +1,31 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1102 &-6360799083277375346 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: idle + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 875860c524713964ab714c92b453d20f, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: --- !u!1107 &-894046212993577963 AnimatorStateMachine: serializedVersion: 6 @@ -12,16 +38,19 @@ AnimatorStateMachine: - serializedVersion: 1 m_State: {fileID: 1153167672967841192} m_Position: {x: 250, y: 30, z: 0} + - serializedVersion: 1 + m_State: {fileID: -6360799083277375346} + m_Position: {x: 290, y: 130, z: 0} m_ChildStateMachines: [] m_AnyStateTransitions: [] m_EntryTransitions: [] m_StateMachineTransitions: {} m_StateMachineBehaviours: [] m_AnyStatePosition: {x: 50, y: 20, z: 0} - m_EntryPosition: {x: 50, y: 120, z: 0} + m_EntryPosition: {x: 50, y: 130, z: 0} m_ExitPosition: {x: 800, y: 120, z: 0} m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} - m_DefaultState: {fileID: 1153167672967841192} + m_DefaultState: {fileID: -6360799083277375346} --- !u!91 &9100000 AnimatorController: m_ObjectHideFlags: 0 diff --git a/Assets/Resources/Sprites/Menu/anims/idle.anim b/Assets/Resources/Sprites/Menu/anims/idle.anim index 684acefaf..c044b95f6 100644 --- a/Assets/Resources/Sprites/Menu/anims/idle.anim +++ b/Assets/Resources/Sprites/Menu/anims/idle.anim @@ -6,7 +6,7 @@ AnimationClip: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: + m_Name: idle serializedVersion: 6 m_Legacy: 0 m_Compressed: 0 diff --git a/Assets/Resources/Sprites/Menu/placeholder.jpg b/Assets/Resources/Sprites/Menu/placeholder.jpg deleted file mode 100644 index 88ed2f0b0..000000000 Binary files a/Assets/Resources/Sprites/Menu/placeholder.jpg and /dev/null differ diff --git a/Assets/Scenes/Menu.unity b/Assets/Scenes/Menu.unity index 9292e3b04..a230b4885 100644 --- a/Assets/Scenes/Menu.unity +++ b/Assets/Scenes/Menu.unity @@ -217,7 +217,8 @@ GameObject: - component: {fileID: 456377512} - component: {fileID: 456377511} - component: {fileID: 456377513} - m_Layer: 0 + - component: {fileID: 456377514} + m_Layer: 13 m_Name: appLogo m_TagString: Untagged m_Icon: {fileID: 0} @@ -261,8 +262,8 @@ SpriteRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 + m_SortingLayerID: 610346305 + m_SortingLayer: 1 m_SortingOrder: 0 m_Sprite: {fileID: 21300000, guid: d8a7801991b67b441a0f24aa1b61a191, type: 3} m_Color: {r: 1, g: 1, b: 1, a: 1} @@ -284,7 +285,7 @@ Transform: m_GameObject: {fileID: 456377510} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 1.5, z: 0} - m_LocalScale: {x: 0.5, y: 0.5, z: 0.4936359} + m_LocalScale: {x: 0.5, y: 0.5, z: 0} m_Children: [] m_Father: {fileID: 660691654} m_RootOrder: 0 @@ -308,6 +309,18 @@ Animator: m_HasTransformHierarchy: 1 m_AllowConstantClipSamplingOptimization: 1 m_KeepAnimatorControllerStateOnDisable: 0 +--- !u!114 &456377514 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 456377510} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c24dbc5920cd0640bdc60049a81155d, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &660691652 GameObject: m_ObjectHideFlags: 0 @@ -635,7 +648,7 @@ GameObject: - component: {fileID: 793941019} - component: {fileID: 793941018} m_Layer: 0 - m_Name: placeholder + m_Name: bg m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -681,12 +694,12 @@ SpriteRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 - m_Sprite: {fileID: 21300000, guid: 5aa9095149b92d34ca59802672e4f0cf, type: 3} + m_Sprite: {fileID: 21300000, guid: 81664847397e71e4ab2b5fccc793e56c, type: 3} m_Color: {r: 1, g: 1, b: 1, a: 1} m_FlipX: 0 m_FlipY: 0 m_DrawMode: 0 - m_Size: {x: 19.2, y: 12.8} + m_Size: {x: 19.2, y: 10.8} m_AdaptiveModeThreshold: 0.5 m_SpriteTileMode: 0 m_WasSpriteAssigned: 1 @@ -701,7 +714,7 @@ Transform: m_GameObject: {fileID: 793941017} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalScale: {x: 2, y: 2, z: 2} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 3 @@ -733,7 +746,7 @@ AudioSource: m_Enabled: 1 serializedVersion: 4 OutputAudioMixerGroup: {fileID: 0} - m_audioClip: {fileID: 8300000, guid: 5599aff283b14614a9c2aac420a4ab7e, type: 3} + m_audioClip: {fileID: 8300000, guid: 44ffe30fcab40d049881a73909d21a8e, type: 3} m_PlayOnAwake: 1 m_Volume: 1 m_Pitch: 1 @@ -1340,3 +1353,56 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2143530439 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2143530441} + - component: {fileID: 2143530440} + m_Layer: 0 + m_Name: conductor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2143530440 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2143530439} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3448fd31b0f9dd84fafc6dacb25f3896, type: 3} + m_Name: + m_EditorClassIdentifier: + songBpm: 114 + secPerBeat: 0 + songPosition: 0 + songPositionInBeats: 0 + musicSource: {fileID: 844289316} + firstBeatOffset: 0 + isPlaying: 1 + isPaused: 0 + metronome: 0 + timeSinceLastTempoChange: 0 +--- !u!4 &2143530441 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2143530439} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 3.8093402, y: -0.72132933, z: 78.88945} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Scripts/Menu/LogoAnimator.cs b/Assets/Scripts/Menu/LogoAnimator.cs new file mode 100644 index 000000000..90ae4c620 --- /dev/null +++ b/Assets/Scripts/Menu/LogoAnimator.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using UnityEngine; + +public class LogoAnimator : MonoBehaviour +{ + // Start is called before the first frame update + void Start() + { + Animator animator = gameObject.GetComponent(); + animator.Play("Beat"); + } + + void Update() + { + if (HeavenStudio.MenuConductor.instance.songPositionInBeats % 1f == 0f) + { + //animator.Play("Beat"); + Debug.Log("Beat"); + } + } +} diff --git a/Assets/Scripts/Menu/LogoAnimator.cs.meta b/Assets/Scripts/Menu/LogoAnimator.cs.meta new file mode 100644 index 000000000..cea94c6bb --- /dev/null +++ b/Assets/Scripts/Menu/LogoAnimator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c24dbc5920cd0640bdc60049a81155d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Menu/MenuConductor.cs b/Assets/Scripts/Menu/MenuConductor.cs new file mode 100644 index 000000000..f4c2f909e --- /dev/null +++ b/Assets/Scripts/Menu/MenuConductor.cs @@ -0,0 +1,364 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Audio; + +using Starpelly; + +namespace HeavenStudio +{ + // [RequireComponent(typeof(AudioSource))] + public class MenuConductor : MonoBehaviour + { + // Song beats per minute + // This is determined by the song you're trying to sync up to + public float songBpm; + + // The number of seconds for each song beat + public float secPerBeat; + + // The number of seconds for each song beat, inversely scaled to song pitch (higer pitch = shorter time) + public float pitchedSecPerBeat => (secPerBeat / musicSource.pitch); + + // Current song position, in seconds + private float songPos; // for Conductor use only + public float songPosition; + + // Current song position, in beats + private float songPosBeat; // for Conductor use only + public float songPositionInBeats; + + // Current time of the song + private float time; + + // an AudioSource attached to this GameObject that will play the music. + public AudioSource musicSource; + + // The offset to the first beat of the song in seconds + public float firstBeatOffset; + + // Conductor instance + public static MenuConductor instance; + + // Conductor is currently playing song + public bool isPlaying; + + // Conductor is currently paused, but not fully stopped + public bool isPaused; + + // Last reported beat based on song position + private float lastReportedBeat = 0f; + + // Metronome tick sound enabled + public bool metronome = false; + + public float timeSinceLastTempoChange = 0; + + private bool beat; + + // private AudioDspTimeKeeper timeKeeper; + + void Start() + { + instance = this; + Play(0); + } + + public void SetBeat(float beat) + { + float secFromBeat = GetSongPosFromBeat(beat); + + if (musicSource.clip != null) + { + if (secFromBeat < musicSource.clip.length) + musicSource.time = secFromBeat; + else + musicSource.time = 0; + } + + GameManager.instance.SetCurrentEventToClosest(beat); + songPosBeat = beat; + songPositionInBeats = songPosBeat; + } + + public void Play(float beat) + { + bool negativeOffset = firstBeatOffset < 0f; + bool negativeStartTime = false; + + // Debug.Log("starting playback @ beat " + beat + ", offset is " + firstBeatOffset); + + var startPos = GetSongPosFromBeat(beat); + if (negativeOffset) + { + time = startPos; + } + else + { + negativeStartTime = startPos - firstBeatOffset < 0f; + + if (negativeStartTime) + time = startPos - firstBeatOffset; + else + time = startPos; + } + + //TODO: make this take into account past tempo changes + songPosBeat = GetBeatFromSongPos(time - firstBeatOffset); + // Debug.Log("corrected starting playback @ beat " + songPosBeat); + + isPlaying = true; + isPaused = false; + + if (SongPosLessThanClipLength(startPos)) + { + if (negativeOffset) + { + var musicStartTime = startPos + firstBeatOffset; + + if (musicStartTime < 0f) + { + musicSource.time = startPos; + musicSource.PlayScheduled(AudioSettings.dspTime - firstBeatOffset / musicSource.pitch); + } + else + { + musicSource.time = musicStartTime; + musicSource.PlayScheduled(AudioSettings.dspTime); + } + } + else + { + if (negativeStartTime) + { + musicSource.time = startPos; + } + else + { + musicSource.time = startPos + firstBeatOffset; + } + + musicSource.PlayScheduled(AudioSettings.dspTime); + } + } + + // GameManager.instance.SetCurrentEventToClosest(songPositionInBeats); + } + + public void Pause() + { + isPlaying = false; + isPaused = true; + + musicSource.Pause(); + } + + public void Stop(float time) + { + this.time = time; + + songPosBeat = 0; + songPositionInBeats = 0; + + isPlaying = false; + isPaused = false; + + musicSource.Stop(); + } + float test; + + public void Update() + { + secPerBeat = 60f / songBpm; + + if (isPlaying) + { + var dt = Time.unscaledDeltaTime * musicSource.pitch; + + time += dt; + + songPos = time; + songPosition = songPos; + + songPosBeat += (dt / secPerBeat); + songPositionInBeats = songPosBeat; + // songPositionInBeats = Time.deltaTime / secPerBeat; + + if (metronome) + { + if (ReportBeat(ref lastReportedBeat)) + { + Util.Jukebox.PlayOneShot("metronome"); + } + else if (songPositionInBeats < lastReportedBeat) + { + lastReportedBeat = Mathf.Round(songPositionInBeats); + } + } + } + } + + public bool ReportBeat(ref float lastReportedBeat, float offset = 0, bool shiftBeatToOffset = true) + { + bool result = songPositionInBeats + (shiftBeatToOffset ? offset : 0f) >= (lastReportedBeat) + 1f; + if (result) + { + lastReportedBeat += 1f; + if (lastReportedBeat < songPositionInBeats) + { + lastReportedBeat = Mathf.Round(songPositionInBeats); + } + } + return result; + } + + public float GetLoopPositionFromBeat(float beatOffset, float length) + { + return Mathf.Repeat((songPositionInBeats / length) + beatOffset, 1); + } + + public float GetPositionFromBeat(float startBeat, float length) + { + float a = Mathp.Normalize(songPositionInBeats, startBeat, startBeat + length); + return a; + } + + public float GetBeatFromPosition(float position, float startBeat, float length) + { + return Mathp.DeNormalize(position, startBeat, startBeat + length); + } + + public float GetPositionFromMargin(float targetBeat, float margin) + { + return GetPositionFromBeat(targetBeat - margin, margin); + } + + public float GetBeatFromPositionAndMargin(float position, float targetBeat, float margin) + { + return GetBeatFromPosition(position, targetBeat - margin, margin); + } + + private List GetSortedTempoChanges(Beatmap chart) + { + //iterate over all tempo changes, adding to counter + List tempoChanges = chart.tempoChanges; + tempoChanges.Sort((x, y) => x.beat.CompareTo(y.beat)); //sorts all tempo changes by ascending time (GameManager already does this but juste en cas...) + return tempoChanges; + } + + public float GetSongPosFromBeat(float beat) + { + Beatmap chart = GameManager.instance.Beatmap; + SetBpm(chart.bpm); + + //initial counter + float counter = 0f; + + //time of last tempo change, to know how much to add to counter + float lastTempoChangeBeat = 0f; + + //iterate over all tempo changes, adding to counter + List tempoChanges = GetSortedTempoChanges(chart); + foreach (var t in tempoChanges) + { + if (t.beat > beat) + { + // this tempo change is past our requested time, abort + break; + } + // Debug.Log("tempo change at " + t.beat); + + counter += (t.beat - lastTempoChangeBeat) * secPerBeat; + // Debug.Log("counter is now " + counter); + + // now update to new bpm + SetBpm(t.tempo); + lastTempoChangeBeat = t.beat; + } + + //passed all past tempo changes, now extrapolate from last tempo change until requested position + counter += (beat - lastTempoChangeBeat) * secPerBeat; + + // Debug.Log("GetSongPosFromBeat returning " + counter); + return counter; + } + + //thank you @wooningcharithri#7419 for the psuedo-code + private float BeatsToSecs(float beats, float bpm) + { + // Debug.Log("BeatsToSecs returning " + beats / bpm * 60); + return beats / bpm * 60f; + } + private float SecsToBeats(float s, float bpm) + { + // Debug.Log("SecsToBeats returning " + s / 60f / bpm); + return s / 60f * bpm; + } + + public float GetBeatFromSongPos(float seconds) + { + // Debug.Log("Getting beat of seconds " + seconds); + Beatmap chart = GameManager.instance.Beatmap; + float lastTempoChangeBeat = 0f; + float lastBpm = chart.bpm; + float counterSeconds = -firstBeatOffset; + + List tempoChanges = GetSortedTempoChanges(chart); + foreach (var t in tempoChanges) + { + float beatToNext = t.beat - lastTempoChangeBeat; + float secToNext = BeatsToSecs(beatToNext, lastBpm); + float nextSecs = counterSeconds + secToNext; + + // Debug.Log("nextSecs is " + nextSecs + ", seconds " + seconds); + if (nextSecs >= seconds) + break; + + lastTempoChangeBeat = t.beat; + lastBpm = t.tempo; + counterSeconds = nextSecs; + } + + // Debug.Log("lastTempoChangeBeat is " + lastTempoChangeBeat + ", counterSeconds is " + counterSeconds); + + return lastTempoChangeBeat + SecsToBeats(seconds - counterSeconds, lastBpm); + } + // + + // convert real seconds to beats + public float GetRestFromRealTime(float seconds) + { + return seconds/pitchedSecPerBeat; + } + + public void SetBpm(float bpm) + { + this.songBpm = bpm; + secPerBeat = 60f / songBpm; + } + + public void SetVolume(int percent) + { + musicSource.volume = percent / 100f; + } + + public float SongLengthInBeats() + { + if (!musicSource.clip) return 0; + return GetBeatFromSongPos(musicSource.clip.length); + } + + public bool SongPosLessThanClipLength(float t) + { + if (musicSource.clip != null) + return t < musicSource.clip.length; + else + return false; + } + + public bool NotStopped() + { + return Conductor.instance.isPlaying == true || Conductor.instance.isPaused == true; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Menu/MenuConductor.cs.meta b/Assets/Scripts/Menu/MenuConductor.cs.meta new file mode 100644 index 000000000..c83725fd6 --- /dev/null +++ b/Assets/Scripts/Menu/MenuConductor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3448fd31b0f9dd84fafc6dacb25f3896 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: