How could i recognize 2 separate key presses as one key? - c#

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

Related

Detect When Key Lifted - New Unity Input System

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?

How to play a sound all the time while a picturebox is moving C#

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

On key pressed multiple times

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

KeyEventArgs to VirtualKeyCode

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

Is there any C# implementation or way to have a textbox that takes a key sequence as input?

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.

Categories