2016-05-24 5 views
2

Я пытаюсь создать виртуальный джойстик, который пользователь может перемещать с помощью мыши в WPF на C#. Я пытаюсь использовать полярную систему координат, потому что хочу, чтобы ручка джойстика оставалась в круге.C# WPF - Виртуальный джойстик с использованием полярных координат

Я получаю какое-то действительно странное поведение - если у кого-то есть опыт с подобными вещами, любые советы будут приятными. Спасибо

EDIT: Я получил его работу. Я разместил обновленный код ниже. Это отнюдь не хорошее/профессиональное решение, но оно работает. Поэтому я надеюсь, что если кто-то в будущем попытается выполнить эту же задачу, это может помочь. Я попытался добавить некоторые комментарии, чтобы объяснить, что происходит. Ну вот!

ПРИМЕЧАНИЕ. Если вы пытаетесь использовать это для своей программы, обратите внимание на наличие двух жестко заданных значений, которые вы должны изменить. Первый - это x_starting/y_starting. Вот где ваша виртуальная ручка джойстика должна быть сброшена. И далее - радиус при вычислении максимально возможного значения. Удостоверьтесь, что это половина ширины фонового джойстика.

Код:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 


namespace WpfApplication4 
{ 
    public partial class MainWindow : Window 
    { 

     public MainWindow() 
     { 
      InitializeComponent(); 
     } 
     double radius; 
     bool captured = false; 
     double x_shape, x_canvas, y_shape, y_canvas; //Canvas is used to keep track of where the joystick is on screen, 
                 // shape is used for where the knob is. 
     UIElement source = null; 
     double y_starting = 180;  //The starting X and Y position for the Knob. (CHANGE TO WHERE UR CANVAS.TOP/CANVAS.LEFT IS FOR THE KNOB) 
     double x_starting = 105; 

     private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
     { 
      string objname = ((Ellipse)sender).Name; 
      if (objname == "Knob") 
      { 
       source = (UIElement)sender; 
       Mouse.Capture(source); 
       captured = true; 
       x_shape = Canvas.GetLeft(reference); 
       x_canvas = e.GetPosition(Knob).X; 
       y_shape = Canvas.GetTop(reference); 
       y_canvas = e.GetPosition(Knob).Y; 
      } 
     } 

     private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
     { 
      string objname = ((Ellipse)sender).Name; 
      Mouse.Capture(null); 
      captured = false; 
      if (objname == "Knob") { 
       // x_shape = x_starting; 
       // y_shape = y_starting; 
       Canvas.SetLeft(source, x_starting); 
       Canvas.SetTop(source, y_starting);   //Reset to our starting values 
       XTextBlock.Text = x_starting.ToString(); 
       YTextBlock.Text = y_starting.ToString(); 

      } 
     } 

     private void Ellipse_MouseMove(object sender, MouseEventArgs e) 
     { 
      double x = e.GetPosition(reference).X;  //Getting mouse pos relative to the center of your joystick (I have an empty textblock there called reference) 
      double y = e.GetPosition(reference).Y; 
      double r = Math.Sqrt((x * x) + (y * y)); //Calculate radius.. 
      XMousePos.Text = x.ToString(); 
      YMousePos.Text = y.ToString(); 

      string objname = ((Ellipse)sender).Name; 

      double theta = Math.Atan2(y, x);  //Calculate theta.. 
      Theta.Text = theta.ToString(); 


      double x1 = (r * Math.Cos(theta));  //This converts polar coordinates to cartesian plane coordinates. 
      double y1 = (r * Math.Sin(theta)); 
      XPolPos.Text = x1.ToString(); 
      YPolPos.Text = y1.ToString(); 


      double xmax = (62.5 * Math.Cos(theta)); //Calculate a max so that your knob stays within the circle. The radius value should be half the width of the 
      double ymax = (62.5 * Math.Sin(theta)); //  background of your joystick. 
      X2PolPos.Text = xmax.ToString(); 
      Y2PolPos.Text = ymax.ToString(); 

      if (objname == "Knob") { 
       if (captured) 
       { 
        if ((((x1 > 0) && (x1 < xmax)) || ((x1 <= 0) && (x1 > xmax))) && (((y1 > 0) && (y1 < ymax)) || ((y1 <= 0) && (y1 > ymax)))) //Seems like bad way to do it. But this is how i check to see if knob is in bounds. 
        { 
         x = e.GetPosition(reference).X;   //Get the values and calculate it again. 
         y = e.GetPosition(reference).Y; 
         r = Math.Sqrt((x * x) + (y * y)); 
         theta = Math.Atan2(y, x); 
         x1 = (r * Math.Cos(theta)); 
         y1 = (r * Math.Sin(theta));   

         x_shape += x1 - x_canvas;    //Changing our values and moving the knob. 
         Canvas.SetLeft(source, x_shape); 
         x_canvas = x1; 
         y_shape += y1 - y_canvas; 
         Canvas.SetTop(source, y_shape); 
         y_canvas = y1; 

         XTextBlock.Text = x_shape.ToString(); 
         YTextBlock.Text = y_shape.ToString(); 
        } 
       } 
      } 
     } 
    } 
} 

И XAML только в случае, если:

