This chapter will walk you through the process of adding a script to a Character, and giving examples of the kind of custom behavior which can be implemented with this functionality.
Start by creating a scripted class file with the .hxc extension (in the mods/mymod/scripts/characters if you want to keep things organized).
// Remember to import each class you want to reference in your script! import funkin.play.character.SparrowCharacter; // Choose a name for your scripted class that will be unique, and make sure to specifically extend the correct character class. // SparrowCharacter is the one to use for XML-based characters. // This class's functions will override the default behavior for the character. class WhittyCharacter extends SparrowCharacter { public function new() { // The constructor gets called whenever the character is spawned. // The constructor takes one parameter, which is the song ID for the song you are applying the script to. super('whitty'); } // Add override functions here! }
You can then add override functions to perform custom behavior.
Character Classes
If you use a different animation type for a character, you need to specify a different base script class, otherwise the character will fail to load.
funkin.play.character.SparrowCharacteris used for characters that have Sparrow spritesheet animations.funkin.play.character.MultiSparrowCharacteris used for characters that have several Sparrow spritesheet animations to combine into one character.funkin.play.character.PackerCharacteris used for characters that have Packer spritesheet animations.funkin.play.character.AnimateAtlasCharacteris used for characters that have Adobe Animate texture atlases.funkin.play.character.BaseCharacterhas empty stubs for all the rendering and animation handlers, and is only useful for people who want to reimplement their character's animation system by hand.
Custom Behavior when Playing Animations
If you want something to happen while a character is playing a specific animation or just not want to play an animation at a specific moment even when called to, you should override the playAnimation function with your own behavior. An example of what you can do with this is found in the character script file for Boyfriend1
// ... // Line 17 override function playAnimation(name:String, restart:Bool, ignoreOther:Bool) { if (name == "fakeoutDeath" && !this.debug) { doFakeoutDeath(); } else { super.playAnimation(name, restart, ignoreOther); } } // ...
Custom Behavior when Finishing Animations
To trigger custom behavior when finishing animations, it is important to override the function onAnimationFinished to include your behavior. For example, the script file for Nene2 switches her knife state after a specific animation is finished:
// ... // Line 384 function onAnimationFinished(name:String) { super.onAnimationFinished(name); switch (currentState) { case STATE_RAISE: if (name == "raiseKnife") { animationFinished = true; transitionState(); } // ... } } // ...
Custom Behavior on an Animation Frame
Several custom behaviors may require specific timings when playing certain animations, such as having haptic feedback for some of the animations. For this you would need to override the function onAnimationFrame. The script for Pico (Playable)3 uses this to create a casing mid-way throughout his gun cocking.
// ... // Line 308 function onAnimationFrame(name:String, frameNumber:Int, frameIndex:Int) { super.onAnimationFrame(name, frameNumber, frameIndex); // ... if (name == "cock" && frameNumber == 3) { createCasing(); } } // ...
Custom Death Quotes
When you die in a week 7 song, the game plays one of Tankman's insults before fading in the game over music. This is done by overriding the function getDeathQuote in the character script. An example for how this is done is found in the script file for Boyfriend holding Girlfriend4
// ... // Line 16 override function getDeathQuote():Null<String> { var randomCensor:Array<Int> = []; if (!Preferences.naughtyness) { // 1 - cock // 3 - shitty // 8 - shit // 13 - fuck // 17 - shit, asshole // 21 - fucking randomCensor = [1, 3, 8, 13, 17, 21]; } else if (Constants.CENSOR_EXPLETIVES) { randomCensor = [13, 21]; } return Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, randomCensor)); } // ...
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/characters/bf.hxc ↩
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/characters/nene.hxc ↩
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/characters/pico-playable.hxc ↩
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/characters/bf-holding-gf.hxc ↩