Passing Level parameter when transition to next Scene? - objective-c

I'm trying to figure out the best way to pass a level parameter between Scenes using Spritebuilder and Cocos2D in Xcode.
I'm using the standard code below to transition between scenes.
[[CCDirector sharedDirector] replaceScene:[CCBReader loadAsScene:#"Gameplay"]];
Any help will be much appreciated.

Assuming that the Gameplay.ccb has a GameplayClass assigned as its custom class, and that class has a property named currentLevel, you can access the instance and assign the level as follows:
CCScene* theScene = [CCBReader loadAsScene:#"Gameplay"];
GameplayClass* game = (GameplayClass*)theScene.children.firstObject;
game.currentLevel = 3;
[[CCDirector sharedDirector] replaceScene:theScene];
Note that by the time currentLevel is assigned the GameplayClass will have already run its init and didLoadFromCCB methods since that happens during loadAsScene. If you need further init processing override onEnter in GameplayClass:
-(void) onEnter
{
[super onEnter]; // must call super
switch (self.currentLevel)
{
// other switches omitted...
case 3:
// your level 3 code here
break;
}
}

In my game (made with Cocos2d 2.0 + CocosBuilder) I added a class method loadWithLevelID:levelID to the class GameObjectLayer that manages the gameplay elements:
#implementation GameObjectLayer {
G1LevelID * _levelID;
}
// load a Luminetic Land game object layer
+ (instancetype)loadWithLevelID:(G1LevelID*)levelID {
NSString * levelFileName = ... builds levelFileName from levelID;
GameObjectLayer * gol = (GameObjectLayer*) [CCBReader nodeGraphFromFile:levelFileName];
[gol setLevelID:levelID];
return gol;
}
- (void)setLevelID:(G1LevelID*)levelID {
_levelID = levelID;
}
So now I can create a GameObjectLayer typing
GameObjectLayer * gol = [GameObjectLayer loadWithLevelID:levelID];
In general, adding a "load" method to the classes that map ccbi files offers the following benefits:
Only the class knows the name of its related ccbi file.
The ccbi file to create a given class is referenced only once in the entire project.
All the logic to set up an object (e.g. of type G1GameObjectLayer) is inside the object itself.
I think you can follow a similar approach with your CCScene subclass.

Related

Struggling with SKScene inheritance

I have a subclass of SKScene called SPGamePlayScene. It does a bunch of stuff. I need to make another scene that does the same stuff as SPGamePlayScene, but a little more/little differently, so I thought I would subclass my SPGamePlayScene for this new scene, SPPracticeScene. however im having issues with my class level instantiation methods.
below is the method of SPGamePlayScene:
//SPGamePlayScene.m
+ (instancetype)sceneWithSize:(CGSize)size colored:(BOOL)colored {
SPGamePlayScene *gamePlayScene = [SPGamePlayScene sceneWithSize:size];
gamePlayScene.colored = colored;
gamePlayScene.backgroundColor = [SKColor whiteColor];
return gamePlayScene;
}
this works fine. However in my subclass of SKGamePlayScene, SPPracticeScene, I need to create an SPPracticeScene object using its super class's sceneWithSize colored method.
//SPPracticeScene.m which inherits from SPGamePlayScene
+ (instancetype)sceneWithSize:(CGSize)size {
//this keeps giving me a SPGamePlayScene but I need it to be a SPPracticeScene
SPPracticeScene *practiceScene = (SPPracticeScene *)[self sceneWithSize:size colored:NO];
//this line throws an exception because practiceHud is not a property of SPGamePlayScene
practiceScene.practiceHud = [SPPracticeHud practiceHudAtPosition:CGPointMake(0, practiceScene.frame.size.height - 20) inClef:[SPGameState sharedInstance].clef inFrame:practiceScene.frame];
[practiceScene addChild:practiceScene.practiceHud];
return practiceScene;
}
I know that this returns an SPGamePlayScene so I tried casting it with no luck. The line that sets the practiceHud property causes a crash since SPGamePlayScene does not have that property (even though its supposed to be an SPPracticeScene. This is my first foray into custom subclasses/inheritance so im probably misunderstanding something about the way things need to be done/what types need to be returned etc. How can I make this work?
I ended up just overriding the initWithSize method and using an instance level initializer instead of a class level one. using an init method returning an id seemed to do the trick.
//SPGamePlayScene.m
(id)initWithSize:(CGSize)size colored:(BOOL)colored {
if (self = [super initWithSize:size]) {
//some custom code here
}
return self;
}
//SPPracticeScene.m which inherits from SPGamePlayScene
- (id)initWithSize:(CGSize)size {
//init it using the SPGamePlayScene custom initializer
if (self = [super initWithSize:size colored:NO]) {
//some custom code here
}
return self;
}
using the above code let me use the custom initializer for SPPracticeScenes super class, but add additional functionality to it that I needed, while making sure the object returned was of the correct type. I would love to know how I could have made it work with my class level initializers though.

Setting/getting global variables in objective-C

I am writing an app which is a sort of dictionary - it presents the user with a list of terms, and when clicked on, pops up a dialog box containing the definition. The definition itself may also contain terms, which in turn the user can click on to launch another definition popup.
My main app is stored in 'myViewController.m'. It calls a custom UIView class, 'CustomUIView.m' to display the definition (this is the dialog box that pops up). This all works fine.
The text links from the CustomUIView then should be able to launch more definitions. When text is tapped in my CustomUIView, it launches another CustomUIView. The problem is, that this new CustomUIView doesn't have access to the hash map which contains all my dictionary's terms and definitions; this is only available to my main app, 'myViewController.m'.
Somehow, I need to make my hash map, dictionaryHashMap, visible to every instance of the CustomUIView class. dictionaryHashMap is created in myViewController.m when the app opens and doesn't change thereafter.
I don't wish to limit the number of CustomUIViews that can be opened at the same time (I have my reasons for doing this!), so it would be a little resource intensive to send a copy of the dictionaryHashMap to every instance of the CustomUIView. Presumably, the solution is to make dictionaryHashMap a global variable.
Some of my code:
From myViewController.m:
- (void)viewDidLoad
{
self.dictionaryHashMap = [[NSMutableDictionary alloc] init]; // initialise the dictionary hash map
//... {Code to populate dictionaryHashMap}
}
// Method to pop up a definition dialog
- (void)displayDefinition:(NSString *) term
{
NSArray* definition = [self.dictionaryHashMap objectForKey:term]; // get the definition that corresponds to the term
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
[definitionPopup setTitle: term];
[definitionPopup setMessage: definition];
[definitionPopup show];
}
// Delegation for sending URL presses in CustomUIView to popupDefinition
#pragma mark - CustomUIViewDelegate
+ (void)termTextClickedOn:(CustomUIView *)customView didSelectTerm:(NSString *)term
{
myViewController *t = [[myViewController alloc] init]; // TODO: This instance has no idea what the NSDictionary is
[t displayDefinition:term];
}
From CustomUIView.m:
// Intercept clicks on links in UIWebView object
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
[myViewController termTextClickedOn:self didSelectTerm:request];
return NO;
}
return YES;
}
Any tips on how to make the dictionaryHashMap visible to CustomUIView would be much appreciated.
I have tried making the dictionaryHashMap global by doing the following:
Changing all instances of 'self.dictionaryHashMap' to 'dictionaryHashMap'
Adding the line 'extern NSMutableDictionary *dictionaryHashMap;' to CustomUIView.h
Adding the following outside of my implementation in myViewController.m: 'NSMutableDictionary *dictionaryHashMap = nil;'
However, the dictionaryHashMap remains invisible to CustomUIView. As far as I can tell, it actually remains a variable which is local to myViewController...
It's not resource-intensive to pass around the reference (pointer) to dictionaryHashMap. A pointer to an object is only 4 bytes. You could just pass it from your view controller to your view.
But I don't know why you even need to do that. Your view is sending a message (termTextClickedOn:didSelectTerm:) to the view controller when a term is clicked. And the view controller already has a reference to the dictionary, so it can handle the lookup. Why does the view also need a reference to the dictionary?
Anyway, if you want to make the dictionary a global, it would be more appropriate to initialize it in your app delegate, in application:didFinishLaunchingWithOptions:. You could even make the dictionary be a property of your app delegate and initialize it lazily.
UPDATE
I didn't notice until your comment that termTextClickedOn:didSelectTerm: is a class method. I assumed it was an instance method because myViewController starts with a lower-case letter, and the convention in iOS programming is that classes start with capital letters. (You make it easier to get good help when you follow the conventions!)
Here's what I'd recommend. First, rename myViewController to MyViewController (or better, DefinitionViewController).
Give it a property that references the dictionary. Whatever code creates a new instance of MyViewController is responsible for setting this property.
Give CustomUIView properties for a target and an action:
#property (nonatomic, weak) id target;
#property (nonatomic) SEL action;
Set those properties when you create the view:
- (void)displayDefinition:(NSString *)term {
NSArray* definition = [self.dictionaryHashMap objectForKey:term];
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
definitionPopup.target = self;
definitionPopup.action = #selector(termWasClicked:);
...
In the view's webView:shouldStartLoadWithRequest: method, extract the term from the URL request and send it to the target/action:
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
NSString *term = termForURLRequest(request);
[self.target performSelector:self.action withObject:term];
return NO;
}
return YES;
}
In the view controller's termWasClicked: method, create the new view controller and set its dictionary property:
- (void)termWasClicked:(NSString *)term {
MyViewController *t = [[MyViewController alloc] init];
t.dictionary = self.dictionary;
[t displayDefinition:term];
}
Create a class that will be used as singleton. Example.
You Should always keep your data in separate class as the mvc pattern suggest and that could be achieved by using a singleton class for all your dictionary terms and accesing them from every custom view when needed.

How can I make scene transitions pass values to the initializer of target class?

So, I am a bit confused about how to implement the following behavior with cocos2d:
Imagine, a user is on a "Level Select" scene... They choose the appropriate level they want to start on, and tap to begin.. This initiates:
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[Game scene] withColor:ccBLACK]];
--- but.. How am I supposed to give Game the information about what level the user selected? I was looking through the cocos code, and I don't see any thing allowing one to declare a custom init method with arguments for the class.. So I am just curious what the conventional way to solve this problem is?
To be super clear, I want something like:
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[Game scene] performSelector:#selector(initWithLevel) withObject:userLevel withColor:ccBLACK]];
which would then call Game's initWithLevel: instead of the default init method (that it appears +node calls)... Is there any way to do this?
Add a #property to your Game class that takes the level selected by the user. I'm assuming it's a simple integer number. Then initialize the Game class with the scene class method, set the property, and use it in the transition.
It really helps to not cram multiple lines into one. That way those possibilities start jumping right in your face:
// initialize your game class
Game* scene = [Game scene];
scene.userSelectedLevel = 10;
// or alternatively:
[scene setUserSelectedLevel:10];
// then transition
CCTransitionScene* transi = [CCTransitionFade transitionWithDuration:1.0
scene:scene
withColor:ccBLACK]
[[CCDirector sharedDirector] replaceScene:transi];
You can also change your Game class' scene method to take this parameter as input, but frankly just using a property is easier:
+(id) sceneWithLevelNumber:(int)levelNumber
{
// assign level number to Game instance here
}
// call it like this:
[Game sceneWithLevelNumber:10]
Ideally we use one static/single instance of main class...
Now create MyGame class to maintain game specific data like - score, level..
You can access this class in anywhere in the screen..mainmenu, level selection, game screen..
In MyGame.h file
#interface MyGame : NSObject
{
int mLevel;
}
#property(nonatomic,assign) int level;
+ (MyGame *)sharedGameObject;
#end
In MyGame.m file
static MyGame *gGame = nil;
#implementation MyGame
#synthesize level = mLevel;
+(MyGame*)sharedGameObject
{
if(!gGame)
{
gGame = [[MyGame alloc] init];
}
return gGame;
}
#end
//In level selection screen
[MyGame sharedGameObject].level = 5;//selected level
//In game screen,
int level = [MyGame sharedGameObject].level ;

Optimal way to setup UIView in UIViewController?

I'm wondering if my method to setup my UIViewController is optimal or just plain stupid.
I have typedef'ed an enum with some categories. Let's say 6 different categories.
So depending on which category is the selected one. My UIViewController have a switch which will call different method to setup my UIView according to the selected category.
Just wondering if this is a good method to do this, or should I consider creating 6 different UIViewControllers?
A discussion with pro and cons is very much appreciated.
Thanks.
They are basically the same.
Sample code:
switch (self.category) {
case vegetables:
recipe = [[[WebServices sharedInstance].recipeDictionary objectForKey:self.chosenCategory] objectAtIndex:4]; //Needs to be made random
descriptionText.text = recipe.recipeDescription;
[self setupText];
[self setupVegetablesView];
break;
case dairy:
recipe = [[[WebServices sharedInstance].recipeDictionary objectForKey:self.chosenCategory] objectAtIndex:4]; //Needs to be made random
descriptionText.text = recipe.recipeDescription;
[self setupText];
[self setupDairyProductsView];
break;
- (void)setupVegetablesView
{
descriptionText.textColor = [UIColor colorWithRed:0/255.0 green:103/255.0 blue:55/255.0 alpha:1];
background.image = imageBackgroundVegetables;
topBar.image = topBarForVegetables;
subtitle.image = subtitleImageVegetables;
subtitleLink.image = subtitleLinkBarVegetables;
...
}
Depends on your situation. If the view controllers are similar, than this makes sense. But if they are completely different from each other, use separate subclasses.
I would implement it as following
• i would several UIView derived class each one for the type of UIView that i need
For example, i would have VegatableView and DiaryView
• each one of these view will have the same base class of for example MyBaseView
• MyBaseView will have a function called setup this function will need to be implemented in each of my derived classes (vegetable and diary)
• depending on your enum i would create one of these concrete classes and call the setup function
Example:
switch (self.category) {
MyBaseView recipe;
case vegetables:
//Create an instance of VegetableView
recipe = [[VegetableView alloc] init];
break;
case dairy:
//Create an instance of DiaryView
recipe = [[VegetableView alloc] init];
break;
}
//Call setup for the created view
[recipe setup];
//Setup function in vegetableView.m
- (void)setup
{
//Do some vegetable setup stuff
}
//Setup function in diaryView.m
- (void)setup
{
//Do some diary setup stuff
}
In this way, i would minimize the different code, i would make the parameter equal for both the types of view
Also adding new views will be rather easy, just subclass MyBaseView and implement a setup function that is specialized for your new view
Hence increase the objects decoupling and reducing complexity

Identify object calling draw() method with OpenGL

Is there a way to identify which object is calling the draw method.
Creation:
joint.model = [[Box alloc] init];
The calling code:
[joint.model draw];
The draw method (within Box class):
-(void)draw
{
glBindVertexArrayOES(_boxVAO);
glDrawArrays(GL_TRIANGLES, 0, 7055*3);
}
How can i receive the joint object in my draw method?
If more class info is necessary i can attach, but i did not assume since theres not much more.
The model object needs to have a pointer back to joint in order to use it in the -draw method. So you need to either modify the Box class to have a pointer to whatever type joint is, or if Box is defined by a framework you're using, you need to subclass it. So you could do either:
#class Box {
Model* model; // Or whatever type model is.
}
Or if that's not an option, you could do this:
#class BetterBox : Box {
Model* model; // Or whatever type model is.
}
And make sure that model.joint is created like this:
model.joint = [[BetterBox alloc] init]; // or [[Box alloc] init] if you modified the Box class
[model.joint setModel:model];
Then in your draw method, you can simply access model like this:
- (void)draw
{
[model someMethod];
//... etc. ...
}