<Window x:Class="WpfApplication4.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525" MouseMove="Window_MouseMove" KeyDown="Window_KeyDown"> 
<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="*"/> 

    </Grid.ColumnDefinitions> 
    <StackPanel Orientation="Vertical"> 
     <TextBlock Text="KNOB POSITION"/> 
     <TextBlock Name="XTextBlock"/> 
     <TextBlock Name="YTextBlock"/> 
     <TextBlock Text="MOUSE POSITION"/> 
     <TextBlock Name="XMousePos"/> 
     <TextBlock Name="YMousePos"/> 
     <TextBlock Text="POLAR COORDINATES"/> 
     <TextBlock Name="XPolPos"/> 
     <TextBlock Name="YPolPos"/> 
    </StackPanel> 
    <Canvas Name="LayoutRoot" Grid.Column="1"> 
     <Ellipse Fill="#FFF4F4F5" Name ="Joystick" Height="125" Canvas.Left="51" Stroke="Black" Canvas.Top="128" Width="125" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" MouseMove="Ellipse_MouseMove"/> 
     <Ellipse Fill="#FFF4F4F5" Name="Knob" Height="15" Canvas.Left="105" Stroke="Black" Canvas.Top="180" Width="15" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" MouseMove="Ellipse_MouseMove"/> 
    </Canvas> 
</Grid> 

+1

Просто сказать «я получаю некоторые очень странное поведение» не очень помогает. Расскажите, какое поведение вы на самом деле получаете. – ChrisF

+0

@ChrisF Мне удалось это исправить. Это далеко не идеальное решение, но оно работает. – Fivestar

ответ

1

Когда мы говорим о джойстиком polar coordinate system не кажется, чтобы быть полезным.

Нам нужны X и Y смещение в диапазоне [-1; 1]. Мы можем легко оценить его знание Поле (большой) Radius, Filed Center point и Mouse coordinates.

Как это работает (удалите все события, кроме Ellipse_MouseMove). Участник m_vtJoystickPos удерживает выбранное положение Joystick.

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     UpdateKnobPosition(); 
    } 

    /// <summary> 
    /// Current joystick position 
    /// </summary> 
    Vector m_vtJoystickPos = new Vector(); 
    private void Ellipse_MouseMove(object sender, MouseEventArgs e) 
    { 
     double fJoystickRadius = Joystick.Height * 0.5; 

     //Make coords related to the center 
     Vector vtJoystickPos = e.GetPosition(Joystick) - 
      new Point(fJoystickRadius, fJoystickRadius); 

     //Normalize coords 
     vtJoystickPos /= fJoystickRadius; 

     //Limit R [0; 1] 
     if (vtJoystickPos.Length > 1.0) 
      vtJoystickPos.Normalize(); 

     XMousePos.Text = vtJoystickPos.X.ToString(); 
     YMousePos.Text = vtJoystickPos.Y.ToString(); 

     //Polar coord system 
     double fTheta = Math.Atan2(vtJoystickPos.Y, vtJoystickPos.X); 
     XPolPos.Text = fTheta.ToString(); //Angle 
     YPolPos.Text = vtJoystickPos.Length.ToString(); //Radius 

     if (e.LeftButton == MouseButtonState.Pressed) 
     {         
      m_vtJoystickPos = vtJoystickPos; 
      UpdateKnobPosition(); 
     } 
    } 

    void UpdateKnobPosition() 
    { 
     double fJoystickRadius = Joystick.Height * 0.5; 
     double fKnobRadius = Knob.Width * 0.5; 
     Canvas.SetLeft(Knob, Canvas.GetLeft(Joystick) + 
      m_vtJoystickPos.X * fJoystickRadius + fJoystickRadius - fKnobRadius); 
     Canvas.SetTop(Knob, Canvas.GetTop(Joystick) + 
      m_vtJoystickPos.Y * fJoystickRadius + fJoystickRadius - fKnobRadius); 
    } 
} 

Я также включил оценку полярной CS (прокомментировал). BTW Polar CS (R, Угол).

XAML:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:WpfApplication1" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
      <ColumnDefinition Width="*"/> 

     </Grid.ColumnDefinitions> 
     <StackPanel Orientation="Vertical"> 
      <TextBlock Text="KNOB POSITION"/> 
      <TextBlock Name="XTextBlock"/> 
      <TextBlock Name="YTextBlock"/> 
      <TextBlock Text="MOUSE POSITION"/> 
      <TextBlock Name="XMousePos"/> 
      <TextBlock Name="YMousePos"/> 
      <TextBlock Text="POLAR COORDINATES"/> 
      <TextBlock Name="XPolPos"/> 
      <TextBlock Name="YPolPos"/> 
     </StackPanel> 
     <Canvas Name="LayoutRoot" Grid.Column="1"> 
      <Ellipse Fill="#FFF4F4F5" Name ="Joystick" Height="125" Canvas.Left="51" Stroke="Black" Canvas.Top="127" Width="125" MouseMove="Ellipse_MouseMove"/> 
      <Ellipse Fill="#FFF4F4F5" Name="Knob" Height="16" Canvas.Left="106" Stroke="Black" Canvas.Top="182" Width="15" MouseMove="Ellipse_MouseMove"/> 
     </Canvas> 
    </Grid> 
</Window> 
+0

Интересно. Я также посмотрю на это. Спасибо – Fivestar

+0

Я добавил 'UpdateKnobPosition()', чтобы убедиться, что он соответствует 'm_vtJoystickPos'. Вы можете сделать proerty 'JoystickPosition' и обновить его на внешнем' set' (это позволит вам установить позицию из кода) –