I have a problem that I'm struggling with.. I want to move an image using my keyboard to the left, right, up or down and in a diagonal way. I searched the web and found, that to use 2 diffrent keys I need to remember the previous key, so for that I'm using a bool dictionary.
in my main Form class this is how the KeyDown event looks like:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
baseCar.carAccelerate(e.KeyCode.ToString().ToLower());
carBox.Refresh(); //carbox is a picturebox in my form that store the image I want to move.
}
My KeyUp event:
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
baseCar.carBreak(e.KeyCode.ToString().ToLower());
}
My Paint event:
private void carBox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(Car, baseCar.CharPosX, baseCar.CharPosY); // Car is just an image
}
And my baseCar class:
private Dictionary KeysD = new Dictionary(); // there is a method to set the W|A|S|D Keys, like: KeysD.Add("w",false)
public void carAccelerate(string moveDir)
{
KeysD[moveDir] = true;
moveBeta();
}
public void moveBeta()
{
if (KeysD["w"])
{
this.CharPosY -= this.carMoveYSpeed;
}
if (KeysD["s"])
{
CharPosY += carMoveYSpeed;
}
if (KeysD["a"])
{
CharPosX -= carMoveXSpeed;
}
if (KeysD["d"])
{
CharPosX += carMoveXSpeed;
}
}
public void carBreak(string str)
{
KeysD[str] = false;
}
Anyway it works, but my problem is that I can't get back to the first pressed key for example:
I pressed W to move up and then the D key to go diagonal, how ever when I release the D key it wont go Up again because the KeyDown event is "dead" and wont call the carAccelerate() method again.. and I can't figure out how to fix it..
Can any one help me please? Maybe there is a better way to handle the keys? im open to any ideas! And I hope you can understand it, my english isnt the best :S
Typically you don't hande the key events directly for these kinds of things. Instead, you keep track of what keys are currently pressed. Physics calculations are done on some interval, which can be done with a timer. Quick and dirty example below. However, this is not the kind of thing you should be attempting with WinForms.
private const int ACCELERATION = 1;
private HashSet<Keys> pressed;
private int velocityX = 0;
private int velocityY = 0;
public Form1()
{
InitializeComponent();
pressed = new HashSet<Keys>();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
pressed.Add(e.KeyCode);
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
pressed.Remove(e.KeyCode);
}
private void timer1_Tick(object sender, EventArgs e)
{
car.Location = new Point(
car.Left + velocityX,
car.Top + velocityY);
if (pressed.Contains(Keys.W)) velocityY -= ACCELERATION;
if (pressed.Contains(Keys.A)) velocityX -= ACCELERATION;
if (pressed.Contains(Keys.S)) velocityY += ACCELERATION;
if (pressed.Contains(Keys.D)) velocityX += ACCELERATION;
}
Related
When I hold a key in my game to move my player:
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
{
Player.MoveUp();
}
}
The player instantly moves one step as soon as I press the down arrow, and then pauses for a short duration before starting to smoothly move again. Why's this? How can I prevent it?
The answer in the proposed duplicate is incorrect, unfortunately. It doesn't ignore repeated KeyDown events, and so will gradually increase the "delta" value in the direction being handled by each key case. It also doesn't respond to the keypress immediately (i.e. no action happens until the first timer tick).
This answer to Holding Arrow Keys Down For Character Movement C# .Net ISSUES explains how to ignore the subsequent KeyDown events, but doesn't explain how then your character would move.
In other words, I couldn't find a duplicate question that actually correctly answers your question. So…
The basic technique you want to do is:
Don't move on the actual key input. Instead, generate your own timing logic that will move the object.
Instead of using the KeyDown event to actually move the object, use it to set a movement direction, which is then processed by your timing logic.
There are a variety of ways to accomplish this. One version would look like this:
private bool _moveUp;
private bool _moveDown;
private bool _moveLeft;
private bool _moveRight;
// You can add the Timer in the Winforms Designer instead if you like;
// The Interval property can be configured there at the same time, along
// with the Tick event handler, simplifying the non-Designer code here.
private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };
public MainForm()
{
InitializeComponent();
_movementTimer.Tick += movementTimer_Tick;
}
private void movementTimer_Tick(object sender, EventArgs e)
{
_DoMovement();
}
private void _DoMovement()
{
if (_moveLeft) Player.MoveLeft();
if (_moveRight) Player.MoveRight();
if (_moveUp) Player.MoveUp();
if (_moveDown) Player.MoveDown();
}
// You could of course override the OnKeyDown() method instead,
// assuming the handler is in the Form subclass generating the
// the event.
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
// Ignore key repeats...let the timer handle that
return;
}
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = true;
break;
case Keys.Down:
_moveDown = true;
break;
case Keys.Left:
_moveLeft = true;
break;
case Keys.Right:
_moveRight = true;
break;
}
_DoMovement();
_movementTimer.Start();
}
public void MainForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = false;
break;
case Keys.Down:
_moveDown = false;
break;
case Keys.Left:
_moveLeft = false;
break;
case Keys.Right:
_moveRight = false;
break;
}
if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
{
_movementTimer.Stop();
}
}
Do note that the timer objects in .NET have limited resolution. I show an interval of 100 ms (10 times per second) above (same as in the other question's answer), and this is about as frequent an update as you're going to reliably get. Even then, the timer's Tick event may not (and probably won't) be raised on exactly 100 ms intervals. There will be some variation back and forth. But it will be close enough for a basic game.
If you need more precision than that, you will have to implement your own state-polling-and-animation loop somewhere. That's a whole other ball o' wax. :)
A subjectivly elegent approach:
public partial class Form1 : Form
{
private static Timer timer;
private static bool[] keys_down;
private static Keys[] key_props;
private void Form1_Load(object sender, EventArgs e)
{
keys_down = new bool[4];
key_props = new []{Keys.A, Keys.D, Keys.W, Keys.S};
timer = new Timer();
timer.Interval = 15; // Roughly 67 FPS
timer.Tick += tick;
timer.Start();
KeyDown += key_down_event;
KeyUp += key_up_event;
... // More things to do when the form loads.
}
private void tick(Object source, EventArgs e)
{
... // Do this every timing interval.
byte n = 0;
foreach (var v in keys_down)
{
if (n == 3 && v)
... // If the "s" key is being held down, no key delay issues. :)
n++;
}
...
}
private void key_down_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = true;
n++;
}
}
private void key_up_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = false;
n++;
}
}
public Form1()
{
InitializeComponent();
}
}
I was looking for solutions to create a small copy of Flappy Bird. Perhaps my decision will help someone.
I used the addition of a player position with a variable in timer to simulate gravity. When I press W, I turned off the further reception of the command using bool and simply inverted gravity
private void time_Tick(object sender, EventArgs e)
{
if (bird.Location.Y >= 540)
{
bird.Location = new Point(bird.Location.X, 540);
}
else
{
bird.Location = new Point(bird.Location.X, bird.Location.Y+grav);
}
}
private void Form1_KeyPress(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == false)
{
press = true;
grav = -10;
}
return;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == true)
{
press = false;
grav = 5;
}
return;
}
A bit lost here with first time accessing cursor's properties for movement.
I want a pictureBox(player) to move slowly towards the mouse cursor after fst_click(bool) is enabled. I have tried a multitude of methods but have had no success. I have tried cases but my return methods are inoperable any help on this?
I though maybe a mouse listener of some sort, to update the location of the mouse and depending on timer speed move towards the location but paths are definitely not my strong point.
private int _x;
private int _y;
private bool MRight = false;
private bool MLeft = false;
private bool MUp = false;
private bool MDown = false;
bool fst_click = false;
private Position _objPosition;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
clicking = true;
}
public void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{ clicking = true; }
}
I removed the incorrect case statement and added a different bool. This statement did switch between 4 cases W, A, S, D but the player location couldn't be updated smoothly?
I also added a timer to attempt to insert a boolean that would create smooth movement but again I couldn't relate to the players' location?
public void tmrMoving_Tick(object sender, EventArgs e)
{
if (clicking == true)
{
player.Location = Cursor.Position;
clicking = false;
}
}
private void Form1_MouseMoveHandler(object sender, MouseEventArgs e)
{
//Point mousePoint = new Point(player.Location.X, player.Location.Y);
}
Update
I have added the new coding below, if there are any ideas on how to repair this as I get no functionality from it. The cursor needs to be moving towards the clicked position at a certain speed, the commented out code is a failed attempt and I am looking to replace it.
namespace games1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
tmrMoving.Enabled = true;
Invalidate();
}
private void tmrMoving_Tick(object sender, EventArgs e)
{
var xdiff = Cursor.Position.X - player.Location.X;
var ydiff = Cursor.Position.Y - player.Location.Y;
var diff = Math.Sqrt(xdiff + ydiff);
// diff = ydiff / 10;
// diff = xdiff / 10;
// xdiff = player.Location.X;
// ydiff = player.Location.Y;
}
}
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 9 years ago.
Improve this question
when a mouse button is pressed on a control, it doesn't fire any events on it anymore.
I need that, since i wanted to enable myself to navigate through a control by "dragging" it.
The Drag events aren't getting fired either. Don't know why it is this way. It's useless.
I need an event thats getting fired when the mouse moves. Where is it?
Edit:
Look, when you hold the left mouse-button on Google-Map, you can move on the map with your mouse movement. I want to do the same thing with my UserControl. I overrided the OnPaint-Method so it would simply displays a grid. I have also implemented functions to move around with keys. It all works so far. Now i want to move around with the mouse by holding down the left mouse-button and move it. It should be easy and obvious, but it isn't.
So i subscribed all the mouse- and drag-events. Like that:
public partial class GameBoard : UserControl
{
private int m_CellWidth = 5;
private int m_CellHeight = 5;
private Point m_Position = Point.Empty;
private Point m_MousePoint = Point.Empty;
public GameBoard()
{
InitializeComponent();
ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Point firstPoint = new Point(m_Position.X % m_CellWidth, m_Position.Y % m_CellHeight);
int countVisibleCols = (int)Math.Ceiling((double)((double)Width / (double)m_CellWidth));
int countVisibleRows = (int)Math.Ceiling((double)((double)Height / (double)m_CellHeight));
Pen artistsPen = new Pen(Brushes.Black);
for (int i = 0; i < countVisibleCols; i++)
{
Point startPoint = new Point(firstPoint.X + i * m_CellWidth, 0);
Point endPoint = new Point(firstPoint.X + i * m_CellWidth, Height);
e.Graphics.DrawLine(artistsPen, startPoint, endPoint);
}
for (int i = 0; i < countVisibleRows; i++)
{
Point startPoint = new Point(0, firstPoint.Y + i * m_CellHeight);
Point endPoint = new Point(Width, firstPoint.Y + i * m_CellHeight);
e.Graphics.DrawLine(artistsPen, startPoint, endPoint);
}
}
private void GameBoard_MouseUp(object sender, MouseEventArgs e)
{
}
private void GameBoard_MouseMove(object sender, MouseEventArgs e)
{
}
private void GameBoard_MouseLeave(object sender, EventArgs e)
{
}
private void GameBoard_MouseHover(object sender, EventArgs e)
{
}
private void GameBoard_MouseEnter(object sender, EventArgs e)
{
}
private void GameBoard_MouseDown(object sender, MouseEventArgs e)
{
}
private void GameBoard_MouseDoubleClick(object sender, MouseEventArgs e)
{
}
private void GameBoard_MouseClick(object sender, MouseEventArgs e)
{
}
private void GameBoard_DragDrop(object sender, DragEventArgs e)
{
}
private void GameBoard_DragEnter(object sender, DragEventArgs e)
{
}
private void GameBoard_DragOver(object sender, DragEventArgs e)
{
}
private void GameBoard_DragLeave(object sender, EventArgs e)
{
}
}
(Actual subscriptions happens in the designer.)
The Problem is: If the left mouse-button has beeing clicked & hold on my control, NOT ONE of those events are getting fired anymore. So i don't know how to implement the desired feature.
Use the MouseMove() event. You can determine if the Left button is down during a move using the e.Button parameter. Here's an example with a Button:
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
this.Text = "Left: " + e.X.ToString() + ", " + e.Y.ToString();
}
else if (e.Button == System.Windows.Forms.MouseButtons.None)
{
this.Text = e.X.ToString() + ", " + e.Y.ToString();
}
}
Not all controls behave the same, however. Give more details about exactly which control you're trying to use and how.
In the Visual Studio designer for a form, there's an Events button in the Properties window of a component that typically looks like a lightning bolt. You can use this to tie events to functions. Could this be missing?
I need to know how to get mouse position when I press a key (insert).
This is what I trying to do:
I have a form1 with one buuton, when you press that button it call another form. But before call the form2 i need to get mouse position from an external application. To do this, the user must hover the cursor over requested position and press 'INSERT'.
public partial class _CalibrateGeneralStep2 : Form
{
public _CalibrateGeneralStep2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Application.Restart();
}
private void button2_Click(object sender, EventArgs e)
{
this.Hide();
///// HERE I NEED TO WAIT UNTIL USER PRESS 'INSERT' KEY BEFORE CALL _CalibrateGeneralStep3 /////
_CalibrateGeneralStep3 frm = new _CalibrateGeneralStep3();
frm.Show();
}
}
I try with keypress and keydown but I dont know use it well.
Thanks... sorry if my english is not good...
You can use
System.Windows.Forms.Cursor.Position: "It represents the current cursor position in screen co-ordinates"
Note: Please refer to the example to see how it works
You can use the KeyDown Event of the form (You can add it from the Designer to be sure it's wired properly)
Since you cannot just wait for the key press event inside your button2_Click, I've used a private field to store the fact that the button have been pressed. Now each time the user press Insert, you check if the button have been pressed and the cursor position. If both are correct, generate the new form.
I've defined the needed cursor position with the 2 constants at the top of the class, and you should also choose a better name for "hasButton2BeenClicked", depending of your business context haha.
public partial class _CalibrateGeneralStep2 : Form
{
private const int NEEDED_X_POSITION = 0;
private const int NEEDED_Y_POSITION = 0;
private bool hasButton2BeenClicked = false;
public _CalibrateGeneralStep2()
{
InitializeComponent();
KeyPreview = true;
}
private void button1_Click(object sender, EventArgs e)
{
Application.Restart();
}
private void button2_Click(object sender, EventArgs e)
{
hasButton2BeenClicked = true;
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Insert && IsCursorAtTheCorrectPosition() && hasButton2BeenClicked)
{
GoToNextStep();
}
}
private bool IsCursorAtTheCorrectPosition()
{
return Cursor.Position.X == NEEDED_X_POSITION && Cursor.Position.Y == NEEDED_Y_POSITION;
}
private void GoToNextStep()
{
this.Hide();
new _CalibrateGeneralStep3().Show();
}
}
I am programming something right now in c#, trying to "convert" a console application to a windows Forms application and I wanted to do something like the following:
if("keypress == nokey")
{
system.threading.thread.sleep ***
}
while(nokeyispressed)
{
system.threading...
}
Basically ask if no key is pressed sleep for some time and this.close();
so that if no key is pressed, do something...
I just can't get it to work.
I would be very greatful for some help..:D
If no key pressed, then no KeyDown event raised. So, your handler will not be called.
UPDATE (option with loop removed, because timer will make same for you as loop on different thread with sleep timeouts):
Here is sample with timer:
private bool _keyPressed;
private void TimerElapsed(object sender, EventArgs e)
{
if (!_keyPressed)
{
// do what you need
}
}
private void KeyDownHandler(object sender, KeyEventArgs e)
{
_keyPressed = true;
switch (e.KeyCode)
{
// process pressed key
}
_keyPressed = false;
}
UPDATE: I think good idea to verify how many time elapsed since last key down before decide if no keys were pressed
private DateTime _lastKeyDownTime;
private const int interval = 100;
private void LoadHandler(object sender, EventArgs e)
{
// start Threading.Timer or some other timer
System.Threading.Timer timer = new System.Threading.Timer(DoSomethingDefault, null, 0, interval);
}
private void DoSomethingDefault(object state)
{
if ((DateTime.Now - _lastKeyDownTime).TotalMilliseconds < interval)
return;
// modify UI via Invoke
}
private void KeyDown(object sender, KeyEventArgs e)
{
_lastKeyDownTime = DateTime.Now;
switch (e.KeyCode)
{
// directly modify UI
}
}