Using Unity's new input system: have one button do different things? - c#

I am making a game using the new input system, but I have gotten stuck when trying to have a button do two things.
Currently, I have an action map for gameplay, and the mouse click acts as the "fire" event.
When the player clicks on a UI element (for example, an inventory slot), I don't want the "fire" event to occur, but rather another suitable method.
I tried to use two action maps (gameplay and UI), but I don't want the player to stop moving (due to gameplay input not being picked up on the UI action map).
I'm sure there is a simple solution to this, but there is a lack of extensive information on the new input system. I am thinking I may have to add an IPointer OnEnter, OnExit interface to the monobehaviour script and have the input script decide which action is best suited at the time of being pressed. Is there a more efficient way to do this?

Put the responsibility on the Firing script to check that player state is valid. You can expose player state information in order to check if the player is in a menu or in some other state that does not allow Fire to happen.
CanFire is a property in the player state composed from any number of checks.
public class PlayerState
{
.. other properties like IsInMenu, IsInCutscene, IsDead, etc..
public bool CanFire => !IsInMenu && !IsInCutscene && !IsDead;
}
Then in your firing script when the event is fired, check the CanFire boolean in the player state.
if (playerState.CanFire) Fire();

Related

How to see if PlayerInput action was performed? Unity, New Input System

I'm using the new inptu system, and i want a way to see if an input action has been performed (only once, like GetButtonDown).
If I use ".ReadValue()", the if statement returns true not only once, but for as long as the "inventoryAction button" is held down.
private void Awake()
{
playerInput = GetComponent<PlayerInput>();
inventoryAction = playerInput.actions["Inventory"];
}
private void Update()
{
if (inventoryAction.ReadValue<float>() == 1)
{
Debug.Log("inventory key was pressed");
}
}
How do I only get the "performed" part in an if statement (with the new input system)? Is there something like GetButtonDown in the new input system?
Starting with version 1.1.1, actions have a WasPressedThisFrame method which basically does this.
The documentation(linked above) includes the following code snippet:
var fire = playerInput.actions["fire"];
if (fire.WasPressedThisFrame() && fire.IsPressed())
StartFiring();
else if (fire.WasReleasedThisFrame())
StopFiring();
As the code example shows, there are also WasReleasedThisFrame and IsPressed which are essentially GetButtonUp and GetButton, respectively.
Note that there are special considerations to make that make them behave differently than GetButtonX calls, especially where buttons release and press thresholds are substantially different, or where buttons are pressed and released multiple times in a frame.
1.1.1 IsPressed docs:
This method is different from simply reading the action's current float value and comparing it to the press threshold and is also different from comparing the current actuation of activeControl to it. This is because the current level of actuation might have already fallen below the press threshold but might not yet have reached the release threshold.
This method works with any type of action, not just buttons.
Also note that because this operates on the results of EvaluateMagnitude(), it works with many kind of controls, not just buttons. For example, if an action is bound to a StickControl, the control will be considered "pressed" once the magnitude of the Vector2 of the control has crossed the press threshold.
Finally, note that custom button press points of controls (see pressPoint) are respected and will take precedence over defaultButtonPressPoint.
1.1.1 WasPressedThisFrame docs:
This method is different from WasPerformedThisFrame() in that it is
not bound to phase. Instead, if the action's level of actuation (that
is, the level of magnitude -- see EvaluateMagnitude() -- of the
control(s) bound to the action) crossed the press threshold (see
defaultButtonPressPoint) at any point in the frame, this method will
return true. It will do so even if there is an interaction on the
action that has not yet performed the action in response to the press.
This method works with any type of action, not just buttons.
Also note that because this operates on the results of
EvaluateMagnitude(), it works with many kind of controls, not just
buttons. For example, if an action is bound to a StickControl, the
control will be considered "pressed" once the magnitude of the Vector2
of the control has crossed the press threshold.
Finally, note that custom button press points of controls (see
pressPoint) are respected and will take precedence over
defaultButtonPressPoint.
Be sure to read the documentation for your version for more information.

Unity Best way to overwrite `StandardAssetsScripts`

I am Developing a FPS Game and I am using The FPSController that comes with the Standard Assets. I am developing a Gamepad controller to control the mobile game.
After configuring all inputs of the controller I want to replace the code as following:
if (!m_Jump)
{
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
}
The code should be like following:
if(Application.platform == RuntimePlatform.Android)
{
if (!m_Jump)
{
m_Jump = Input.GetButtonDown("Joystick A");
}
}else{
if (!m_Jump)
{
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
}
}
What is the best way to modify the FPS Controller Scripts and add These Inputs?
Thanks in advance.
You can go around this in a way that does not require you to edit or add any aditional code.
Under Edit>Project Settings > Input you can find the InputManager and "axis". In here you will find all the pre-defined input values and their corresponding buttons. If you unfold the Input Axis "Jump" You'll see it has a variable called "Positive button" which is by default space, and an empty "alternative button". If you add your desired button to the alternative button sectiong it will trigger the CrossPlatformInputManager.GetButtonDown("Jump"); action upon pressing the corresponding button on your gamepad.
you can also if you prefer extend the list and add your own triggers.
The input manager offers out of the box support for keyboard and mouse, joystick, and gamepad more information can be found in the docs here
The advantage of doing this is that this requires no additional code or conditions. Which is probably the most optimalised way to go aroung it

