Detect only a specific Keyboard combination in Unity - c#

In my game I need to check for a specific keyboard combination, lets say Left Shift + B.
If I do it normally with
if(Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.B)) {
Debug.Log("correct");
}
It will accept everything as long as it has those two controls, so Left Shift + B, Left Shift + C + B, Left Shift + any keyboard button to be honest + B will return true as well.
My question is if I can detect somehow if ONLY those two were pressed, I already tried going foreach char c in KeyCode and detecting whether that Input was my B and then setting bool to correct or false but that doesn't really work.
Any ideas?

Event.current and OnGUI
Though usually it is not used so often anymore you can check the input in OnGUI using Event.current.
Every time a key goes down, store it in a list of currently pressed keys. When this keys goes up remove it from the list. Then you can simply check if the list contains the according keys and if the length matches the expected key amount.
public HashSet<KeyCode> currentlyPressedKeys = new HashSet<KeyCode>();
private void OnGUI()
{
if (!Event.current.isKey) return;
if (Event.current.keyCode != KeyCode.None)
{
if (Event.current.type == EventType.KeyDown)
{
currentlyPressedKeys.Add(Event.current.keyCode);
}
else if (Event.current.type == EventType.KeyUp)
{
currentlyPressedKeys.Remove(Event.current.keyCode);
}
}
// Shift is actually the only Key which is not treated as a
// EventType.KeyDown or EventType.KeyUp so it has to be checked separately
// You will not be able to check which of the shift keys is pressed!
if (!Event.current.shift)
{
return;
}
// As said shift is check on another way so we want only
// exactly 1 key which is KeyCode.B
if (currentlyPressedKeys.Count == 1 && currentlyPressedKeys.Contains(KeyCode.B))
Debug.Log("Only Shift + B");
}
It has to be done in OnGUI since there might be multiple events in one single frame. This is exclusive and will ony fire while Shift + B is pressed.
If you rather put this somewhere in your scene and make the values static
public class KeysManager : MonoBehaviour
{
public static bool ShiftPressed;
public static HashSet<KeyCode> currentlyPressedKeys = new HashSet<KeyCode>();
private void OnGUI()
{
if (!Event.current.isKey) return;
if (Event.current.keyCode != KeyCode.None)
{
if (Event.current.type == EventType.KeyDown)
{
currentlyPressedKeys.Add(Event.current.keyCode);
}
else if (Event.current.type == EventType.KeyUp)
{
currentlyPressedKeys.Remove(Event.current.keyCode);
}
}
ShiftPressed = Event.current.shift;
}
}
Then you can as before use something like
private void Update()
{
if (KeysManager.ShiftPressed && KeysManager.currentlyPressedKeys.Count == 1 && KeysManager.currentlyPressedKeys.Contains(KeyCode.B))
{
Debug.Log("Only Shift + B exclusively should trigger this");
}
}
Iterating with Input.GetKey through all KeyCode
Alternatively you could as commented check all possible keys.
However, this might or might not be an issue regarding performance. You'll have to test that and decide whether it is acceptable in your specific case.
using System.Linq;
...
// will store all buttons except B and LeftShift
KeyCode[] otherKeys;
private void Awake ()
{
// This simply returns an array with all values of KeyCode
var allKeys = (KeyCode[])Enum.GetValues(typeof(KeyCode));
// This uses Linq Where in order to only keep entries that are different from
// KeyCode.B and KeyCode.LeftShift
// ToArray finally converts the IEnumerable<KeyCode> into a KeyCode[]
otherKeys = allKeys.Where(k => k != KeyCode.B && k != KeyCode.LeftShift).ToArray();
}
private void Update()
{
if(Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.B) && !AnyOtherKeyPressed())
{
// Happens while ONLY LeftShift + B is pressed
}
}
// Return true if any other key
// is pressed except B and LeftShift
private bool AnyOtherKeyPressed()
{
foreach (var keyCode in otherKeys)
{
if(Input.GetKey(keyCode)) return true;
}
return false;
}
Maybe we worry too much and it doesn't matter (I woudln't believe that but just theoretically ^^) than you could even take it one level up and make it more flexible.
[Serializable]
public class KeyCombo
{
// Note I'll be lazy here .. you could create a custom editor
// for making sure each keyCode is unique .. but another time
public List<KeyCode> keyCodes = new List<KeyCode>();
// This will show an event in the Inspector so you can add callbacks to your keyCombos
// this is the same thing used in e.g. Button onClick
public UnityEvent whilePressed;
// Here all other keyCodes will be stored
[HideInInspector] public KeyCode[] otherKeys;
// Return true if any other key
// is pressed except B and LeftShift
public bool AnyOtherKeyPressed()
{
foreach (var keyCode in otherKeys)
{
if (Input.GetKey(keyCode)) return true;
}
return false;
}
}
public List<KeyCombo> keyCombos = new List<KeyCombo>();
private void Awake()
{
// This simply returns an array with all values of KeyCode
var allKeys = (KeyCode[])Enum.GetValues(typeof(KeyCode));
foreach (var keyCombo in keyCombos)
{
// This uses Linq Where in order to only keep entries that are different from
// the ones listed in keyCodes
// ToArray finally converts the IEnumerable<KeyCode> into a KeyCode[]
keyCombo.otherKeys = allKeys.Where(k => !keyCombo.keyCodes.Contains(k)).ToArray();
}
}
private void Update()
{
foreach (var keyCombo in keyCombos)
{
if (keyCombo.keyCodes.All(Input.GetKey) && !keyCombo.AnyOtherKeyPressed())
{
keyCombo.whilePressed.Invoke();
}
}
}
With this you can now add multiple KeyCombos and check them individually - However be aware that every additional keyCombo also means one additional iteration through all other keys so ... it's far away from perfect.

