I am currently working on a platform video game with Unity and am not sure how to detect when a key is lifted using the new input system.
I would like to know, if possible, can you detect when the key is lifted and assign the value to a bool, using my current settings.
So far, I have this code:
public void Jump(InputAction.CallbackContext context)
{
if (context.performed)
{
jump = true;
wasJumpLifted = true;
}
if (context.canceled)
{
wasJumpLifted = false;
}
else
{
wasJumpLifted = true;
}
}
The problem with this code is that it will only set wasJumpLifted to true if you press down the jump key again, as the entire function will not be invoked without me pressing the jump key.
Here's the solution
public bool isPressed;
public void Jump(InputAction.CallbackContext context)
{
// To keep the state in a boolean.
isPressed = (!context.started || context.performed) ^ context.canceled;
// When the key is pressed down.
if (context.started && !context.performed)
Debug.Log("Pressed Down");
// When the key is lifted up.
if (context.canceled && !context.performed)
Debug.Log("Lifted Up");
}
In my projects I use the one-liner:
public void Jump(InputAction.CallbackContext context) => isPressed = (!context.started || context.performed) ^ context.canceled;
This can be hard to read, but when do you need to read the code to handle these actions?
Related
I have created a pause screen Area2D for my game, in any case the player goes AFK. I wrote a code so whenever the key P is pressed it appears and everything pauses, and if it is clicked it disappears and everything goes back to normal. I wrote a code, but it didn't work
I tried this:
public override void _PhysicsProcess(float delta)
{
// Makes the pause screen visible when P is pressed
if (Input.IsActionPressed("Pause"))
{
Visible = true;
}
}
public override void _Ready()
{
}
public void _on_PauseScreen_mouse_entered()
{
if (Input.IsActionPressed("click"))
{
Visible = false;
}
}
But it only works when clicked on the edges, I know that's how collisions are but how do I make it so when anywhere the sprite is pressed, it disappears?
The problem in your code should be, that you are just checking if the action is pressed, when the mouse entered the area.
If you move the mouse to the middle of the area and click then, your event was already fired.
Create a variable to track, if the mouse is currently in the area by using entered and exit signals and use that to determine if something should happen when you click:
bool bMouseEntered = false
public override void _PhysicsProcess(float delta)
{
// Makes the pause screen visible when P is pressed
if (Input.IsActionPressed("Pause"))
{
Visible = true;
}
if (Input.IsActionPressed("click") && bMouseEntered)
{
Visible = false;
}
}
public override void _Ready()
{
}
public void _on_PauseScreen_mouse_entered()
{
bMouseEntered = true;
}
public void _on_PauseScreen_mouse_exited()
{
bMouseEntered = false;
}
I want it so if it is pressed anywhere on the pause screen, it dissapears.
That is probably the simplest case. And the easier way to do it is to put the check in _physics_process, where the other check is:
public override void _PhysicsProcess(float delta)
{
// Makes the pause screen visible when P is pressed
if (Input.IsActionPressed("Pause"))
{
Visible = true;
}
if (Input.IsActionPressed("click"))
{
Visible = false;
}
}
So it has nothing to do with collision or the mouse. It is just checking for the action regardless of where or how it happens.
We can argue that using _input or _unhandled_input is better, in particular if timing is critical, but I don't expect it to be an issue in this case.
Addendum:
I want it so when it is pressed anywhere ON THE PAUSE SCREEN. it disappears. it doesn't cover the full screen.
At the time of writing Bugfish already has a working solution. I'll give you an alternative.
Since you are using an Area2D it must have the property input_pickable, when it is set to true (which is the default) it allows to "pick" the Area2D with the mouse. And you would get this interaction in the input_event signal or overwriting _input_event. Which would look like this:
void _InputEvent(Viewport viewport, InputEvent ev, int shapeIdx)
{
var btn = ev as InputEventMouseButton;
if(btn != null && ev.ButtonIndex == ButtonList.Left && ev.Pressed)
{
Visible = false;
}
}
I'm making a flashlight mechanic that turns on the light when 'F' is pressed and turned off when 'F' is pressed again.
However, when I press the button it registers it twice. I've logged the output of this and the flashlight is turned on and off at the same time.
I know that I must be doing something wrong. What is the best way to solve this issue?
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Flashlight : MonoBehaviour
{
public GameObject flashlight;
public bool lightIsOn;
void Start()
{
lightIsOn = false;
}
void Update()
{
if (lightIsOn == false)
{
if (Input.GetKey(KeyCode.F))
{
flashlight.SetActive(true);
lightIsOn = true;
Debug.Log("flashlight is on");
}
}
if (lightIsOn == true)
{
if (Input.GetKey(KeyCode.F))
{
flashlight.SetActive(false);
lightIsOn = false;
Debug.Log("Flashlight is off");
}
}
}
}
private bool lightIsOn = false;
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
lightIsOn = !lightIsOn;
flashlight.SetActive(lightIsOn );
Debug.Log("flashlight is " + lightIsOn);
}
}
}
Simply revert the lightIsOn bool on press. Also, use GetKeyDown so that it calls only once when you press
fafase's answer is the recommended way to go, but to explain why your original code was appearing to run twice is because you had two if statements in there instead of an if-else statement.
When the function starts, lightIsOn is equal to false, it then hits your first if statement checking if it is false so it runs the code inside setting lightIsOn to true. However, it then hit your next if statement below it anyway checking if lightIsOn is true and so immediately changing it back to false. Your code would of worked with this slight change:
void Update()
{
if (lightIsOn == false)
{
if (Input.GetKey(KeyCode.F))
{
flashlight.SetActive(true);
lightIsOn = true;
Debug.Log("flashlight is on");
}
}
else if (lightIsOn == true)
{
if (Input.GetKey(KeyCode.F))
{
flashlight.SetActive(false);
lightIsOn = false;
Debug.Log("Flashlight is off");
}
}
}
As it would of only ran one of the conditions. The difference here is the code is told 'Do this OR this' instead of 'do this AND this'. Would still recommend going with fafase's answer though for reasons already given.
I have an object with a script that moves it along a path. On the same object is a script that, if activated, sends it back to its start point. The mover script is default active and the home is by default inactive. I want it so that if I press "2", mover deactivates and home activates, and if I press "1", the scripts go back to how they were. states 1 & 2 should be exclusive. I have created a script, on the same object, as follows:
public class activator : MonoBehaviour
{
bool mover = new bool();
bool home = new bool();
void Start ()
{
mover = true;
home = false;
}
void Update ()
{
if (Input.GetButtonDown("1"))
{
mover = true;
home = false;
}
if (Input.GetButtonDown("2"))
{
mover = false;
home = true;
}
if(mover == true)
{
GetComponent<router>().enabled = true;
GetComponent<routeRepairs>().enabled = false;
}
else if(home == true)
{
GetComponent<router>().enabled = false;
GetComponent<routeRepairs>().enabled = true;
}
}
}
note the actual scripts I'm trying to activate and deactivate from the object are router and routeRepairs. When I actually run the game in playmode nothing happens after the button presses.
There is a question that stackoverflow is telling me is similar but the solutions given aren't doing anything for me. So, any help with how to get this working would be greatly appreciated! Thanks in advance!
one more thing: I hope to add more movement scripts that should be activated and deactivated in the same way, so solutions that only apply to two states (ie moving or home) aren't ideal. probably not important but it just occurred to me that it was worth qualifying..?
Are the inputs actually recognized? Put a print inside the input if to see if it actually gets the input. If not, maybe your input is not configured correctly. Or you can try using KeyCode instead like:
if (Input.GetKeyDown(KeyCode.Alpha1))
Also, you don't need the extra booleans or the extra if statements. Just get the scripts in start and then directly enable/disable them
router router;
routerRepairs routerRepairs;
void Start ()
{
router = GetComponent<router>();
routerRepairs = GetComponent<routerRepairs>();
}
void Update ()
{
if (Input.GetKeyDown(KeyCde.Alpha1))
{
router.enabled = true;
routerRepairs.enabled = false;
}
if (Input.GetKeyDown(KeyCde.Alpha2))
{
router.enabled = false;
routerRepairs.enabled = true;
}
}
As you most probably know, Unity3D have terrible buil-in input system what is unable to change configuration runtime so i decised to write own input system based on SharpDX DirectInput. I know well directInput is not officialy recomendet but i like it for its ability to work with device of all kinds (Like Trust dual stick gamepad GTX 28 of mine. Originaly purchased for PSX emulation).
I using class below to representate button object
public class InputButton
{
public JoystickOffset button;
public Key key;
public int pressValue;
public int relaseValue;
public bool isJoystick;
public InputButton(JoystickOffset button, int pressValue, int relaseValue)
{
this.button = button;
this.pressValue = pressValue;
this.relaseValue = relaseValue;
isJoystick = true;
}
public InputButton(Key key, int pressValue, int relaseValue)
{
this.key = key;
this.pressValue = pressValue;
this.relaseValue = relaseValue;
isJoystick = false;
}
}
Then i replace Unity's (pretty terrible method by the way) Input.GetKeyDown with my own (If you name class same as one of unity's classes you replace it. I know someone must not like use of static here but i see it very benefical)
public static bool GetKeyDown(InputButton button)
{
bool pressed = false;
keyboard.Poll();
keyboardData = keyboard.GetBufferedData();
if (button.isJoystick == false)
{
foreach (var state in keyboardData)
{
if (state.Key == button.key && state.Value == button.pressValue)
{
pressed = true;
}
}
}
return pressed;
}
But before everything i call Input.Initialize() from another class (during Awake()). it looks like this :
public static void Initialize()
{
directInput = new DirectInput();
var joystickGuid = Guid.Empty;
foreach (var deviceInstance in directInput.GetDevices(SharpDX.DirectInput.DeviceType.Joystick, DeviceEnumerationFlags.AttachedOnly))
{
joystickGuid = deviceInstance.InstanceGuid;
}
if (joystickGuid == Guid.Empty)
{
foreach (var deviceInstance in directInput.GetDevices(SharpDX.DirectInput.DeviceType.Gamepad, DeviceEnumerationFlags.AttachedOnly))
{
joystickGuid = deviceInstance.InstanceGuid;
}
}
if (joystickGuid != Guid.Empty)
{
joystick = new Joystick(directInput, joystickGuid);
joystick.Properties.BufferSize = 128;
joystick.Acquire();
}
keyboard = new Keyboard(directInput);
keyboard.Properties.BufferSize = 128;
keyboard.Acquire();
}
Now to the problem. When i click to anything outside game window in the editor, keys does not respond anymore. I checked everything and both directInput and keyboard are still in variables. Most likely some problem with "Focus" of window because this problem looks like directInput instance or keyboard gets disconnected as soon as game window lost focus (focus is lost when window is not active window, active window is not "active" but so called "focused").
Do someone know why this happenig and how to repair it ?
EDIT : Looks like this problem is somehow connected to window(s). I have settings and i'm able to switch fullscreen runtime. As long as i'm in fullscreen it works fine but when i switch to window it stop working.
Thanks.
-Garrom
Now i see myself as very stupid person... Anyway i was right abou window focus. When game window lost focus it (somehow) break directInput. I solved this useing unity's callback OnApplicationFocus and re-Initialize (call Initialize(). See original question) every time game window gets focused.
So for a game i'm working i have a bunch of different keys doing different things (for example w key makes the player look up and the e key makes the character look up right).
I'm wondering is there a way to make the W + the D key make the player look up right, even though those keys are already being used.
private void FrmGM_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player1));
bulletnumb = 1;
}
if (e.KeyCode == Keys.E)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player2));
bulletnumb = 2;
}
if (e.KeyCode == Keys.D)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player3));
bulletnumb = 3;
}
if (e.KeyCode == Keys.C)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player4));
bulletnumb = 4;
}
if (e.KeyCode == Keys.X)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player5));
bulletnumb = 5;
}
if (e.KeyCode == Keys.Z)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player6));
bulletnumb = 6;
}
if (e.KeyCode == Keys.A)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player7));
bulletnumb = 7;
}
if (e.KeyCode == Keys.Q)
{
picplayer.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.IPT_game_player8));
bulletnumb = 8;
}
this is the code for my keys presses
Store letters in a set when the KeyDown events arrive, and remove them in the KeyUp event handler. This way your KeyDown would be able to "see" if the "companion key" has been pressed:
private readonly ISet<char> keysCurrentlyDown = new HashSet<char>();
private void FrmGM_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.W)
{
if (keysCurrentlyDown.Contains('D')) {
// Call some method to handle W+D
} else {
...
}
keysCurrentlyDown.Add('W');
} else if (e.KeyCode == Keys.D)
{
if (keysCurrentlyDown.Contains('W')) {
// Call some method to handle W+D
} else {
...
}
keysCurrentlyDown.Add('D');
}
...
}
private void FrmGM_KeyUp(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.W) {
keysCurrentlyDown.Remove('W');
} else if (e.KeyCode == Keys.D) {
keysCurrentlyDown.Remove('D');
} ...
}
}
The first line of offense for this sort of thing is just to make sure that each key can be activated concurrently, and that the combination of the effects of each key have the result you want. Your code will be more maintainable and easier to write if you follow that approach, rather than trying to special-case the combine key input.
For example, you can have a set of flags that represent the current key state, and then on your game's frame update, check those flags and set the character state to the correct thing (i.e. instead of setting specific images in the key-handling itself, as you're doing now).
So you could have flags like:
bool lookUp, lookRight, lookDown, lookLeft;
Then key-down for W would set the lookUp flag, while key-up for the same key would clear the flag. Likewise e.g. for D, for the lookRight flag.
Then on your game frame update, you check the flags. If lookUp and lookRight are both set, then you use the "look up and right" graphic. If just one or the other flag are set, then you use the plain "look up" or "look right" graphic. Likewise for other keys and their combinations. You could even resolve lookUp and lookDown, or lookLeft and lookRight, as whatever the default graphic would be (i.e. as if the user pressed no keys).
Note that if you're willing to use p/invoke, you can use the GetAsyncKeyState() function, and not even bother with the key-down event. Just check the key state before each frame update for the game, and update the visuals according to the current keyboard state. That way, you let Windows do all the key state tracking, and you don't waste time tracking key state that doesn't matter (e.g. if the user causes multiple changes to the key state between frames).
That said, if you can't/won't do any of the above, then yes...all you need to do is check for the combination first (i.e. you got the key-down for both, without a key-up for the initially-pressed key of the combination).
Do note that taking that approach means that you will still take the effect for the first key pressed, and then have to switch to the combination effect.
use the
PresentationCore.dll and the
WindowsBase.dll as reference in your project
PresentationCore.dll has a Keyboard - Class and WindowsBase.dll has a Key - Class.
then:
using System;
using System.Windows.Forms;
using System.Windows.Input;
namespace ViewTest
{
public partial class ViewTest : Form
{
private void ViewTest_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.IsKeyDown(System.Windows.Input.Key.W) &&
Keyboard.IsKeyDown(System.Windows.Input.Key.D))
{
// do something
}
}
}
it's a simple way to do this :)
Look into overriding the ProcessCmdKey
protected override bool ProcessCmdKey(ref Message m, Keys k)
{
if (k == (Keys.Control | Keys.D))
{
MessageBox.Show("CTRL+D");
return true;
}
return base.ProcessCmdKey(ref m, k);
}