操纵杆控件






3.14/5 (6投票s)
2005年5月31日
3分钟阅读

55662

2665
一个使用简化的极坐标系来返回方向和大小的操纵杆控制,以及一个使用该坐标系的自定义 Vehicle UserControl 的 Offset 方法。
引言
此项目提供了一个操纵杆控制,该控制使用简化的极坐标系(即,仅使用八个最粗略的罗盘点来表示方向 - 北,东北,东,东南等)。它还提供了一个自定义车辆类,该类可以在操纵杆使用的罗盘坐标系中获取偏移量。
背景
该控制使用三角函数来获得操纵杆的角度 - FindOrientation
使用 Math.Atan2(double, double)
来获取操纵杆光线与 x 轴之间的弧度角。但不要害怕 - 操纵杆实际上返回的是下面枚举的一个成员,而不是其方向的角度,并且您可以使用简单的欧几里德几何学将其和大小(鼠标指针与操纵杆中心的径向距离)转换为 x 和 y 偏移量,以馈送到一些(希望)移动的物体。当然,如果我继续生成版本 0.2,返回以弧度表示的方向,您将需要更多的三角函数来处理它。
项目中实际上有两个自定义控件 - Joystick
和 Vehicle
。后者只是一个 UserControl
,它有一个自定义的 Offset
方法,该方法采用方向和大小,而不是运动的常用 x 和 y 分量。一个向量,如果你愿意的话,并且正在思考微积分而不是 STL。
使用代码
如果您想在自己的项目中使用 JoyStickControl
,您将不得不处理它使用的极坐标系。也许我应该在这里提到,该控件是学校作业的结果 - 如果靠我自己,我会使用普通的 x-y 坐标或一个完整的极坐标系,方向以弧度传递。就目前而言,操纵杆包含一个公共枚举
public enum compassPoints
{
north,
northeast,
east,
southeast,
south,
southwest,
west,
northwest
}
它具有可能通过其 MouseMoving
事件传递的所有方向值。在我自己的使用中,我让主窗体订阅了自定义事件;然后窗体调用 Vehicle
的 Offset
方法,该方法处理坐标,如下所示
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 毫秒调用 Vehicle
的 Offset
方法,同时鼠标悬停在 Joystick
上,并且由于 Joystick.MouseMovingEvent
最初连续触发以响应其自身的 MouseMove
事件(每秒触发 870 亿次),可怜的计时器在添加我的筛选代码之前从未有机会做任何事情。