The things im using when i port a mod to mobile
This should be used for the FNF 0.2.8 update and engines that have this version of FNF
Windows Compile Instructions for Android
1.- JDK - Download the version
11
of it - Android Studio - I recomend you to download the latest version
- NDK - Download the version
r21e
(This is the version recomended by Lime)
-
Install JDK, Android Studio Unzip the NDK (the NDK does not need to be installed because its a zip archive)
-
We need to set up Android Studio for this go to android studio and find android sdk (in settings -> Appearance & Behavior -> system settings -> android sdk)
-
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/jigsaw-4277821/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="mobile" /> <!-- in order to not have the mobile assets in another builds -saw -->
Then, After the Libraries, or where the packeges are located add
<haxelib name="extension-androidtools" if="android" />
Add
<!--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" />
<!--Target SDK-->
<android target-sdk-version="29" if="${lime < 8.1.0}" />
</config>
</section>
<section if="ios">
<!--Dependency-->
<dependency name="Metal.framework" if="${lime < 8.0.0}" />
</section>
- Setup Controls.hx
After these lines
import flixel.input.actions.FlxActionSet;
import flixel.input.keyboard.FlxKey;
Add
#if mobile
import mobile.flixel.FlxButton;
import mobile.flixel.FlxHitbox;
import mobile.flixel.FlxVirtualPad;
#end
Before these lines
override function update()
{
super.update();
}
Add
#if mobile
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 BLANK: // 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 A_B | A_B_C | 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 BLANK: // 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 BLANK: // 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 A_B | A_B_C | 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 BLANK: // 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 and MusicBeatSubstate.hx.
In the lines you import things add
#if mobile
import mobile.flixel.FlxHitbox;
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 mobile
var hitbox:FlxHitbox;
var virtualPad:FlxVirtualPad;
var trackedInputsHitbox:Array<FlxActionInput> = [];
var trackedInputsVirtualPad:Array<FlxActionInput> = [];
public function addVirtualPad(DPad:FlxDPadMode, Action:FlxActionMode, visible:Bool = true):Void
{
if (virtualPad != null)
removeVirtualPad();
virtualPad = new FlxVirtualPad(DPad, Action);
virtualPad.visible = visible;
add(virtualPad);
controls.setVirtualPadUI(virtualPad, DPad, Action);
trackedInputsVirtualPad = controls.trackedInputsUI;
controls.trackedInputsUI = [];
}
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];
}
}
public function removeVirtualPad():Void
{
if (trackedInputsVirtualPad.length > 0)
controls.removeVirtualControlsInput(trackedInputsVirtualPad);
if (virtualPad != null)
remove(virtualPad);
}
public function addHitbox(visible:Bool = true):Void
{
if (hitbox != null)
removeHitbox();
hitbox = new FlxHitbox(3, Std.int(FlxG.width / 4), FlxG.height, [0xFF00FF, 0x00FFFF, 0x00FF00, 0xFF0000]);
hitbox.visible = visible;
add(hitbox);
controls.setHitBox(hitbox);
trackedInputsHitbox = controls.trackedInputsNOTES;
controls.trackedInputsNOTES = [];
}
public function addHitboxCamera(DefaultDrawTarget:Bool = true):Void
{
if (hitbox != null)
{
var camControls:FlxCamera = new FlxCamera();
camControls.bgColor.alpha = 0;
FlxG.cameras.add(camControls, DefaultDrawTarget);
hitbox.cameras = [camControls];
}
}
public function removeHitbox():Void
{
if (trackedInputsHitbox.length > 0)
controls.removeVirtualControlsInput(trackedInputsHitbox);
if (hitbox != null)
remove(hitbox);
}
#end
override function destroy():Void
{
#if mobile
if (trackedInputsHitbox.length > 0)
controls.removeVirtualControlsInput(trackedInputsHitbox);
if (trackedInputsVirtualPad.length > 0)
controls.removeVirtualControlsInput(trackedInputsVirtualPad);
#end
super.destroy();
#if mobile
if (virtualPad != null)
virtualPad = FlxDestroyUtil.destroy(virtualPad);
if (hitbox != null)
hitbox = FlxDestroyUtil.destroy(hitbox);
#end
}
And somehow you finished adding the mobile controls to your mod now on every state/substate add
//if you want to add the vpad to a state
#if mobile
addVirtualPad(LEFT_FULL, A_B);
#end
//if you want it to have a camera
#if mobile
addVirtualPadCamera(); // add false into () if isn't the default draw camera...
#end
//if you want to remove it at some moment use
#if mobile
removeVirtualPad();
#end
//if you want to add the hitbox to a state
#if mobile
addHitbox();
#end
//if you want it to have a camera
#if mobile
addHitboxCamera(); // add false into () if isn't the default draw camera...
#end
//if you want to remove it at some moment use
#if mobile
removeHitbox();
#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 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 mobile
var justTouched:Bool = false;
for (touch in FlxG.touches.list)
if (touch.justPressed)
justTouched = true;
if (justTouched)
// Your code
#end