You can use Event.current.modifiers if you're doing combined keystrokes like Alt + Q etc. Something like this
if (!Event.current.isKey || Event.current.keyCode == KeyCode.None) return;
switch (Event.current.type) {
case EventType.KeyDown:
if (Event.current.modifiers == EventModifiers.Alt) {
switch (Event.current.keyCode) {
case KeyCode.Q:
break;
default:
break;
}
}
break;
default:
break;
}

Related

Why this function is not called properly?

I have a script where i take an integer from player prefs.
Below in code i am comparing value taken from player prefs and value taken from script name,casted to integer.In debug I see that values are changing f.e
first is 1 and second is 1,then after i button click,one value is increased by 1,and now
first is 1 and second is 2.But my code executes same as first time,but it should behave diferrently bcs if values are same it should do this,and if values are diferent it should do that.
Below is my code
Problem is when i use value from PlayerPrefs.
nazivKategorije = PlayerPrefs.GetString("kategorija");
if (nazivKategorije == "male")
{
brojOA = PlayerPrefs.GetInt("BrojOdgAsocijacije");
PlayerPrefs.SetInt("BROJOA", brojOA);
}else if (nazivKategorije == "srednje")
{
brojOA = PlayerPrefs.GetInt("BrojOdgAsocijacijeS");
PlayerPrefs.SetInt("BROJOA", brojOA);
}
else if (nazivKategorije == "velike")
{
brojOA = PlayerPrefs.GetInt("BrojOdgAsocijacijeV");
PlayerPrefs.SetInt("BROJOA", brojOA);
}
Debug.Log("NAZIV KATEGORIJE" + nazivKategorije);
//If I declare int above,and use its values there is not problem with this part of code.
In other script i increase this PlayerPrefs values.So to repeat:
when i had one value declared above,on top all worked fine.Now when I added this values from PlayerPrefs code wont run,actualy dont compare if:
brojBroja == broj or NOT.
First time it compare,and then it doesnt,It just throw first part of code no matter are values equal or not.
private void OnEnable()
{
int brojBroja= PlayerPrefs.GetInt("BROJOA");
if (brojBroja == broj)
{
GameEvents.OnBoardCompleted += ShowKonacnoRjesenje;
}
if (brojBroja != broj)
{
GameEvents.OnBoardCompleted += nePrikazujOpet;
}
}
private void OnDisable()
{
GameEvents.OnBoardCompleted -= ShowKonacnoRjesenje;
GameEvents.OnBoardCompleted -= nePrikazujOpet;
}

Detect number of keys being pushed Unity C#

