Why won't my dot move?

nmartin
Unregistered
 
Post: #1
I'm teaching myself cocoa, and I'm trying to do some very simple animation.

I've made a few random circles appear in a window. Now I want one of them to move.

In my view, I first initialize the objects, and add a timer:
Code:
- (id)initWithFrame:(NSRect)frame
{
    size_t k;
    SmartObject *obstacle;
    NSColor *color;
    self = [super initWithFrame:frame];
    if (self == nil)
    return nil;
    
    int w = frame.size.width;
    int h = frame.size.height;
    srand(time(NULL));
    obstacles = [[NSMutableArray alloc] initWithCapacity:OBSTACLE_COUNT];
    for (k = 0; k < OBSTACLE_COUNT; k++) {
    color = [NSColor redColor];
    obstacle = [[SmartObject alloc] init];
    [obstacle setRadius:OBSTACLE_RADIUS];
        
        float sx = rand() % w;
        float sy = rand() % h;
        center.x = sx;
        center.y = sy;
        [obstacle setColor:color];
    [obstacle setCenter:center];
    [obstacles addObject:obstacle];
    [obstacle release];
    }

        color = [NSColor blueColor];
    dot = [[SmartDot alloc] init];
    [dot setColor:color];
    [dot setRadius:DOT_RADIUS];
        float sx = rand() % w;
        float sy = rand() % h;
        center.x = sx;
        center.y = sy;
    [dot setCenter:center];
    [self setNeedsDisplay:YES];
    centralTimer = [[NSTimer scheduledTimerWithTimeInterval:0.1
        target:dot
        selector:@selector(move:)
        userInfo:nil
        repeats:YES] retain];
    
    return self;
}

And I also have a drawrect method:
Code:
- (void)drawRect:(NSRect)rect {
    NSRect bounds;
    SmartObject *obstacle;
    size_t k, count;
    CGContextRef context;

    context = [[NSGraphicsContext currentContext] graphicsPort];

    CGContextSetRGBFillColor(context, 0.7, 0.7, 0.9, 1);


    count = [obstacles count];
    for (k = 0; k < count; k++) {
    obstacle = [obstacles objectAtIndex:k];
    bounds = [self boundsForObstacle:obstacle];
    if (NSIntersectsRect(bounds, rect))
        [obstacle draw];
    }
    bounds = [self boundsForObstacle:dot];
    if (NSIntersectsRect(bounds, rect))
    [dot draw];
}

In my "dot" class, I have the method that the timer calls:
Code:
- (void)move:(id)sender
{
   _center.x+=1;
   [view setNeedsDisplay:YES];
}

It displays all the dots correctly, and "move:" is getting called periodically, but it never goes back to drawrect after the first time. I thought the setNeedsDisplay:YES makes drawrect get called again. Am I missing something here?

