views:

115

answers:

2

I have used 4 NSButtons and assigned them to the 4 arrow keys separately to move in four different directions. Now I want to use two keystrokes, left arrow and up arrow together simultaneously, for north east movement, how can I do it?

I am only able to use one keystroke at a time , I need to catch two key strokes simultaneously, I need it for my game project.

A: 

You might want to use a different approach for that, such as an NSView that intercepts the keys directly; rather than using separate NSButtons. Just a thought. (The event for this is keyDown, btw.)

Edit: You might also want to look into learning to crawl before you can walk; before you delve straight into the game programming, you should probably familiarize yourself with the responder chain, NSEvent and friends, and NSResponder, specifically NSView.

To be even more specific, you need to subclass NSView (the subclass you are using to show your game tableau should do nicely) and override

- (void)keyDown:(NSEvent *)theEvent
- (void)keyUp:(NSEvent *)theEvent

These will let you track key presses in a far more sophisticated manner.

You will find all the documentation you need for this in the Xcode Help menu.

Williham Totland
+1  A: 

I agree with Williham: Why would you use NSButtons to capture key events? This is pretty unusual. You capture key events by actually "capturing" them. E.g. by subclassing NSView.

Please exclude the code below being very compact, but I don't want to waste more space than necessary:

#import <Cocoa/Cocoa.h>

enum {
    upKeyPressed    = 1,
    leftKeyPressed  = 2,
    downKeyPressed  = 4,
    rightKeyPressed = 8
};

@interface MyView : NSView
{
    uint32_t pressedKeys_;
}
@end

@implementation MyView

- (void)makeMyWindowKey:(NSTimer *)timer
{
    // Force app to foreground
    [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
    // Make window key and display on screen
    [[timer userInfo] makeKeyAndOrderFront:self];
}

- (BOOL)acceptsFirstResponder
{
    // YES, we want to be first responder
    return YES;
}

- (void)printPressedKeys
{
    printf("Keys currently pressed: ");
    if (!pressedKeys_) {
        printf("NONE");
    } else {
        if (pressedKeys_ & upKeyPressed) {
            printf("UP ");
        }
        if (pressedKeys_ & downKeyPressed) {
            printf("DOWN ");
        }
        if (pressedKeys_ & leftKeyPressed) {
            printf("LEFT ");
        }
        if (pressedKeys_ & rightKeyPressed) {
            printf("RIGHT ");
        }
    }
    printf("\n");
}

- (void)keyUp:(NSEvent *)anEvent;
{
    BOOL printPressedKeys = YES;    
    switch ([anEvent keyCode]) {
        case 126: // UP
        pressedKeys_ &= ~upKeyPressed;
        break;

        case 123: // LEFT
        pressedKeys_ &= ~leftKeyPressed;
        break;

        case 125: // DOWN
        pressedKeys_ &= ~downKeyPressed;
        break;

        case 124: // RIGHT
        pressedKeys_ &= ~rightKeyPressed;
        break;

        default:
        // Ignore this key
        printPressedKeys = NO;
    }
    if (printPressedKeys) {
        [self printPressedKeys];
    }
}

- (void)keyDown:(NSEvent *)anEvent;
{
    BOOL printPressedKeys = YES;
    switch ([anEvent keyCode]) {
        case 126: // UP
        pressedKeys_ |= upKeyPressed;
        break;

        case 123: // LEFT
        pressedKeys_ |= leftKeyPressed;
        break;

        case 125: // DOWN
        pressedKeys_ |= downKeyPressed;
        break;

        case 124: // RIGHT
        pressedKeys_ |= rightKeyPressed;
        break;

        case 53: // ESC
        // QUIT
        [[NSApplication sharedApplication] terminate:self];
        // Fallthrough

        default:
        printPressedKeys = NO;
    }

    if (printPressedKeys) {
        [self printPressedKeys];
    }
}
@end

int main (
    int argc,
    char ** argv
) {
    // We need a pool, since we won't call NSApplicationMain
    NSAutoreleasePool * pool = [NSAutoreleasePool new];
    // W/o calling NSApplicationMain, we must do this before drawing UI
    [NSApplication sharedApplication];

    NSWindow * win = [[NSWindow alloc]
        initWithContentRect:NSMakeRect(100, 100, 500, 500)
        styleMask:NSTitledWindowMask | NSClosableWindowMask
        backing:NSBackingStoreBuffered
        defer:YES
    ];
    MyView * mv = [[MyView alloc] init];
    [win setContentView:mv];
    // Retained by window anyway
    [mv release];

    // Since we are no real UI application by now and since we have
    // no Info.plist, we must programmatically become a UI process,
    // which is in fact possible
    ProcessSerialNumber myProcess = { 0, kCurrentProcess };
    TransformProcessType(
        &myProcess,
        kProcessTransformToForegroundApplication
    );

    // The window we just created is our main window
    [win becomeMainWindow];

    // Our view is the first responder
    [win setInitialFirstResponder:[win contentView]];

    // We want the window to become key as soon as main runloop is running
    [NSTimer
        scheduledTimerWithTimeInterval:0.1
        target:mv
        selector:@selector(makeMyWindowKey:)
        userInfo:win
        repeats:NO
    ];

    [[NSApplication sharedApplication] run];

    // We should never get here

    // Remove window from screen and discard
    [win orderOut:nil];
    [win release];

    [pool release];
    printf("Quitting\n");
    return 0;
}

Store in test.m, compile using

gcc -framework Cocoa -o test test.m 

run using

./test

Monitor the output in Terminal while pressing arrow keys, quit by pressing ESC key. As you can see, it will print UP LEFT if both, up arrow and left arrow key are pressed and it will say DOWN RIGHT if down and right arrow key are pressed.

Mecki
+1 for a nice demonstration of a non-nib application.
Rob Keniger