如何在Cocoa中处理同时和重叠的按键

时间:2014-12-13 12:30:37

标签: cocoa event-handling game-engine

起初,Cocoa游戏的键盘事件处理似乎很简单:我添加了-acceptsFirstResponder和-becomeFirstResponder覆盖到我的自定义游戏地图视图,然后覆盖-moveUp:, - moveDown:, - moveLeft:和-moveRight:处理箭头键。

但是对于大多数其他游戏来说有一个很大的不同:它一次只能接受一个按键。因此,如果我按住向上箭头键让我的角色向前跑,那么快速按右箭头键来回避和障碍,我的角色会停在其轨道上,好像我已经释放向上箭头键。

这对于文本输入是有意义的,其中一个人可能意外地仍然按住一个角色而另一个手指按下下一个角色,但是对于游戏来说这很烦人。

如何将任意组合键组合在一起?

1 个答案:

答案 0 :(得分:1)

解决方案是跟踪自己关键的内容。覆盖-keyDown-keyUp以跟踪正在按下哪些键。我正在使用C ++ unordered_set,但Objective-C NSIndexSet也能正常工作:

@interface ICGMapView : NSView
{
    std::unordered_set  pressedKeys;
}

@end

并在实施中:

-(void) keyDown:(NSEvent *)theEvent
{
    NSString    *   pressedKeyString = theEvent.charactersIgnoringModifiers;
    unichar         pressedKey = (pressedKeyString.length > 0) ? [pressedKeyString characterAtIndex: 0] : 0;
    if( pressedKey )
        pressedKeys.insert( pressedKey );
}


-(void) keyUp:(NSEvent *)theEvent
{
    NSString    *   pressedKeyString = theEvent.charactersIgnoringModifiers;
    unichar         pressedKey = (pressedKeyString.length > 0) ? [pressedKeyString characterAtIndex: 0] : 0;
    if( pressedKey )
    {
        auto foundKey = pressedKeys.find( pressedKey );
        if( foundKey != pressedKeys.end() )
            pressedKeys.erase(foundKey);
    }
}

然后在你的班级中添加一个NSTimer,定期检查是否有任何按键被按下,如果是,则对它们做出反应:

-(void) dispatchPressedKeys: (NSTimer*)sender
{
    BOOL    shiftKeyDown = pressedKeys.find(ICGShiftFunctionKey) != pressedKeys.end();
    for( unichar pressedKey : pressedKeys )
    {
        switch( pressedKey )
        {
            case 'w':
                [self moveUp: self fast: shiftKeyDown];
                break;
            ...
        }
    }
}

由于您的计时器在此处以一定间隔进行轮询,并且您无法使该间隔过快,因为它是发送密钥重复的速率,理论上可能会丢失持续时间短于您的密钥的按键定时器间隔。为了避免这种情况,您可以将结构存储在数组中,而不仅仅是集合中的按键。这个结构会记住最初按下按键的时间,以及发送最后一个按键事件的时间。

这样,当用户开始按住某个键时,您会立即触发该键的处理,并记下该键何时发生。从那时起,您的-dispatchPressedKeys:方法将检查自上次处理该特定密钥以来它是否足够长,并且会为每个到期密钥发送密钥重复。作为奖励,当一个密钥被释放时,你也可以通知自己。