防止鼠标离开我的窗体

7
我给我的程序添加了一些MouseMove和MouseClick事件,现在需要解决以下问题:
1. 获取"全局"鼠标移动,以便我可以在窗体外读取鼠标位置。 2. 防止鼠标首先离开窗体。
我的项目是一个游戏,所以像大多数其他游戏一样防止鼠标离开我的窗体(当然,如果您使用alt + tab在不同窗口之间切换时,可以将其移出)。查看要求全局鼠标移动的其他问题的答案,它们似乎对我的需求过于复杂。是否有一种简单的方法来防止鼠标超出窗体边界?或者实际上在第一次跨越边界之前就防止它穿过边界,使鼠标保持在客户区域内。
关于游戏的其他信息: 这个游戏是一个短暂的生存游戏,持续5-30秒钟(在30秒后变得太难存活下去),你必须用鼠标躲避子弹。当你将鼠标移出窗体时,播放器(附加到鼠标的System.Windows.Forms.Panel)停止移动并立即被子弹击中,这是防止鼠标离开该区域的好方法。

7
作为一位用户,我认为这样做很粗鲁。也许当鼠标不在您的窗口上时,停止游戏会更好? - SJuan76
只是稍微加一点(作为评论,而不是答案),但这种行为会让我非常恼火。事实上,它会经常使我无法玩这样的游戏(因为我通常是通过鼠标点击切换窗口) - Andrew Barber
请阅读我添加的“附加信息”部分,这并不是真正的问题,因为鼠标只会被锁定10-30秒,而且您可以随时自杀。 - user2032433
所以,假设你已经成功让鼠标停留在游戏客户端区域内。现在,假设你的游戏发生了崩溃。哎呀!鼠标现在被卡在屏幕的那个区域里,没有出路了!最好采纳SJuan76的建议,直到鼠标移回客户端区域之前,暂停游戏。 - RobH
当焦点丢失或游戏关闭时,我可以看到它恢复正常,因为限制(和取消限制)鼠标移动的是游戏,但出于同样的原因,如果游戏崩溃,我不知道鼠标移动如何恢复正常。操作系统不会仅仅因为游戏不再运行就取消限制鼠标移动。它只知道某个程序告诉它限制鼠标移动,因此期望同一个程序将其设置回正常状态。其他游戏这样做并不代表这是正确的做法。 - RobH
显示剩余3条评论
5个回答

5
晚了些,但可能会有用。您可以订阅表单的MouseLeaveMouseMove事件,并像这样处理它们:
    private int X = 0;
    private int Y = 0;

    private void Form1_MouseLeave(object sender, EventArgs e)
    {
        Cursor.Position = new Point(X, Y);
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (Cursor.Position.X < this.Bounds.X + 50 )
            X = Cursor.Position.X + 20;
        else
            X = Cursor.Position.X - 20;

        if (Cursor.Position.Y < this.Bounds.Y + 50)
            Y = Cursor.Position.Y + 20;
        else
            Y = Cursor.Position.Y - 20;           
    }

上述代码将确保鼠标光标永远不会超出表单范围。请确保在游戏结束时取消订阅事件。
编辑: Hans Passants的答案比我的答案更有意义。在MouseEnter上使用Cursor.Clip
private void Form1_MouseEnter(object sender, EventArgs e)
    {
        Cursor.Clip = this.Bounds;
    }

你可以在出现任何错误/崩溃的情况下释放光标(我相信你能捕捉到它们):
Cursor.Clip = Rectangle.Empty;

这么简单,可为什么大家都在抱怨呢?哇,谢谢 :P - user2032433
确保在游戏结束时取消订阅事件。如果您的程序意外崩溃,它将无法取消订阅事件,这会导致问题。这就是所有抱怨的原因。 - RobH
我非常困惑为什么在2013年这个问题引起了如此多的负面意见。鼠标捕获被几个虚拟机接口使用,如果需要,它非常有用,可以使鼠标集中在虚拟操作系统上。关键词在于“如果需要”。 - CrazyIvan1974

4

你不能限制鼠标,这会影响用户操作开始菜单等功能。最接近的方法是分配Cursor.Clip属性。但是这很容易被用户通过按Ctrl+Esc等快捷键轻松地打败,而且没有任何通知。

最好的方法是订阅表单的Deactivated事件,它可靠地告诉您用户是否切换到了另一个程序。Activated事件将告诉您用户何时返回。当然,当游戏得分依赖于保持游戏对象运动时,用户将很少有实际原因这样做。因此,请不要忘记为用户提供一种方便的方法来暂停游戏,例如使用Escape键。


2

我不知道你的确切问题的解决方案,但我有一个完全不同的想法。根据你告诉我的内容,为什么不让游戏变得更难一些呢:给游戏区域添加边框,例如4像素宽的矩形,你不能碰到它们。如果你碰到了它们,你就会死亡并释放鼠标。


1
其他的答案并没有真正让我感到安慰,而这个想法实际上非常好。之前没有想到过,谢谢! :) - user2032433

1
您可以使用Cursor类。例如:
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;

关于防止用户将鼠标移动到表单外面的问题,最好的方法可能是知道表单在屏幕上的坐标,并附加一个 MouseMove 事件,检查鼠标是否在表单矩形内。
要了解表单在屏幕上的位置,请参考 this 问题。

0

出于两个原因,我不建议使用全局鼠标移动控制。

  • 这是糟糕的设计,你应该尊重操作系统的边界。如果您想要这种行为,请将应用程序全屏。唯一应执行此类操作的应用程序是“kiosk”模式应用程序,它们锁定整个操作系统(以防止操作员滥用)。

  • 全局键钩子很混乱,不能保证工作,并且很危险,因为它们影响操作系统的关键部分(所有控件)。您代码中的一个错误可能导致需要重新启动计算机。

话虽如此,上次我检查时(很久以前,在Vista上),SetWindowsHookEx 仍然可以使用(但我记得它没有官方支持),这是一个非托管调用,所以您必须进行pinvoke,但是您可以拒绝传递会将鼠标移动到应用程序范围之外的消息。我不确定操作系统是否会允许您控制光标(我只在桌面盒子上阻止过键盘),但这可能是您最好的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接