Problem With Motion in NSView

Sage
Posts: 1,066
Joined: 2004.07
Post: #1
I'm trying to get a sprite to be controlled by keyboard input. The input finally works (only took me a half hour to get) but now it draws it in a really odd fashion where it only flashes the character every once in a while. Anyone know why this might be occuring? It it'll help, I can post the .app on my site for download but I'm hoping that won't be necessary. Here's the code that I suppose matters. I only have the MyView.h and MyView.m so that's where all of this is located. There's only the view in my app so there isn't much else needing to be elaborated on.
Code:
- (id)initWithFrame:(NSRect)frameRect
{
    if ((self = [super initWithFrame:frameRect]) != nil)
    {
        // Add initialization code here
        spriteLoc = NSMakePoint(0,0);
        sprite = [NSImage imageNamed:@"sprite.png"];
    }
    return self;
}

- (void)awakeFromNib
{
    [[self window] makeFirstResponder:self];
    controlTimer = [[NSTimer scheduledTimerWithTimeInterval:.001
                                                     target:self
                                                   selector:@selector(checkKeys)
                                                   userInfo:nil
                                                    repeats:YES] retain];
    [[NSRunLoop currentRunLoop] addTimer:controlTimer
                                 forMode:NSEventTrackingRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer:controlTimer
                                 forMode:NSModalPanelRunLoopMode];
}
Code:
- (void)drawRect:(NSRect)rect
{
    [[NSColor blueColor] set];
    NSRectFill([self bounds]);
    NSRect    myRect;
    myRect.size = [sprite size];
    [sprite drawAtPoint:spriteLoc
               fromRect:myRect
              operation:NSCompositeSourceOver
               fraction:1];
}
Code:
- (void)checkKeys
{
    if(up)
        spriteLoc.y++;
    if(down)
        spriteLoc.y--;
    if(left)
        spriteLoc.x--;
    if(right)
        spriteLoc.x++;
    [self setNeedsDisplay:YES];
}
This last one is just the keyDown: and keyUp: methods but maybe they have something to do with it:
Code:
- (void)keyDown:(NSEvent*)theEvent
{
    unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
    if(key == NSUpArrowFunctionKey || key == 'w' || key == 'W')
    {
        up = YES;
        return;
    }
    if(key == NSDownArrowFunctionKey || key == 's' || key == 'S')
    {
        down = YES;
        return;
    }
    if(key == NSLeftArrowFunctionKey || key == 'a' || key == 'A')
    {
        left = YES;
        return;
    }
    if(key == NSRightArrowFunctionKey || key == 'd' || key == 'D')
    {
        right = YES;
        return;
    }
}

- (void)keyUp:(NSEvent*)theEvent
{
    unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
    if(key == NSUpArrowFunctionKey || key == 'w' || key == 'W')
    {
        up = NO;
        return;
    }
    if(key == NSDownArrowFunctionKey || key == 's' || key == 'S')
    {
        down = NO;
        return;
    }
    if(key == NSLeftArrowFunctionKey || key == 'a' || key == 'A')
    {
        left = NO;
        return;
    }
    if(key == NSRightArrowFunctionKey || key == 'd' || key == 'D')
    {
        right = NO;
        return;
    }
}
Quote this message in a reply
Member
Posts: 153
Joined: 2004.12
Post: #2
Is your timer firing at the correct interval?

There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #3
Is there a good way to check that? Is that the problem, you think?
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #4
Asking for a timer that fires 1000 times per second is pretty excessive. I've never seen a need for one that goes any faster than 120 times per second. It's an easy thing to try; increase your timer interval and see if the problem goes away.

- Alex Diener
Quote this message in a reply
Member
Posts: 208
Joined: 2005.04
Post: #5
most hollywood movies (film) are shot at 24 FPS, while TV (NTSC) is 29.97 FPS. So ~30 FPS should do you just fine. In other words, try setting your timer interval around 0.033.
Quote this message in a reply
Moderator
Posts: 771
Joined: 2003.04
Post: #6
Andrew Wrote:most hollywood movies (film) are shot at 24 FPS, while TV (NTSC) is 29.97 FPS. So ~30 FPS should do you just fine. In other words, try setting your timer interval around 0.033.

But both movies & TV have motion blur. You need higher frame rates to fake smooth motion on a game, 60 is the maximum FPS you can get with Apple's LCDs, but CRTs & LCDs from other vendors can have higher refresh rates.

Also, it would probably be a good idea to use frame rate independent animation (basically, dont just ++ or -- the x or y position of your sprite, use some constant value multiplied by the time elapsed since the last frame, that is, the difference between the current time and the previous frame time).
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #7
Well I'm sure I'll do the frame-rate independent stuff later, I was just trying to get the keys and everything to work.

I tried increasing the timer to .015 and .1 but neither fixed the problem. I'm going to post the app on my site and I'll return with a URL.
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #8
Quote this message in a reply
Member
Posts: 153
Joined: 2004.12
Post: #9
Weird... Could you post the project folder?

There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #10
Quote this message in a reply
Apprentice
Posts: 9
Joined: 2005.05
Post: #11
Hey Nick,

This seemed to fix the issue for me.

Code:
- (void)drawRect:(NSRect)rect
{
    [[NSColor blueColor] set];
    NSRectFill([self bounds]);
    //NSRect    myRect;
    //myRect.size = [sprite size];
    [sprite drawAtPoint:spriteLoc
        fromRect:NSZeroRect
        operation:NSCompositeSourceOver
        fraction:1];
}


All the best,

Lyndon.
Quote this message in a reply
Moderator
Posts: 771
Joined: 2003.04
Post: #12
Try adding this to drawRect:
Code:
    myRect.origin.x = 0.0f;
    myRect.origin.y = 0.0f;


Also, probably a good idea to add this to initWithFrame:
Code:
up = down = left = right = NO;

Edit: Shaddy beat me to this post... His solution for drawRect is better Wink
Quote this message in a reply
Sage
Posts: 1,066
Joined: 2004.07
Post: #13
I went with PowerMac's suggestion and it worked perfectly. Thanks a ton.

On a related note, how would I do the frame-rate independent motion in Cocoa? I did it once in SDL but that was with some SDL function for getting the time.
Quote this message in a reply
Member
Posts: 208
Joined: 2005.04
Post: #14
Quote:Also, probably a good idea to add this to initWithFrame:
Code:
up = down = left = right = NO;

This is unnecessary since up, down, etc. are instance variables. In objective-C, they automatically are initialized to zero.
Quote this message in a reply
Member
Posts: 208
Joined: 2005.04
Post: #15
Nick Wrote:how would I do the frame-rate independent motion in Cocoa?

I think PowerMacX already answered your question Wink

PowerMacX Wrote:use some constant value multiplied by the time elapsed since the last frame, that is, the difference between the current time and the previous frame time).
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Motion Sensor Query for 3D game iphone-query 1 4,604 Sep 2, 2011 09:26 PM
Last Post: PoseMotion
  Device motion detection using Acceleration data. RockyDilse 4 8,512 Feb 16, 2011 12:57 AM
Last Post: RockyDilse
  Mouse Events in NSView Chandhu 1 4,194 Feb 28, 2008 02:08 AM
Last Post: kuon_
  NSView loaded from bundle won't draw... Joseph Duchesne 1 3,563 Feb 12, 2006 03:33 AM
Last Post: Cochrane
  NSImage and NSView problems Joseph Duchesne 1 3,959 Aug 19, 2005 07:36 AM
Last Post: unknown