I have a program where I want to move a Graphic with the keyboard keys. I originally had a function to move the graphic whenever pressed a button, but I abandoned that method to get around the keyboard repeat delay. Instead, I decided I would use a timer, which I would enable upon the KeyPess event, and disable on the KeyUp event. At first, I used 4 different timers for each different direction, and although it worked I noticed my program had begun to freeze quite often. I decided to use a single timer for all movements, and use if statements to determine the direction. Now, it seems that my Graphic doesn't move at all, even though all I did was copy and paste code.
enum Direction
{
Left, Right, Up, Down
}
private Direction _objectDirection;
int _x = 100, _y = 100;
private void Form1_Paint(object sender, PaintEventArgs e)
{
Picture.MakeTransparent(Color.Transparent);
e.Graphics.DrawImage(Picture, _x, _y);
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
{
if (timerAnimation.Enabled == false)
{
AnimationMaxFrame = 3;
timerAnimation.Enabled = true;
}
_objectDirection = Direction.Up;
timerMovement.Enabled = true;
}
//The rest of this code is omitted to save space, it is repeated 4 times with the only
//changes being the key pressed, and the object direction.
Invalidate();
}
void Form1_KeyUp(object sender, KeyEventArgs e)
{
timerAnimation.Enabled = false;
timerMovement.Enabled = false;
Picture = Idle;
this.Refresh();
}
private void timerMovement_Tick(object sender, EventArgs e)
{
if (_objectDirection == Direction.Up)
{
if (_y > 24)
{ _y = _y - 2; }
else
{ timerMovement.Enabled = false; }
//This if statement is to ensure that the object doesn't leave the form.
//I have tried removing them already, they're not the problem.
}
//Again, this is shortened to save space, it is repeated for each direction.
Invalidate();
}
What is preventing my graphic from moving, and is there a better way to do this? There is still a lot of functionality I want to add to this, but it's already freezing.
Not sure you are making a game with WinForms, but to the point...
You need to handle key pressed events, when a key pressed event fires set a boolean flag in your code depending on if the event was a press or release. Then in your update code check the flag and do your movement accordingly.
It would be something like this (example code):
bool moveup = false;
void KeyPressed(object sender, KeyEventArgs e)
{
// check for keys that trigger starting of movement
if (e.KeyCode == Keys.W) moveup = true;
}
void KeyReleased(object sender, EventEventArgs e)
{
// check for keys that trigger stopping of movement
if (e.KeyCode == Keys.W) moveup = false;
}
void TimerTick(obect sender, EventArgs e)
{
if (moveup)
{
// move your object
}
}
Related
I am trying to make a simple game in C# using Visual Studio Windows Form Application. I want to let the user be able to move the blue box upwards, rightwards, downwards, and leftwards freely using the corresponding keys.
I am using a Timer which detects the new location of the box every 0.1 seconds, and a keydown event that actually changes the location of the box.
The box needs to keep moving in the corresponding direction while the key is held down.
My problem is, my current program does the job except that when the user first presses a key, the box moves a little bit once and pauses for a moment before it keeps moving. I want to make this box move more smoothly from the first key press without pausing like that. This might be difficult to explain by words so I added a gif file.
Is there a way to fix this? Here is my current code.
private int posX, posY; //Both initialized in Form Load event
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
posY -= 3;
else if (e.KeyCode == Keys.Right)
posX += 3;
else if (e.KeyCode == Keys.Down)
posY += 3;
else if (e.KeyCode == Keys.Left)
posX -= 3;
}
//Timer ticks every 0.1 second
private void Timer_Tick(object sender, EventArgs e)
{
Box.Location = new Point(posX, posY);
labelPosX.Text = posX.ToString(); //Testing purposes
labelPosY.Text = posY.ToString(); //Testing purposes
}
I would love to use KeyDown event to achieve this, but if there is a better or more common way actually used in real game worlds, I would love to know about it too!
Use Keyboard.IsKeyDown method in your Timer_Tick method, and don't listen for the keydown event.
Like so:
double posX, posY;
private void Timer_Tick(object sender, EventArgs e)
{
double velocity = /*(speed: pixels per seconds)*/ 100 * /*(timer tick time in seconds)*/ 0.003;
if (Keyboard.IsKeyDown(Keys.Up))
{
posY -= velocity;
}
else if (Keyboard.IsKeyDown(Keys.Down))
{
posY += velocity;
}
//Also, don't put else here, so you can go diagonally.
if (Keyboard.IsKeyDown(Keys.Left))
{
posX -= velocity;
}
else if (Keyboard.IsKeyDown(Keys.Right))
{
posX += velocity;
}
Box.Location = new Point((int)posX, (int)posY);
labelPosX.Text = posX.ToString(); //Testing purposes
labelPosY.Text = posY.ToString(); //Testing purposes
}
public static class Keyboard
{
private static readonly HashSet<Keys> keys = new HashSet<Keys>();
public static void OnKeyDown(object sender, KeyEventArgs e)
{
if (keys.Contains(e.KeyCode) == false)
{
keys.Add(e.KeyCode);
}
}
public static void OnKeyUp(object sender, KeyEventArgs e)
{
if (keys.Contains(e.KeyCode))
{
keys.Remove(e.KeyCode);
}
}
public static bool IsKeyDown(Keys key)
{
return keys.Contains(key);
}
}
And to use the Keyboard class, sets the Form1's KeyDown and KeyUp events in the InitializeComponent method.
KeyDown += Keyboard.OnKeyDown;
KeyUp += Keyboard.OnKeyUp;
You can control it's speed by changing the velocity.
I know the title was little unclear but i will explain.
My Example:
when i press 'D' my object will move to right,
same for 'A' just move to left,
but when i press 'Space' while holding 'A'/'D',the object will just o the function of the 'Space'.
if (e.KeyCode == Keys.A)
{
ply.Left -= 3;
}
if (e.KeyCode == Keys.D)
{
ply.Left += 3;
}
if (e.KeyCode == Keys.Space) {
ply.Top -= 100;
}
You can track the state of multiple keys through the use of both KeyUp and KeyDown by use of a HashSet<Keys>:
private HashSet<Keys> _keys = new HashSet<Keys>();
public void Control_KeyDown(object sender, KeyEventArgs e)
{
_keys.Add(e.KeyCode);
}
public void Control_KeyUp(object sender, KeyEventArgs e)
{
_keys.Remove(e.KeyCode);
}
public bool IsKeyDown(Keys keyCode)
{
return _keys.Contains(keyCode);
}
Now, whenever you want to check to see if a key is down, just do:
if(IsKeyDown(Keys.A))
{
ply.Left -= 3;
}
Keep in mind that if you just check key states in one of the key events, you will probably have issues with not detecting repeats properly. If you really want to handle this properly, you should use a Timer with a fairly small Interval that periodically polls with IsKeyDown and handles all your input.
With a Timer named timer1 that has an interval of 100, you can handle all your input like this, and have fairly responsive input to the full range of keys supported by your hardware.
private void timer1_Tick(object sender, EventArgs e)
{
if (IsKeyDown(Keys.A))
{
ply.Left -= 3;
}
if (IsKeyDown(Keys.S))
{
ply.Top += 3;
}
if (IsKeyDown(Keys.D))
{
ply.Left += 3;
}
if (IsKeyDown(Keys.W))
{
ply.Top -= 3;
}
}
As Bradley said, you could save all key events to a keystate cache.
This could look something like this pseudo code:
Event KeyDown(e) -> keystate[e.KeyCode] = true
Event KeyUp(e) -> keystate[e.KeyCode] = false
That way, you should always know every state.
I'm currently writing a program that holds down a mouse button when initially clicked, and continues to hold it until the user presses the mouse button for a second time.
The program works by detecting input globally using a MouseHookListener and then uses an input simulator to hold down the mouse button that has been pressed.
The program is able to hold down the mouse as intended, but there is an issue with the original mouse click that signals the program to simulate the button being held; it still gets carried out. I know that the MouseHookListener uses low level hooks to operate, but it's HookCallBack() method can't be overridden due to it being protected.
Is there any way to block out the original mouse input? Or is there a way to make the original input held in until the mouse is clicked once more?
This is the code I've produced, thus far (note - the mListener is being activated in a forum else where):
public MouseHold()
{
mListener = new MouseHookListener(new GlobalHooker());
mListener.MouseClick += mListener_MouseClick;
}
private bool isDown;
private int count = 0;
private InputSimulator sim = new InputSimulator();
public MouseHookListener mListener;
private void mListener_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (isDown)
{
isDown = false;
Console.Out.WriteLine(count);
}
else
{
Console.Out.WriteLine(count);
isDown = true;
sim.Mouse.LeftButtonDown();
}
}
}
I am the author of the library you mentioned https://github.com/gmamaladze/globalmousekeyhook.
To enable your usecase I have extended the library by one more event. Please note that the construction of listener has also slightly changed in the new version.
I beleave that with the example below you are able to accomplish your scenario without involving InputSimulator library.
How it works?
First mouse down set the flag m_SupressNextUp - down is fired.
Mouse button up which follows previous down is supressed and set the flag m_SupressNextDown
Second down is supressed too and reset the flag m_SupressNextUp
Second up is fired and reset m_SupressNextDown
System is returned to initial state.
internal class Sample
{
private IKeyboardMouseEvents m_GlobalHook;
private bool m_SupressNextUp;
private bool m_SupressNextDown;
public void Subscribe()
{
m_GlobalHook = Hook.GlobalEvents();
m_GlobalHook.MouseDownExt += GlobalHookMouseDownExt;
m_GlobalHook.MouseUpExt += GlobalHook_MouseUpExt;
}
void GlobalHook_MouseUpExt(object sender, MouseEventExtArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (m_SupressNextUp)
{
Console.WriteLine(#"First mouse up supress.");
e.Handled = true;
m_SupressNextDown = true;
}
else
{
Console.WriteLine(#"Second mouse up - make it heppen.");
m_SupressNextDown = false;
}
}
}
private void GlobalHookMouseDownExt(object sender, MouseEventExtArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (m_SupressNextDown)
{
Console.WriteLine(#"Second mouse down supress.");
e.Handled = true;
m_SupressNextUp = false;
}
else
{
Console.WriteLine(#"First mouse down - make it heppen.");
m_SupressNextUp = true;
}
}
}
public void Unsubscribe()
{
m_GlobalHook.MouseDownExt -= GlobalHookMouseDownExt;
m_GlobalHook.MouseUpExt -= GlobalHook_MouseUpExt;
m_GlobalHook.Dispose();
}
}
after reviewing the documentation for the input simulator I am guessing you need to reverse the mouse down input.
the source code for the input simulator has a mouse up method
private void mListener_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (isDown)
{
isDown = false;
Console.Out.WriteLine(count);
sim.Mouse.LeftButtonUp(); //maybe try this
}
else
{
Console.Out.WriteLine(count);
isDown = true;
sim.Mouse.LeftButtonDown();
}
}
}
I am working on a gaming project with a base to help get me started. The base is overall extremely helpful, however there is a part of the code that confuses me as to how to manipulate correctly.
When this code is run, the player sprite will indefinitely move in a direction until the other key press is used. If a playerMoveY is added, it will get stuck moving in diagonals.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
playerMoveX = -1;
}
else if (e.KeyCode == Keys.Right)
{
playerMoveX = 1;
}
Without seeing your full code, it's going to be hard to say for sure, but it is probably because you don't have a KeyUp event to reset the move value.
Similarly to how you created a KeyDown event to use the Form1_KeyDown method, try something like this for KeyUp
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
playerMoveX = 0;
}
else if (e.KeyCode == Keys.Right)
{
playerMoveX = 0;
}
}
I want to be able to use the space key to modify the behavior of the mouse while it is held down. Without knowing better I am imagining it involves some kind of coordination between two (or three) event handler - mousemove, keydown, and keyup. But I am wondering if there is some way to handle it entirely within one event handler - mousemove.
Example code to give an idea of what I hope to be able to do...
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (Keyboard.KeyDown == Keys.Space)
{
/* Do modified behavour for left mouse being held down while
space is also held down */
}
else
{
// Do normal behavour for left mouse being held down
}
}
}
Is something like this possible or will I have to save the state of the space key to a class variable using keydown event handler and check it with the mouse move handler?
IT could be done using Control.ModifierKeys & Control.MouseButtons. But only works for kays like shift, ctrl and alt.
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if ((Control.ModifierKeys & Keys.Shift) != 0)
{
if ((Control.MouseButtons & MouseButtons.Left) != 0)
{ // here you go
}
}
}
You should set a variable in your KeyDown-Event and check it in your MouseEvent:
bool buttonpressed = false;
private void KeyDown_Event(object s, System.Windows.Forms.KeyEventArgs e)
{
if(e.KeyCode == KeyCode.Space)
buttonpressed = true;
else
buttonpressed = false;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (buttonPressed)
{
/* Do modified behavour for left mouse being held down while
space is also held down */
}
else
{
// Do normal behavour for left mouse being held down
}
}
}
I'd have a variable that tracks when Keys.Space is in the Pressed state, then trigger it with KeyUp and KeyDown events
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
// Check if Key.Space pressed
if(SpacePressed) {
// Do something
}
}
private void KeyPressed_Event(object sender, KeyEventArgs e) {
// Check if Key.Space pressed
if(e.Key == Key.Space) {
SpacePressed = true;
}
}
private void KeyRelease_Event(object sender, KeyEventArgs e) {
// Check if Key.Space pressed
if(e.Key == Key.Space) {
SpacePressed = false;
}
}
You could use PInvoke to call GetKeyState in User32.dll.