The PlayState class is the core gameplay state where all rhythm gaming occurs. It manages the song, characters, stage, notes, scoring, health, and all gameplay mechanics.
Overview
PlayState is implemented as a MusicBeatSubState so it can be loaded as a child of the chart editor. It handles:
- Song playback and synchronization
- Note rendering and hit detection
- Character animations and stage elements
- Health and scoring systems
- Input processing with latency compensation
- Camera movement and effects
- Cutscenes and dialogue
Accessing PlayState
// Access the singleton instance (only exists during gameplay)
if (PlayState.instance != null)
{
trace("Current health: " + PlayState.instance.health);
trace("Current score: " + PlayState.instance.songScore);
}
PlayState.instance is null when not in gameplay. Always check for null before accessing.
Initialization
PlayStateParams
PlayState is initialized using a PlayStateParams typedef:
typedef PlayStateParams =
{
targetSong:Song,
?targetDifficulty:String,
?targetVariation:String,
?targetInstrumental:String,
?practiceMode:Bool,
?botPlayMode:Bool,
?playtestResults:Bool,
?minimalMode:Bool,
?startTimestamp:Float,
?playbackRate:Float,
?overrideMusic:Bool,
?cameraFollowPoint:FlxPoint,
?mirrored:Bool
}
targetDifficulty
String
default:"Constants.DEFAULT_DIFFICULTY"
The difficulty to play (e.g., “easy”, “normal”, “hard”).
targetVariation
String
default:"Constants.DEFAULT_VARIATION"
The chart variation to use.
Alternate instrumental ID if the song supports multiple instrumentals.
Whether to start in Practice Mode (no score/health penalty).
Whether to start in Bot Play Mode (auto-play).
If true, skips loading stage and characters, using a simple background.
Start position in milliseconds. Used for practice and chart playtesting.
Song playback speed multiplier (1.0 = 100% speed).
Example:
// Load a song from the registry
var song = SongRegistry.instance.fetchEntry("tutorial");
// Create params
var params:PlayStateParams = {
targetSong: song,
targetDifficulty: "hard",
practiceMode: false,
botPlayMode: false
};
// Switch to PlayState
LoadingState.loadPlayState(params, function()
{
FlxG.switchState(() -> new PlayState(params));
});
Key Properties
Song Properties
The currently active song.
The currently selected difficulty.
The currently selected chart variation.
The currently selected instrumental ID.
The currently active stage with all props and characters.
Gameplay State
Player’s current health (0-2 range, starts at 1.0).
Starting position in milliseconds when the countdown ends.
Song playback speed multiplier (1.0 = 100%).
Whether Practice Mode is active.
Whether Bot Play Mode is active.
Whether Minimal Mode is active (no stage/characters).
Whether the countdown before the song is active.
Whether an animated cutscene is playing and gameplay is stopped.
Whether inputs are disabled (used after song ends or in stage editor).
Camera Properties
The object the gameplay camera follows. Tween this to move the camera smoothly.
Current camera zoom level without modifiers applied.
Camera bop intensity multiplier applied on beat hits.
How many beats between camera zooms. Default is one zoom per measure (4 beats).
Audio Volume
Volume of the instrumental track (0.0-1.0).
Volume of the player vocals track (0.0-1.0).
Volume of the opponent vocals track (0.0-1.0).
Key Methods
startCountdown()
Starts the countdown before the song begins.
public function startCountdown():Countdown
Returns: The Countdown object that was created.
Example:
// Start countdown after a cutscene
function onCutsceneComplete():Void
{
PlayState.instance.startCountdown();
}
endSong()
Ends the song and transitions to results or next song.
public function endSong(?ignoreDelay:Bool = false):Void
Whether to skip the end-of-song delay.
pauseGame()
Pauses the game and opens the pause menu.
function pauseGame():Void
resetCamera()
Resets the camera zoom and forces focus on the camera follow point.
public function resetCamera(?resetZoom:Bool = true, ?cancelTweens:Bool = true):Void
Whether to reset zoom to the default stage zoom.
Whether to cancel any active camera tweens.
focusOnCharacter()
Moves the camera to focus on a specific character.
public function focusOnCharacter(char:BaseCharacter, ?useMidpoint:Bool = true):Void
The character to focus on.
Whether to use the character’s midpoint for positioning.
Example: Custom Gameplay Mod
import funkin.play.PlayState;
import funkin.Conductor;
import flixel.FlxG;
class HealthDrainMod
{
public static function init():Void
{
// Listen for beat hits
Conductor.beatHit.add(onBeatHit);
}
static function onBeatHit():Void
{
if (PlayState.instance == null) return;
// Drain health every 4 beats
if (Conductor.instance.currentBeat % 4 == 0)
{
PlayState.instance.health -= 0.1;
trace("Health drained! Current: " + PlayState.instance.health);
}
}
}
Example: Custom Camera Movement
import funkin.play.PlayState;
import flixel.tweens.FlxTween;
import flixel.tweens.FlxEase;
class CameraController
{
public static function panToPosition(x:Float, y:Float, duration:Float = 1.0):Void
{
if (PlayState.instance == null) return;
var followPoint = PlayState.instance.cameraFollowPoint;
FlxTween.tween(followPoint, {x: x, y: y}, duration, {
ease: FlxEase.sineInOut,
onComplete: function(_)
{
trace("Camera pan complete");
}
});
}
public static function zoomTo(zoom:Float, duration:Float = 0.5):Void
{
if (PlayState.instance == null) return;
FlxTween.tween(PlayState.instance, {currentCameraZoom: zoom}, duration, {
ease: FlxEase.sineInOut
});
}
}
Example: Accessing Stage Characters
import funkin.play.PlayState;
class CharacterController
{
public static function makeBoyfriendDance():Void
{
var playState = PlayState.instance;
if (playState?.currentStage == null) return;
var boyfriend = playState.currentStage.getBoyfriend();
if (boyfriend != null)
{
boyfriend.dance();
}
}
public static function changeDadIdle(newIdle:String):Void
{
var playState = PlayState.instance;
if (playState?.currentStage == null) return;
var dad = playState.currentStage.getDad();
if (dad != null)
{
dad.playAnimation(newIdle, true);
}
}
}
Health System
Health ranges from 0.0 to 2.0, with 1.0 being the starting value:
- Health < 0.0: Player dies (triggers game over)
- Health = 1.0: Starting health (center of health bar)
- Health > 2.0: Clamped to maximum
Health changes based on note hits:
- Perfect hit: +0.023 health
- Note miss: -0.0475 health
- Ghost tap: Small penalty (configurable)
// Modify health directly
PlayState.instance.health += 0.1; // Heal
PlayState.instance.health -= 0.2; // Damage
// Check health state
if (PlayState.instance.health <= 0)
{
trace("Player should die");
}
Volume Control
Control individual audio track volumes:
// Mute instrumental during dialogue
PlayState.instance.instrumentalVolume = 0.0;
// Lower opponent vocals
PlayState.instance.opponentVocalsVolume = 0.5;
// Restore all volumes
PlayState.instance.instrumentalVolume = 1.0;
PlayState.instance.playerVocalsVolume = 1.0;
PlayState.instance.opponentVocalsVolume = 1.0;
See Also