views:

229

answers:

4

Hi all, Let me see if I can articulate what I'm trying to do... I'll distill it to the core issue.

I have an objective-c program and a c callback function. The context is I am using cocos2d and chipmunk physics with the iphone sdk. What I am trying to do is update "score" upon a collision between a bullet and a monster. Everything works fine except for updating the score.

the game logic is in obj-c but the actual collision logic is done in a c function that gets called when things collide. For example, it is there that I render the flames/explosion of the collision. It is also there that I want to update the score.

Here's the problem: But score is an instance variable of the "GAME" object and I do not have visibility into that instance variable. I suppose I can pass into the c function another parameter by reference called &addscore or something, but was wondering if there is a more obvious way to do it since there might be a bunch of other variables beyond 'score' that must be updated upon the collission.

the net is I need variables in the object to be seen by the c function, and vice versa: those variables set in the function to be seen in the object.

thanks in advance.

+1  A: 

Your knowledge and thoughts about gaining access to Obj-C instance variables within C code by passing them by reference seems correct.

If you need to pass a number of variables I assume they collectively make up some kind of "game state" description. Perhaps these could be grouped into a seperate object or structure and passed into the C function via a single parameter?

As an example, the following class:

typedef struct GameState {
  int score;
  int numberOfBulletsLeft;
} GameState;

@interface GAME : NSObject {
  GameState state;
}

...

@end

Would work well with a C function such as the following

void myCFunctionThatDoesCollisiondetectionEtc(GameState * state) {
  NSLog(@"You have %d bullets left", state->numberOfBulletsLeft);
}

If your existing GAME object has suitable properties or messages, you may even be able to do something like the following, where you simply pass your entire Objective-C object:

void myCFunctionThatDoesCollisionDetectionEtc(GAME * myGame) {
  if (...)
    [myGame increaseScore:50];
  else
    [myGame decreaseScore:50];
}

A third alternative would be to change the return value of your C function to indicate if a collision has been detected and then leave it up to the Objective-C code within the GAME class to update the score and any other actions which may need to occur.

Christopher Fairbairn
Thank you very much Christopher. I ended up doing option #2. It's a cool pattern, to be able to access through simple pointers entire obj-c objects and access them inside C. So many uses. I would also assume that you can pass generic void pointers and cast them within the function to make the use case more general. Cheers.
Ferris
A: 

No, you do not need variables in your object to be visible by the function. That breaks encapsulation, and it's a bad idea. When you want an object to do something (like changing some internal value such as the score) you should provide a method to accomplish that effect.

If you allow code outside your class to alter your instance variables, then you're throwing away a key advantage of object-oriented programming.

NSResponder
yes actually that's what i had done... once retrieved the object pointer, i built accessor methods to perform my internal variable updates using standard obj-c. thanks for the followup.
Ferris
A: 

If the game object is a singleton you can access it from any scope (which includes your C callback). You would make a singleton if you only ever want one instance of some object to exist. A singleton can always be reached from anywhere by sending a message to the class to retrieve the singleton instance:

[Game sharedGameInstance];

Alternatively, chipmunk allows for you to pass a void * data to the callback. This is to accommodate the programmer to send information he needs to the callback.

You can send a pointer to your game object instance in that void * to the callback, like so:

cpSpaceAddCollisionPairFunc( space, 1, 2, &functionName, (void *)game );

void functionName( cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data ) {
    GameClass * game = (GameClass *)data;
    // do whatever you need here. You can call messages on game as per usual.
}
nash
+1  A: 

To improve on that last answer a bit more, you can do the following:

cpSpaceAddCollisionPairFunc( space, 1, 2, (cpCollFunc)functionName, game);

void functionName( cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, GameClass *game) {
    [game doSomethingToScoreSomething];
}

No need to cast the pointer inside of the function, you can cast the function pointer instead. That's what I've done internally with the Chipmunk source and it's a lot cleaner.

slembcke