Playmaker Forum

PlayMaker Help & Tips => PlayMaker Help => Topic started by: Thore on April 25, 2019, 03:49:18 PM

Title: New Input System: how to call method via strings?
Post by: Thore on April 25, 2019, 03:49:18 PM
Hello, the super brief question is this: How can I turn a few strings, set in the action by the user, into a piece of executable code?


FsmObject = MyScript;
FsmString continent = "Africa";
FsmString land = "Kenya";
FsmString city = "Nairobi";

as a code reflection that can be executed as code, so:

Code: [Select]
I also need to know how I can turn a user-provided object (e.g. per drag and drop) to reference it, like so:

Code: [Select]
public MyScript scriptInstance
So that it be used in the way as outlined above.

In the end, the custom action requires a script (generated by Unity), to access an instance of it. Then it would invoke a method somehow created from the strings given. I've looked into the call method action, which does something similar (it seems), but I cannot figure out how to change it.

Any ideas?
Title: Re: New Input System: how to call method via strings?
Post by: Thore on April 25, 2019, 04:30:24 PM
Maybe a bit of background helps :D

Unity is going to switch to a new input system (currently in preview). You can define a list of (input) actions (shoot, jump etc) and add various inputs (WASD, Dpad etc) to to it, and categorize that by control schemes and contexts (called action maps, one for menu controls, one for in-game controls etc).

Basically, you end up with a string like so: myInputScript.gameplay.jump.perform. A Playmaker action would be neat where you can name your inputscript, the action map string ("gameplay") and the action name ("jump") and have that send an event when triggered.

Example: In your jumping FSM, you simply say "listening for my 'jump' input" and if it hears something, trigger the event.

Some Tutorials
Brackeys: Infallible Code: (also see follow up, which uses the more robust solution via asset)

Thanks :D
Title: A Working Prototype
Post by: Thore on April 26, 2019, 11:06:11 AM
Here's a prototype I came up with, for those who want to experiment a little. It works for Unity 2019.1 (0f2) and Input System preview 0.2.8 (which is current at time of posting). I am still a code newbie and it just reflects my learning process. Maybe it gets someone else started to do something more robust ;)

The setup and adjustment is simple, and it can be easily deleted as well with no problems. I recommend to use my setup first, and adjust and change names around later.

