
AudioSettings.dspTime instead of Time.timeSinceLevelLoad to track the position in a song.SongManager class to track the position in a song, create notes, and other song management functions. // ( ) float songPosition; // ( ) float songPosInBeats; // float secPerBeat; // ( ) float dsptimesong; Start() function: void Start() { // // bpm secPerBeat = 60f / bpm; // dsptimesong = (float) AudioSettings.dspTime; // GetComponent<AudioSource>().Play(); } bpm to secPerBeat . Later secPerBeat will be used to calculate the position in the song in the beats, which is very important for creating notes.dsptimesong . We use AudioSettings.dspTime instead of Time.timeSinceLevelLoad , because Time.timeSinceLevelLoad updated only in each frame, and AudioSettings.dspTime updated more often, as this is an audio system timer. To keep track of the song, you need to use the audio timer. In this way, we can avoid the delay caused by the time difference between frame updates and audio updates.Update() function calculates the position in a song using AudioSettings.dspTime : void Update() { // songPosition = (float) (AudioSettings.dspTime - dsptimesong); // songPosInBeats = songPosition / secPerBeat; } dsptimesong ) from the current AudioSettings.dspTime . We got the position in seconds, but in the music world the notes are recorded in beats. Therefore, it is better to convert the position in seconds to position in beats. Dividing the songPosition by secPerBeat (second / (second / hit)), we get the position in beats.
songPosition == 1.75 ), then we know that we are in the position 1.75 ( songPosition ) / 0.5 ( secPerBeat ) = 3.5 beats, and it is necessary to create a beat note 3.5. // float bpm; // float[] notes; // , int nextIndex = 0; bpm is the number of beats per minute. As we have seen, for convenience they are converted to secPerBeat .notes is an array in which all positions of notes in beats are stored. For example, for the notes presented in the figure, the notes array will contain {1f, 2f, 2.5f, 3f, 3.5f, 4.5f} :
nextIndex is an integer needed to traverse the array. It is initialized to 0, because the next note to be created will be the first note of the song. When creating a note, the counter nextIndex incremented by one.Update() function. However, you first need to determine how many strokes will be shown in advance.
songPosInBeats = songPosition / secPerBeat; , the following lines: if (nextIndex < notes.Length && notes[nextIndex] < songPosInBeats + beatsShownInAdvance) { Instantiate( /* */ ); // nextIndex++; } nextIndex < notes.Length ). If the notes still remain, then we check if the song reached the beat at which the next note should be created ( notes[nextIndex] < songPosInBeats + beatsShownInAdvance ). If reached, create a note and increment nextIndex to keep track of the next note you want to create.MusicNote class and leave only the Update() function, in which we move every note: // void Update() { transform.position = Vector2.Lerp( SpawnPos, RemovePos, (BeatsShownInAdvance - (beatOfThisNote - songPosInBeats)) / BeatsShownInAdvance ); } 
notes arrays, deleting notes is performed by checking the position relative to the deletion line, long-duration notes are implemented by tracking the initial and final stroke, etc.Source: https://habr.com/ru/post/324798/
All Articles