Keep Links between two gameObjects when scenes change in unity

I'm Working on a game, and i want to create a mute button. So i wrote a script for that and i attach this script into a gameObject that don't destroy on load.
I link the script with a UI Button present in a main menu. This UI button is destroy when i'm changing scenes.
when i start my game, and i click on the button, the audio turns off, and when i click back, it turns on.
But when i change to another scene, and i go back to the main menu, my script doesn't have the UI Button attach to it. so when i touch the button the audio doesn't change is behavior
I would like to know if it's possible to maintain the link between the UI Button and the script (attach to a normal GameObject), even if the UI Button is destroyed?
i tried this:
ButtonGameObject = GameObject.Find("UI Button");
but it doesn't work.
How can i fix that?
Thanks a lot.
There are many ways to work around this, but here's one:
Step 1: If you haven't already done so, implement a weak singleton pattern on your mute script (let's call it MuteScript.cs for now).
private static MuteScript singleton {get; private set;}
private void Awake() {
if (singleton == null) singleton = this;
[whatever other stuff you were already doing in Awake()]
}
public static void ToggleMute(Graphic graphic)
{
singleton._ToggleMute(graphic);
}
private void _ToggleMute(Graphic graphic)
{
[whatever code you were running in your original mute method]
}
Step 2: Attach a simple script to your UI button:
public class MuteButton: MonoBehaviour
{
Graphic myGraphic;
private void Awake() {
myGraphic = GetComponent<Graphic>();
}
public void OnClick() {
MuteScript.ToggleMute(myGraphic);
//I assume you want to do something like change the colour of the button when
//the player toggles it. Passing the Graphic to the MuteScript is the easiest
//way of doing this. If you really want to keep your code clean, though,
//I recommend expanding the MuteButton class with methods to take care of
//the UI side of things.
}
}
Step 3: In Unity Editor, setup your button to call its own OnClick() method, not the MuteScript method.
-
Now when you click the button, it will call the static MuteScript.ToggleMute(), which accesses the static-cached singleton reference, which in turn points back to your original object.
Singletons and static accessors are great for efficiency in Unity because they save you from having to call expensive search functions like FindObjectsOfType(). The only gotcha is that you have to be careful about not having multiple copies of a singleton-class object lying around, especially when using DontDestroyOnLoad().
So a better approach rather than having a script search and grab the component is to use a PlayerPrefs class from Unity. Essentially, it will hold onto all important aspects of the game and auto fill information.
This is a great tool for a lot of user customization aspects of a game. When using this, have a script (sceneController would be a good name) that will run when the scene starts (create a blank object at 0,0,0) and then under void Start() have the script grab the mute/unmute button: GameObject.Find("MuteButton") or (my favorite) give it a tag called MuteButton and run: GameOject.FindWithTag("MuteButton"). Also once you get a link to the button, add a listener to it for when the button is pressed.
Also store the sound manager in a gameController that will be passed throughout the game. This would control the soundManager and have access to that. So sceneManager will also need a reference to gameManager.
Using a script that is just for player preferences (a controller if you will) is a better way to organize and contain any preferences for the users. Just better clarity and separation.
Example
SceneController Object Script
class SceneController {
GameObject muteButton;
void Start() {
muteButton = GameObject.FindWithTag("muteButton");
muteButton.AddListener(muteButtonCheck);
}
void muteButtonClick() {
if (PlayerPrefs.GetInt("playerMute")) {
// If 1 (on)
// Set sound off
PlayerPref.SetInt("playerMute", 0);
} else {
// It's 0 (off)
// Set sound on
PlayerPrefs.SetInt("playerMute", 1);
}
}
}

Add delay in C# in Unity3D projects

