Skip to content

4. Custom States & Substates

ItsFellow edited this page May 4, 2026 · 3 revisions

Before reading through, be sure to follow the basic scripting rules as defined on the Scripting section of this wiki.


Basics

Custom states / substates are located within your mod's scripts folder as follows.

flowchart TD
    MOD(Your mod) --> SCRIPTS{Scripts}
    SCRIPTS --> STATES[states]
    SCRIPTS --> SUBSTATES[substates]


    STATES --> state.hx@{shape: card}
    SUBSTATES --> substate.hx@{shape: card}
Loading

Functions

Custom states / substates share the global functions defined here, but for ease of reading, they will also be stated here.


Name

When the function is ran Fields?
onLoad Function is run immediately upon the script being loaded.
onCreate Function is run when the state is created.
onCreatePost Function is run after super.create();is called in the script's respective state.
onUpdate(elapsed:Float) Function is run every time the function update is called. elapsed:Float is the amount of time (in seconds) that has passed since the last update call.
onUpdatePost(elapsed:Float) Function is run every time the function update is called, specifically after super.update(elapsed); is called. elapsed:Float is the amount of time (in seconds) that has passed since the last update call.
onDestroy() Function is run upon destruction of a state.
onSubstateClose() Function is run whenever the function close() is run in a SubState.
onStepHit() Function is run every time a step passes. Only runs if proper song setup is done with funkin.backend.Conductor.
onBeatHit() Function is run every time a beat passes. Only run if proper song setup is done with funkin.backend.Conductor.
onSectionHit() Function is run every time a section passes. Only run if proper song setup is done with funkin.backend.Conductor

It is worth noting that all functions relating to funkin.backend.Conductor will not run if Conductor is not properly set up within a state to a song.
An example of setting up conductor properly is:

import funkin.audio.FunkinSound;
import funkin.backend.Conductor;

function onLoad()
{
  FunkinSound.playMusic(Paths.music("Your song here"));
  Conductor.bpm = 150; // set this to your song's bpm
}

function onUpdate(elapsed:Float)
{
  if (FlxG.sound.music != null)
    Conductor.songPosition = FlxG.sound.music.time;
}

After doing this, the functions onStepHit, onBeatHit & onSectionHit should run properly.


Opening your custom state

To switch to your custom state, simply run:

FlxG.switchState(()->{new ScriptedState("script location")});


It is worth nothing you do not need to include the file ending or scripts/states/ in your script location string, simply input your script name from within scripts/states/.
Examples of opening the custom states scripts/states/TestState.hx and 'scripts/states/organization/TestState.hx` are:

FlxG.switchState(()->{new ScriptedState("TestState")});

and

FlxG.switchState(()->{new ScriptedState("organization/TestState")});


It is also worth noting you do not need to import ScriptedState, as it is a default import on all script types.


Opening your custom Substate

To switch to your custom substate, simply run:

openSubstate(new ScriptedSubstate("script location");


It is worth nothing you do not need to include the file ending or scripts/substates/ in your script location string, simply input your script name from within scripts/substates/.
Examples of opening the custom states scripts/substates/TestSubstate.hx and 'scripts/substates/organization/TestSubstate.hx` are:

openSubState(new ScriptedSubstate("TestSubstate"));

and

openSubState(new ScriptedSubstate("organization/TestSubstate"));


It is also worth noting you do not need to import ScriptedSubtate, as it is a default import on all script types.

Clone this wiki locally