General Idea
The new input file keeps track of all the key bindings and control schemes, as Input Manager did in the past. It generates a script with its own class. This is then read from a different script that sits o the player (called PlayerInput.cs in my example), think character controller etc. Here, all the important values are collected. You can then fetch them from there via Get Property into Playmaker, or make custom actions (which I'll do later on).


What Should Happen
Check the inspector of the PlayerInput script, you've attached and observe the values. Try these buttons:

A / D = changes movement around
Space = Jumping
TAB = Toggle
L-CTRL = Shooting toggle
Arrow Keys = Arrow Vector

You'll see that the Jumping stays true for a second and then switches to false. This duration is controlled by Jumping Grace. You can view keybindings by clicking on the MasterInput asset file (not the script), to see how it's done, and for testing add your own bindings.

How to Customize
Here's how it works. All of the names you'll use should be usable in code, but be intelligble. You can use spaces, too ("Menu Up" is "MenuUp" in code).

Action Maps: this is basically a context in the game where one type of controls is active. You want to use e.g. one to navigate the main menu, and another one for the controls in the game. For now, stay with one. Mine is called "Gameplay". When you change this, you need to change the code as well.

Actions: things you do in your game, like Jump, Shoot, Move etc. Important, don't name them after buttons, which defeats the purpose. You need to use the same names in the code later (spaces will be ignored).

Bindings: simply add whatever key, button or input method you want to associate with that action. However, the type you use must correspond to the code later. For a normal button press, use the standard binding, which you can later get with simple bools. When you want to have an axis (formerly "Horizontal" etc), use 1D Composite, but then you also need to use the float variant (no worries, I added an example, see Movement). You can also get four directions with the 2D Composite, which returns a Vector2. I also provided that as an example (see Arrow Vector).

Control Schemes: in the upper corner, you can also define control schemes, which would be stuff like Gamepad, Keyboard etc. To do this, click on the individual bindings and check the associated checkmark. E.g. click on the "A/D" binding and check it for "Keyboard". You can set in the corner which scheme (or any) is visible at a time. I'd leave this away for now.

Interactions within the actions or bindings you can add interactions. The purpose is to make it so that only e.g. pressing the button counts as "jump", but not releasing it. If you have not set this, it would fire the button twice.

Some Code Tips
You'll take a minute to wrap your mind around it, but it's quite simple and elegant. Now that you have an idea how this works in general, you can easily make your own file. Right Click in project > Input Actions and voila you have your own blank file. Fill something in, and be sure to generate the class file. If you give a path, make sure you add the whole shebang, including the class name and .cs at the end. If you have errors, you're probably accidentially have a duplicate. Delete one version.

So you'll work with two scripts. The one generated and the one that sits on your character. Therefore...
Code: [Select]
MasterInput masterInput; is a reference to your generated class. The upper case refers to that class, the lowercase is the variable to store an instance of this (and could be named anything you like). Think of this as "bool myBool", or "MyClass myClassVariable". In Awake, we need to create one instance to work with,
Code: [Select]
masterInput = new MasterInput();. This must correspond to your generated script class.

Code: [Select]
// mapping actions
    private InputAction move;
    private InputAction jump;
    private InputAction toggle;
    private InputAction shoot;
    private InputAction twoAxis;

Here you simply add the actions you want to use later on. I use the same variables as I used in the input, just as lower case variants.

Code: [Select]
    // values we want to get later.
    public float moving = 0f;
    public bool jumping = false;
    public bool toggling = false;
    public bool shooting = false;
    public Vector2 arrowVector;

In this block, you add the type of variables you want to store to use later for Playmaker. Bindings (i.e. button presses) can use bools. 1D Composite bindings need float, and 2D Composite needs Vector2.

Code: [Select]
        move = masterInput.Gameplay.Move;
        move.performed += OnMove;
        move.cancelled += OnMove;

Here is how an action is referenced. The first line basically sets the path, so that the two lines afterwards are shorter. But you could also say "masterInput.Gameplay.Move.performed ..." if you want. It's always the name you gave in for your Actions with a On suffix. I.e. action = "Loiter" becomes "OnLoiter;". In case of doubt, you can open your generated input action file and scroll to the end, where you see this:     
Code: [Select]
void OnMove(InputAction.CallbackContext context);
And that also explains how the function (or method) works:     

public void OnToggle(InputAction.CallbackContext context)
        toggling = !toggling; // flips the bool.


I added a few variants, so you can copy and paste things together. Jumping as an example uses a coroutine to remember the button press, which is useful for some purposes. You can use it, or not. Shoot uses an alternative implementation, which I wouldn't use. For trigger like fire-and-forget button presses, use the toogle version, and in Playmaker use the Bool Changed action.

How To With Playmaker
For now, you can drag and drop the very script you have on the player into Playmaker's variables pane, which creates an object variable. Then drag and drop the script into a state, and Get Property. In the newly created action, you can then change the Target Object to your variable without a fuzz. Click on Property and pick the variable you want to read from the script (these are the " // values we want to get later.") , and finally store it into a Playmaker variable. Set the thing to every frame, and you're good to go.

Now that you can read inputs, button presses and axis, how to work with this? Try this. Store movement, and feed it into Set Velocity 2D, for instance. Use Bool Test and Bool Changed for button presses.

For now it's usable, but keep in mind input system is in preview and may change. Enjoy.
Title: Re: New Input System: how to call method via strings?
Post by: Thore on April 26, 2019, 02:25:29 PM
Since I have it now, here's one custom action to grab the moving variable from the script, to get you started. You can make similar ones, put all in one place or break it up.