none
Работа с Клавиатурой WPF C#. RRS feed

  • Вопрос

  • Добрый вечер,  

    цель: плавное движения объекта на canvas
    влево / вправо при нажатии клавиш Key.Left / Key.Right  соответственно.
    еще так же участвует клавиша space.
    проблемы:

    1. перемещение объекта не плавное .
    2. иногда при нажатии на клавишу  не всегда сразу происходит
    движение , иногда требуется нажать еще раз, так же при нажатии 
    на пробел , невозможно перемещение объекта. 

    Пожалуйста подскажите как можно решит эти 2 проблемы.

    код :

            // Keyboard event
            public void KeyDown_Event(object sender, KeyEventArgs e)
            {
    
                // Player Keys - Right/Left
                if (e.Key == Key.Right) player.position.X += player.velocity;
    
                if (e.Key == Key.Left) player.position.X -= player.velocity;
    
                player.UpdatePosition();
    
                // Bullet Key-Space
                if (e.Key == Key.Space)
                {
                    foreach (GameObject bullet in bullets)
                    {
                        if (!bullet.alive)
                        {
                            ((Bullet)bullet).SetBullet(player);
                            bullet.Draw(cnvsPlayingArea);
                            
                            break;
                        }
                    }
                } // if Key.Space;
    
            }

    21 февраля 2013 г. 18:22

Ответы

  • мне кажется вам лучше использовать анимацию на основе DependencyProperty

    например вот так:

                DoubleAnimation myDoubleAnimation = new DoubleAnimation();
                myDoubleAnimation.From = ;//текушая позиция элемента
                myDoubleAnimation.By = ;//дельта смещения может быть как + так и -
                myDoubleAnimation.Duration =  new Duration(TimeSpan.FromSeconds(5));//время за которое передвигается объект
                objImage.BeginAnimation(Canvas.LeftProperty, myDoubleAnimation); 



    • Изменено Brash_O 22 февраля 2013 г. 8:59
    • Помечено в качестве ответа Abolmasov Dmitry 28 февраля 2013 г. 6:56
    22 февраля 2013 г. 8:58
  • Смотрите вот примерная реализация того что я говорил

    перечисление для представления какие кнопки нажаты

    public enum KeyPressed
    {
    None,
    Left,
    Right,
    Space
    }

    В классе

    private KeyPressed LastKey = KeyPressed.None;
    
    Private KeyPressed PrevoiceKey = KeyPressed.None;
    
    private void OnKeyDown(object sender, KeyEventArgs e)
    {
    	switch(e.Key)
    	{
    		case Key.Left:
    			LastKey = KeyPressed.Left;
    			break;
    		case Key.Right:
    			LastKey = KeyPressed.Right;
    			break;
    		case Key.Space:
    			PrevoiceKey = LastKey;
    			LastKey = KeyPressed.Space;
    			break;
    	}
    }
    
    private void OnKeyUp(object sender, KeyEventArgs e)
    {
    	switch(e.Key)
    	{
    		case Key.Left:
    		case Key.Right:
    			LastKey = KeyPressed.None;
    			break;
    		case Key.Space:
    			if (LastKey != PrevoiceKey && PrevoiceKey != KeyPressed.None)
    				LastKey = PrevoiceKey;
    			break;
    	}
    }
    
    private void OnTimerTick()
    {
    	switch(LastKey)
    	{
    		case KeyPressed.Left:
    			//перемещение влево
    			break;
    		case KeyPressed.Right:
    			//перемещение в право
    			break;
    		case KeyPressed.Space:
    			//Обработка нажания пробела
    			LastKey = PrevoiceKey;
    			break;
    	}
    }
    
    
    Время срабатывания таймера нужно подобрать довольно маленьким и передвигать обьекты по немногу

    • Помечено в качестве ответа Abolmasov Dmitry 28 февраля 2013 г. 6:56
    25 февраля 2013 г. 6:18

