fixing beat and measure lines with time sig changes (#2)

This commit is contained in:
TheZoroForce240
2025-01-11 14:16:46 -05:00
committed by GitHub
parent 0e47728d0f
commit 2f6207b11b
3 changed files with 203 additions and 61 deletions
+13 -1
View File
@@ -192,6 +192,7 @@ final class Conductor
beatsPerMeasure: song.meta.beatsPerMeasure.getDefault(4),
stepsPerBeat: CoolUtil.floorInt(song.meta.stepsPerBeat.getDefault(4))
};
curChangeIndex = 0;
bpmChangeMap = [curChange];
if (song.events == null) return;
@@ -450,8 +451,19 @@ final class Conductor
}
}
public static function getMeasuresInTime(measureTime:Float, from:Int = 0):Float {
var index = getMeasuresInChangeIndex(measureTime, from);
if (index == -1) return measureTime * (60000 / 100 / 4);
else if (index == 0) return measureTime * (15000 / bpmChangeMap[index].bpm) * bpmChangeMap[index].stepsPerBeat * bpmChangeMap[index].beatsPerMeasure;
else {
var change = bpmChangeMap[index];
var stepTime = change.stepTime + (measureTime - change.measureTime) * change.stepsPerBeat * change.beatsPerMeasure;
return getStepsWithBPMInTime(stepTime, index, getStepsWithIndexInBPM(stepTime, index));
}
}
public static function getBeatsInTime(beatTime:Float, from:Int = 0):Float {
var index = getStepsInChangeIndex(beatTime, from);
var index = getBeatsInChangeIndex(beatTime, from);
if (index == -1) return beatTime * (60000 / 100);
else if (index == 0) return beatTime * (15000 / bpmChangeMap[index].bpm) * bpmChangeMap[index].stepsPerBeat;
else {
+4 -3
View File
@@ -16,6 +16,7 @@ import flixel.input.keyboard.FlxKey;
import flixel.sound.FlxSound;
import flixel.math.FlxPoint;
import funkin.editors.charter.CharterBackdropGroup.CharterBackdropDummy;
import funkin.editors.charter.CharterBackdropGroup.CharterGridSeperatorBase;
import funkin.backend.system.Conductor;
import funkin.backend.chart.*;
import funkin.backend.chart.ChartData;
@@ -622,6 +623,8 @@ class Charter extends UIState {
gridBackdrops.bottomLimitY = __endStep * 40;
eventsBackdrop.bottomSeparator.y = gridBackdrops.bottomLimitY-2;
CharterGridSeperatorBase.lastConductorSprY = Math.NEGATIVE_INFINITY;
updateWaveforms();
}
@@ -1631,11 +1634,9 @@ class Charter extends UIState {
}
function _view_showeventSecSeparator(t) {
t.icon = (Options.charterShowSections = !Options.charterShowSections) ? 1 : 0;
eventsBackdrop.eventSecSeparator.visible = gridBackdrops.sectionsVisible = Options.charterShowSections;
}
function _view_showeventBeatSeparator(t) {
t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0;
eventsBackdrop.eventBeatSeparator.visible = gridBackdrops.beatsVisible = Options.charterShowBeats;
}
function _view_switchWaveformDetail(t) {
t.icon = (Options.charterLowDetailWaveforms = !Options.charterLowDetailWaveforms) ? 1 : 0;
@@ -1837,8 +1838,8 @@ class Charter extends UIState {
public function updateBPMEvents() {
buildEvents();
Conductor.mapBPMChanges(PlayState.SONG);
Conductor.changeBPM(PlayState.SONG.meta.bpm, cast PlayState.SONG.meta.beatsPerMeasure.getDefault(4), cast PlayState.SONG.meta.stepsPerBeat.getDefault(4));
Conductor.mapBPMChanges(PlayState.SONG);
refreshBPMSensitive();
}
@@ -12,8 +12,6 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
public var conductorSprY:Float = 0;
public var bottomLimitY:Float = 0;
public var sectionsVisible:Bool = true;
public var beatsVisible:Bool = true;
// Just here so you can update display sprites all dat and above
public var strumlinesAmount:Int = 0;
@@ -61,8 +59,6 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
grid.conductorFollowerSpr.y = conductorSprY;
grid.bottomSeparator.y = (grid.bottomLimit.y = bottomLimitY)-2;
grid.sectionSeparator.visible = sectionsVisible;
grid.beatSeparator.visible = beatsVisible;
grid.waveformSprite.shader = strumLine.waveformShader;
@@ -119,8 +115,7 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
public var waveformSprite:FlxSprite;
public var conductorFollowerSpr:FlxSprite;
public var beatSeparator:FlxBackdrop;
public var sectionSeparator:FlxBackdrop;
public var beatSeparator:CharterGridSeperator;
public var notesGroup:FlxTypedGroup<CharterNote> = new FlxTypedGroup<CharterNote>();
public var strumLine:CharterStrumline;
@@ -136,23 +131,14 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
waveformSprite.updateHitbox();
add(waveformSprite);
sectionSeparator = new FlxBackdrop(null, Y, 0, 0);
sectionSeparator.y = -2;
sectionSeparator.visible = Options.charterShowSections;
beatSeparator = new FlxBackdrop(null, Y, 0, 0);
beatSeparator.y = -1;
beatSeparator.visible = Options.charterShowBeats;
for(sep in [sectionSeparator, beatSeparator]) {
sep.makeSolid(1, 1, -1);
sep.alpha = 0.5;
sep.scrollFactor.set(1, 1);
sep.scale.set((4 * 40), sep == sectionSeparator ? 4 : 2);
sep.updateHitbox();
}
beatSeparator = new CharterGridSeperator();
beatSeparator.makeSolid(1, 1, -1);
beatSeparator.alpha = 0.5;
beatSeparator.scrollFactor.set(1, 1);
beatSeparator.scale.set((4 * 40), 2);
beatSeparator.updateHitbox();
add(beatSeparator);
add(sectionSeparator);
add(notesGroup);
bottomSeparator = new FlxSprite(0,-2);
@@ -200,15 +186,12 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
alpha = strumLine.strumLine.visible ? 0.9 : 0.4;
} else alpha = 0.9;
for (spr in [gridBackDrop, sectionSeparator, beatSeparator, topLimit, bottomLimit,
for (spr in [gridBackDrop, beatSeparator, topLimit, bottomLimit,
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite]) {
spr.x = x; if (spr != waveformSprite) spr.alpha = alpha;
spr.cameras = this.cameras;
}
sectionSeparator.spacing.y = (10 * Conductor.beatsPerMeasure * Conductor.stepsPerBeat) - 1;
beatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1;
topLimit.scale.set(4 * 40, Math.ceil(FlxG.height / cameras[0].zoom));
topLimit.updateHitbox();
topLimit.y = -topLimit.height;
@@ -237,6 +220,159 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
}
}
class CharterGridSeperatorBase extends FlxSprite {
private static var minStep:Float = 0;
private static var maxStep:Float = 0;
private static var minBeat:Float = 0;
private static var maxBeat:Float = 0;
private static var minMeasure:Float = 0;
private static var maxMeasure:Float = 0;
private static var lastMinBeat:Float = -1;
private static var lastMaxBeat:Float = -1;
private static var lastMinMeasure:Float = -1;
private static var lastMaxMeasure:Float = -1;
public static var lastConductorSprY:Float = Math.NEGATIVE_INFINITY;
private static var beatStepTimes:Array<Float> = [];
private static var measureStepTimes:Array<Float> = [];
private static var timeSignatureChangeGaps:Array<Float> = [];
private function recalculateBeats() {
var conductorSprY = Charter.instance.gridBackdrops.conductorSprY;
if (conductorSprY == lastConductorSprY) return;
var zoomOffset = ((FlxG.height * (1/cameras[0].zoom)) * 0.5);
minStep = (conductorSprY - zoomOffset)/40;
maxStep = (conductorSprY + zoomOffset)/40;
var minTime:Float = Conductor.getStepsInTime(minStep);
var maxTime:Float = Conductor.getStepsInTime(maxStep);
var minBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(minTime)];
var maxBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(maxTime)];
minBeat = Conductor.getTimeInBeats(minTime);
maxBeat = Conductor.getTimeInBeats(maxTime);
minMeasure = minBpmChange.measureTime + (minBeat - minBpmChange.beatTime) / minBpmChange.beatsPerMeasure;
maxMeasure = maxBpmChange.measureTime + (maxBeat - maxBpmChange.beatTime) / maxBpmChange.beatsPerMeasure;
//cap out the beats/measures at the end of the song
var endTime = Conductor.getStepsInTime(Charter.instance.__endStep);
var endBeat = Conductor.getTimeInBeats(endTime);
var endBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(endTime)];
var endMeasure = endBpmChange.measureTime + (endBeat - endBpmChange.beatTime) / endBpmChange.beatsPerMeasure;
if (maxBeat > endBeat) maxBeat = endBeat;
if (maxMeasure > endMeasure) maxMeasure = endMeasure;
if (minMeasure < 0) minMeasure = 0;
if (minBeat < 0) minBeat = 0;
//only calculate if needed
if ((minBeat != lastMinBeat) || (maxBeat != lastMaxBeat) || (minMeasure != lastMinMeasure) || (maxMeasure != lastMaxMeasure) || lastConductorSprY == Math.NEGATIVE_INFINITY) {
calculateTimeSignatureGaps();
calculateStepTimes();
lastMinBeat = minBeat;
lastMaxBeat = maxBeat;
lastMinMeasure = minMeasure;
lastMaxMeasure = maxMeasure;
}
lastConductorSprY = conductorSprY;
}
private inline function calculateTimeSignatureGaps() {
//for time signatures that start mid step
timeSignatureChangeGaps.splice(0, timeSignatureChangeGaps.length);
for (i => change in Conductor.bpmChangeMap) {
if (change.stepTime >= minStep && change.stepTime <= maxStep) {
//get step while ignoring the current change
var index = CoolUtil.boundInt(i-1, 0, Conductor.bpmChangeMap.length - 1);
var step = Conductor.getTimeWithBPMInSteps(change.songTime, index, Conductor.getTimeWithIndexInBPM(change.songTime, index));
if (Math.ceil(step) - step > 0) { //mid step change
timeSignatureChangeGaps.push(step);
}
}
}
}
private inline function calculateStepTimes() {
beatStepTimes.splice(0, beatStepTimes.length);
for (i in Math.floor(minBeat)...Math.ceil(maxBeat)) {
beatStepTimes.push(Conductor.getTimeInSteps(Conductor.getBeatsInTime(i)));
}
measureStepTimes.splice(0, measureStepTimes.length);
for (i in Math.floor(minMeasure)...Math.ceil(maxMeasure)) {
measureStepTimes.push(Conductor.getTimeInSteps(Conductor.getMeasuresInTime(i)));
}
}
override public function draw() {
//should only need to recalculate once per frame and will be shared across each instance
recalculateBeats();
drawTimeSignatureChangeGaps();
if (Options.charterShowBeats) drawBeats();
if (Options.charterShowSections) drawMeasures();
}
private function drawBeats(offset:Float = 0.0) {
for (i in beatStepTimes) {
y = (i*40)+offset;
super.draw();
}
}
private function drawMeasures(offset:Float = 0.0) {
for (i in measureStepTimes) {
y = (i*40)+offset;
super.draw();
}
}
private function drawTimeSignatureChangeGaps() {
if (timeSignatureChangeGaps.length == 0) return;
var prevColor = color;
var prevBlend = blend;
color = 0xFF888888;
blend = MULTIPLY;
for (step in timeSignatureChangeGaps) {
y = step*40;
var diff = Math.ceil(step) - step;
scale.y = diff*40;
updateHitbox();
super.draw();
}
color = prevColor;
blend = prevBlend;
}
}
class CharterGridSeperator extends CharterGridSeperatorBase {
override private function drawBeats(offset:Float = 0.0) {
scale.y = 2;
updateHitbox();
super.drawBeats(-2);
}
override private function drawMeasures(offset:Float = 0.0) {
scale.y = 4;
updateHitbox();
super.drawMeasures(-3);
}
}
class CharterBackdropDummy extends UISprite {
var parent:CharterBackdropGroup;
public function new(parent:CharterBackdropGroup) {
@@ -258,8 +394,7 @@ class CharterBackdropDummy extends UISprite {
}
class EventBackdrop extends FlxBackdrop {
public var eventBeatSeparator:FlxBackdrop;
public var eventSecSeparator:FlxBackdrop;
public var eventBeatSeparator:CharterEventGridSeperator;
public var topSeparator:FlxSprite;
public var bottomSeparator:FlxSprite;
@@ -269,25 +404,10 @@ class EventBackdrop extends FlxBackdrop {
alpha = 0.9;
// Separators
eventSecSeparator = new FlxBackdrop(null, Y, 0, 0);
eventSecSeparator.y = -2;
eventSecSeparator.visible = Options.charterShowSections;
eventBeatSeparator = new FlxBackdrop(null, Y, 0, 0);
eventBeatSeparator.y = -1;
eventBeatSeparator.visible = Options.charterShowBeats;
for(sep in [eventSecSeparator, eventBeatSeparator]) {
sep.makeSolid(1, 1, -1);
sep.alpha = 0.5;
sep.scrollFactor.set(1, 1);
}
eventSecSeparator.scale.set(20, 4);
eventSecSeparator.updateHitbox();
eventBeatSeparator.scale.set(10, 2);
eventBeatSeparator.updateHitbox();
eventBeatSeparator = new CharterEventGridSeperator();
eventBeatSeparator.makeSolid(1, 1, -1);
eventBeatSeparator.alpha = 0.5;
eventBeatSeparator.scrollFactor.set(1, 1);
bottomSeparator = new FlxSprite(0,-2);
bottomSeparator.makeSolid(1, 1, -1);
@@ -308,23 +428,32 @@ class EventBackdrop extends FlxBackdrop {
public override function draw() {
super.draw();
eventSecSeparator.spacing.y = (10 * Conductor.beatsPerMeasure * Conductor.stepsPerBeat) - 1;
eventBeatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1;
eventSecSeparator.cameras = cameras;
eventSecSeparator.x = (x+width) - 20;
if (eventSecSeparator.visible) eventSecSeparator.draw();
eventBeatSeparator.cameras = cameras;
eventBeatSeparator.x = (x+width) - 10;
if (eventBeatSeparator.visible) eventBeatSeparator.draw();
eventBeatSeparator.xPos = x+width;
eventBeatSeparator.draw();
topSeparator.x = (x+width) - 20;
topSeparator.cameras = this.cameras;
if (!eventSecSeparator.visible) topSeparator.draw();
if (!Options.charterShowSections) topSeparator.draw();
bottomSeparator.x = (x+width) - 20;
bottomSeparator.cameras = this.cameras;
bottomSeparator.draw();
}
}
class CharterEventGridSeperator extends CharterGridSeperatorBase {
public var xPos:Float = 0.0;
override private function drawBeats(offset:Float = 0.0) {
scale.set(10, 2);
updateHitbox();
x = xPos-10;
super.drawBeats(-2);
}
override private function drawMeasures(offset:Float = 0.0) {
scale.set(20, 4);
updateHitbox();
x = xPos-20;
super.drawMeasures(-3);
}
override private function drawTimeSignatureChangeGaps() {}
}