Thanks!
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #2
Well, yes and no. The way I understand it, setNeedsDisplay just tells Cocoa that the view will be redrawn when the entire view tree is redrawn.. (I'm not a 100% on this, so YMMV!) You will also need to call [MyView display] to actually start the redraw process.

In reality, I don't think you will need to call setNeedsDisplay, but try with different combinations and see what works out for you. Just remember to call [MyView display] from within your timer callback.
Quote this message in a reply
Member
Posts: 304
Joined: 2002.04
Post: #3
if you make a code example the we can d/l Im sure we can figure it out. However by looking at your sample code I wonder:

Does the dot object know what view it is in. In the line "[view setNeedsDisplay:YES];" is view set correctly.

The timer should really fire to a method in your view (actually it should fire to a method in your game's model object - but Im not going to get religious here). Then the game's model object should send move messages to all of the objects in the game. Because when you start adding more animation - you dont want to have a timer for each and every object that moves on screen.

Try forcing the window to redraw - minimize it and then restore it - or resize the window. When the window refreshes has the dot moved?

hth,
Codemattic
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #4
[view setNeedsDisplay:YES] effectively forces a redraw in the next event loop iteration (a few handful milliseconds, typically), but gives the OS enough time to make optimizations to the update process. It is the recommended way to update a view. And for me, it has always worked.

Are you setting the view member of your object? A call to [nil someMethod] will not generate any error.
Quote this message in a reply
nmartin
Unregistered
 
Post: #5
Thanks for the help so far.

I'm pretty sure that I've set up the connection to "view" correctly. I added it as an outlet, and connected it in interface builder.

I've changed the code so that the timer calls method in the view, and that calls [dot move].

I've tried both [view display] and [view setNeedsDisplay:YES]. If I resize or minimize the window, then the dot does move. It's just not updating on its own.

I've uploaded the code to http://www.lumiere.net/~nat/SmartDot.zip

(I'm making a simple AI/obstacle avoidance program. First step is to get the darn thing to move though! Smile )

Thanks!
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #6
As I don't have XCode, I haven't tried to compile your program, but I think I know what's wrong.

The view outlet isn't set for your dot. If you have one hooked up from IB, then that's got the view set alright, but the dot you move around in your program is another one.

Here, you create another dot than the one made from IB:

Code:
dot = [[SmartDot alloc] init];
[dot setColor:color];
[dot setRadius:DOT_RADIUS];
float sx = rand() % w;
float sy = rand() % h;
center.x = sx;
center.y = sy;
[dot setCenter:center];


IMHO, this is the way you should do it. However, that dot never gets it's view set. (These connections are made on a per-instance basis, not a per-class basis)

Now, there are two ways you could go:
ï Just add a setView method to your dot, and call [dot setView self]; after the last line above
ï Rework your architecture a little.

I would recommend the latter if you're going to do a lot of work with SmartDot, because your current way is going to put you into a lot of trouble down the road.

I'd recommend you to do the following:

ï Remove everything about a dot from the nib file
ï Remove the view outlet from the dot
ï Remove anything from the [SmartDot move] method that isn't related to AI and movement.
ï Let the view handle all the drawing (which you already do)
ï From the [SmartDotView doAnimation] method, call [self setNeedsDisplay:YES] after [dot move]

This will make everything a lot easier to you. You have started out really good with your object hierarchy, deriving SmartDot off of SmartObject. Otherwise than this little rearrangement, I think you're doing pretty good! Smile
Quote this message in a reply
nmartin
Unregistered
 
Post: #7
I took your suggestions, and it works great! I added another NSRect so that it won't leave a trail. This is a much better way to do it, this way if I have multiple objects moving, it'll only call setNeedsDisplay once.

Thanks! I'll bump this if I get obstacle avoidance working.
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #8
Good to hear! Keep moving, and I'll watch this post. Wink
Quote this message in a reply
nmartin
Unregistered
 
Post: #9
Cool, I got some basic collision avoidance working. Right now the program displays 25 randomly placed red dots ("obstacles"), a white dot ("the goal") and two blue dots ("smart dots.")

The smart dots should move over to the goal without colliding with any of the obstacles.

Generally it works well. Most times I run it, it will work correctly, or at least close.

Current problems:
The goal and smart dots sometimes appear inside an obstacle.

The smart dots can get stuck.

The smart dots won't reach the goal if the goal is very close to an obstacle.

The obstacles are using rectangular collision masks, and it's very obvious sometimes.

Still, I'm pretty happy with it. Here's the app if you want to see it: http://www.lumiere.net/~nat/SmartDot.app.zip

If you want to see any of the code, let me know.

Thanks for the help, again!
Quote this message in a reply
Entropy
Unregistered
 
Post: #10
cant run the app if its compiled as zero_link Wink
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #11
Quote:Originally posted by Entropy
cant run the app if its compiled as zero_link Wink

That's because zeroLink is evil. It doesn't recognize multiply defined symbols, for example, and can cause a lot of havoc by not correctly resolving "extern" symbols.
Quote this message in a reply
nmartin
Unregistered
 
Post: #12
Here we go, now with no zero linking!

http://www.lumiere.net/~nat/SmartDot0.1.app.zip


I think I know one way to get the dots to stop getting stuck. I need to put in some anti-backtracking code, and that way it can't oscillate between two pixels.
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #13
Sometimes the dots move straight through the obstacles..?

(See how easy it became to add multiple SmartDots now? Smile
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  simple function to move an object not doing as wanted burrows111 11 4,246 Apr 16, 2010 11:33 PM
Last Post: StealthyCoin
  My pongball won't move!! clapton541 1 1,889 Jan 13, 2007 10:27 AM
Last Post: clapton541