I'm pretty new to coding on Xcode (or objective-c in general) and I can't seem to get rid of these errors:
//
// HelloWorldLayer.m
// FirstGame
//
// Created by Kostas on 1/14/12.
// Copyright __MyCompanyName__ 2012. All rights reserved.
//
// Import the interfaces
#import "HelloWorldLayer.h"
#import "GamePlay.h"
// HelloWorldLayer implementation
#implementation HelloWorldLayer
+(id) scene {
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init {
if( (self=[super init] )) {
[CCMenuItemFont setFontName:#"Marker Felt"];
[CCMenuItemFont setFontSize:35];
CCMenuItem *Play = [CCMenuItemFont itemFromString:#"PLAY"
target:self
selector:#selector(gotoGameplay:)];
CCMenu *menu = [CCMenu menuWithItems: Play, nil];
menu.position = ccp(240, 160);
[menu alignItemsVerticallyWithPadding:10];
[self addChild:menu];
}
return self;
}
-(void) goToGameplay: (id) sender {
[[CCDirector sharedDirector]
replaceScene:[[CCTransitionFade
transitionWithDuration:1
scene:[GamePlay node]
]]; **<-----Here is my error it says "Expected identifier"**
}
- (void) dealloc {
[super dealloc];
}
#end
The expected identifier is just what X-Code came up with.
If you count your brackets, you'll see that you have two more opening brackets than closing brackets. I've indented them here so you can see the problem clearly.
-(void) goToGameplay: (id) sender {
[
[CCDirector sharedDirector]
replaceScene:
[ // <-- either this is extra
[CCTransitionFade transitionWithDuration:1
scene:[GamePlay node]
]
];
//]; <-- or this is missing
}
The compiler is trying to tell you that it wasn't expecting to find a semicolon in the middle of a message send expression. I'm not familiar enough with the Cocos2D framework to know what exactly you're trying to do, but at least you can see what problem is.
This line:
[[CCDirector sharedDirector]
replaceScene:[[CCTransitionFade
transitionWithDuration:1
scene:[GamePlay node]
]];
has 5 open brackets and only 4 close brackets. There needs to be the same number (and in the right places). Most likely you need to get rid of one of the two open brackets after replaceScene:.
BTW - why are you using such and old Xcode? You should use the latest - 4.5.1.
Change
[[CCDirector sharedDirector]
replaceScene:[[CCTransitionFade
transitionWithDuration:1
scene:[GamePlay node]
]];
to,
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFade transitionWithDuration:1
scene:[GamePlay node]]];
That should fix the issue. You had an extra [ before [CCTransitionFade transitionWithDuration:1 scene:[GamePlay node]]
Related
So I create a delegate protocol and corresponding property in my scene. In my view controller that holds all my scenes I set the scenes delegate to itself so I can call a fullscreen ad(from the view controller) in the menu which is the first game scene called. This works great I can call the method. But after i transition to the level scenes then back to the menu scene calling my method to show fullscreen ad does nothing. Im assuming because the delegate being set to itself has been lost. How can I set the delegate back to itself within the game scene? This is how I do it inside the View controller.M
myScene = [GameScene unarchiveFromFile:#"GameScene"];
((GameScene *)myScene).mySceneDelegate = self; // i need to be able to do this within the scene
myScene.scaleMode = SKSceneScaleModeResizeFill;
// Present the scene.
[skView presentScene:myScene];
How I call the fullScreen method thats in my VC from my menu scene...
[self.mySceneDelegate showFullScreen:self];
...so in my code when I transition from my LevelOne scene I try to reassign the delegate with the following code but it nothing happens
-(void)goBackToMenu{
GameScene *newscene = [GameScene sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsCloseHorizontalWithDuration:1]];
((GameScene *)newscene).mySceneDelegate = self;
}
I have also tried setting the property attribute to Strong instead of weak with no luck either
**********************ALL CODE **************************************
More code.. my Viewcontroller.h
#protocol TCAMySceneDelegate;
#interface GameViewController : UIViewController<TCAMySceneDelegate>
#property(nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;
#end
my view controller.m
- (void)viewDidLoad
{
[super viewDidLoad];
SKView * skView = (SKView *)self.view;
myScene = [GameScene unarchiveFromFile:#"GameScene"];
((GameScene *)myScene).mySceneDelegate = self;
// Present the scene.
[skView presentScene:myScene];
}
-(void)showFullScreen:(GameScene *)gameScene{
NSLog(#"Show Full Screen");
[revmobFS loadWithSuccessHandler:^(RevMobFullscreen *fs) {
[fs showAd];
NSLog(#"Ad loaded");
} andLoadFailHandler:^(RevMobFullscreen *fs, NSError *error) {
NSLog(#"Ad error: %#",error);
} onClickHandler:^{
NSLog(#"Ad clicked");
} onCloseHandler:^{
NSLog(#"Ad closed");
}];
[RevMobAds startSessionWithAppID:#"547251235f2a043608a66f1a"
withSuccessHandler:^{
NSLog(#"Session started with block");
// Here, you should call the desired ad unit
revmobFS = [[RevMobAds session] fullscreen];
[revmobFS showAd];
// [RevMobAds session].testingMode = RevMobAdsTestingModeWithAds;
revmobFS.delegate = self;
[revmobFS loadAd];
} andFailHandler:^(NSError *error) {
NSLog(#"Session failed to start with block");
}];
}
my game scene.h file..
#protocol TCAMySceneDelegate;
#interface GameScene : SKScene
#property (nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;
#end
#protocol TCAMySceneDelegate <NSObject>
-(void)showFullScreen:(GameScene *)gameScene;
#end
my game scene.m file..
-(void)didMoveToView:(SKView *)view {
//Show Ad
[self.mySceneDelegate showFullScreen:self];
}
now the above showFullScreen: is called but online when the gameScene (my game menu) is presented the first time .. after transitioning to other scenes in the games such as my levels then go back to my menu(gamescene) it never gets called. Hope this is more clear now
how i switch to another scene from my game scene.m
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
NSLog(#"Level Selected: %#", node.name);
int x = [node.name intValue];
switch (x) {
case 1:{
LevelOne *newscene = [LevelOne sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
break;
}
case 2:{
LevelTwo *newscene = [LevelTwo sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
case 3:{
LevelThree *newscene = [LevelThree sceneWithSize:self.size];
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
//..........
default:
break;
}
}
LevelOne.h code...
#import <SpriteKit/SpriteKit.h>
#import "DrawCanvas.h"
#import "GameScene.h"
#interface LevelOne : SKScene<SKPhysicsContactDelegate>
#end
Personally, I found your description as well as the code you posted a bit lacking in details as well as being a bit confusing. When providing code you will probably get better/quicker help if you provide more detail. For example, which file does goBackToMenu belong to?
How/in what context is
[self.mySceneDelegate showFullScreen:self];
called? I would assume that it is from some trigger point in the GameScene. And what is the method signature of showFullScreen?
And since you don't indicate what type mySceneDelegate is, you're making the readers guess what you mean.
So trying to decipher what you are asking...
Let's assume you have some delegate which is called GameSceneDelegate. I am also going to assume you already know SKSceneDelegate exists and are either using it already but just haven't posted that code or don't need to use it.
Your GameViewController would have it's interface defined as:
#interface GameViewController : UIViewController<GameSceneDelegate>
And your GameScene would have a property defined like this:
#property (nonatomic, weak) id<GameSceneDelegate> mySceneDelegate;
Note you should be using weak here. Because if your GameViewController ever has a strong reference to your GameScene, then you've just created a retain cycle.
You have a few options of when to set mySceneDelegate. If I know I must have a delegate defined, what I often do is add it as an argument to the initializers. This way it is clear that it must have a delegate. So, given you are using class methods to generate instances, you'd do something like this:
#interface GameScene : SKScene
#property (nonatomic, weak) id<GameSceneDelegate> mySceneDelegate;
+ (instancetype)unarchiveFromFile:(NSString *)file mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate;
+ (instancetype)sceneWithSize:(CGSize)size mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate;
#end
And you those methods would look like:
+ (instancetype)unarchiveFromFile:(NSString *)file mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
GameScene *scene = [super unarchiveFromFile:file];
scene.mySceneDelegate = mySceneDelegate;
return scene;
}
+ (instancetype)sceneWithSize:(CGSize)size mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
GameScene *scene = [super sceneWithSize:size];
scene.mySceneDelegate = mySceneDelegate;
return scene;
}
Note to make this work, you'll need to define this in the GameViewController.h (or a separate file for the category if you wish)
#interface SKScene (Unarchive)
+ (instancetype)unarchiveFromFile:(NSString *)file;
#end
The alternative is to immediately set mySceneDelegate right after creating your instance of the scene.
Realistically, if you are doing this, it should work and your delegate's should stay in tact. FWIW, I am actually doing this in my game. So I know it works.
Okay, so this is where things are hazy in your question. You indicate the delegate is "lost". The only way the delegate can be "lost" would be if someone explicitly sets it to nil. Or the other possiblity I suppose is your GameScene is being dealloc'd and you don't realize it.
I'd setup two things to help debug it.
In GameScene add
- (void)dealloc
{
NSLog(#"I'm being dealloc'd");
}
- (void)setMySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
_mySceneDelegate = mySceneDelegate;
NSLog(#"Setting mySceneDelegate");
}
So now you can try and determine where it is "getting lost". If any of those print out to the console at the time it is lost, simply set a breakpoint in those methods to see who is calling it.
One other pieces of info that would also help are
What do you mean assigning a delegate to itself? I have no idea what
that means.
You mentioned you assume the delegate is lost. Why? Do you have
evidence or is this purely a guess?
If you can provide some answers and better detail, we can probably figure out what is going awry.
Okay, now looking at your code, I think your problem is here in GameScene.m:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
NSLog(#"Level Selected: %#", node.name);
int x = [node.name intValue];
switch (x) {
case 1:{
LevelOne *newscene = [LevelOne sceneWithSize:self.size];
newScene.mySceneDelegate = self.mySceneDelegate;
// Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
break;
}
case 2:{
LevelTwo *newscene = [LevelTwo sceneWithSize:self.size];
newScene.mySceneDelegate = self.mySceneDelegate;
// Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
case 3:{
LevelThree *newscene = [LevelThree sceneWithSize:self.size];
newScene.mySceneDelegate = self.mySceneDelegate;
// Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
[self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
break;
}
//..........
default:
break;
}
}
What I am doing here is assigning the the new scene's mySceneDelegate to the existing one being used.
You can clean this code up to be more readable.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
NSLog(#"Level Selected: %#", node.name);
int x = [node.name intValue];
GameScene *newScene = nil;
switch (x) {
case 1:
newScene = [LevelOne sceneWithSize:self.size];
// Or, if you can make it work, newScene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
break;
case 2:
newScene = [LevelTwo sceneWithSize:self.size];
// Or, if you can make it work, newScene = [LevelTwo sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
break;
case 3:
newScene = [LevelThree sceneWithSize:self.size];
// Or, if you can make it work, newScene = [LevelThree sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
break;
// other cases..........
default:
break;
}
if (newScene) {
newScene.mySceneDelegate = self.mySceneDelegate;
[self.view presentScene:newScene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
}
}
For your levels, you would need to do the following:
#import <SpriteKit/SpriteKit.h>
#import "DrawCanvas.h"
#import "GameScene.h"
#interface LevelXYZ : GameScene<SKPhysicsContactDelegate>
#end
Where XYZ is the level (eg. One). By doing this, you inherit mySceneDelegate from GameScene. If for some reason you are insistent that the level scenes are not inheriting from GameScene, then add a base class which holds the delegate. So that would be something like:
#interface MyBaseScene : SKScene
#property(nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;
#end
And then GameScene would be this:
#interface GameScene : MyBaseScene
And your level scenes would be
#interface LevelXYZ : MyBaseScene<SKPhysicsContactDelegate>
Note a couple of things I did.
Changed newscene to newScene. In iOS, you typically use came case.
you were doing it for mySceneDelegate. Consistency is key.
Originally declared newScene as nil and assigned as need. Then at the end of the switch, if a newScene exists, assign the delegate and
present. This way, you are only doing it in one place. It makes life
easier. For example, if you wanted to change the duration for each
one, then what? You have to change ever case. Note there is a
downfall to this. If you wanted on level to have a special duration,
then it doesn't quite work. But you can change it by making the
duration and transition type variables.
BTW, I would strongly advise against having your scene class contain the code that does the actual transition. I would either have it call a delegate to do it or find another way (like using a notification that a scene change is needed). For example, in my current game, I have my view controller be the controller of all scene changes. The advantage of this is you can also call scene transitions from other external forces (and not have your scene be the one that manages it).
Lastly, you can hopefully see that the only way the issue was discoverable was by presenting the right code. I will agree it is hard to know what code to present, because if there is too much code, then people may not read it. It's a fine line, but some food for thought.
I needed to convert my code to ARC. I have an CCArray that I use to draw a path. I fill the objects of CCArray values from a different class.
Problem is after converting to ARC, CCArray returns always null
I can not see what I am doing wrong.
Ladybug.h
#interface Ladybug : CCSprite <CCTargetedTouchDelegate>{
CCArray *linePathPosition;
}
#property (nonatomic, strong) CCArray *linePathPosition;
#end
Ladybug.m
#synthesize linePathPosition;
-(id) init
{
if( (self=[super init] )) {
self.linePathPosition = [[CCArray alloc] init];
}
return self;
}
-(void) updatePosition:(CGPoint) position
{
[self.linePathPosition addObject:[NSValue valueWithCGPoint:position]];
NSLog(#"line path %#",linePathPosition);
}
-(void) breakMoveLadyBug
{
[self.linePathPosition removeAllObjects];
}
In main .m
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
Ladybug *ladybug1 = (Ladybug *)[self getChildByTag:99];
CCMotionStreak* streak = (CCMotionStreak *)[self getChildByTag:999];
CGPoint touchLocation = [touch locationInView: [touch view]];
CGPoint curPosition = [[CCDirector sharedDirector] convertToGL:touchLocation];
if (ladybug1.isSelected) {
streak.position = curPosition;
[ladybug1 updatePosition:curPosition];
NSLog(#"Cur position %#",NSStringFromCGPoint(curPosition));
if (!ladybug1.isMoving) {
[ladybug1 startMoveLadyBug];
}
}
}
Log:
Cur position {331, 110}
line path (null)
What am I doing wrong? What is the proper way to define and init CCArray with ARC?
This isn't a problem with ARC or CCArray. The problem enlies in your understanding of objective-c.
To solve your problem, where ever you are doing this line [self addChild:ladybug1 z:999 tag:99]. Do this instead:
Ladybug *ladybug1 = [[Ladybug alloc] init];
[self addChild:ladybug1 z:0 tag:99];
Note: this is done outside of the ccTouchMoved function, in some function that adds the ladybug objects to the scene/layer.
Then your ccTouchMoved function will have an allocated ladybug object to work with.
The problem being that if you never call the init function on a ladybug object, the CCArray will never get allocated so everything will be null. You will then try to add points to an null array, and that will do nothing. You can do all this tag stuff you want (I personally have no idea why you are doing that), but you need to specify the tag for the ladybug object. So add it to the CCLayer/CCScene (w/e you are using), and set its tag. Then you can use the getChildByTag function.
I'm having an odd, irritating issue.
For example, in my main menu screen, I have a button that says "Instructions."
Once I click that, in the instructions layer, there is a button that takes you back to the main menu.
However, for some reason, the button action is not exclusive to the sprite image. If i click 3 inches away from the 'backtomenu' button, it still takes me back to the main menu.
So, my question is, how can I make a button be clicked only if you click the actual image?
(this is how I create a button)
- (id) init
{
if((self = [super init]))
{
[self instructions];
}
return self;
}
- (void) instructions
{
bgI = [CCSprite spriteWithFile:#"testbackground11.png"];
[bgI setPosition:ccp(160,240)];
ccTexParams params = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
[bgI.texture setTexParameters:¶ms];
[self addChild:bgI z:0];
returnToMenu = [CCMenuItemImage itemFromNormalImage:#"berry2.png"
selectedImage:#"berry2_selected.png"
target : self
selector: #selector (ifReturnToMenu:)];
CCMenu *myReturnMenu = [CCMenu menuWithItems:returnToMenu, nil];
[myReturnMenu alignItemsVertically];
[self addChild: myReturnMenu];
}
- (void) ifReturnToMenu: (CCMenuItem *) menuItem
{
if(menuItem == returnToMenu)
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFade transitionWithDuration:0.5f scene: [MainMenu scene]]];
}
I am not sure how 'isReturnToMenu' is fired, but you can try this
- (void) ifReturnToMenu: (CCMenuItem *) menuItem{
if(menuitem == returnToMenu){
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFade transitionWithDuration:0.5f scene: [MainMenu scene]]];
}
}
If it doesnt work, you'll need to post the code that fires it so we can help you more
I want to add a CCSprite on my UIView, is it possible to do that? What I found were the ways to add UIKit on Cocos2D, but I want the its reverse. I want to add Cocos2D on UIKit.
Any help will be appreciated. Thanks!
You can do this by setting following method in your uiviewcontroller class
- (void)setupCocos2D {
CCGLView *glView = [CCGLView viewWithFrame:CGRectMake(100, 100, 200, 200)
pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8
depthFormat:0 // GL_DEPTH_COMPONENT16_OES
];
glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view insertSubview:glView atIndex:0];
[[CCDirector sharedDirector] setView:glView];
CCScene *scene = [StarVideoView scene];
[[CCDirector sharedDirector] runWithScene:scene];
}
and calling this method in your viewDidLoad method as
[self setupCocos2D];
here startVideoView is my ccscene class and ofcourse you have to add cocos2d.h library in your uiviewcontroller class :)
Cocos2D does its drawings inside of a EAGLView class, which is just a wrapper for a CAEAGLLayer. So, you might try creating a director with its associated EAGLView, thendo this to add the cocos2d open gl layer to your view:
[myView.layer addSublayer:[[CCDirector sharedDirector] openGLView].layer];
Situation:
I'm getting some mysterious crashing shortly after a CCCallFunc. In short, we have a button. The button has a tag to identify it later. When the button is pressed, we run some actions to animate it, and when the animation is done, we CCCallFunc another method to transition to another scene. We crash shortly after the CCCallFunc. Source and errors below.
Point Of Crash (in cocos2d source):
// From CCActionInstant.m of cocos2d
-(void) execute
{
/*** EXC_BAD_ACCESS on line 287 of CCActionInstant.m ***/
[targetCallback_ performSelector:selector_];
}
#end
Snapshot of Thread 1:
My Code:
Below is some source taken from MenuLayer.m (a simple menu to display a button).
// from MenuLayer.m
// …
#implementation MenuLayer
-(id) init{
if((self=[super init])) {
/****** Create The Play Button (As a CCMenu) ********/
CCSprite *playSprite = [CCSprite spriteWithFile:#"playbutton.png"];
CCMenuItemSprite *playItem = [CCMenuItemSprite itemFromNormalSprite:playSprite selectedSprite:nil target:self selector:#selector(animateButton:)];
playItem.tag = 3001;
playItem.position = ccp(160.0f, 240.0f);
CCMenu *menu = [CCMenu menuWithItems:playItem, nil];
menu.position = ccp(0.0f, 0.0f);
[self addChild:menu z:0];
}
}
// ...
- (void)animateButton:(id)sender{
/*** Run an animation on the button and then call a function ***/
id a1 = [CCScaleTo actionWithDuration:0.05 scale:1.25];
id a2 = [CCScaleTo actionWithDuration:0.05 scale:1.0];
id aDone = [CCCallFunc actionWithTarget:self selector:#selector(animationDone:)];
[sender runAction:[CCSequence actions:a1,a2,aDone, nil]];
}
- (void)animationDone:(id)sender{
/*** Identify button by tag ***/
/*** Call appropriate method based on tag ***/
if([(CCNode*)sender tag] == 3001){
/*** crashes around here (see CCActionInstant.m) ***/
[self goGame:sender];
}
}
-(void)goGame:(id)sender{
/*** Switch to another scene ***/
CCScene *newScene = [CCScene node];
[newScene addChild:[StageSelectLayer node]];
if ([[CCDirector sharedDirector] runningScene]) {
[[CCDirector sharedDirector] replaceScene:newScene]];
}else {
[[CCDirector sharedDirector] runWithScene:newScene];
}
}
Use CCCallFuncN instead of CCCallFun.
CCCallFuncN passes the Node as parameter, the problem with CCCallFun is that you are loosing reference of the node.
I test your code with CCCallFuncN and works ok.
Just a hunch. Besides checking for memory leaks, try to schedule a selector with a 0 second interval instead of directly sending the goGame message. I have a suspicion that director's replaceScene causes a cleanup of the scene and all objects associated with it. That in turn could leave the CCCallFunc action in an undefined state. Although normally it works fine - which is to say that this is just another indication about something sketchy, memory- respectively object-lifetime-management-wise.
Btw, if you support iOS 4 as a minimum, use CCCallBlock instead of CCCallFunc. That's safer and cleaner.