Change event params a bit and fix cam zooming

This commit is contained in:
Ralty
2025-06-30 10:49:38 +07:00
parent a6fcb3d237
commit 241b87ad58
9 changed files with 109 additions and 72 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ class EventsData {
],
"BPM Change" => [{name: "Target BPM", type: TFloat(1.00, 9999, 0.001, 3), defValue: 100}],
"Continuous BPM Change" => [{name: "Target BPM", type: TFloat(1.00, 9999, 0.001, 3), defValue: 100}, {name: "Time (steps)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4}],
"Time Signature Change" => [{name: "Target Beat Count", type: TFloat(1), defValue: 4}, {name: "Target Step Count", type: TFloat(1), defValue: 4}],
"Time Signature Change" => [{name: "Target Numerator", type: TFloat(1), defValue: 4}, {name: "Target Denominator", type: TFloat(1), defValue: 4}],
"Scroll Speed Change" => [
{name: "Tween Speed?", type: TBool, defValue: true},
{name: "New Speed", type: TFloat(0.01, 99, 0.01, 2), defValue: 1.},
+34 -14
View File
@@ -6,6 +6,12 @@ import funkin.backend.chart.ChartData;
import funkin.backend.system.interfaces.IBeatReceiver;
import funkin.editors.charter.Charter;
enum BeatType {
STEP;
BEAT;
MEASURE;
}
@:structInit
class BPMChangeEvent
{
@@ -25,6 +31,16 @@ class BPMChangeEvent
final class Conductor
{
public static function getBeats(?every:BeatType, interval:Float, offset:Float = 0):Float {
final beat = switch(every) {
case MEASURE: curMeasureFloat;
case STEP: curStepFloat;
default: curBeatFloat;
}
if (interval <= 0) return beat - offset;
else return Math.floor((beat - offset) * interval) / interval;
}
/**
* FlxSignals
*/
@@ -96,12 +112,21 @@ final class Conductor
return bpmChangeMap.length == 0 ? 4 : bpmChangeMap[curChangeIndex].beatsPerMeasure;
/**
* Number of steps per beat (bottom number in time signature). Defaults to 4.
* Number of steps per beat. Defaults to 4.
* Not a divisor number for time signature, it does the complete opposite.
* It's because CNE Conductor is based in sixteenth note instead of beat.
*/
public static var stepsPerBeat(get, never):Float;
public static var stepsPerBeat(get, never):Int;
private static function get_stepsPerBeat()
return bpmChangeMap.length == 0 ? 4 : bpmChangeMap[curChangeIndex].stepsPerBeat;
/**
* How much value notes to divide for beat (bottom or divisor number in time signature).
* Only for a convinient way to access divisor instead of multiply by steps per beat.
*/
public static var denominator(get, never):Int;
private static function get_denominator() return Math.floor(16 / stepsPerBeat);
/**
* Current step
*/
@@ -231,13 +256,13 @@ final class Conductor
if (curChange.songTime != time) curChange = mapBPMChange(curChange, time, curChange.bpm);
curChange.beatsPerMeasure = params[0];
curChange.stepsPerBeat = params[1];
curChange.stepsPerBeat = Math.floor(16 / params[1]); // convert from denominator to stepsPerBeat
curChange.stepTime = CoolUtil.floorInt(curChange.stepTime + .99998);
curChange.beatTime = CoolUtil.floorInt(curChange.beatTime + .99998);
curChange.measureTime = CoolUtil.floorInt(curChange.measureTime + .99998);
} else if (name == "Continuous BPM Change") {
}
else if (name == "Continuous BPM Change") {
var prevBPM = curChange.bpm;
if (curChange.bpm == params[0]) {
invalidEvents.push(e);
@@ -266,7 +291,7 @@ final class Conductor
bpmChangeMap = [curChange];
invalidEvents = [];
for(event in Charter.instance.eventsGroup.members) {
for (event in Charter.instance.eventsGroup.members) {
event.events.sort(function(a, b) {
if (MathUtil.equal(a.time, b.time)) {
if (a.name == "Continuous BPM Change") return 1;
@@ -282,11 +307,8 @@ final class Conductor
if ((e.name == "BPM Change" || e.name == "Time Signature Change" || e.name == "Continuous BPM Change")) {
curChange = mapEvent(e, curChange);
}
}
}
}
private static var elapsed:Float;
@@ -303,12 +325,10 @@ final class Conductor
return;
}
if (lastSongPos != (lastSongPos = FlxG.sound.music.time - songOffset)) {
// update conductor
if (lastSongPos != (lastSongPos = FlxG.sound.music.time - songOffset))
songPosition = lastSongPos;
} else {
else
songPosition += songOffset + elapsed * 1000;
}
}
private static function onStateSwitch(newState:FlxState) {
@@ -13,7 +13,7 @@ class ConductorInfo extends FramerateCategory {
_text += '\n - ${Conductor.curStep} steps';
_text += '\n - ${Conductor.curMeasure} measures';
_text += '\nCurrent BPM: ${Conductor.bpm}';
_text += '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.stepsPerBeat}';
_text += '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.denominator}';
this.text.text = _text;
super.__enterFrame(t);
+7 -6
View File
@@ -1243,12 +1243,13 @@ class Charter extends UIState {
for (strumLine in strumLines.members) strumLine.vocals.pause();
}
songPosInfo.text = '${CoolUtil.timeToStr(Conductor.songPosition)} / ${CoolUtil.timeToStr(songLength)}'
+ '\nStep: ${curStep}'
+ '\nBeat: ${curBeat}'
+ '\nMeasure: ${curMeasure}'
+ '\nBPM: ${Math.floor(Conductor.bpm*1000)/1000}'
+ '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.stepsPerBeat}';
var curChange = Conductor.curChange;
songPosInfo.text = '${CoolUtil.timeToStr(songPos)} / ${CoolUtil.timeToStr(songLength)}'
+ '\nStep: $curStep'
+ '\nBeat: $curBeat'
+ '\nMeasure: $curMeasure'
+ '\nBPM: ${(curChange != null && curChange.continuous && curChange.endSongTime > songPos) ? CoolUtil.quantize(Conductor.bpm, 2) : Conductor.bpm}'
+ '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.denominator}';
if (charterCamera.zoom != (charterCamera.zoom = lerp(charterCamera.zoom, __camZoom, __firstFrame ? 1 : 0.125)))
updateDisplaySprites();
@@ -140,7 +140,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
graphic = defaultPath;
}
return new FlxSprite().loadGraphic(graphic);
return new FlxSprite(graphic);
}
/**
@@ -154,7 +154,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
public static function getEventComponent(type:String, x:Float = 0.0, y:Float = 0.0) {
var componentPath = Paths.image("editors/charter/event-icons/components/" + type);
if(Assets.exists(componentPath))
return new FlxSprite(x, y).loadGraphic(componentPath);
return new FlxSprite(x, y, componentPath);
Logs.trace('Could not find component $type', WARNING);
return null;
@@ -423,12 +423,10 @@ class EventNumber extends FlxSprite {
this.align = align;
this.spacing = spacing;
if (number == 0) {
this.digits.insert(0, 0);
} else {
}
else {
var decimals:Float = FlxMath.roundDecimal(Math.abs(number % 1), precision);
if (decimals > 0) this.digits.insert(0, FRAME_POINT);
while(decimals > 0) {
@@ -15,7 +15,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
public var songNameTextBox:UITextBox;
public var bpmStepper:UINumericStepper;
public var beatsPerMeasureStepper:UINumericStepper;
public var stepsPerBeatStepper :UINumericStepper;
public var denominatorStepper :UINumericStepper;
public var needsVoicesCheckbox:UICheckbox;
public var customPropertiesButtonList:UIButtonList<PropertyButton>;
@@ -63,10 +63,10 @@ class CharterMetaDataScreen extends UISubstateWindow {
add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22));
stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, metadata.stepsPerBeat, 1, 0, 1, null, 54);
add(stepsPerBeatStepper);
denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, Math.floor(16 / metadata.stepsPerBeat), 1, 0, 1, null, 54);
add(denominatorStepper);
needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", metadata.needsVoices);
needsVoicesCheckbox = new UICheckbox(denominatorStepper.x + 80 + 26, denominatorStepper.y, "Voices", metadata.needsVoices);
add(needsVoicesCheckbox);
addLabelOn(needsVoicesCheckbox, "Needs Voices");
needsVoicesCheckbox.y += 6; needsVoicesCheckbox.x += 4;
@@ -142,7 +142,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
}
public function saveMeta() {
for (stepper in [bpmStepper, beatsPerMeasureStepper, stepsPerBeatStepper])
for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper])
@:privateAccess stepper.__onChange(stepper.label.text);
var customVals = {};
@@ -154,7 +154,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
name: songNameTextBox.label.text,
bpm: bpmStepper.value,
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
stepsPerBeat: Std.int(stepsPerBeatStepper.value),
stepsPerBeat: Std.int(16 / denominatorStepper.value),
needsVoices: needsVoicesCheckbox.checked,
displayName: displayNameTextBox.label.text,
icon: iconTextBox.label.text,
@@ -19,7 +19,7 @@ class SongCreationScreen extends UISubstateWindow {
public var songNameTextBox:UITextBox;
public var bpmStepper:UINumericStepper;
public var beatsPerMeasureStepper:UINumericStepper;
public var stepsPerBeatStepper :UINumericStepper;
public var denominatorStepper :UINumericStepper;
public var needsVoicesCheckbox:UICheckbox;
public var instExplorer:UIFileExplorer;
public var voicesExplorer:UIFileExplorer;
@@ -79,12 +79,12 @@ class SongCreationScreen extends UISubstateWindow {
songDataGroup.add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22));
stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54);
songDataGroup.add(stepsPerBeatStepper);
denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54);
songDataGroup.add(denominatorStepper);
var voicesUIText:UIText = null;
needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", true);
needsVoicesCheckbox = new UICheckbox(denominatorStepper.x + 80 + 26, denominatorStepper.y, "Voices", true);
needsVoicesCheckbox.onChecked = function(checked) {
if (voicesExplorer == null) return;
@@ -243,14 +243,14 @@ class SongCreationScreen extends UISubstateWindow {
}
function saveSongInfo() {
for (stepper in [bpmStepper, beatsPerMeasureStepper, stepsPerBeatStepper])
for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper])
@:privateAccess stepper.__onChange(stepper.label.text);
var meta:ChartMetaData = {
name: songNameTextBox.label.text,
bpm: bpmStepper.value,
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
stepsPerBeat: Std.int(stepsPerBeatStepper.value),
stepsPerBeat: Std.int(16 / denominatorStepper.value),
needsVoices: needsVoicesCheckbox.checked,
displayName: displayNameTextBox.label.text,
icon: iconTextBox.label.text,
+49 -31
View File
@@ -224,23 +224,29 @@ class PlayState extends MusicBeatState
*/
public var camZooming:Bool = false;
/**
* Interval of cam zooming (beats).
* For example: if set to 4, the camera will zoom every 4 beats.
* Interval of cam zooming (in conductor values).
* Example: If Interval is 1 and Beat Type is on MEASURE, it'll zoom every a measure.
* NOTE: Will set to 4 if not found any other time signatures unlike 4/4
*/
public var camZoomingInterval:Int = 4;
public var camZoomingInterval:Float = 1;
/**
* Number of Conductor values to offset camZooming by.
*/
public var camZoomingOffset:Float = 0;
/**
* Beat type for interval of cam zooming.
* Example: If Beat Type is on STEP and Interval is 2, it'll zoom every 2 steps.
* NOTE: Will set to BEAT if not found any other time signatures unlike 4/4
*/
public var camZoomingEvery:BeatType = MEASURE;
/**
* Stores what was the last beat for the cam zooming intervals.
*/
public var camZoomingLastBeat:Float;
/**
* How strong the cam zooms should be (defaults to 1)
*/
public var camZoomingStrength:Float = 1;
/**
* Number of Beats to offset camZooming by.
* Will automatically be set when a Time Signature Change Occurs.
*/
public var camZoomingOffset:Float = 0;
/**
* The curBeat position of the last Time Signature Change that occured
*/
public var lastTimeSigBeat:Float = 0;
/**
* Maximum amount of zoom for the camera.
*/
@@ -1020,21 +1026,34 @@ class PlayState extends MusicBeatState
{
if (songData == null) songData = SONG;
events = songData.events != null ? [for(e in songData.events) e] : [];
// get first camera focus
for(e in events) {
if (e.time > 10) break;
if (e.name == "Camera Movement") {
executeEvent(e);
break;
var foundCam = false;
var foundSigs = songData.meta.beatsPerMeasure.getDefault(4) != 4 || songData.meta.stepsPerBeat.getDefault(4) != 4;
if (events == null) events = [];
else events = [
for (e in songData.events) {
switch (e.name) {
case "Camera Movement": if (!foundCam && e.time < 10) {
foundCam = true;
executeEvent(e);
}
case "Time Signature Change": if (!foundSigs && (e.params[0] != 4 || e.params[1] != 4)) {
foundSigs = true;
}
}
e;
}
];
if (!foundSigs) {
camZoomingInterval = 4;
camZoomingEvery = BEAT;
}
events.sort(function(p1, p2) {
return FlxSort.byValues(FlxSort.DESCENDING, p1.time, p2.time);
});
camZoomingInterval = cast songData.meta.beatsPerMeasure.getDefault(4);
curSong = songData.meta.name.toLowerCase();
inst = FlxG.sound.load(Paths.inst(SONG.meta.name, difficulty));
@@ -1284,6 +1303,15 @@ class PlayState extends MusicBeatState
}
}
if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom) {
var beat = Conductor.getBeats(camZoomingEvery, camZoomingInterval, camZoomingOffset);
if (camZoomingLastBeat != beat) {
camZoomingLastBeat = beat;
FlxG.camera.zoom += 0.015 * camZoomingStrength;
camHUD.zoom += 0.03 * camZoomingStrength;
}
}
if (doIconBop)
for (icon in iconArray)
if (icon.updateBump != null)
@@ -1470,9 +1498,6 @@ class PlayState extends MusicBeatState
if (strumLines.members[event.params[0]] != null && strumLines.members[event.params[0]].characters != null)
for (char in strumLines.members[event.params[0]].characters)
if (char != null) char.playAnim(event.params[1], event.params[2], null);
case "Time Signature Change":
lastTimeSigBeat = Conductor.getTimeInBeats(event.time);
// the rest is automatically handled by conductor
case "Unknown": // nothing
}
}
@@ -1885,13 +1910,6 @@ class PlayState extends MusicBeatState
override function beatHit(curBeat:Int)
{
super.beatHit(curBeat);
camZoomingOffset = curBeat - lastTimeSigBeat;
if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom && camZoomingOffset % camZoomingInterval == 0) {
FlxG.camera.zoom += 0.015 * camZoomingStrength;
camHUD.zoom += 0.03 * camZoomingStrength;
}
if (doIconBop)
for (icon in iconArray)
@@ -53,7 +53,7 @@ class GameplayOptions extends OptionsScreen {
__lastBeat = Conductor.curBeat;
}
var beat = Math.floor(Conductor.getStepForTime(FlxG.sound.music.time) / Conductor.stepsPerBeat);
var beat = Math.floor(Conductor.getTimeInBeats(FlxG.sound.music.time));
if (__lastSongBeat != beat) {
__metronome.replay();
__lastSongBeat = beat;