This should be used for the FNF 0.2.8 update and engines that have this version of FNF
Windows Compile Instructions for Android
- Download
-
Extract them
-
Open CMD/Powershell and do these commands:
cd (path of sdk)/cmdline-tools/bin
./sdkmanager.bat --sdk_root=(path of sdk) --licenses
And accept all of the licenses
-
Run command
lime setup android
in CMD/PowerShell (You need to insert the program paths) -
Open project in CMD/PowerShell
cd (path to fnf source)
And run commandlime build android -final
The apk will be generated in this path (path to source)\export\release\android\bin\app\build\outputs\apk\debug
- You Need to install
extension-androidtools
To Install it You Need To Open Command prompt/PowerShell And Type
haxelib git extension-androidtools https://github.com/MAJigsaw77/extension-androidtools.git
-
Download the repository code and paste it in your source code folder
-
You Need to add these things in
Project.xml
On This Line
<!--Mobile-specific-->
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false" />
Replace It With
<!--Mobile-specific-->
<window if="mobile" orientation="landscape" fullscreen="true" resizable="false" allow-shaders="true" require-shaders="true" allow-high-dpi="true" />
Add
<assets path="mobile" rename="assets/mobile" if="mobileC" />
Then, After the Libraries, or where the packeges are located add
<haxelib name="extension-androidtools" if="android" />
Add
<!--Mobile Controls Define-->
<define name="mobileC" if="mobile" /> <!--Can be added windows, mac or linux-->
<!--Always enable Null Object Reference check-->
<haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" />
<haxedef name="HXCPP_STACK_TRACE" />
<section if="android">
<config>
<!--Gradle-->
<!--<android gradle-version="7.4.2" gradle-plugin="7.3.1" />--> <!-- ENABLE THIS IF YOU HAVE ISSUES AT COMPILE -->
<!--Target SDK-->
<android min-sdk-version="16" target-sdk-version="29" max-sdk-version="33" if="${lime < 8.1.0}"/>
</config>
</section>
<section if="ios">
<!--Dependency-->
<dependency name="Metal.framework" if="${lime < 8.0.0}" />
</section>
<haxedef name="no-deprecation-warnings" unless="debug" />
- Setup Controls.hx
After these lines
import flixel.input.actions.FlxActionSet;
import flixel.input.keyboard.FlxKey;
Add
#if mobileC
import mobile.flixel.FlxButton;
import mobile.flixel.FlxHitbox;
import mobile.flixel.FlxVirtualPad;
#end
Before these lines
override function update()
{
super.update();
}
Add
#if mobileC
public var trackedInputsUI:Array<FlxActionInput> = [];
public var trackedInputsNOTES:Array<FlxActionInput> = [];
public function addButtonNOTES(action:FlxActionDigital, button:FlxButton, state:FlxInputState):Void
{
if (button == null)
return;
var input:FlxActionInputDigitalIFlxInput = new FlxActionInputDigitalIFlxInput(button, state);
trackedInputsNOTES.push(input);
action.add(input);
}
public function addButtonUI(action:FlxActionDigital, button:FlxButton, state:FlxInputState):Void
{
if (button == null)
return;
var input:FlxActionInputDigitalIFlxInput = new FlxActionInputDigitalIFlxInput(button, state);
trackedInputsUI.push(input);
action.add(input);
}
public function setHitBox(Hitbox:FlxHitbox):Void
{
if (Hitbox == null)
return;
inline forEachBound(Control.NOTE_LEFT, (action, state) -> addButtonNOTES(action, Hitbox.hints[0], state));
inline forEachBound(Control.NOTE_DOWN, (action, state) -> addButtonNOTES(action, Hitbox.hints[1], state));
inline forEachBound(Control.NOTE_UP, (action, state) -> addButtonNOTES(action, Hitbox.hints[2], state));
inline forEachBound(Control.NOTE_RIGHT, (action, state) -> addButtonNOTES(action, Hitbox.hints[3], state));
}
public function setVirtualPadUI(VirtualPad:FlxVirtualPad, DPad:FlxDPadMode, Action:FlxActionMode):Void
{
if (VirtualPad == null)
return;
switch (DPad)
{
case UP_DOWN:
inline forEachBound(Control.UI_UP, (action, state) -> addButtonUI(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.UI_DOWN, (action, state) -> addButtonUI(action, VirtualPad.buttonDown, state));
case LEFT_RIGHT:
inline forEachBound(Control.UI_LEFT, (action, state) -> addButtonUI(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.UI_RIGHT, (action, state) -> addButtonUI(action, VirtualPad.buttonRight, state));
case UP_LEFT_RIGHT:
inline forEachBound(Control.UI_UP, (action, state) -> addButtonUI(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.UI_LEFT, (action, state) -> addButtonUI(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.UI_RIGHT, (action, state) -> addButtonUI(action, VirtualPad.buttonRight, state));
case LEFT_FULL | RIGHT_FULL:
inline forEachBound(Control.UI_UP, (action, state) -> addButtonUI(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.UI_DOWN, (action, state) -> addButtonUI(action, VirtualPad.buttonDown, state));
inline forEachBound(Control.UI_LEFT, (action, state) -> addButtonUI(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.UI_RIGHT, (action, state) -> addButtonUI(action, VirtualPad.buttonRight, state));
case BOTH_FULL:
inline forEachBound(Control.UI_UP, (action, state) -> addButtonUI(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.UI_DOWN, (action, state) -> addButtonUI(action, VirtualPad.buttonDown, state));
inline forEachBound(Control.UI_LEFT, (action, state) -> addButtonUI(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.UI_RIGHT, (action, state) -> addButtonUI(action, VirtualPad.buttonRight, state));
inline forEachBound(Control.UI_UP, (action, state) -> addButtonUI(action, VirtualPad.buttonUp2, state));
inline forEachBound(Control.UI_DOWN, (action, state) -> addButtonUI(action, VirtualPad.buttonDown2, state));
inline forEachBound(Control.UI_LEFT, (action, state) -> addButtonUI(action, VirtualPad.buttonLeft2, state));
inline forEachBound(Control.UI_RIGHT, (action, state) -> addButtonUI(action, VirtualPad.buttonRight2, state));
case NONE: // do nothing
}
switch (Action)
{
case A:
inline forEachBound(Control.ACCEPT, (action, state) -> addButtonUI(action, VirtualPad.buttonA, state));
case B:
inline forEachBound(Control.BACK, (action, state) -> addButtonUI(action, VirtualPad.buttonB, state));
case P:
inline forEachBound(Control.PAUSE, (action, state) -> addButtonUI(action, VirtualPad.buttonP, state));
case A_B | A_B_C | A_B_E | A_B_X_Y | A_B_C_X_Y | A_B_C_X_Y_Z | A_B_C_D_V_X_Y_Z:
inline forEachBound(Control.ACCEPT, (action, state) -> addButtonUI(action, VirtualPad.buttonA, state));
inline forEachBound(Control.BACK, (action, state) -> addButtonUI(action, VirtualPad.buttonB, state));
case NONE: // do nothing
}
}
public function setVirtualPadNOTES(VirtualPad:FlxVirtualPad, DPad:FlxDPadMode, Action:FlxActionMode):Void
{
if (VirtualPad == null)
return;
switch (DPad)
{
case UP_DOWN:
inline forEachBound(Control.NOTE_UP, (action, state) -> addButtonNOTES(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.NOTE_DOWN, (action, state) -> addButtonNOTES(action, VirtualPad.buttonDown, state));
case LEFT_RIGHT:
inline forEachBound(Control.NOTE_LEFT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.NOTE_RIGHT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonRight, state));
case UP_LEFT_RIGHT:
inline forEachBound(Control.NOTE_UP, (action, state) -> addButtonNOTES(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.NOTE_LEFT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.NOTE_RIGHT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonRight, state));
case LEFT_FULL | RIGHT_FULL:
inline forEachBound(Control.NOTE_UP, (action, state) -> addButtonNOTES(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.NOTE_DOWN, (action, state) -> addButtonNOTES(action, VirtualPad.buttonDown, state));
inline forEachBound(Control.NOTE_LEFT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.NOTE_RIGHT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonRight, state));
case BOTH_FULL:
inline forEachBound(Control.NOTE_UP, (action, state) -> addButtonNOTES(action, VirtualPad.buttonUp, state));
inline forEachBound(Control.NOTE_DOWN, (action, state) -> addButtonNOTES(action, VirtualPad.buttonDown, state));
inline forEachBound(Control.NOTE_LEFT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonLeft, state));
inline forEachBound(Control.NOTE_RIGHT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonRight, state));
inline forEachBound(Control.NOTE_UP, (action, state) -> addButtonNOTES(action, VirtualPad.buttonUp2, state));
inline forEachBound(Control.NOTE_DOWN, (action, state) -> addButtonNOTES(action, VirtualPad.buttonDown2, state));
inline forEachBound(Control.NOTE_LEFT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonLeft2, state));
inline forEachBound(Control.NOTE_RIGHT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonRight2, state));
case NONE: // do nothing
}
switch (Action)
{
case A:
inline forEachBound(Control.ACCEPT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonA, state));
case B:
inline forEachBound(Control.BACK, (action, state) -> addButtonNOTES(action, VirtualPad.buttonB, state));
case P:
inline forEachBound(Control.PAUSE, (action, state) -> addButtonNOTES(action, VirtualPad.buttonP, state));
case A_B | A_B_C | A_B_E | A_B_X_Y | A_B_C_X_Y | A_B_C_X_Y_Z | A_B_C_D_V_X_Y_Z:
inline forEachBound(Control.ACCEPT, (action, state) -> addButtonNOTES(action, VirtualPad.buttonA, state));
inline forEachBound(Control.BACK, (action, state) -> addButtonNOTES(action, VirtualPad.buttonB, state));
case NONE: // do nothing
}
}
public function removeVirtualControlsInput(Tinputs:Array<FlxActionInput>):Void
{
for (action in this.digitalActions)
{
var i = action.inputs.length;
while (i-- > 0)
{
var x = Tinputs.length;
while (x-- > 0)
{
if (Tinputs[x] == action.inputs[i])
action.remove(action.inputs[i]);
}
}
}
}
#end
- Setup MusicBeatState.hx
In the lines you import things add
#if mobileC
import mobile.MobileControls;
import mobile.flixel.FlxVirtualPad;
import flixel.FlxCamera;
import flixel.input.actions.FlxActionInput;
import flixel.util.FlxDestroyUtil;
#end
After these lines
inline function get_controls():Controls
return PlayerSettings.player1.controls;
Add
#if mobileC
var mobileControls:MobileControls;
var virtualPad:FlxVirtualPad;
var trackedInputsMobileControls:Array<FlxActionInput> = [];
var trackedInputsVirtualPad:Array<FlxActionInput> = [];
public function addVirtualPad(DPad:FlxDPadMode, Action:FlxActionMode):Void
{
if (virtualPad != null)
removeVirtualPad();
virtualPad = new FlxVirtualPad(DPad, Action);
add(virtualPad);
controls.setVirtualPadUI(virtualPad, DPad, Action);
trackedInputsVirtualPad = controls.trackedInputsUI;
controls.trackedInputsUI = [];
}
public function removeVirtualPad():Void
{
if (trackedInputsVirtualPad.length > 0)
controls.removeVirtualControlsInput(trackedInputsVirtualPad);
if (virtualPad != null)
remove(virtualPad);
}
public function addMobileControls(DefaultDrawTarget:Bool = true):Void
{
if (mobileControls != null)
removeMobileControls();
mobileControls = new MobileControls();
switch (MobileControls.mode)
{
case 'Pad-Right' | 'Pad-Left' | 'Pad-Custom':
controls.setVirtualPadNOTES(mobileControls.virtualPad, RIGHT_FULL, NONE);
case 'Pad-Duo':
controls.setVirtualPadNOTES(mobileControls.virtualPad, BOTH_FULL, NONE);
case 'Hitbox':
controls.setHitBox(mobileControls.hitbox);
case 'Keyboard': // do nothing
}
trackedInputsMobileControls = controls.trackedInputsNOTES;
controls.trackedInputsNOTES = [];
var camControls:FlxCamera = new FlxCamera();
camControls.bgColor.alpha = 0;
FlxG.cameras.add(camControls, DefaultDrawTarget);
mobileControls.cameras = [camControls];
mobileControls.visible = false;
add(mobileControls);
}
public function removeMobileControls():Void
{
if (trackedInputsMobileControls.length > 0)
controls.removeVirtualControlsInput(trackedInputsMobileControls);
if (mobileControls != null)
remove(mobileControls);
}
public function addVirtualPadCamera(DefaultDrawTarget:Bool = true):Void
{
if (virtualPad != null)
{
var camControls:FlxCamera = new FlxCamera();
camControls.bgColor.alpha = 0;
FlxG.cameras.add(camControls, DefaultDrawTarget);
virtualPad.cameras = [camControls];
}
}
#end
override function destroy():Void
{
#if mobileC
if (trackedInputsMobileControls.length > 0)
controls.removeVirtualControlsInput(trackedInputsMobileControls);
if (trackedInputsVirtualPad.length > 0)
controls.removeVirtualControlsInput(trackedInputsVirtualPad);
#end
super.destroy();
#if mobileC
if (virtualPad != null)
virtualPad = FlxDestroyUtil.destroy(virtualPad);
if (mobileControls != null)
mobileControls = FlxDestroyUtil.destroy(mobileControls);
#end
}
- Setup MusicBeatSubstate.hx
In the lines you import things add
#if mobileC
import mobile.flixel.FlxVirtualPad;
import flixel.FlxCamera;
import flixel.input.actions.FlxActionInput;
import flixel.util.FlxDestroyUtil;
#end
After these lines
inline function get_controls():Controls
return PlayerSettings.player1.controls;
Add
#if mobileC
var virtualPad:FlxVirtualPad;
var trackedInputsVirtualPad:Array<FlxActionInput> = [];
public function addVirtualPad(DPad:FlxDPadMode, Action:FlxActionMode):Void
{
if (virtualPad != null)
removeVirtualPad();
virtualPad = new FlxVirtualPad(DPad, Action);
add(virtualPad);
controls.setVirtualPadUI(virtualPad, DPad, Action);
trackedInputsVirtualPad = controls.trackedInputsUI;
controls.trackedInputsUI = [];
}
public function removeVirtualPad():Void
{
if (trackedInputsVirtualPad.length > 0)
controls.removeVirtualControlsInput(trackedInputsVirtualPad);
if (virtualPad != null)
remove(virtualPad);
}
public function addVirtualPadCamera(DefaultDrawTarget:Bool = true):Void
{
if (virtualPad != null)
{
var camControls:FlxCamera = new FlxCamera();
camControls.bgColor.alpha = 0;
FlxG.cameras.add(camControls, DefaultDrawTarget);
virtualPad.cameras = [camControls];
}
}
#end
override function destroy():Void
{
#if mobileC
if (trackedInputsVirtualPad.length > 0)
controls.removeVirtualControlsInput(trackedInputsVirtualPad);
#end
super.destroy();
#if mobileC
if (virtualPad != null)
virtualPad = FlxDestroyUtil.destroy(virtualPad);
#end
}
And somehow you finished adding the mobile controls to your mod now on every state/substate add
#if mobileC
addVirtualPad(LEFT_FULL, A_B);
#end
//if you want to remove it at some moment use
#if mobileC
removeVirtualPad();
#end
//if you want it to have a camera
#if mobileC
addVirtualPadCamera(); //if hud disappears add false inside to ().
#end
//in states, these need to be added before super.create();
//in substates, in fuction new at the last line add these
//on Playstate.hx after all of the
//obj.cameras = [...];
//things, add
#if mobileC
addMobileControls(); //if hud disappears add false inside to ().
#end
//if you want to remove it at some moment use
#if mobileC
removeMobileControls();
#end
//to make the controls visible the code is
#if mobileC
mobileControls.visible = true;
#end
//to make the controls invisible the code is
#if mobileC
mobileControls.visible = false;
#end
- Prevent the Android BACK Button
in TitleState.hx
After
override public function create():Void
Add
#if android
FlxG.android.preventDefaultKeys = [BACK];
#end
- Set An action to the BACK Button
You can set one with
#if android || FlxG.android.justReleased.BACK #end
- On
sys.FileSystem
,sys.io.File
Stuff
This is not working with app storage but on phone storage it will work with this
SUtil.getStorageDirectory() +
This will make the game use the phone storage but you will have to add one thing in your source
In Main.hx before
addChild(new FlxGame(gameWidth, gameHeight, initialState, zoom, framerate, framerate, skipSplash, startFullscreen));
Add
SUtil.checkFiles();
This will check for android storage permisions and the assets
or mods
directories
- On Crash Application Alert
On Main.hx after
public function new()
Add
SUtil.uncaughtErrorHandler();
- File Saver
This is a feature to save files with sys.io.File in phone storage in saves
directory
SUtil.saveContent("your file name", ".txt", "lololol");
- Do an action when you press on the screen
#if mobileC
var justTouched:Bool = false;
for (touch in FlxG.touches.list)
if (touch.justPressed)
justTouched = true;
if (justTouched)
//Your code
#end