C#WPF - 使用极坐标的虚拟操纵杆

时间:2016-05-24 18:40:11

标签: c# wpf joystick

我正在尝试创建一个虚拟操纵杆,用户可以在C#中使用鼠标在WPF中移动。我正在尝试使用极坐标系,因为我希望操纵杆的旋钮保持在圆圈中。

我得到了一些非常奇怪的行为 - 如果有人有这方面的经验,任何提示都会很好。谢谢

编辑:我搞定了。我在下面发布了更新的代码。它绝不是一个好的/专业的解决方案,但它的工作原理。所以我希望如果将来有人试图做同样的任务,它可能会有所帮助。我试着添加一些评论来解释发生了什么。你走了!

注意:如果您尝试在程序中使用此功能,请注意必须更改两个硬编码值。第一个是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 个答案:

答案 0 :(得分:1)

当我们谈论操纵杆时,polar coordinate system似乎没有帮助。

我们需要的是范围内的X和Y偏移[-1; 1]。我们可以通过Field(big)Radius,Filed Center pointMouse 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);
    }
}

我还包括Polar CS评估(评论)。 BTW极地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>