Все ответы

  • Добрый день.

    А что у вас за класс от которого объявлена переменная player? Это, насколько я понял, не визуальный элемент? Если мое предположение правильно, то что у вас происходит в UpdatePosition?

    22 февраля 2013 г. 5:54
    Отвечающий
  • переменная player из класса Player
    по сути это Image.

     в UpdatePosition() происходит изменение 
    позиции картинки на canvas и задание границ.

    код функции UpdatePosition():

            public override void UpdatePosition()
            {
                // Move Left/Right
                Canvas.SetLeft(this.objImage, this.position.X);
                // bounding borders Left/Right
                // Right border
                if ( this.position.X >= (GameObject.playingArea.Width - this.objImage.Width) )
                    this.position.X = ((GameObject.playingArea.Width - this.objImage.Width)-12);
                // Left border
                if (this.position.X <= 0)
                    this.position.X = 12;
                
            }

    22 февраля 2013 г. 8:05
  • мне кажется вам лучше использовать анимацию на основе DependencyProperty

    например вот так:

                DoubleAnimation myDoubleAnimation = new DoubleAnimation();
                myDoubleAnimation.From = ;//текушая позиция элемента
                myDoubleAnimation.By = ;//дельта смещения может быть как + так и -
                myDoubleAnimation.Duration =  new Duration(TimeSpan.FromSeconds(5));//время за которое передвигается объект
                objImage.BeginAnimation(Canvas.LeftProperty, myDoubleAnimation); 



    • Изменено Brash_O 22 февраля 2013 г. 8:59
    • Помечено в качестве ответа Abolmasov Dmitry 28 февраля 2013 г. 6:56
    22 февраля 2013 г. 8:58
  • Я не совсем вас понимаю,
    мне надо что бы движения происходили
    при нажатии на клавиши клавиатуры,
    т.е. управление движением персонажа в игре - динамически.
    как это можно сделать с анимацией?
    22 февраля 2013 г. 14:31
  • Анимацию вам предложили для плавности. То есть вы вместо "player.position.X += player.velocity" вызываете процедуру которая будет совершать анимацию, правда при маленьком шаге это не имеет смысла.

    Относительно движение объекта клавиатурой. Смотрите темы 1, 2, 3. Там обсуждались подобные вопросы (некоторые применительно к WinForms, но суть та же).


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    22 февраля 2013 г. 14:58
  • в вашем ответе вот в этой теме 
    вы не могли бы пояснить вот это предложение:
    "2. В событии KeyDown проверять не KeyData, а с помощью api функции GetKeyState."

    Я объясню чего я хочу добиться-
    при нажатии клавиш Key.Left / Key.Right,
    происходит движение объекта влево или вправо.

    при этом если нажимается клавиша пробел ( Space ) то происходит 
    движение другого объекта в моем случаи происходит выпуск снаряда.
    как я могу этого добиться с помощью функции GetKeyState ?
    спасибо.
    22 февраля 2013 г. 17:10
  • В вашем случае GetKeyState не нужен, так как можно жать только одну клавишу. Это например если я хочу одновременно вверх и влево. Я дал эти ссылки что бы вы почерпнули чужой опыт и возможно смогли решить вашу проблему.

    Давайте так. Вы сделаете новый проект с простым объектом на Canvas и реализуете в нем логику движения влево/вправо (пока без пули) и выложите или проект или код, а мы вместе поковыряем и посмотрим что можно улучшить, а что изменить.


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    22 февраля 2013 г. 17:14
  • спасибо за ответ.
    но к сожалению в моем варианте
    это не совсем подходит , т.е.
    у меня есть отдельная функция для 
    проверки коллизии , основанная на пересечении,
    2-ух треугольников / позиции точек X/Y ,
    когда я реализовал перемещение с помощью анимации,
    коллизия перестала правильно функционировать .

    код перемещения с помощью анимации:

            public void MoveHorizontal(double by)
            {
                DoubleAnimation step = new DoubleAnimation();
                step.Duration = TimeSpan.FromMilliseconds(10);
    
                step.From = Canvas.GetLeft(this.objImage);
                step.By = by;
        
                this.objImage.BeginAnimation(Canvas.LeftProperty, step);
    
    
            }

    функция коллизии:

            public bool IsCollide(GameObject objCollideWith)
            {
                if (this.GameObjectRect.IntersectsWith(objCollideWith.GameObjectRect) && !_vulnerable )
                {
                    return true;
                }
                return false;
            }

    22 февраля 2013 г. 17:59
  •  

    Я не совсем понял вашу фразу : "реализуете в нем логику движения влево/вправо (пока без пули)"
    логику движения которая у меня в нынешнем проекте
    или просто заново сделать движения объекта?

    по тому что в моем нынешнем проекте
    player.postion это Point position.

    на этом я и основываю движения объекта по оси X/Y
    и проверку коллизии/границ (что бы не выходил за границы canvas).


    вот то что я попробовал сделать в новом проекте
    правда границы у меня не получается задать , объект все равно за них
    выходит.

    
        public partial class MainWindow : Window
        {
            Canvas playArea;
            Rectangle player;
    
    
            public MainWindow()
            {
                InitializeComponent();
    
                playArea = new Canvas();
                playArea.Width = 780;
                playArea.Height = 550;
    
                this.Width = 800;
                this.Height = 600;
                this.Content = playArea;
    
                player = new Rectangle();
                player.Width = 84;
                player.Height = 84;
                player.Fill = Brushes.Red;
    
                Canvas.SetTop(player, (playArea.Height - player.Height));
                Canvas.SetLeft(player, 0);
    
                playArea.Children.Add(player);
    
                this.KeyDown += KeyDown_Event;
            }
    
    
    
            void KeyDown_Event(object sender, KeyEventArgs e)
            {
                switch (e.Key)
                {
                   case Key.Left:
                MoveHorizontal(-20);
                Bounds();
                break;
                    case Key.Right:
                MoveHorizontal(20);
                Bounds();
                break;
                }
            }
    
    
            void MoveHorizontal(double by)
            {
                DoubleAnimation step = new DoubleAnimation();
                step.Duration = TimeSpan.FromMilliseconds(10);
    
                step.From = Canvas.GetLeft(player);
                step.By = by;
    
                player.BeginAnimation(Canvas.LeftProperty, step);
    
            }
    
    
            void Bounds()
            {
                if ((Canvas.GetLeft(player) + player.ActualWidth) >= (playArea.Width))
                    Canvas.SetLeft(player, playArea.Width - player.ActualWidth);
    
                if (Canvas.GetLeft(player) - player.Width <= 0)
                    Canvas.SetLeft(player, 2);
           }
    
    
    
    
    
    
        }


    22 февраля 2013 г. 19:12
  • В вашем случае GetKeyState не нужен, так как можно жать только одну клавишу. Это например если я хочу одновременно вверх и влево. 

    Почему не нужен ?

    я как раз и хочу при нажатой клавиши Key.Right или Key.Left
    иметь возможность использовать клавишу space,
    т.е. производить движение объекта и стрельбу .

    22 февраля 2013 г. 19:55
  • Может вам тогда попробовать логику отрабатывать не в OnKeyDown а по таймеру, а в OnKeyDown только запоминать какая кнопка была нажата, по таймеру ее обработать и сбросить

    вот игрушка SpaceInvaders на C# там как раз такой же принцип используется

    23 февраля 2013 г. 18:16
  • Я не совсем вас понимаю, что вы имеете ввиду
    под фразой : " OnKeyDown только запоминать какая кнопка была нажата, по таймеру ее обработать и сбросить" ?
    зачем в таймере обрабатывать движения объекта ?

    23 февраля 2013 г. 18:32
  • В событии OnKeyDown может обрабатываться только 1 кнопка за раз, а таким способом вы сможете обработать нажатие дополнительной кнопки и продолжить обработку предыдущей кнопки, например вы нажимаете влево, по таймеру она обрабатывается n раз потом не отпуская вы нажимаете пробел, запоминаете его и по таймеру обрабатываете пробел, и сбрасываете его, при этом после сброса пробела вы ставите что сейчас нажали влево и продолжаете по таймеру обрабатывать влево и так пока не будет событии OnKeyUp в котором вы проверяете что если пользователь нажал влево и сейчас отпустил влево то сбрасываете что бы таймер не обрабатывал влево

    посмотри по ссылке что я привел как там это организованно

    23 февраля 2013 г. 19:29
  • наверное из за отсутствия опыта, у меня не получается
    понять как это можно реализовать.
    по поводу ссылки,
    на этом сайте я не нашел реализации того о чем вы писали.
    24 февраля 2013 г. 17:42
  • Смотрите вот примерная реализация того что я говорил

    перечисление для представления какие кнопки нажаты

    public enum KeyPressed
    {
    None,
    Left,
    Right,
    Space
    }

    В классе

    private KeyPressed LastKey = KeyPressed.None;
    
    Private KeyPressed PrevoiceKey = KeyPressed.None;
    
    private void OnKeyDown(object sender, KeyEventArgs e)
    {
    	switch(e.Key)
    	{
    		case Key.Left:
    			LastKey = KeyPressed.Left;
    			break;
    		case Key.Right:
    			LastKey = KeyPressed.Right;
    			break;
    		case Key.Space:
    			PrevoiceKey = LastKey;
    			LastKey = KeyPressed.Space;
    			break;
    	}
    }
    
    private void OnKeyUp(object sender, KeyEventArgs e)
    {
    	switch(e.Key)
    	{
    		case Key.Left:
    		case Key.Right:
    			LastKey = KeyPressed.None;
    			break;
    		case Key.Space:
    			if (LastKey != PrevoiceKey && PrevoiceKey != KeyPressed.None)
    				LastKey = PrevoiceKey;
    			break;
    	}
    }
    
    private void OnTimerTick()
    {
    	switch(LastKey)
    	{
    		case KeyPressed.Left:
    			//перемещение влево
    			break;
    		case KeyPressed.Right:
    			//перемещение в право
    			break;
    		case KeyPressed.Space:
    			//Обработка нажания пробела
    			LastKey = PrevoiceKey;
    			break;
    	}
    }
    
    
    Время срабатывания таймера нужно подобрать довольно маленьким и передвигать обьекты по немногу

    • Помечено в качестве ответа Abolmasov Dmitry 28 февраля 2013 г. 6:56
    25 февраля 2013 г. 6:18