mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-02-12 14:33:03 +00:00
I discovered a new profiling tool and stayed up until 5 AM optimizing shit.
This commit is contained in:
parent
b42e4ceb67
commit
8dd07d2763
|
@ -363,7 +363,13 @@ class SongEventData
|
|||
* The timestamp of the event. The timestamp is in the format of the song's time format.
|
||||
*/
|
||||
@:alias("t")
|
||||
public var time:Float;
|
||||
public var time(default, set):Float;
|
||||
|
||||
function set_time(value:Float):Float
|
||||
{
|
||||
_stepTime = null;
|
||||
return time = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind of the event.
|
||||
|
@ -398,11 +404,13 @@ class SongEventData
|
|||
}
|
||||
|
||||
@:jignored
|
||||
public var stepTime(get, never):Float;
|
||||
var _stepTime:Null<Float> = null;
|
||||
|
||||
function get_stepTime():Float
|
||||
public function getStepTime(force:Bool = false):Float
|
||||
{
|
||||
return Conductor.getTimeInSteps(this.time);
|
||||
if (_stepTime != null && !force) return _stepTime;
|
||||
|
||||
return _stepTime = Conductor.getTimeInSteps(this.time);
|
||||
}
|
||||
|
||||
public inline function getDynamic(key:String):Null<Dynamic>
|
||||
|
@ -488,7 +496,13 @@ class SongNoteData
|
|||
* The timestamp of the note. The timestamp is in the format of the song's time format.
|
||||
*/
|
||||
@:alias("t")
|
||||
public var time:Float;
|
||||
public var time(default, set):Float;
|
||||
|
||||
function set_time(value:Float):Float
|
||||
{
|
||||
_stepTime = null;
|
||||
return time = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for the note. Represents the index on the strumline.
|
||||
|
@ -533,15 +547,18 @@ class SongNoteData
|
|||
this.kind = kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp of the note, in steps.
|
||||
*/
|
||||
@:jignored
|
||||
public var stepTime(get, never):Float;
|
||||
var _stepTime:Null<Float> = null;
|
||||
|
||||
function get_stepTime():Float
|
||||
/**
|
||||
* @param force Set to `true` to force recalculation (good after BPM changes)
|
||||
* @return The position of the note in the song, in steps.
|
||||
*/
|
||||
public function getStepTime(force:Bool = false):Float
|
||||
{
|
||||
return Conductor.getTimeInSteps(this.time);
|
||||
if (_stepTime != null && !force) return _stepTime;
|
||||
|
||||
return _stepTime = Conductor.getTimeInSteps(this.time);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -594,20 +611,34 @@ class SongNoteData
|
|||
return getStrumlineIndex(strumlineSize) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a hold note, this is the length of the hold note in steps.
|
||||
* @default 0 (not a hold note)
|
||||
*/
|
||||
public var stepLength(get, set):Float;
|
||||
@:jignored
|
||||
var _stepLength:Null<Float> = null;
|
||||
|
||||
function get_stepLength():Float
|
||||
/**
|
||||
* @param force Set to `true` to force recalculation (good after BPM changes)
|
||||
* @return The length of the hold note in steps, or `0` if this is not a hold note.
|
||||
*/
|
||||
public function getStepLength(force = false):Float
|
||||
{
|
||||
return Conductor.getTimeInSteps(this.time + this.length) - this.stepTime;
|
||||
if (this.length <= 0) return 0.0;
|
||||
|
||||
if (_stepLength != null && !force) return _stepLength;
|
||||
|
||||
return _stepLength = Conductor.getTimeInSteps(this.time + this.length) - getStepTime();
|
||||
}
|
||||
|
||||
function set_stepLength(value:Float):Float
|
||||
public function setStepLength(value:Float):Void
|
||||
{
|
||||
return this.length = Conductor.getStepTimeInMs(value) - this.time;
|
||||
if (value <= 0)
|
||||
{
|
||||
this.length = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var lengthMs:Float = Conductor.getStepTimeInMs(value) - this.time;
|
||||
this.length = lengthMs;
|
||||
}
|
||||
_stepLength = null;
|
||||
}
|
||||
|
||||
@:jignored
|
||||
|
|
|
@ -9,10 +9,12 @@ import flixel.FlxG; // This one in particular causes a compile error if you're u
|
|||
// These are great.
|
||||
using Lambda;
|
||||
using StringTools;
|
||||
using funkin.util.tools.ArrayTools;
|
||||
using funkin.util.tools.ArraySortTools;
|
||||
using funkin.util.tools.ArrayTools;
|
||||
using funkin.util.tools.Int64Tools;
|
||||
using funkin.util.tools.IteratorTools;
|
||||
using funkin.util.tools.MapTools;
|
||||
using funkin.util.tools.SongEventDataArrayTools;
|
||||
using funkin.util.tools.SongNoteDataArrayTools;
|
||||
using funkin.util.tools.StringTools;
|
||||
#end
|
||||
|
|
|
@ -22,17 +22,6 @@ class NoteSprite extends FlxSprite
|
|||
return this.strumTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time at which the note should be hit, in steps.
|
||||
*/
|
||||
public var stepTime(get, never):Float;
|
||||
|
||||
function get_stepTime():Float
|
||||
{
|
||||
// TODO: Account for changes in BPM.
|
||||
return this.strumTime / Conductor.stepLengthMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extra attribute for the note.
|
||||
* For example, whether the note is an "alt" note, or whether it has custom behavior on hit.
|
||||
|
|
|
@ -142,12 +142,14 @@ class SustainTrail extends FlxSprite
|
|||
return (susLength * 0.45 * scroll);
|
||||
}
|
||||
|
||||
function set_sustainLength(s:Float)
|
||||
function set_sustainLength(s:Float):Float
|
||||
{
|
||||
if (s < 0) s = 0;
|
||||
if (s < 0.0) s = 0.0;
|
||||
|
||||
if (sustainLength == s) return s;
|
||||
|
||||
height = sustainHeight(s, getScrollSpeed());
|
||||
updateColorTransform();
|
||||
// updateColorTransform();
|
||||
updateClipping();
|
||||
return sustainLength = s;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,9 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
if (this.eventData == null) return;
|
||||
|
||||
this.x = (ChartEditorState.STRUMLINE_SIZE * 2 + 1 - 1) * ChartEditorState.GRID_SIZE;
|
||||
if (this.eventData.stepTime >= 0) this.y = this.eventData.stepTime * ChartEditorState.GRID_SIZE;
|
||||
|
||||
var stepTime:Float = inline eventData.getStepTime();
|
||||
this.y = stepTime * ChartEditorState.GRID_SIZE;
|
||||
|
||||
if (origin != null)
|
||||
{
|
||||
|
@ -174,7 +176,7 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
public static function wouldEventBeVisible(viewAreaBottom:Float, viewAreaTop:Float, eventData:SongEventData, ?origin:FlxObject):Bool
|
||||
{
|
||||
var noteHeight:Float = ChartEditorState.GRID_SIZE;
|
||||
var notePosY:Float = eventData.stepTime * ChartEditorState.GRID_SIZE;
|
||||
var notePosY:Float = eventData.getStepTime() * ChartEditorState.GRID_SIZE;
|
||||
if (origin != null) notePosY += origin.y;
|
||||
|
||||
// True if the note is above the view area.
|
||||
|
|
|
@ -98,8 +98,9 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
*/
|
||||
public static function wouldHoldNoteBeVisible(viewAreaBottom:Float, viewAreaTop:Float, noteData:SongNoteData, ?origin:FlxObject):Bool
|
||||
{
|
||||
var noteHeight:Float = noteData.stepLength * ChartEditorState.GRID_SIZE;
|
||||
var notePosY:Float = noteData.stepTime * ChartEditorState.GRID_SIZE;
|
||||
var noteHeight:Float = noteData.getStepLength() * ChartEditorState.GRID_SIZE;
|
||||
var stepTime:Float = inline noteData.getStepTime();
|
||||
var notePosY:Float = stepTime * ChartEditorState.GRID_SIZE;
|
||||
if (origin != null) notePosY += origin.y;
|
||||
|
||||
// True if the note is above the view area.
|
||||
|
@ -138,10 +139,11 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
this.x = cursorColumn * ChartEditorState.GRID_SIZE;
|
||||
|
||||
// Notes far in the song will start far down, but the group they belong to will have a high negative offset.
|
||||
if (this.noteData.stepTime >= 0)
|
||||
// noteData.getStepTime() returns a calculated value which accounts for BPM changes
|
||||
var stepTime:Float =
|
||||
inline this.noteData.getStepTime();
|
||||
if (stepTime >= 0)
|
||||
{
|
||||
// noteData.stepTime is a calculated value which accounts for BPM changes
|
||||
var stepTime:Float = this.noteData.stepTime;
|
||||
// Add epsilon to fix rounding issues?
|
||||
// var roundedStepTime:Float = Math.floor((stepTime + 0.01) / noteSnapRatio) * noteSnapRatio;
|
||||
this.y = stepTime * ChartEditorState.GRID_SIZE;
|
||||
|
|
|
@ -170,10 +170,12 @@ class ChartEditorNoteSprite extends FlxSprite
|
|||
this.x = cursorColumn * ChartEditorState.GRID_SIZE;
|
||||
|
||||
// Notes far in the song will start far down, but the group they belong to will have a high negative offset.
|
||||
if (this.noteData.stepTime >= 0)
|
||||
// noteData.getStepTime() returns a calculated value which accounts for BPM changes
|
||||
var stepTime:Float =
|
||||
inline this.noteData.getStepTime();
|
||||
if (stepTime >= 0)
|
||||
{
|
||||
// noteData.stepTime is a calculated value which accounts for BPM changes
|
||||
this.y = this.noteData.stepTime * ChartEditorState.GRID_SIZE;
|
||||
this.y = stepTime * ChartEditorState.GRID_SIZE;
|
||||
}
|
||||
|
||||
if (origin != null)
|
||||
|
@ -230,11 +232,13 @@ class ChartEditorNoteSprite extends FlxSprite
|
|||
|
||||
/**
|
||||
* Return whether a note, if placed in the scene, would be visible.
|
||||
* This function should be made HYPER EFFICIENT because it's called a lot.
|
||||
*/
|
||||
public static function wouldNoteBeVisible(viewAreaBottom:Float, viewAreaTop:Float, noteData:SongNoteData, ?origin:FlxObject):Bool
|
||||
{
|
||||
var noteHeight:Float = ChartEditorState.GRID_SIZE;
|
||||
var notePosY:Float = noteData.stepTime * ChartEditorState.GRID_SIZE;
|
||||
var stepTime:Float = inline noteData.getStepTime();
|
||||
var notePosY:Float = stepTime * ChartEditorState.GRID_SIZE;
|
||||
if (origin != null) notePosY += origin.y;
|
||||
|
||||
// True if the note is above the view area.
|
||||
|
|
|
@ -590,7 +590,13 @@ class ChartEditorState extends HaxeUIState
|
|||
/**
|
||||
* Whether the note preview graphic needs to be FULLY rebuilt.
|
||||
*/
|
||||
var notePreviewDirty:Bool = true;
|
||||
var notePreviewDirty(default, set):Bool = true;
|
||||
|
||||
function set_notePreviewDirty(value:Bool):Bool
|
||||
{
|
||||
trace('Note preview dirtied!');
|
||||
return notePreviewDirty = value;
|
||||
}
|
||||
|
||||
var notePreviewViewportBoundsDirty:Bool = true;
|
||||
|
||||
|
@ -1178,6 +1184,21 @@ class ChartEditorState extends HaxeUIState
|
|||
*/
|
||||
var playbarHead:Null<Slider> = null;
|
||||
|
||||
/**
|
||||
* The label by the playbar telling the song position.
|
||||
*/
|
||||
var playbarSongPos:Null<Label> = null;
|
||||
|
||||
/**
|
||||
* The label by the playbar telling the song time remaining.
|
||||
*/
|
||||
var playbarSongRemaining:Null<Label> = null;
|
||||
|
||||
/**
|
||||
* The label by the playbar telling the note snap.
|
||||
*/
|
||||
var playbarNoteSnap:Null<Label> = null;
|
||||
|
||||
/**
|
||||
* The current process that is lerping the scroll position.
|
||||
* Used to cancel the previous lerp if the user scrolls again.
|
||||
|
@ -1799,8 +1820,6 @@ class ChartEditorState extends HaxeUIState
|
|||
// dispatchEvent gets called here.
|
||||
super.update(elapsed);
|
||||
|
||||
FlxG.mouse.visible = true;
|
||||
|
||||
// These ones happen even if the modal dialog is open.
|
||||
handleMusicPlayback();
|
||||
handleNoteDisplay();
|
||||
|
@ -1815,30 +1834,13 @@ class ChartEditorState extends HaxeUIState
|
|||
handlePlaybar();
|
||||
handlePlayhead();
|
||||
handleNotePreview();
|
||||
handleHealthIcons();
|
||||
|
||||
handleFileKeybinds();
|
||||
handleEditKeybinds();
|
||||
handleViewKeybinds();
|
||||
handleTestKeybinds();
|
||||
handleHelpKeybinds();
|
||||
|
||||
// DEBUG
|
||||
#if debug
|
||||
if (FlxG.keys.justPressed.E && !isHaxeUIDialogOpen)
|
||||
{
|
||||
currentSongMetadata.timeChanges[0].timeSignatureNum = (currentSongMetadata.timeChanges[0].timeSignatureNum == 4 ? 3 : 4);
|
||||
}
|
||||
#end
|
||||
|
||||
// Right align the BF health icon.
|
||||
if (healthIconBF != null)
|
||||
{
|
||||
// Base X position to the right of the grid.
|
||||
var baseHealthIconXPos:Float = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 15);
|
||||
// Will be 0 when not bopping. When bopping, will increase to push the icon left.
|
||||
var healthIconOffset:Float = healthIconBF.width - (HealthIcon.HEALTH_ICON_SIZE * 0.5);
|
||||
healthIconBF.x = baseHealthIconXPos - healthIconOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2102,10 +2104,12 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
else
|
||||
{
|
||||
trace('Clicked outside grid, deselecting all items.');
|
||||
|
||||
// Deselect all items.
|
||||
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
|
||||
if (currentNoteSelection.length > 0 || currentEventSelection.length > 0)
|
||||
{
|
||||
trace('Clicked outside grid, deselecting all items.');
|
||||
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2242,9 +2246,12 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
if (!FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
trace('Clicked and dragged outside grid, deselecting all items.');
|
||||
// Deselect all items.
|
||||
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
|
||||
if (currentNoteSelection.length > 0 || currentEventSelection.length > 0)
|
||||
{
|
||||
trace('Clicked and dragged outside grid, deselecting all items.');
|
||||
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2370,9 +2377,12 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
if (!FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
trace('Clicked outside grid, deselecting all items.');
|
||||
// Deselect all items.
|
||||
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
|
||||
if (currentNoteSelection.length > 0 || currentEventSelection.length > 0)
|
||||
{
|
||||
trace('Clicked outside grid, deselecting all items.');
|
||||
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2392,7 +2402,8 @@ class ChartEditorState extends HaxeUIState
|
|||
{
|
||||
// Handle extending the note as you drag.
|
||||
|
||||
var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorSnappedMs) - currentPlaceNoteData.stepTime;
|
||||
var stepTime:Float = inline currentPlaceNoteData.getStepTime();
|
||||
var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorSnappedMs) - stepTime;
|
||||
var dragLengthMs:Float = dragLengthSteps * Conductor.stepLengthMs;
|
||||
var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE;
|
||||
|
||||
|
@ -2677,7 +2688,7 @@ class ChartEditorState extends HaxeUIState
|
|||
// Kill the note sprite and recycle it.
|
||||
noteSprite.noteData = null;
|
||||
}
|
||||
else if (currentSongChartNoteData.indexOf(noteSprite.noteData) == -1)
|
||||
else if (!currentSongChartNoteData.fastContains(noteSprite.noteData))
|
||||
{
|
||||
// This note was deleted.
|
||||
// Kill the note sprite and recycle it.
|
||||
|
@ -2692,6 +2703,9 @@ class ChartEditorState extends HaxeUIState
|
|||
noteSprite.updateNotePosition(renderedNotes);
|
||||
}
|
||||
}
|
||||
// Sort the note data array, using an algorithm that is fast on nearly-sorted data.
|
||||
// We need this sorted to optimize indexing later.
|
||||
displayedNoteData.insertionSort(SortUtil.noteDataByTime.bind(FlxSort.ASCENDING));
|
||||
|
||||
var displayedHoldNoteData:Array<SongNoteData> = [];
|
||||
for (holdNoteSprite in renderedHoldNotes.members)
|
||||
|
@ -2702,13 +2716,13 @@ class ChartEditorState extends HaxeUIState
|
|||
{
|
||||
holdNoteSprite.kill();
|
||||
}
|
||||
else if (currentSongChartNoteData.indexOf(holdNoteSprite.noteData) == -1 || holdNoteSprite.noteData.length == 0)
|
||||
else if (!currentSongChartNoteData.fastContains(holdNoteSprite.noteData) || holdNoteSprite.noteData.length == 0)
|
||||
{
|
||||
// This hold note was deleted.
|
||||
// Kill the hold note sprite and recycle it.
|
||||
holdNoteSprite.kill();
|
||||
}
|
||||
else if (displayedHoldNoteData.indexOf(holdNoteSprite.noteData) != -1)
|
||||
else if (displayedHoldNoteData.fastContains(holdNoteSprite.noteData))
|
||||
{
|
||||
// This hold note is a duplicate.
|
||||
// Kill the hold note sprite and recycle it.
|
||||
|
@ -2721,6 +2735,9 @@ class ChartEditorState extends HaxeUIState
|
|||
holdNoteSprite.updateHoldNotePosition(renderedNotes);
|
||||
}
|
||||
}
|
||||
// Sort the note data array, using an algorithm that is fast on nearly-sorted data.
|
||||
// We need this sorted to optimize indexing later.
|
||||
displayedHoldNoteData.insertionSort(SortUtil.noteDataByTime.bind(FlxSort.ASCENDING));
|
||||
|
||||
// Remove events that are no longer visible and list the ones that are.
|
||||
var displayedEventData:Array<SongEventData> = [];
|
||||
|
@ -2734,7 +2751,7 @@ class ChartEditorState extends HaxeUIState
|
|||
// Kill the event sprite and recycle it.
|
||||
eventSprite.eventData = null;
|
||||
}
|
||||
else if (currentSongChartEventData.indexOf(eventSprite.eventData) == -1)
|
||||
else if (!currentSongChartEventData.fastContains(eventSprite.eventData))
|
||||
{
|
||||
// This event was deleted.
|
||||
// Kill the event sprite and recycle it.
|
||||
|
@ -2749,12 +2766,24 @@ class ChartEditorState extends HaxeUIState
|
|||
eventSprite.updateEventPosition(renderedEvents);
|
||||
}
|
||||
}
|
||||
// Sort the note data array, using an algorithm that is fast on nearly-sorted data.
|
||||
// We need this sorted to optimize indexing later.
|
||||
displayedEventData.insertionSort(SortUtil.eventDataByTime.bind(FlxSort.ASCENDING));
|
||||
|
||||
// Let's try testing only notes within a certain range of the view area.
|
||||
// TODO: I don't think this messes up really long sustains, does it?
|
||||
var viewAreaTopMs:Float = scrollPositionInMs - (Conductor.measureLengthMs * 2); // Is 2 measures enough?
|
||||
var viewAreaBottomMs:Float = scrollPositionInMs + (Conductor.measureLengthMs * 2); // Is 2 measures enough?
|
||||
|
||||
// Add notes that are now visible.
|
||||
for (noteData in currentSongChartNoteData)
|
||||
{
|
||||
// Remember if we are already displaying this note.
|
||||
if (noteData == null || displayedNoteData.indexOf(noteData) != -1)
|
||||
if (noteData == null) continue;
|
||||
// Check if we are outside a broad range around the view area.
|
||||
if (noteData.time < viewAreaTopMs || noteData.time > viewAreaBottomMs) continue;
|
||||
|
||||
if (displayedNoteData.fastContains(noteData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -2766,7 +2795,7 @@ class ChartEditorState extends HaxeUIState
|
|||
// If we can reuse a deleted note, do so.
|
||||
// If a new note is needed, call buildNoteSprite.
|
||||
var noteSprite:ChartEditorNoteSprite = renderedNotes.recycle(() -> new ChartEditorNoteSprite(this));
|
||||
trace('Creating new Note... (${renderedNotes.members.length})');
|
||||
// trace('Creating new Note... (${renderedNotes.members.length})');
|
||||
noteSprite.parentState = this;
|
||||
|
||||
// The note sprite handles animation playback and positioning.
|
||||
|
@ -2780,9 +2809,9 @@ class ChartEditorState extends HaxeUIState
|
|||
if (noteSprite.noteData != null && noteSprite.noteData.length > 0 && displayedHoldNoteData.indexOf(noteSprite.noteData) == -1)
|
||||
{
|
||||
var holdNoteSprite:ChartEditorHoldNoteSprite = renderedHoldNotes.recycle(() -> new ChartEditorHoldNoteSprite(this));
|
||||
trace('Creating new HoldNote... (${renderedHoldNotes.members.length})');
|
||||
// trace('Creating new HoldNote... (${renderedHoldNotes.members.length})');
|
||||
|
||||
var noteLengthPixels:Float = noteSprite.noteData.stepLength * GRID_SIZE;
|
||||
var noteLengthPixels:Float = noteSprite.noteData.getStepLength() * GRID_SIZE;
|
||||
|
||||
holdNoteSprite.noteData = noteSprite.noteData;
|
||||
holdNoteSprite.noteDirection = noteSprite.noteData.getDirection();
|
||||
|
@ -2840,7 +2869,7 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
var holdNoteSprite:ChartEditorHoldNoteSprite = renderedHoldNotes.recycle(holdNoteFactory);
|
||||
|
||||
var noteLengthPixels:Float = noteData.stepLength * GRID_SIZE;
|
||||
var noteLengthPixels:Float = noteData.getStepLength() * GRID_SIZE;
|
||||
|
||||
holdNoteSprite.noteData = noteData;
|
||||
holdNoteSprite.noteDirection = noteData.getDirection();
|
||||
|
@ -2904,6 +2933,22 @@ class ChartEditorState extends HaxeUIState
|
|||
FlxG.watch.addQuick("holdNotesRendered", renderedHoldNotes.members.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle aligning the health icons next to the grid.
|
||||
*/
|
||||
function handleHealthIcons():Void
|
||||
{
|
||||
// Right align the BF health icon.
|
||||
if (healthIconBF != null)
|
||||
{
|
||||
// Base X position to the right of the grid.
|
||||
var baseHealthIconXPos:Float = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 15);
|
||||
// Will be 0 when not bopping. When bopping, will increase to push the icon left.
|
||||
var healthIconOffset:Float = healthIconBF.width - (HealthIcon.HEALTH_ICON_SIZE * 0.5);
|
||||
healthIconBF.x = baseHealthIconXPos - healthIconOffset;
|
||||
}
|
||||
}
|
||||
|
||||
function buildSelectionSquare():FlxSprite
|
||||
{
|
||||
if (selectionSquareBitmap == null)
|
||||
|
@ -2930,23 +2975,27 @@ class ChartEditorState extends HaxeUIState
|
|||
// Move the playhead to match the song position, if we aren't dragging it.
|
||||
if (!playbarHeadDragging)
|
||||
{
|
||||
var songPosPercent:Float = songPos / songLengthInMs;
|
||||
playbarHead.value = songPosPercent * 100;
|
||||
var songPosPercent:Float = songPos / songLengthInMs * 100;
|
||||
if (playbarHead.value != songPosPercent) playbarHead.value = songPosPercent;
|
||||
}
|
||||
|
||||
var songPosSeconds:String = Std.string(Math.floor((songPos / 1000) % 60)).lpad('0', 2);
|
||||
var songPosMinutes:String = Std.string(Math.floor((songPos / 1000) / 60)).lpad('0', 2);
|
||||
var songPosString:String = '${songPosMinutes}:${songPosSeconds}';
|
||||
|
||||
setUIValue('playbarSongPos', songPosString);
|
||||
if (playbarSongPos == null) playbarSongPos = findComponent('playbarSongPos', Label);
|
||||
if (playbarSongPos != null && playbarSongPos.value != songPosString) playbarSongPos.value = songPosString;
|
||||
|
||||
var songRemainingSeconds:String = Std.string(Math.floor((songRemaining / 1000) % 60)).lpad('0', 2);
|
||||
var songRemainingMinutes:String = Std.string(Math.floor((songRemaining / 1000) / 60)).lpad('0', 2);
|
||||
var songRemainingString:String = '-${songRemainingMinutes}:${songRemainingSeconds}';
|
||||
|
||||
setUIValue('playbarSongRemaining', songRemainingString);
|
||||
if (playbarSongRemaining == null) playbarSongRemaining = findComponent('playbarSongRemaining', Label);
|
||||
if (playbarSongRemaining != null
|
||||
&& playbarSongRemaining.value != songRemainingString) playbarSongRemaining.value = songRemainingString;
|
||||
|
||||
setUIValue('playbarNoteSnap', '1/${noteSnapQuant}');
|
||||
if (playbarNoteSnap == null) playbarNoteSnap = findComponent('playbarNoteSnap', Label);
|
||||
if (playbarNoteSnap != null && playbarNoteSnap.value != '1/${noteSnapQuant}') playbarNoteSnap.value = '1/${noteSnapQuant}';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3268,7 +3317,8 @@ class ChartEditorState extends HaxeUIState
|
|||
var charPreviewToolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
|
||||
if (charPreviewToolbox == null) return;
|
||||
|
||||
var charPlayer:Null<CharacterPlayer> = charPreviewToolbox.findComponent('charPlayer');
|
||||
// TODO: Re-enable the player preview once we figure out the performance issues.
|
||||
var charPlayer:Null<CharacterPlayer> = null; // charPreviewToolbox.findComponent('charPlayer');
|
||||
if (charPlayer == null) return;
|
||||
|
||||
currentPlayerCharacterPlayer = charPlayer;
|
||||
|
@ -3303,7 +3353,8 @@ class ChartEditorState extends HaxeUIState
|
|||
var charPreviewToolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
|
||||
if (charPreviewToolbox == null) return;
|
||||
|
||||
var charPlayer:Null<CharacterPlayer> = charPreviewToolbox.findComponent('charPlayer');
|
||||
// TODO: Re-enable the player preview once we figure out the performance issues.
|
||||
var charPlayer:Null<CharacterPlayer> = null; // charPreviewToolbox.findComponent('charPlayer');
|
||||
if (charPlayer == null) return;
|
||||
|
||||
currentOpponentCharacterPlayer = charPlayer;
|
||||
|
|
|
@ -6,6 +6,8 @@ import flixel.FlxBasic;
|
|||
import flixel.util.FlxSort;
|
||||
#end
|
||||
import funkin.play.notes.NoteSprite;
|
||||
import funkin.data.song.SongData.SongEventData;
|
||||
import funkin.data.song.SongData.SongNoteData;
|
||||
|
||||
/**
|
||||
* A set of functions related to sorting.
|
||||
|
@ -39,7 +41,17 @@ class SortUtil
|
|||
*/
|
||||
public static inline function byStrumtime(order:Int, a:NoteSprite, b:NoteSprite)
|
||||
{
|
||||
return FlxSort.byValues(order, a.noteData.time, b.noteData.time);
|
||||
return noteDataByTime(order, a.noteData, b.noteData);
|
||||
}
|
||||
|
||||
public static inline function noteDataByTime(order:Int, a:SongNoteData, b:SongNoteData)
|
||||
{
|
||||
return FlxSort.byValues(order, a.time, b.time);
|
||||
}
|
||||
|
||||
public static inline function eventDataByTime(order:Int, a:SongEventData, b:SongEventData)
|
||||
{
|
||||
return FlxSort.byValues(order, a.time, b.time);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
62
source/funkin/util/tools/SongEventDataArrayTools.hx
Normal file
62
source/funkin/util/tools/SongEventDataArrayTools.hx
Normal file
|
@ -0,0 +1,62 @@
|
|||
package funkin.util.tools;
|
||||
|
||||
import funkin.data.song.SongData.SongEventData;
|
||||
|
||||
/**
|
||||
* A static extension which provides utility functions for `Array<SongEventData>`s.
|
||||
*/
|
||||
class SongEventDataArrayTools
|
||||
{
|
||||
/**
|
||||
* Queries whether the provided `SongEventData` is contained in the provided array.
|
||||
* The input array must be already sorted by `time`.
|
||||
* Vastly more efficient than `array.indexOf`.
|
||||
* This is not crazy or premature optimization, I'm writing this because `ChartEditorState.handleNoteDisplay` is using like 71% of its CPU time on this.
|
||||
* @param arr The array to search.
|
||||
* @param note The note to search for.
|
||||
* @param predicate
|
||||
* @return The index of the note in the array, or `-1` if it is not present.
|
||||
*/
|
||||
public static function fastIndexOf(input:Array<SongEventData>, note:SongEventData):Int
|
||||
{
|
||||
// I would have made this use a generic/predicate, but that would have made it slower!
|
||||
|
||||
// Thank you Github Copilot for suggesting a binary search!
|
||||
var lowIndex:Int = 0;
|
||||
var highIndex:Int = input.length - 1;
|
||||
var midIndex:Int;
|
||||
var midNote:SongEventData;
|
||||
|
||||
// When lowIndex overtakes highIndex
|
||||
while (lowIndex <= highIndex)
|
||||
{
|
||||
// Get the middle index of the range.
|
||||
midIndex = Std.int((lowIndex + highIndex) / 2);
|
||||
|
||||
// Compare the middle note of the range to the note we're looking for.
|
||||
// If it matches, return the index, else halve the range and try again.
|
||||
midNote = input[midIndex];
|
||||
if (midNote.time < note.time)
|
||||
{
|
||||
// Search the upper half of the range.
|
||||
lowIndex = midIndex + 1;
|
||||
}
|
||||
else if (midNote.time > note.time)
|
||||
{
|
||||
// Search the lower half of the range.
|
||||
highIndex = midIndex - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found it!
|
||||
return midIndex;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static inline function fastContains(input:Array<SongEventData>, note:SongEventData):Bool
|
||||
{
|
||||
return fastIndexOf(input, note) != -1;
|
||||
}
|
||||
}
|
69
source/funkin/util/tools/SongNoteDataArrayTools.hx
Normal file
69
source/funkin/util/tools/SongNoteDataArrayTools.hx
Normal file
|
@ -0,0 +1,69 @@
|
|||
package funkin.util.tools;
|
||||
|
||||
import funkin.data.song.SongData.SongNoteData;
|
||||
|
||||
/**
|
||||
* A static extension which provides utility functions for `Array<SongNoteData>`s.
|
||||
*/
|
||||
class SongNoteDataArrayTools
|
||||
{
|
||||
/**
|
||||
* Queries whether the provided `SongNoteData` is contained in the provided array.
|
||||
* The input array must be already sorted by `time`.
|
||||
* Vastly more efficient than `array.indexOf`.
|
||||
* This is not crazy or premature optimization, I'm writing this because `ChartEditorState.handleNoteDisplay` is using like 71% of its CPU time on this.
|
||||
* @param arr The array to search.
|
||||
* @param note The note to search for.
|
||||
* @param predicate
|
||||
* @return The index of the note in the array, or `-1` if it is not present.
|
||||
*/
|
||||
public static function fastIndexOf(input:Array<SongNoteData>, note:SongNoteData):Int
|
||||
{
|
||||
// I would have made this use a generic/predicate, but that would have made it slower!
|
||||
|
||||
// Prefix with some simple checks to save time.
|
||||
if (input.length == 0) return -1;
|
||||
if (note.time < input[0].time || note.time > input[input.length - 1].time) return -1;
|
||||
|
||||
// Thank you Github Copilot for suggesting a binary search!
|
||||
var lowIndex:Int = 0;
|
||||
var highIndex:Int = input.length - 1;
|
||||
|
||||
// When lowIndex overtakes highIndex
|
||||
while (lowIndex <= highIndex)
|
||||
{
|
||||
// Get the middle index of the range.
|
||||
var midIndex = Std.int((lowIndex + highIndex) / 2);
|
||||
|
||||
// Compare the middle note of the range to the note we're looking for.
|
||||
// If it matches, return the index, else halve the range and try again.
|
||||
var midNote = input[midIndex];
|
||||
if (midNote.time < note.time)
|
||||
{
|
||||
// Search the upper half of the range.
|
||||
lowIndex = midIndex + 1;
|
||||
}
|
||||
else if (midNote.time > note.time)
|
||||
{
|
||||
// Search the lower half of the range.
|
||||
highIndex = midIndex - 1;
|
||||
}
|
||||
// Found it? Do a more thorough check.
|
||||
else if (midNote == note)
|
||||
{
|
||||
return midIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We may be close, so constrain the range (but only a little) and try again.
|
||||
highIndex -= 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static inline function fastContains(input:Array<SongNoteData>, note:SongNoteData):Bool
|
||||
{
|
||||
return fastIndexOf(input, note) != -1;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue