Objective-C game development: communication between components - objective-c

I am using Cocos2d game development framework for iPhone.
Let's focus on the battle scene:
The battle scene has as children: battlers layer, HUD layer, menu layer, background layer, etc...
Sometimes, it is necessary that my battlers layer "contacts" my HUD layer (like call a function within it).
I find this hard. Basically, my battlers layer needs some kind of.. instance or reference of the HUD layer in order to call a function within it, right? But I don't know how to have such thing.
Currently, this is what I do:
The battlers layer will run a function in the scene (its parent), and, inside such function, I will "locate" the HUD layer child, and call the function I need in it.
Now, that is kind of inconvenient. What would you do in such situation?

It sounds to me that you may have over-designed this. What I might do is something like this.
I'd have an IScene. Each of my scene classes implements this IScene. IScene has a property called "HUD", another called "Menu", etc.
The current IScene is set into a global static instance ::CurrentScene
When the current scene needs to contact the menu, I say: ::CurrentScene->Menu->SomeFuncion().
Would that work for you?

I'd suggest you take a look at Cocoa's NSNotificationCenter and related classes. Apple has a guide to the subject here.
It might work something like this.
In your HUD layer, you subscribe to notifications with the name #"battleLayerStuff":
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doThisWhenSomethingHappens:)
name:#"battleLayerStuff"
object:nil];
And in your battle layer, when something happens, you post a notification with the same name:
[[NSNotificationCenter defaultCenter] postNotificationName:#"battleLayerStuff"
object:battleObject];
The object part is optional, but can be helpful if you want to send more information than just "something happened".
If you want to extract information from the object you send you do this in the doThisWhenSomethingHappens: method:
- (void)doThisWhenSomethingHappens:(NSNotification *)notification
{
BattleObject *battleObject = (BattleObject *) notification.object;
// Do stuff with object
}

You could use NSNotification Center. This allows you to send messages in one object and have multiple other objects react to them.
// The object that wants to receive the message registers with NSNotificationcenter
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(receiveScoreUpdateEvent:)
name:#"scoreUpdateEvent"
object:nil ];
In the same object you need to define the method that your selector points to:
- (void)startLocating:(NSNotification *)notification {
NSNumber *scoreObject = [[notification userInfo] objectForKey:#"score"];
// Do something with the new score
}
Another object can then send a message with the updated score at any time and your HUD would react to it:
[[NSNotificationCenter defaultCenter] postNotificationName:#"scoreUpdateEvent"
object:self userInfo:[NSNumber numberWithInt:5345] forKey:#"score"]];

Related

Load Title ViewController When applicationDidBecomeActive:

I've created an app that has two viewcontrollers. The app opens to a title screen (general UIViewController titled 'Title') with a segue connection to the second view that is a custom class (OSViewController titled 'MapView'). As it is, the app suspends when entered into the background state so it opens right where you left off which is typically in MapView.
I want to know what I need to do to have the app start at the title screen when it becomes active. Preferably, I'd like it to open to the title screen if it is inactive for more than 1 minute. From what I've been reading, it seems like I would make a call in applicationDidBecomeActive: method in my AppDelegate to code this in. Please provide me the code to put in the applicationDidBecomeActive: method (if that's the right place to put it) that will reopen my app to the title screen when transitioning from the inactive state to the active state. My app is almost finished but I'd like to fix this issue and I don't have a lot of experience dealing with app states. Thanks in advance for your time.
If you need more information just ask.
You can also register a class as an observer of the "didBecomeActive" notification. You should place this in the viewDidLoad or the init method of your class.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
In this case, willBecomeActive: is a method that you have defined in your class that get's called when the app becomes active again. That might look something like this:
- (void)willBecomeActive:(NSNotification *)notification {
if (self.navigationController.topViewController == self) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
You'll also need to add this in your viewDidUnload method
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
EDIT:
Thanks #AMayes for the advice. I don't believe key/value observing is necessary in this instance.

Execute code when app reopens

I have a Single View Application. When I hit the home button and ‘minimise’ the application I want to be able to execute code when the user reopens it.
For some reason viewDidAppear and viewWillAppear do not execute when I minimise and reopen the application.
Any suggestions?
Thanks in advance
sAdam
You can either execute code in the app delegate in
- (void)applicationDidBecomeActive:(UIApplication *)application
or register to observe the UIApplicationDidBecomeActiveNotification notification and execute your code in response.
There is also the notification UIApplicationWillEnterForegroundNotification and the method - (void)applicationWillEnterForeground:(UIApplication *)application in the app delegate.
To hook up notifications add this at an appropriate point
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Define a the corresponding method
- (void)didBecomeActive:(NSNotification *)notification;
{
// Do some stuff
}
Then don't forget to remove yourself an observer at an appropriate point
[[NSNotificationCenter defaultCenter] removeObserver:self];
Discussion
You most likely only want your viewController to respond to events whilst it is the currently active view controller so a good place to register for the notifications would be viewDidLoad and then a good place to remove yourself as an observer would be viewDidUnload
If you are wanting to run the same logic that occurs in your viewDidAppear: method then abstract it into another method and have viewDidAppear: and the method that responds to the notification call this new method.
This is because since Apple implemented "Multitasking", apps are completely reloaded when you start them again, just as if you had never closed them. Because of this, there is no reason for viewDidAppear to be called.
You could either implement
- (void)applicationWillEnterForeground:(UIApplication *)application
and do there what ever you want. Or you register for the notification UIApplicationWillEnterForegroundNotification in your view controller. Do this in viewDidLoad:
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myAppWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification object:nil];
And of course implement the specified selector and do there what you want.
I am not sure how the answer by #Paul.s performs the OP request since registering UIApplicationDidBecomeActiveNotification will be executed twice:
When launching the app
When application goes into the background
A better practice will be to decouple those events into 2 different notifications:
UIApplicationDidBecomeActiveNotification:
Posted when the app becomes active.
An app is active when it is receiving events. An active app can be said to have focus. It gains focus after being launched, loses focus when an overlay window pops up or when the device is locked, and gains focus when the device is unlocked.
Which basically means that all logic related to "when application launched for the first time"
UIApplicationWillEnterForegroundNotification:
Posted shortly before an app leaves the background state on its way to becoming the active app.
Conclusion
This way we can create a design that will perform both algorithms but as a decoupled way:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName1) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethodName2) name:UIApplicationDidBecomeActiveNotification object:nil];
This because you don't redraw your view. Use applicationWillEnterForeground in the AppDelegate instead. This should work fine for you.

Sharing data across ViewControllers

After reading a bunch of stackoverflow posts and forums I got really confused on this subject. Alot speak about using delegates, but can result in memory problem if there is a big amount of data.
I am only to handle around 600-bytes of data from a TCP/IP device and override the existing value with the new.
I have made a RootViewController which has a special view and the TCP/IP socket, furthermore I have two other ViewControllers which has to use data from the RootViewController (TCP/IP).
Should I perhaps make a seperate class for the TCP stuff instead of mixing it up in my RootViewController?
Also how should I handle the sharing of data across the ViewControllers?
Thanks in advance.
If you are doing a lot with your TCP 'stuff' (for example get the data, display the data, reformat the data, compair or change the data and then re-save etc) then I would suggest creating a separate class (probably NSObject) that has all the metods to handle the TCP stuff and then create an instance of that object in your root view controller.
As for getting the data in other view controllers you would have to create a reference to your root view controller in other view controllers and then connect them in interface builder (or programmatically). Just make sure you dont create a brand new instance of root view controller (i.e. dont use [[RootViewController alloc] init];)
Does that make sense? If you post more about the structure of your app (like are you using a navigation controller etc) I could be more specific for you.
Good luck!
For sharing data among classes you can use notifications mechanism. Post a notification in your FromClass.m:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:yourTCPData, #"TCPData", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"gotTCPData" object:nil userInfo:options];
In your ToClass.m viewDidLoad add this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(makeSomeThingUsefulWithTCPData:)name:#"gotTCPData" object:nil];
...and somewhere in your ToClass.m add this method:
- (void)makeSomeThingUsefulWithTCPData:(NSNotification *)notification {
NSDictionary *dict = [notification userInfo];
yourTCPDataClass *yourTCPDataObject = [dict objectForKey:#"TCPData"];
// Here make something useful with your TCP data
}
Also add this in your ToClass.m viewDidUnload to release observer object when view will be unloaded:
[[NSNotificationCenter defaultCenter] removeObserver:self];

How to pause timer from appdelegate in xcode and objective c

I am making a game for iPhone and want to be able to pause a timer when a user's game is interrupted like when they hit the home button. I know that in the app delegate there is a method when the app leaves the foreground called:
- (void)applicationWillResignActive:(UIApplication *)application
What I am struggling with is how to pause the timer. I have a function in my view controller that's called pauseGame and is used for when the user wants to pause the game. I was thinking that it would be easiest to pause the game by using this method. I cannot however understand how to call this method. Any ideas? And sorry for the beginner question.
The shortest way is to Use Notifications:
1. define a custom notification, at your application delegate (or anywhere else...)
#define kApplicationWillResignActiveNotification
#"kApplicationWillResignActiveNotification"
2. dispatch the notification when the applicationWillResignActive: method is called
[[NSNotificationCenter defaultCenter] postNotificationName:
kApplicationWillResignActive object:nil];
3. listen to that notification where ever you want in your project (* import the header file where you #defined the notification)
[[NSNotificationCenter defaultCenter] addObserver:self
selector: #selector(appResigned:)
name:kApplicationWillResignActiveNotification object: nil];
4. you can get the NSNotification Object if you add it to your selector
-(void)appResigned:(NSNotification *)notification;

Adding 2 observer with one name in NSNotificationCenter in 1 class

I have a observer problem with NSNotificationCenter in my app.
My AppDelegate class has 2 service class to get data by url which called ExhibitionService & NewsService.
This 2 service class uses one Queueloader class in itself.
When I wrote 2 observer to listen service load operations in my appdelegate class, it returns error and crashes.
APP DELEGATE CLASS
ExhibitionLoaderService *exhibitionService = [[ExhibitionLoaderService alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exhitibionServiceComplete :) name:**CserviceComplete** object:nil];
[exhibitionService load];
NewsLoaderService *newsService = [[NewsLoaderService alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(newsServiceComplete :) name:**CserviceComplete** object:nil];
[newsService load];
ExhibitionLoaderService.m & NewsLoaderService has the same method
-(void)load
{
Queueloader *que = [[Queueloader alloc] initWithPath:CExhibitionURLPath isVerbose:NO];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didQueComplete:) name:CdidQueueloaderComplete object:nil];
[que startOperation];
[que release];
}
ERROR I GOT
[[NSNotificationCenter defaultCenter] postNotificationName:**CdidQueueloaderComplete** object:results];
2 service class has CdidQueueloaderComplete... Problem is about observers but how? what?
PS. Program received signal EXC_BAD_ACCESS.
Thanks.
There's no problem with having multiple observers of the same notification. The problem you describe sounds a lot like it's related to the lifetime of your observers.
If you deallocate an object that's still registered to listen to notifications, the NSNotificationCenter doesn't know about that. If a notification comes in in the future, the center will forward it to the object it thinks is still listening (but that has gone away), and you get a crash.
The solution to this problem is to ensure that your object is removed as an observer before it is destroyed. There are two ways to do this:
often you'll know when an object should start or stop listening to notifications, and you can make sure you remove it as an observer when it should stop (for example, perhaps view controllers should start listening for model updates when their views appear and stop listening when their views disappear)
other times, an object can look after its own notification lifecycle: perhaps you can start listening in an initialiser and stop listening in -dealloc.
Whatever way you do it, you need to balance adding observers and removing observers so that when an object goes away it's no longer registered with the notification center.