Detect number of keys being pushed Unity C# - 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

Related

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.

Detect only a specific Keyboard combination in Unity

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;
}

Create dictionary with dynamic keys in C#

When programming with C/C++ I sometimes used to have a dictionary with references to functions according to the specified keys. However, I don't really know how to have something similar in C# allowing me dynamic keys.
I'm working on Unity and I'd like for exemple to invoke a function when I press left mouse click on my Unity project, is it possible ?
I'd really appreciate every comments on this.
I think you can use this:
First define a Delegate for your task (Jobs assigned to keys press in your example):
public delegate void MyJob();
Then define your dictionary:
Dictionary<string, MyJob> myBinding = new Dictionary<string, MyJob>()
{
{ "left", delegate() { Console.WriteLine("Left Key pressed"); } },
{ "right", delegate() { Console.WriteLine("Right Key pressed"); } },
{ "up", delegate() { Console.WriteLine("Up Key pressed"); } },
{ "down", delegate() { Console.WriteLine("Down Key pressed"); } }
};
And finally use it:
public void Mapper(string pressedKey)
{
myBinding[pressedKey]();
}
I hope this help you to solve your problem. If you need to more detail let me know.
You can use Dictionary<KeyCode, System.Action> signature to accomplish that. Get all the code codes and store them into an array with System.Enum.GetValues(typeof(KeyCode));. You can then pair your keys with your functions with keyCodeToFuncDic.Add(KeyCode.YourKeyCode, YourFunction);
In the Update function, use for loop to loop over the KeyCodes stored in the beginning. Check if any of those keys is pressed. If pressed, check if it is in the Dictionary. If the that KeyCode exist in the dictionary, use the pressed key to Invoke the function in the Dictionary value, which will eventually call the function that is stored in the Dictionary.
Code:
Dictionary<KeyCode, System.Action> keyCodeToFuncDic = new Dictionary<KeyCode, System.Action>();
Array allKeyCodes;
void Start()
{
//Get all codecodes
allKeyCodes = System.Enum.GetValues(typeof(KeyCode));
//Register Keycodes to functios
keyCodeToFuncDic.Add(KeyCode.Mouse0, myFunction); //Left mouse
keyCodeToFuncDic.Add(KeyCode.Mouse1, myFunction2); //Right mouse
}
void myFunction()
{
Debug.Log("Left Mouse Clicked");
}
void myFunction2()
{
Debug.Log("Right Mouse Clicked");
}
void Update()
{
foreach (KeyCode tempKey in allKeyCodes)
{
//Check if any key is pressed
if (Input.GetKeyDown(tempKey))
{
//Check if the key pressed exist in the dictionary key
if (keyCodeToFuncDic.ContainsKey(tempKey))
{
//Debug.Log("Pressed" + tempKey);
//Call the function stored in the Dictionary's value
keyCodeToFuncDic[tempKey].Invoke();
}
}
}
}
When I see "dynamic keys" I think for something else like having a dynamic type be the key for the dictionary. However what you need can be easily achieved. In C# "pointers to functions" are called delegates (but way better than C pointers to functions) thus you need a dictionary that has the char type as the key and one of those delegates as the value. In .net framework 4.0 and above there are predefined generic delegates called Func<T...> and Action<T...>. Basically if you want your function to return a value you will use Func and the Action will be used in the place of a void method.
So you can have something like this:
var keyMap = new Dictionary<char, Func<string, bool>>()
{
{'w', MoveUp},
{'s', MoveDown},
{'a', s => true}, // using lambda expression
{
'd', delegate(string s)
{
if (string.IsNullOrWhiteSpace(s) == false)
{
//
}
return true;
}
} // using anonymous method
};
// then you can call those like this
var allow = keyMap['w']("some input");
if (allow)
{
// ....
}
public bool MoveUp(string input)
{
return true;
}
public bool MoveDown(string input)
{
return true;
}
Here are the docs https://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx

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.

C# reading joystick input without blocking UI

This will probably sound strange for some of you, but I can not figure out right way to read joystick input without blocking my UI form. I have found this example on internet:
static void Main()
{
// Initialize DirectInput
var directInput = new DirectInput();
// Find a Joystick Guid
var joystickGuid = Guid.Empty;
foreach (var deviceInstance in directInput.GetDevices(DeviceType.Gamepad,
DeviceEnumerationFlags.AllDevices))
joystickGuid = deviceInstance.InstanceGuid;
// If Gamepad not found, look for a Joystick
if (joystickGuid == Guid.Empty)
foreach (var deviceInstance in directInput.GetDevices(DeviceType.Joystick,
DeviceEnumerationFlags.AllDevices))
joystickGuid = deviceInstance.InstanceGuid;
// If Joystick not found, throws an error
if (joystickGuid == Guid.Empty)
{
Console.WriteLine("No joystick/Gamepad found.");
Console.ReadKey();
Environment.Exit(1);
}
// Instantiate the joystick
var joystick = new Joystick(directInput, joystickGuid);
Console.WriteLine("Found Joystick/Gamepad with GUID: {0}", joystickGuid);
//Query all suported ForceFeedback effects
var allEffects = joystick.GetEffects();
foreach (var effectInfo in allEffects)
Console.WriteLine("Effect available {0}", effectInfo.Name);
//Set BufferSize in order to use buffered data.
joystick.Properties.BufferSize = 128;
// Acquire the joystick
joystick.Acquire();
// Poll events from joystick
while (true)
{
joystick.Poll();
var datas = joystick.GetBufferedData();
foreach (var state in datas)
Console.WriteLine(state);
}
}
I have installed sharpdx and I can see joystick axis output in console.
Now I want to display each axis data in separate textbox on my UI. So I changed few lines of code:
joystick.Poll();
var data = joystick.GetBufferedData();
foreach (var state in data)
{
if (state.Offset == JoystickOffset.X)
{
textBox1.Text = state.Value.ToString();
}
}
So this should pass data to textbox. If I write here Console.Writeline(state.Value) I will get data I want to display. Problem is, that this while loop blocks UI.
I would like to put this while loop in private void timer_tick and set maybe 10 milliseconds refresh rate. But I don't know how to access variables like var joystick in separate function. I have also read that this can be done with asynchronous programming. Sadly I am stuck now.
Can anybody help me with as simple solution as possible to this problem? Code example would be great.
Suppose you code with WinForm (deduced from blocking my UI form in your question).
Add a timer to your Form from the designer.
And add these lines in the Form's .ctor:
public partial class JoyStickForm : Form
{
public JoyStickForm()
{
InitializeComponent();
// ...Other initialization code has been stripped...
// Instantiate the joystick
var joystick = new Joystick(directInput, joystickGuid);
//Console.WriteLine("Found Joystick/Gamepad with GUID: {0}", joystickGuid);
timer1.Interval = 100;
timer1.Tick += (s, e) =>
{
joystick.Poll();
var lastState = joystick.GetBufferedData().Last(); //only show the last state
if (lastState != null)
{
if (lastState.Offset == JoystickOffset.X)
{
textBox1.Text = lastState.Value.ToString();
}
}
};
//start the timer
timer1.Enabled = true;
}
}
Explanations:
A refreshing rate of 100 times per second is unnecessarily fast, remember the refreshing rate of most monitors is about 60 Hz. I set it as 100 ms (10 refreshes per second), which is acceptable.
Using lambda expression, you have access to the variables like var joystick from the timer's tick handler.
In each freshing, only the last state in the data is displayed to the TextBox.

Categories

Resources