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



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

但与大多数其他游戏有一个很大的区别:它一次只接受一次按键。因此,如果我按住向上箭头键让我的角色向前奔跑,然后快速按向右箭头键避开和障碍物,我的角色将停止前进,就像我释放了向上箭头键一样。

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

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

解决方案是自己跟踪哪个键关闭。覆盖-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:方法将检查自上次处理该特定密钥以来它是否足够长,并将为每个到期密钥发送密钥重复。作为奖励,当释放密钥时,您也可以通知自己。

相关内容

  • 没有找到相关文章

最新更新