public virtual void Update(LevelTime levelTime) { songPosition = levelTime.SongPosition; // used in draw call /* I'm either insane or insanely brilliant, but it's probably the former. * The problem: Replays kept desyncing because spawner movement was still * framerate-dependent due to accumulating floating point errors * (or something like that). * A normal person would've just locked the update loop to n ticks/s * and called it a day, but in MonoGame, as far as I'm aware, * that also caps the framerate at n fps, and I didn't want to do that. * Instead, I decided to calculate the spawner positions independent of * game state to sidestep this problem. * To do this, I turn the SR and TW node lists into piecewise functions, * multiply them to get a combined movement function, and then, * for each frame, multiply the rotation/ms factor by the integral * of that function between 0 and the current song position. * This appears to have sorted out my issues for the most part. Spawner * movement still varies a little bit in moments with high SR*TW values, * but since these variances don't accumulate anymore, it always * catches itself when the rotation slows down. */ var totalIntegral = combinedSpinWarpFunction.IntegralTo(songPosition); for (int i = 0; i < Spawners.Count; i++) { var spawner = Spawners[i]; if (spawner.IsStillInUse(levelTime.SongPosition) || spawner.ActiveFlares.Count > 0) { spawner.Angle = (float)(spawnerAngles[i] + MathEx.WrapAngle(totalIntegral * Spawner.RotationPerMs)); spawner.Update(levelTime); } } }
/// <summary> /// Creates one wave of a shot. /// </summary> public List <Bullet> Create(Vector2 spawnPoint, float spawnerAngle, Vector2 playerCenter, LevelColors colors) { var bullets = new List <Bullet>(Amount); double waveAngle = spawnerAngle + (90 * MathEx.DegToRad); if (Amount > 1) { waveAngle += Angle / -2; } if (Aim == Aim.Player) { float playerAngle = (float)Math.Atan2( spawnPoint.Y - playerCenter.Y, spawnPoint.X - playerCenter.X) - spawnerAngle; waveAngle += playerAngle; } MathEx.WrapAngle(waveAngle); var angleStep = Angle / (Amount - 1); for (int i = 0; i < Amount; i++) { var bulletAngle = waveAngle + Offset; if (BulletType == BulletType.Homing) { bullets.Add(CreateHomingBullet(spawnPoint, (float)bulletAngle, (float)Speed, (float)HomingLifespan, colors)); } else { bullets.Add(CreateBullet(BulletType, spawnPoint, (float)bulletAngle, (float)Speed, colors)); } waveAngle += angleStep; } return(bullets); }