The character system manages all playable and non-playable characters, their animations, and their behavior during gameplay.
Character Architecture
BaseCharacter Class
All characters extend BaseCharacter, which extends Bopper:
class BaseCharacter extends Bopper
{
public var characterId:String;
public var characterName:String;
public var characterType:CharacterType = OTHER;
public var holdTimer:Float = 0;
public var isDead:Bool = false;
public var debug:Bool = false;
public var currentStage:Null<Stage> = null;
}
Key Properties:
Unique identifier for the character (e.g., “bf”, “dad”, “gf”)
Display name of the character (e.g., “Boyfriend”, “Daddy Dearest”)
Type of character: BF (player), DAD (opponent), GF (girlfriend), or OTHER
Tracks how long the character has been playing the current sing animation (in seconds)
Character Types
Characters are categorized by their role:
enum abstract CharacterType(String)
{
var BF = 'bf'; // Player character
var DAD = 'dad'; // Opponent character
var GF = 'gf'; // Girlfriend/background character
var OTHER = 'other'; // Other characters
}
Character Rendering Types
FNF supports multiple rendering methods:
SparrowCharacter
Uses Sparrow sprite sheets (Adobe Animate/XML format):
class SparrowCharacter extends BaseCharacter
{
public function new(id:String)
{
super(id, CharacterRenderType.Sparrow);
// Loads from assets/images/characters/[id].png + .xml
}
}
AnimateAtlasCharacter
Uses Adobe Animate atlas format:
class AnimateAtlasCharacter extends BaseCharacter
{
public function new(id:String)
{
super(id, CharacterRenderType.AnimateAtlas);
// Loads from assets/images/characters/[id]/ directory
}
}
PackerCharacter
Uses Packer sprite sheets (legacy format):
class PackerCharacter extends BaseCharacter
{
public function new(id:String)
{
super(id, CharacterRenderType.Packer);
// Loads Packer format sprite sheet
}
}
Multi-Sprite Characters
- MultiSparrowCharacter: Multiple Sparrow sheets
- MultiAnimateAtlasCharacter: Multiple Animate atlases
Character data is stored in JSON files at assets/data/characters/[id].json:
{
"version": "1.0.1",
"name": "Boyfriend",
"renderType": "sparrow",
"assetPath": "characters/bf",
"scale": 1.0,
"offsets": [0, 0],
"cameraOffsets": [-100, -100],
"healthIcon": {
"id": "bf",
"scale": 1.0,
"antialiasing": true
},
"animations": [
{
"name": "idle",
"prefix": "BF idle dance",
"offsets": [0, 0],
"looped": false,
"frameRate": 24,
"flipX": false,
"flipY": false
},
{
"name": "singLEFT",
"prefix": "BF NOTE LEFT",
"offsets": [5, -6],
"looped": false,
"frameRate": 24
}
],
"startingAnimation": "idle",
"flipX": false,
"isPixel": false,
"danceEvery": 1,
"singTime": 8.0
}
Character Data Fields
Character data format version (currently “1.0.1”)
Display name for the character
Rendering type: "sparrow", "animateatlas", "packer", "multisparrow", or "multianimateatlas"
Path to character assets (relative to assets/images/)
Base scale multiplier for the character sprite
offsets
Array<Float>
default:"[0, 0]"
Global position offsets as [x, y]
cameraOffsets
Array<Float>
default:"[0, 0]"
Camera focus point offsets as [x, y]
Whether to flip the character sprite horizontally
Whether this is a pixel-art character (disables anti-aliasing)
How many beats between idle dance animations
How long (in steps) to hold sing animations
Name of the animation to play when character is created
Animation Data
Each animation in the animations array:
Internal animation name (e.g., “idle”, “singLEFT”, “singDOWN”)
Animation prefix in the sprite sheet XML
offsets
Array<Float>
default:"[0, 0]"
Position offsets specific to this animation [x, y]
Whether the animation should loop
Frames per second for the animation
Flip this animation horizontally
Flip this animation vertically
Specific frame indices to use (if not using all frames)
Required Animations
Characters should define these core animations:
Idle Animations
{
"name": "idle",
"prefix": "BF idle dance"
}
Or alternating left/right:
[
{
"name": "danceLeft",
"prefix": "BF idle dance LEFT"
},
{
"name": "danceRight",
"prefix": "BF idle dance RIGHT"
}
]
Sing Animations
Required for playable characters:
[
{
"name": "singLEFT",
"prefix": "BF NOTE LEFT"
},
{
"name": "singDOWN",
"prefix": "BF NOTE DOWN"
},
{
"name": "singUP",
"prefix": "BF NOTE UP"
},
{
"name": "singRIGHT",
"prefix": "BF NOTE RIGHT"
}
]
Miss Animations (Optional)
For when the player misses notes:
[
{
"name": "singLEFTmiss",
"prefix": "BF NOTE LEFT MISS"
},
{
"name": "singDOWNmiss",
"prefix": "BF NOTE DOWN MISS"
},
{
"name": "singUPmiss",
"prefix": "BF NOTE UP MISS"
},
{
"name": "singRIGHTmiss",
"prefix": "BF NOTE RIGHT MISS"
}
]
Character Position System
Position Properties
Characters use a feet-based origin system:
public var characterOrigin:FlxPoint; // At character's feet (center-bottom)
public var cornerPosition:FlxPoint; // Top-left corner of sprite
public var feetPosition:FlxPoint; // Bottom-center of sprite
public var cameraFocusPoint:FlxPoint; // Where camera should focus
Character origin is at the feet:
function get_characterOrigin():FlxPoint
{
var xPos = (width / 2); // Horizontal center
var yPos = (height); // Vertical bottom
return new FlxPoint(xPos, yPos);
}
Camera Focus
The camera focuses on the character’s cameraFocusPoint:
public var cameraFocusPoint:FlxPoint = new FlxPoint(0, 0);
This point is automatically updated when the character moves, but not when animation offsets change. This ensures smooth camera motion.
Health Icons
Characters have associated health icons:
{
"healthIcon": {
"id": "bf",
"scale": 1.0,
"antialiasing": true,
"isPixel": false
}
}
ID of the icon asset (looks for assets/images/icons/[id].png)
Scale multiplier for the icon
Whether to use anti-aliasing on the icon
Death Animations
Player characters can define death animation settings:
{
"death": {
"cameraOffsets": [0, 0],
"cameraZoom": 1.0,
"preTransitionDelay": 0.0
}
}
death.cameraOffsets
Array<Float>
default:"[0, 0]"
Camera offset during game over screen [x, y]
Camera zoom level during game over
Delay before transitioning to game over state (in seconds)
Character Bopping
Characters automatically “bop” to the music:
public var danceEvery:Int; // Bop every X beats
public var shouldBop:Bool; // Whether character should bop
The Bopper class handles automatic idle animation:
- If
danceLeft and danceRight exist, alternates between them
- Otherwise plays
idle animation
- Controlled by
danceEvery (beats between bops)
Combo Animations
Characters can have special animations for combos:
public var comboNoteCounts:Array<Int>; // e.g., [50, 100, 250]
public var dropNoteCounts:Array<Int>; // Combo break thresholds
When player hits these combo milestones, special animations can play.
Sing Time
Controls how long characters hold sing animations:
final singTimeSteps:Float; // From character data
public var holdTimer:Float; // Current hold time
Sing animations hold for singTimeSteps to prevent rapid flickering back to idle between notes.
Character Scripts
Characters can be scripted for custom behavior:
class ScriptedCharacter extends BaseCharacter
{
// Custom scripted behavior
}
Scripts can:
- Override animation behavior
- Add custom events
- Implement special mechanics
- Modify character properties dynamically
Loading Characters
Characters are loaded via CharacterDataParser:
var charData:CharacterData = CharacterDataParser.fetchCharacterData("bf");
var character = CharacterDataParser.createCharacter("bf");
The system:
- Loads JSON data from
assets/data/characters/[id].json
- Validates render type and version
- Creates appropriate character class instance
- Loads sprite assets and animations
- Applies offsets and properties
Example: Creating a Custom Character
{
"version": "1.0.1",
"name": "Custom Character",
"renderType": "sparrow",
"assetPath": "characters/custom",
"scale": 1.0,
"offsets": [0, 0],
"cameraOffsets": [-100, -100],
"flipX": false,
"isPixel": false,
"danceEvery": 1,
"singTime": 8.0,
"animations": [
{
"name": "idle",
"prefix": "Custom idle",
"offsets": [0, 0],
"looped": false,
"frameRate": 24
},
{
"name": "singLEFT",
"prefix": "Custom left",
"offsets": [5, -10],
"frameRate": 24
},
{
"name": "singDOWN",
"prefix": "Custom down",
"offsets": [-10, -20],
"frameRate": 24
},
{
"name": "singUP",
"prefix": "Custom up",
"offsets": [0, 20],
"frameRate": 24
},
{
"name": "singRIGHT",
"prefix": "Custom right",
"offsets": [-5, -10],
"frameRate": 24
}
],
"healthIcon": {
"id": "custom",
"scale": 1.0
}
}
Place sprite sheet at assets/images/characters/custom.png and custom.xml.