I currently work on Unity and using C# language.
By far, I need to do something (in smallest case, load another scene).
My question is, how to make this something happens after a few seconds I put the cursor on the area?
this is my code right now.
if (_newGameButton.Contains (Event.current.mousePosition)) {
Application.LoadLevel (1);
Destroy (this);
}
I want to add delay when _newGameButton actives. Right now, when I move my cursor over _newGameButton it'll load scene 1 immediately. I have tried many ways such as using Invoke and WaitForSeconds. None works. If the mistake is how I use it, how's the right way? Thank you so much for your help.
EDIT: This question is answered and I have another question in Activate and Deactivate Invoke function.
To make timers and delays in Unity, simply use Invoke
void Start()
{
Debug.Log("hello from Start.");
Invoke("Test", 3f);
}
private void Test()
{
Debug.Log("hello from 'Test'");
}
Also very handy is InvokeRepeating
Invoke("Test", 5f, 0.5f);
It's that easy.
In your case
if (_newGameButton.Contains (Event.current.mousePosition))
{
Invoke("YourSceneName");
}
private void ChangeScenes()
{
UnityEngine.SceneManagement.SceneManager.LoadScene("ScreenMain");
}
You MUST USE the scene name. Don't forget you MUST HAVE DRAGGED THE SCENE, TO YOUR SCENE LIST. Look at "Build Settings" "Scenes in Build".
Note
That's not really how you load scenes in Unity these days. They changed the syntax. it's more like this...
UnityEngine.SceneManagement.SceneManager.LoadScene("ScreenMain");
If you want to load asynchronously
AsyncOperation ao;
ao = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("SceneName");
while (!ao.isDone)
{
Debug.Log("loading " +ao.progress.ToString("f2"));
yield return null;
}
If you have any questions about scene loading, ask them separately as a new question. Note that you should almost certainly NOT do this "Destroy(this);".
Well ill help you understand something a bit bigger then just pausing cause it seems you are trying to create on OnClick event, nowadays its being used differently.
what you realy want to do is just create the SceneLoader script which can be a very general script such as the following,
using UnityEngine;
using UnityEngine.UI;
public class UIController : MonoBehaviour {
public int numOfSceneToLoad; // number of the scene from the build settings
public void LoadSceneNumber(numOfSceneToLoad){
UnityEngine.SceneManagement.SceneManager.LoadScene(numOfSceneToLoad);
}
}
and then you want to attach this script to your main UI Canvas/Holderplace, go to your button and at the bottom of the inspector you will see onClick(),
click on the + sign and then make the setup as follows, drag the Object you have attached this script to and put it under the "runtime" laber in that small onClick() window, then scroll and find this specific function we just created and in there you can modify the value of numOfSceneToLoad to the scene you want to load, and woilla you have a Dynamic script to load any scene you wish to.
on this note ill reffer you to some pretty amazing toturials made by unity that will teach you how to make a proper UI in unity5.
http://unity3d.com/learn/tutorials/topics/user-interface-ui/ui-button
From your description, it looks to me that your _newGameButton is set to inactive by default and you want it to active and user intractable after few delay.
For that you can create a script and keep reference of _newGameButton in it, something like this:
public GameObject _newGameButton;
and on an event say GameOver, implement like this:
void GameOver()
{
Invoke("EnableNewGameButtonAfterDelay", 5f);
}
void EnableNewGameButtonAfterDelay()
{
_newGameButton.SetActive(true);
// now this game object is active and user can click on it.
}
In this example i demonstrated that the game has been over and after 5 second delay user have new game button set to enable. [This code is not in executable form and just to give you an idea]
Hope this helps!

How would I implement a User Control swapping method?

I'm trying to swap "Scenes", the equivalent of levels in a game. I already know that Forms is not good for gaming, but what I'm making isn't so much a game as it is an interactive movie built out of some dialog options and animated .gifs.
I plan on building each "Scene" in a separate User Control, and then load up those User Controls onto the main form with a separate script that handles all of the scenes. In this separate script, I was going to add the "ChangeScene" method, so I could essentially trigger a scene to change once it finished, and have all scene changes in one script. I've run into two problems so far.
1: I can't seem to call anything from this Scene Handler script. I've been trying to use "Form1.SuspendLayout" and "Form1.ResumeLayout" to swap between Scenes smoothly, but I can't seem to call that, and then when I try to set a boolean in a Scene to true once the scene finishes, I can't actually check that bool from the Scene Handler script. How can I let this one script use the bools from each of the Scenes?
2: Can I call the "Form1.SuspendLayout" method from this Scene Handler script? So far, I was told I needed a reference to 'System.Windows.Forms.Control.SuspendLayout()', although I don't know how to reference that. It's not a namespace or assembly, so how do I reference it?
Sorry if this question seems like a simple mistake. I'm new to Windows Forms and not too well experienced with C#, so any question I could find about this used terms too complicated for me to understand.
Scene Handler here: class SceneHandler :
{
public virtual UserControl changeScene(UserControl currentScene, UserControl newScene)
{
Form1.SuspendLayout();
currentScene.Visible = false;
currentScene.Dock = DockStyle.None;
newScene.Visible = true;
newScene.Dock = DockStyle.Fill;
return newScene; //use "this.SuspendLayout" and "ResumeLayout"
}
}
Forget SuspendLayout.
Load all the UCs upfront but hidden.
Don't worry about performance before you run into problems!
Put all methods that involve more than one UC in the Form as public methods.
Put a public reference to your mainform in each UC and set it right after loading the UC.
Now each control can call mainform.someMethod().
Feel free to ask for more advice and help!

Categories