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
Related
I've got into game development, and I decided to choose Godot as the game engine because C# was supported, and I knew the language.
I've been working on a simple game, and at some point, I ran into a problem.
I wanted to make a sprite invisible when a key was pressed.
I wanted It so if the key was pressed, it did an action permanently.
I tried this, but Input.IsPhysicalKeyPressed((int)) stopped the action as soon as I released the key
public override void _PhysicsProcess(float delta)
{
Visible = true;
if (Input.IsPhysicalKeyPressed((int)KeyList.W) || Input.IsPhysicalKeyPressed((int)KeyList.A) || Input.IsPhysicalKeyPressed((int)KeyList.S) || Input.IsPhysicalKeyPressed((int)KeyList.D))
{
Visible = false;
}
else if (Input.IsPhysicalKeyPressed((int)KeyList.Up) || Input.IsPhysicalKeyPressed((int)KeyList.Left) || Input.IsPhysicalKeyPressed((int)KeyList.Down) || Input.IsPhysicalKeyPressed((int)KeyList.Right))
{
Visible = false;
}
}
How do I fix this problem?
From what I can see, you always set Visible back to true.
If you want to have a toggle, you will need to set Visible to the opposite of Visible:
public override void _PhysicsProcess(float delta)
{
if (Input.IsPhysicalKeyPressed((int)KeyList.W) || Input.IsPhysicalKeyPressed((int)KeyList.A) || Input.IsPhysicalKeyPressed((int)KeyList.S) || Input.IsPhysicalKeyPressed((int)KeyList.D))
{
Visible = !Visible;
}
else if (Input.IsPhysicalKeyPressed((int)KeyList.Up) || Input.IsPhysicalKeyPressed((int)KeyList.Left) || Input.IsPhysicalKeyPressed((int)KeyList.Down) || Input.IsPhysicalKeyPressed((int)KeyList.Right))
{
Visible = !Visible;
}
}
This will not work tho, because it would just toggle the visiblity as long as the key is pressed.
To fix that, you will need to add a way to check if the key has been lifted again, and only then make it able to change the visibility.
I have never worked with the Godot Game Engine, so I am not sure how that works.
I am sure that if you don't know it, a simple google search will do it.
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;
}
I have a pretty simple console app thats quietly waits for a users to press a key, then performs an action based on the what key was pressed. I had some issues with it, but some helpful users over on this post pointed out where I was going wrong.
The code I currently have to handle a single key press is as follows
ConsoleKey key;
do
{
while (!Console.KeyAvailable)
{
// Do something, but don't read key here
}
// Key is available - read it
key = Console.ReadKey(true).Key;
if (key == ConsoleKey.NumPad1)
{
Console.WriteLine(ConsoleKey.NumPad1.ToString());
}
else if (key == ConsoleKey.NumPad2)
{
Console.WriteLine(ConsoleKey.NumPad2.ToString());
}
} while (key != ConsoleKey.Escape);
I'm wondering how I can detect when a combination of 2 or more keys is pressed. I'm not talking about the standard Ctrl + c, but rather something like Ctrl + NumPad1. If a user presses Ctrl + NumPad1, perform action X.
I'm really unsure how to go about doing this, as the current while loop will only loop until a single key is pressed, so wont detect the second key (assuming that its litterally impossible to press two keys at the exact same time.
Is anyone able to provide a steer in the right direction to help achieve this?
I guess you need to check the key modifier. Check the pseudo code below:
ConsoleKeyInfo keyInfo = Console.ReadKey();
Console.WriteLine(keyInfo.Key);
Console.WriteLine(keyInfo.Modifier);
...
if((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) Console.WriteLine("CTL+");
If you capture the ConsoleKeyInfo, you will get additional information, including the Modifiers keys. You can query this to see if the Control key was pressed:
ConsoleKey key;
do
{
while (!Console.KeyAvailable)
{
// Do something, but don't read key here
}
// Key is available - read it
var keyInfo = Console.ReadKey(true);
key = keyInfo.Key;
if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0)
{
Console.Write("Ctrl + ");
}
if (key == ConsoleKey.NumPad1)
{
Console.WriteLine(ConsoleKey.NumPad1.ToString());
}
else if (key == ConsoleKey.NumPad2)
{
Console.WriteLine(ConsoleKey.NumPad2.ToString());
}
} while (key != ConsoleKey.Escape);
I am developing a simple Universal Windows App using C#. I have a RichEditBox and I found a weird behavior when using Control+I key combination, which for some reason inserts a Tab (is that expected?). Because I wanted the combination of keys to toggle Italic font style, I thought the best way was through the KeyDown event.
So, this is my code:
private void richbox_KeyDown(object sender, KeyRoutedEventArgs e)
{
System.Diagnostics.Debug.Write("\nKeyDown : " + e.Key.ToString());
if (e.Key == VirtualKey.Tab)
{
richbox.Document.Selection.TypeText("\t");
e.Handled = true;
}
else if (Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) == Windows.UI.Core.CoreVirtualKeyStates.Down)
{
//If Control is pressed down, check if current key is B,I,U...
System.Diagnostics.Debug.Write(" => Control is down!");
switch (e.OriginalKey)
{
case VirtualKey.B:
toogleBold();
e.Handled = true;
break;
case VirtualKey.I:
e.Handled = true;
toogleItalic();
break;
case VirtualKey.U:
toogleUnderline();
e.Handled = true;
break;
}
}
}
My problem is, the condition on the Else If is not always true when I press the Control key. I would like to understand why and what could I do to fix it.
If I run the code and I press the control key a few times, this is the output:
KeyDown : Control => Control is down!
KeyDown : Control
KeyDown : Control => Control is down!
KeyDown : Control
...
Thanks in advance :)
I tried your code and used debugger output to see what the actual state of Ctrl is in those situations:
var state = Window.Current.CoreWindow.GetKeyState(VirtualKey.Control);
Debug.WriteLine(state);
What I found out is that the second time you press the key, its state is not Down, but Down|Locked, more specifically Windows.UI.Core.CoreVirtualKeyStates.Down | Windows.UI.Core.CoreVirtualKeyStates.Locked. It turns out CoreVirtualKeyStates is a flag enum and it can have multiple values at the same time. In this case you are comparing with == which means you don't get a match. You can first use the HasFlag method or bitwise AND (&) to get the right value out and then compare and you will be good to go!
That means either this:
else if ( Window.Current.CoreWindow.GetKeyState(VirtualKey.Control).
HasFlag( CoreVirtualKeyStates.Down ) )
{
//rest of the code
}
Or this:
else if (
( Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) &
Windows.UI.Core.CoreVirtualKeyStates.Down )
== CoreVirtualKeyStates.Down )
{
//rest of the code
}
Creating a key hook so that when the combination is pressed, the application will open again.
I have looked into various ways of doing it, however I do not know what the input combination will be unlike this example:
if (e.KeyCode == Keys.C && e.Modifiers == Keys.Control && e.Modifiers == Keys.Shift)
{
//Do work
}
else if (e.KeyCode == Keys.V && e.Modifiers == Keys.Control)
{
//Paste
}
Our input combination is from the user, where they select which combination they want to input from a combo box.
public void KeyboardHooks_OnKeyPress(object sender, KeyPressArgs e)
{
//The first input
if (LastKey != Keys.None)
{
Keys combinationOne = (Keys)cmbCombinationOne.SelectedValue;
Keys combinationTwo = (Keys)cmbCombinationTwo.SelectedValue;
}
LastKey = e.Key;
MessageBox.Show("KeyPressed");
}
Not sure on how to go about setting our values to the combo box's
From your code snippets it looks like you're going the route of WinForms key events. If the user does a key combination in your application and you do this 'open' of something else, you're on the right path. You'll just need to make it dynamic to see if the user-defined items are pressed.
So when you save the user settings, convert it to a keycode so you can do the generic
if(e.KeyCode == Settings.FirstModKey && e.KeyCode == Settings.SecondModKey && e.KeyCode == Settings.FirstKey)
You'll need to consider the multiple scenarios, the modifiers Shift, Alt and Control could be none, one, two, or all three. In my above, you could have FirstModKey and SecondModKey the same value if the user chose Ctrl only, or they could be handling if they did Ctrl and Shift both. Then FirstKey is the non-mod key, like 'A'.
the application will open again
However, from the quote it sounds like you want a global hook, that wherever the user is in any application, with yours not running, you want to listen and do work if its your keycode. You need to look into a service and low level hooks. This can come close to keylogging and you need to be careful who your audience is, security risks and concerns that you might be breaking compliance.
{
//The first input
if (LastKey != Keys.None)
{
int combination1 = (int)Enum.Parse(typeof(Keys), cmbCombinationOne.SelectedValue.ToString());
int combination2 = (int)Enum.Parse(typeof(Keys), cmbCombinationTwo.SelectedValue.ToString());
int LastKeyPress = (int)Enum.Parse(typeof(Keys), LastKey.ToString());
ThisKey = e.Key;
if (combination1 == LastKeyPress && combination2 == Convert.ToInt32(ThisKey))
{
MessageBox.Show("Key pressed");
}
}
LastKey = e.Key;
}
This worked with my original existing code