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?
I want to play a sound all the time while a picturebox is moving using keydown event. My code is this:
private bool soundPlaying = false;
private SoundPlayer player = new SoundPlayer(#"Sounds/Step.wav");
private void PlaySound(bool start)
{
if (start)
{
if (!soundPlaying)
{
soundPlaying = true;
player.PlayLooping();
}
}
else
{
soundPlaying = false;
player.Stop();
}
}
private void Joc_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
{
PlaySound(true);
}
}
private void Joc_KeyDown(object sender, KeyEventArgs e)
{
Joc joc = new Joc();
if (e.KeyCode == Keys.W)
{
joc.keyPressW(velocity, gameboard, ColonistList, StoneListMain, treeList, treeFHD, treeHD, stoneFHD, stoneHD, stoneLooted, killedMob, Ship, Wolf, Distance, inventoryButton, openInventory, LifeBarStroke, InnerLifeBar, WolfHealthStroke);
PlaySound(false);
}
else if (e.KeyCode == Keys.S) joc.keyPressS(velocity, gameboard, ColonistList, StoneListMain, treeList, smallStoneList, treeFHD, treeHD, stoneFHD, stoneHD, stoneLooted, killedMob, Ship, Wolf, Distance, inventoryButton, openInventory, LifeBarStroke, InnerLifeBar, WolfHealthStroke);
else if (e.KeyCode == Keys.D) joc.keyPressD(velocity, gameboard, ColonistList, StoneListMain, treeList, smallStoneList, treeFHD, treeHD, stoneFHD, stoneHD, stoneLooted, killedMob, Ship, Wolf, Distance, inventoryButton, openInventory, LifeBarStroke, InnerLifeBar, WolfHealthStroke);
else if (e.KeyCode == Keys.A) joc.keyPressA(velocity, gameboard, ColonistList, StoneListMain, treeList, smallStoneList, treeFHD, treeHD, stoneFHD, stoneHD, stoneLooted, killedMob, Ship, Wolf, Distance, inventoryButton, openInventory, LifeBarStroke, InnerLifeBar, WolfHealthStroke);
}
In the class "Joc" are the walking functions. In this moment, this code is playing one time the sound when I walk the first pixel and when I stopped pressing the key it plays again the sound. How can I solve this problem ?
First of all, let's understand how we are playing a sound.
It's quite simple, right? As you wrote above, you need to call player.Play();.
But does that mean that sound will play continuously? The answer is no, it will not.
How to play a sound all the time while a picturebox is moving C#
So how to make the sound repeatedly play until we want to stop it?
To make that happen, you need to use SoundPlayer.PlayLooping() method, but before that, my suggestion here is instead of instantiating sound player object in the timer1_Tick create a class field.
Now, let's keep it very simple, you want to play sound as long as the KeyDown is calling or let's say another way until you don't release the button, right?
What you need here is to do two things:
Call player.PlayLooping on KeyDown or KeyPress.
Call SoundPlayer.Stop() method like with the player.Stop() call on KeyUp Event.
On most systems, when you hold down a key, you'll start to get repeated keystrokes after a small delay. So you're going to get many, many keydown events...and you'll end up calling PlayLooping() many times as well. This will cause it to reset each time, resulting in choppy sound, or it may reset so quickly that it will seem as if it isn't even playing at all.
To solve this, setup a boolean that tracks whether the sound is already playing or not, so that you only call PlayLooping() once. You should probably also move your SoundPlayer instance out to class level, as there is no need to keep reloading it every time. So on keydown we call PlaySound(true), while on keyup we call PlaySound(false), as shown below (simplified code for clarity):
private bool soundPlaying = false;
private SoundPlayer player = new SoundPlayer(#"Sounds/Step.wav");
private void Joc_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
{
PlaySound(true);
}
}
private void PlaySound(bool start)
{
if (start)
{
if (!soundPlaying)
{
soundPlaying = true;
player.PlayLooping();
}
}
else
{
soundPlaying = false;
player.Stop();
}
}
private void Joc_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
{
PlaySound(false);
}
}
I need to make different commands on Escape key pressed, for example
if Escape key is once pressed .. Draw(250,250);
if Escape key is twice pressed .. Draw(350,350);
if Escape key is triple pressed .. Draw(450,450);
on one key I have to have multiple different commands,
how to make the app to count how many times a key is pressed and using that info .. to run a specific code?
You should create a counter and increment it each time the key is pressed.
If you are talking about double or triple "clicking", you also need to set a Timer.
Each time the timer ends, you reset your counter.
Only thing left is to call the method you want depending on your counter value.
int MyKeyCounter = 0;
Timer CounterResetter = new Timer(1000);
CounterResetter.Elapsed += ResetCounter;
void OnKeyPressEvent(object sender, KeyPressEventArgs e)
{
if (e.Key == Key.Escape)
{
MyKeyCounter++;
if(!CounterResetter.Enabled)
CounterResetter.Start();
}
}
void ResetCounter(Object source, ElapsedEventArgs e)
{
if(MyKeyCounter == 1)
Method1();
else if (MyKeyCounter == 2)
Method2();
...
MyKeyCounter = 0;
}
Attach the event on the control you want and put the fields at the top.
This is the best way I can think of. It waits for the user to input a key, and counts it
if its the escape key. Any other key pressed breaks the loop, then you count the times pressed!
ConsoleKeyInfo key;
int timesPressed = 0;
while(true) {
key = Console.ReadKey();
if (!key.Equals( ConsoleKey.Escape ) || timesPressed >= 3) {
break;
}
timesPressed++;
}
I'm trying to make use of the WindowsInputSimulator library to help me simulate keyPresses.
The software will consist of a client and a server. When a key is entered on the Client, it's KeyEventArgs are sent to the Server. The server then does the following with it:
public void SendKeyDown(Keys keyCode, Keys modifiers)
{
uint nonVK = MapVirtualKey((uint)keyCode, 2);
char mappedChar = Convert.ToChar(nonVK);
if (modifiers.Equals(Keys.None))
{
VirtualKeyCode vkc;
if (Enum.TryParse(VkKeyScan(mappedChar).ToString(), out vkc))
{
InputSimulator.SimulateKeyDown(vkc);
}
}
else
{
//Find out which modifier we're working with.
uint modVK = MapVirtualKey((uint)modifiers, 2);
char modifierChar = Convert.ToChar(modVK);
VirtualKeyCode vkc, modVkc;
if (Enum.TryParse(VkKeyScan(mappedChar).ToString(), out vkc)
&& Enum.TryParse(VkKeyScan(modifierChar).ToString(), out modVkc))
{
InputSimulator.SimulateModifiedKeyStroke(modVkc, vkc);
}
}
}
Which works for single keys. However, I'm trying to work with modifier keys as well, and I'm running in to some trouble. For example, pressing SHIFT + K produces "k2" Which leads me to believe either my transation into VirtualKeyCodes is wonky, or something else is.
Also, when sending these commands, should I catch only the KeyDown / KeyUp events? Or should I also watch for the KeyPress event? I should be able to wrok with arrow keys and non-Character keys as well, which makes me think I should just ignore the KeyPress.
EDIT: Also, how would I know when I'm working with multiple modifiers? How should I be stringing them together?
Thoughts? Thanks!
I was able to get it working with the following. Keep in mind this works for a SINGLE modifier, and a single CHARACTER. Special characters don't yet work with this code, but I figure it's a step in the right direction, and answered my immediate question.
public void SendKey(int keyValue, Keys modifiers)
{
VirtualKeyCode key;
if (modifiers.Equals(Keys.None))
{
if (Enum.TryParse(VkKeyScan(((char)keyValue)).ToString(), out key))
{
InputSimulator.SimulateKeyDown(key);
InputSimulator.SimulateKeyUp(key);
}
}
else if (modifiers.Equals(Keys.Shift) && keyValue >= (int)Keys.A && keyValue <= (int)Keys.Z)
{
if (Enum.TryParse(VkKeyScan(((char) keyValue)).ToString(), out key))
{
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.SHIFT, key);
}
}
else if (modifiers.Equals(Keys.Control) && keyValue >= (int)Keys.A && keyValue <= (int)Keys.Z)
{
if (Enum.TryParse(VkKeyScan(((char)keyValue)).ToString(), out key))
{
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.CONTROL, key);
}
}
else if (modifiers.Equals(Keys.Alt) && keyValue >= (int)Keys.A && keyValue <= (int)Keys.Z)
{
if (Enum.TryParse(VkKeyScan(((char)keyValue)).ToString(), out key))
{
//Alt is named MENU for legacy purposes.
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.MENU, key);
}
}
}
I have an implementation which hooks into the keydown event, which suppresses the keypress event and does some magic. Now I want to show something more friendly than "ControlKey" etc, so there's a switch you can see in the code below. Except I've realised things like the number keys along the top end up as D1, D2, etc., and then there's things like Add showing up for the numpad +. In addition, Print Screen doesn't seem to be recognised.
Is there something I'm missing?
This is a hard question for me to describe fluently, but hopefully you get what I mean. If not I guess I'll be replying to comments and improving this as I go.
private int numKeys = 0;
private List<int> shortcutKeys = new List<int>();
private void textBoxRegionKeys_Click(object sender, EventArgs e)
{
textBoxRegionKeys.SelectAll();
}
private void textBoxRegionKeys_KeyDown(object sender, KeyEventArgs e)
{
// There are certain keys we want to ignore...
if (e.KeyCode != Keys.Delete && e.KeyCode != Keys.Back)
{
// We can handle this ourselves, thanks
e.SuppressKeyPress = true;
// Shortern what we show
string ret = e.KeyCode.ToString();
switch (ret)
{
case "ControlKey": ret = "Ctrl"; break;
case "ShiftKey": ret = "Shift"; break;
case "Menu": ret = "Alt"; break;
}
// If we haven't selected anything, we should be appending
if (textBoxRegionKeys.SelectionLength == 0)
{
if (numKeys > 0)
{
// Does our key already exist in the list?
if (shortcutKeys.Exists(x => x == e.KeyValue))
{
return;
}
textBoxRegionKeys.Text += " + ";
}
textBoxRegionKeys.Text += ret;
shortcutKeys.Add(e.KeyValue);
numKeys++;
}
else
{
textBoxRegionKeys.Text = ret;
shortcutKeys.Clear();
shortcutKeys.Add(e.KeyValue);
numKeys = 1;
}
}
}
The TextBox KeyDown/KeyPress etc will only be raised for keys that may be accepted as input in to the text box (and associated modifiers). As such, you will not see keys handled such as Print Screen etc. The best option I can think of is not ideal, but you could override the ProcessKeyPreview or some other Form level Message interceptor to get notified of ANY key press. Something like...
protected override bool ProcessKeyPreview(ref Message m)
{
var keyCode = (Keys)Enum.ToObject(typeof (Keys), m.WParam);
//Insert some logic
return base.ProcessKeyPreview(ref m);
}
Of course, this method will be invoked whenever the FORM has focus, and a key is pressed, so you would have to filter down by doing some form of check (which again is not ideal)
if(ReferenceEquals(ActiveControl, textBoxRegionKeys)) {}
Which if your dealing with things like UserControls will be very unreliable.
As for formatting in to nice friendly messages, I think you basically you will need your own map of special characters... I am not aware of any Globalized lookup for Keys. I will dig a little and update the answer if I find something.
Edit
Did a little digging and couldn't find anything obvious for nice key mappings. I would just create a map of "friendly" key names:
private static readonly Dictionary<Keys, String> KeysMap = new Dictionary<Keys, String>
{
{ Keys.D1, "1"},
{ Keys.D9, "9"}
};
And do something like:
String friendlyKeyCode;
if (!KeysMap.TryGetValue(keyCode, out friendlyKeyCode))
friendlyKeyCode = keyCode.ToString();
Personally, I find this approach better than a massive switch... but that works too.