I'm trying to make a grid-based movement system, and I have a bug where the player will move an odd amount when multiple inputs come in. Is there a way to detect how many keys on the keyboard are being pushed?
For example, something like:
If(numberOfKeysDown > 1)
or something?
I know you can do touch counts if working with mobile, but I'm not sure about keys. Thanks for your time!
If you just want to actually test what you said it should be enough to check for Input.anyKey
Is any key or mouse button currently held down
if(Input.anyKey)
If you want to check specific keys you could use Input.GetKey and Linq Count like e.g.
// Fancy queries ;)
using System.Linq;
...
// The keycodes you wan to check
private HashSet<KeyCode> keysToCheck = { KeyCode.W, KeyCode.A, KeyCode.S, KeyCode.D };
int numberOfKeysPressed;
private void Update ()
{
numberOfKeysPressed = keysToCheck.Count(key => Input.GetKey(key));
// This basically equals doing
// var numberOfKeysPressed = 0;
// foreach(var key in KeyToCheck)
//{
// if(Input.GetKey(key)) numberOfKeysPressed++;
//}
}
Or you could use Input.GetKeyDown and Input.GetKeyUp and do something like
private HashSet<KeyCode> keysToCheck = { KeyCode.W, KeyCode.A, KeyCode.S, KeyCode.D };
int numberOfKeysPressed;
private void Update ()
{
foreach (var key in KeyToCheck)
{
if(Input.GetKeyDown(key)) numberOfKeysPressed++;
if(Input.GetKeyUp(key)) numberOfKeysPressed--;
}
}
If you want all KeyCode values you could use
private void Awake ()
{
keysToCheck = new HashSet<KeyCode>((KeyCode[])Enum.GetValues(typeof(KeyCode)));
}
If you really want to get the amount of any key presses on the keyboard it would probably be more efficient to directly use Event.current and do something like
HashSet<string> currentlyPressedKeys = new HashSet<string>();
void OnGUI ()
{
Event e = Event.current;
switch(e.type)
{
case EventType.KeyDown:
var key = e.keyCode.ToString();
if(!currentlyPressedKeys.Contains(key)) currentlypressedKeys.Add(key);
break;
case EventType.KeyUp:
var key = e.keyCode.ToString();
if(currentlyPressedKeys.Contains(key)) currentlypressedKeys.Remove(key);
break;
}
}
Now wherever you need to know you can do e.g
if(currentlyPressedKeys.Count > XY)
I dont think theres is a specific method for this.
You could however write your own counter method using Input.GetKeyDown.
Like so
int GetKeysDownCount() {
var keysDown = 0;
if(Input.GetKeyDown(KeyCode.W)
keysDown++;
if(Input.GetKeyDown(KeyCode.A)
keysDown++;
if(Input.GetKeyDown(KeyCode.S)
keysDown++;
if(Input.GetKeyDown(KeyCode.D)
keysDown++;
return keysDown;
}
https://docs.unity3d.com/ScriptReference/Input.GetKeyDown.html

Animation should play only on pressing the button

I have an issue when playing animation in Unity. I made a transaction and set a bool on the animation. The issue here is that if I have an animation on a loop it won't stop. If I unloop it, it would play only once. What I actually want from this is to play only when the player presses the button. Right now my code looks like this. I tried to set the bool to false in different places, but it didn't have the right effect. I would appreciate any idea. Thanks
private void Inflations()
{
inflationTimer -= Time.deltaTime;
//if hands are in mount area inflate
if (OVRInput.GetDown(OVRInput.Button.Any))
{
if (inflationTimer <= 0 && rightHand.inflated)
{
if (inflated)
{
inflationTimer = inflationSpawn;
inflated = false;
inflationAnimator.SetBool("Pressed", true);
//we can add inflations only if they are under 2 and if the button is pressed and released
inflations++;
}
else
{
inflationAnimator.SetBool("Pressed", false);
}
tempo.SetInflations(inflations);
}
}
if (OVRInput.GetUp(OVRInput.Button.Any))
{
if (!inflated)
{
inflated = true;
}
}
if (inflations >= 2)
{
inflationAnimator.SetBool("Pressed", false);
state = stueState.TakeInflator;
inflationsDone = true;
}
}
#ecco is right, you need two states for it
Without seeing your Animator Controller, you could use something like this;
//...
Stack<Action> _animations = new Stack<Action>();
private void Inflations()
{
// ...
if (OVRInput.GetDown(OVRInput.Button.Any))
{
if(inflated){
// your code
_animations.Push(() => inflationAnimator.SetBool("Pressed", true));
// ...
}
}
}
private void Update(){
if(_animations.Count > 0){
var currentAnimation = _animations.Pop();
currentAnimation();
}
}
This eliminates the need for a lot of if checks and is nice to use.
Note: I used lambda expression there for simplicity. You can read more about it in here. And you can read more about Stack containers here
The update function is gonna check if there are any animations in Stack, if there are, it will automatically execute it once and remove it from the stack.

Unity - handle 2 input for keybinding in my game

In my game you can control unit, each unit have spell, and we would like to give to the user the possibility to change the keybinding of his different spell.
The goal is for exemple having the possibility to have a combinaison of input for a spell (exemple : "ctrl + H" send the spell)
I find on the unity Store a plugin named "Rewired" that seem to do that, but it's cost 40€and handle too many feature that I don't want.
So I try to create myself my own script to solve my issue, but I don't know how to create the combination of 2 keycode pressed.
This is my script bellow, do you have any idea on how can I create this ?
KeyCode key;
KeyCode curModifiersKey;(alt, ctrl)
KeyCode nonModifierKey;
KeyCode firstModifierKeyInfo;
KeyCode finalKey;
public void DetectedSeveralInput(KeyCode key)
{
if (key != KeyCode.AltGr)
{
if (key == KeyCode.LeftAlt || key == KeyCode.RightAlt || key == KeyCode.LeftControl || key == KeyCode.RightControl)
{
if (modifierPressedCount == 0)
{
firstModifierKeyInfo = key;
modifierPressedCount += 1;
}
curModifiersKey = key;
}
nonModifierKey = key;
//finalKey = curModifiersKey + nonModifierKey
LogVariables();
}
else
{
Debug.Log("AltGR pressed");
return;
}
}
What I do to detect multiple key press is like this
void Update()
{
bool shiftPressed = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
bool keyPressed = Input.GetKeyUp( /* other key code here */ );
if(shiftPressed && keyPressed)
{
//Do logic here
}
}
I check shift key being held with Input.GetKey, but makes sure that the logic only happens once by making the other check an Input.GetKeyUp so it will only be true on key release.
Thanks Tricko for the answer.
But the issue is that i can't do this sytem when you got like 10 units, who have 4 spells.
My reference on it is Starcraft 2 where you have a full panel for your own keybinding when you can add mouse button or ctrl/shift/alt also.
That why i want to so something that save the 2 input into one (if it's possible) and relate it to the speel n°X for my unit Y

Detecting first or second Mouse Button Release?

I want to know how to detect if the user has released the mouse button the first time, or the times after that:
Pseudo-Code:
if *first* (Input.GetMouseButtonUp(0))
{
do something
}
if *second, third, fourth..etc.* (Input.GetMouseButtonUp(0))
{
do something else
}
I really have no idea on how to accomplish this. I'm sure it's pretty simple though!
This is only an idea, but you could do this using a flag variable, like this:
private static bool WasFirstTimeReleased = false;
if (Input.GetMouseButtonUp(0))
{
if (!WasFirstTimeReleased)
{
WasFirstTimeRelease = true;
//do your stuff for first time
}
else
{
//do your stuff for all other times
}
}
Generally, you have to remember somewhere how many times button was released. Just create field in yours class:
private int clicks = 0;
then:
if (Input.GetMouseButtonUp(0))
{
if(clicks == 0)
{
// do something on first click
}
else
{
// do something on further click
}
clicks++;
}
If the object where you store clicks counter is created each time you press mouse button then mark counter using static word.
Keep track of mouse clicks:
int _leftUp;
void Update()
{
var leftUp = Input.GetMouseButtonUp(0);
if (leftUp) _leftUp++;
// etc ...
}
Easiest way to accomplish this would be to use a counter to check the number of times the user has released the button.
private int releaseCounter = 0;
And then in an if statement:
if (Input.GetMouseButtonUp(0)) {
releaseCounter++;
//If you know on which release you want the code to be executed,
//replace the x with that number.
if (releaseCounter == x) {
//your code here
}
//If you want the code to be executed at set intervals.
//replace the x with the interval number.
if(releaseCounter%x == 0) {
//your code here
}
}
Hope I helped.

Categories

Resources