Hi, I usually don’t post so I’ll try to be as clearer as possible.
Premise:
I’m working on a project in which I want to move a player sprite based on music. It’s something like geometry dash, but I want to try to put slopes and different obstacles and animations. Lets say it’s an hybrid between geometry dash and the music levels of Rayman Legends. If the picture is not right there’s a game called <> that’s preatty similar to the idea of the project.
The question:
This is the script of my Music Controller:
public class Scr_MusicController : MonoBehaviour
{
public static Scr_MusicController instance;
// Variable Initialization
void Awake() {
instance = this;
au_musicSource = GetComponent<AudioSource>();
secPerBeat = 60f/bpm; // 1bpm = 1/60 [Hz] so 1 secPerBeat = 1/60bpm = 60s/60
// unitPerBeat is a normalized time quantity (1/x [1/s])
unitPerBeat = 1/secPerBeat;
}
// Update is called once per frame
void Update()
{
if(!gameHasStarted){
if(Keyboard.current.enterKey.isPressed){
songStart = (float) (AudioSettings.dspTime);
au_musicSource.Play();
gameHasStarted = true;
}
}
if(!au_musicSource.isPlaying) return;
songPosition = (float) (AudioSettings.dspTime - songStart);
songPositionInBeats = Mathf.FloorToInt( songPosition / secPerBeat);
}
}
It controls when the music starts and it calculates each frame the songPosition from the start. I’ve used AudioSettings.dspTime because I read that it works on a “different time level” than whatever Time.* function.
The idea I’ve used for the player movement is the sequent:
public class Scr_PlayerMovement_Legacy : MonoBehaviour
{
Rigidbody2D com_rb2D;
[Header("Velocity Parameters")]
public float velocityMultiplier;
float unitPerBeat;
public float offsetPosition;
void Awake() {
com_rb2D = GetComponent<Rigidbody2D>();
offsetPosition = this.transform.position.x;
}
void Start(){
// secPerBeat [Hz = 1/s]
// unitPerBeat is a normalized velocity (1/x [m/s]) 1f per beat
// unitPerBeat times a number gives the real velocity at which the player moves
unitPerBeat = 1 / Scr_MusicController.instance.secPerBeat;
}
void Update() {
if(!Scr_MusicController.instance.gameHasStarted){
return;
}
// Adding the offsetPosition to the PlayerMovement so that it starts from the correct position
// This can be rewritten so that less calculations must be taken in occurrence each beat
// On the x axis x_moveAmount = uPB [1/s] * songPos [s] * velocity [m/s]
com_rb2D.MovePosition(Vector2.right * (offsetPosition + unitPerBeat * (Scr_MusicController.instance.songPosition) * velocityMultiplier) );
}
}
My problem now is:
I found out that when implementing the y movement with some physics the player sprite stutters. I am aware that MovePosition should be used in FixedUpdate() and not in Update, but even then, nothing changes.
I thought that the problem could be of two kinds:
- Using different time implementations for the movement on the x axis and the y axis
- Since I’m not using discrete values when updating movement on the x axis each frame the computation of each frame is basically
finalposition = startingposition + position calculated based on songposition
I have gotten around the problem by moving the platforms instead of the player, but I worry that having 3 minutes long levels would require a lot of intern computations moving a lot of platforms or big chuncks of SpriteShapes.
My question:
Do you have any suggestions on how I could define discrete time variations for each frame, based on the time difference in each frame between AudioSettings.dspTime calls or more simply songposition values in each frame??
I tried something like this:
public SongInfo: Monobehaviour
{
double currentInterval;
double songStart, songPosition;
double lastValue, currentValue;
void Start(){
songStart = AudioSettings.dspTime;
}
void Update(){
currentInterval = songPosition - lastValue;
songPosition = AudioSettings.dspTime - songStart;
}
IEnumerator GetLastValue(){
yield return new WaitForEndOfFrame();
lastValue = songPosition;
}
}
but as I get from debugging even if using WaitForEndOfFrame() it doesn’t necessarely means that the code after yield return is called at the end of all the script calls in the current frame. I have looked at the Unity Order of Execution but still I can’t get my mind around it.