65.9K
CodeProject 正在变化。 阅读更多。
Home

操纵杆控件

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.14/5 (6投票s)

2005年5月31日

3分钟阅读

viewsIcon

55662

downloadIcon

2665

一个使用简化的极坐标系来返回方向和大小的操纵杆控制,以及一个使用该坐标系的自定义 Vehicle UserControl 的 Offset 方法。

引言

此项目提供了一个操纵杆控制,该控制使用简化的极坐标系(即,仅使用八个最粗略的罗盘点来表示方向 - 北,东北,东,东南等)。它还提供了一个自定义车辆类,该类可以在操纵杆使用的罗盘坐标系中获取偏移量。

背景

该控制使用三角函数来获得操纵杆的角度 - FindOrientation 使用 Math.Atan2(double, double) 来获取操纵杆光线与 x 轴之间的弧度角。但不要害怕 - 操纵杆实际上返回的是下面枚举的一个成员,而不是其方向的角度,并且您可以使用简单的欧几里德几何学将其和大小(鼠标指针与操纵杆中心的径向距离)转换为 x 和 y 偏移量,以馈送到一些(希望)移动的物体。当然,如果我继续生成版本 0.2,返回以弧度表示的方向,您将需要更多的三角函数来处理它。

项目中实际上有两个自定义控件 - JoystickVehicle。后者只是一个 UserControl,它有一个自定义的 Offset 方法,该方法采用方向和大小,而不是运动的常用 x 和 y 分量。一个向量,如果你愿意的话,并且正在思考微积分而不是 STL。

使用代码

如果您想在自己的项目中使用 JoyStickControl,您将不得不处理它使用的极坐标系。也许我应该在这里提到,该控件是学校作业的结果 - 如果靠我自己,我会使用普通的 x-y 坐标或一个完整的极坐标系,方向以弧度传递。就目前而言,操纵杆包含一个公共枚举

public enum compassPoints
{
    north,
    northeast,
    east,
    southeast,
    south,
    southwest,
    west,
    northwest
}

它具有可能通过其 MouseMoving 事件传递的所有方向值。在我自己的使用中,我让主窗体订阅了自定义事件;然后窗体调用 VehicleOffset 方法,该方法处理坐标,如下所示

public void Offset(Joystick.compassPoints orientation, int magnitude)
{
    this.orientation = orientation;
    this.magnitude = (double)magnitude;
    switch (orientation)
    {
        case Joystick.compassPoints.north :
            this.Location = new Point(this.Location.X, 
                            this.Location.Y - (int)this.magnitude);
            break;
        case Joystick.compassPoints.northeast :
            this.Location = new Point(this.Location.X + 
               ((int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0)), 
               this.Location.Y + 
               (int)-Math.Round((Math.Sqrt((Math.Pow(this.magnitude, 2))/2)), 0));
            break;
        case Joystick.compassPoints.east :
            this.Location = 
                 new Point(this.Location.X + magnitude, this.Location.Y);
            break;
        case Joystick.compassPoints.southeast :
            this.Location = new Point(this.Location.X + 
                 (int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0), 
                 this.Location.Y + 
                 (int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2)/2)), 0));
            break;
        case Joystick.compassPoints.south :
            this.Location = 
                 new Point(this.Location.X, this.Location.Y + (magnitude));
            break;
        case Joystick.compassPoints.southwest :
            this.Location = new Point(this.Location.X + 
                 (int)-Math.Round(Math.Sqrt(Math.Pow(this.magnitude, 2)/2), 0), 
                 this.Location.Y + 
                 (int)Math.Round((Math.Sqrt((Math.Pow(this.magnitude, 2))/2)), 0));
            break;
        case Joystick.compassPoints.west :
            this.Location = 
                 new Point(this.Location.X - (magnitude), this.Location.Y);
            break;
        case Joystick.compassPoints.northwest :
            this.Location = new Point(this.Location.X + 
                 (int)-Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0), 
                 this.Location.Y + 
                 (int)-Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0));
            break;
        default :
            break;
    }
}

换句话说,我们只使用罗盘点和一点简单的欧几里德几何学,(对边)2 + (邻边)2 = (斜边)2,以获得一个新的位置。这本身很简单,尽管我需要大量打字。出于精确性的原因,我使用 double 进行所有计算,然后在将它们转换为 int 之前仔细地四舍五入,因为转换否则只会去掉小数点,而不会四舍五入,从而产生草率的结果。

关注点

在处理这个问题的过程中,我发现 Joystick.MouseMove 事件在鼠标悬停在控件上时会持续触发。我不知道这是否是 Microsoft 的本意,或者我是否找到了一个错误。MSDN 简单地说“当鼠标指针在控件上移动时发生”,这听起来应该意味着它要么在鼠标移动到控件上方,要么进入控件时触发(但这当然是多余的,因为控件已经有了一个 MouseEnter 事件),或者它在鼠标在控件上移动时触发 - 在控件内移动时触发。相反,它只是在鼠标在控件内部时持续触发。我最终让 Joystick 通过检查鼠标是否实际移动了来过滤掉噪声,然后再触发其自定义 MouseMovingEvent,窗体会监听该事件。Vehicle 的移动由主窗体上的一个计时器控制,窗体每 18 毫秒调用 VehicleOffset 方法,同时鼠标悬停在 Joystick 上,并且由于 Joystick.MouseMovingEvent 最初连续触发以响应其自身的 MouseMove 事件(每秒触发 870 亿次),可怜的计时器在添加我的筛选代码之前从未有机会做任何事情。

© . All rights reserved.