I'm working on a game with a primary player as an SKSpriteNode. The game has a concept of powerups that can be acquired, and each player can have a set of attachments.
I have a protocol for attachments defined as:
#protocol ESPlayerAttachment <NSObject>
#required
-(ESPlayerAttachmentType)attachmentType;
#optional
-(void)willAttachToPlayer:(ESPlayerSpriteNode *)player;
-(void)didDetachFromPlayer:(ESPlayerSpriteNode *)player;
-(void)player:(ESPlayerSpriteNode *)player willUseAttachmentInParent:(SKNode *)parent;
#end
Additionally, I have a protocl for powerups defined as:
#protocol ESPlayerPowerup <NSObject>
-(void)acquiredByPlayer:(ESPlayerSpriteNode *)player;
#end
Finally, I have a physics body on the power up box, as well as one on the player so that I can detect the collision and attach the powerup. As an example, I have a shield powerup that is implemented as:
-(void)willAttachToPlayer:(ESPlayerSpriteNode *)player
{
[player detachAttachmentsOfType:ESPlayerAttachmentTypeShield];
__weak ESPlayerShipSpriteNode *weakPlayer = player;
[player.parent addChild:_shield];
_shield.position = CGPointMake(player.position.x, player.position.y + 0.1f * _shield.size.height);
[self performSelector:#selector(detachFromPlayer:) withObject:weakPlayer afterDelay:15.0f];
}
What's bothering me is that if I attach the shield powerup to the player in my SKScene class (in the update or didMoveToView methods), everything works. The shield is positioned correctly. However, if I add it through the ESPlayerPowerup method (which gets called during a collision with the player), the node gets positioned relative to SKScene, so it ends up in the bottom left corner of the screen. I can't seem to figure out what the difference is (there's only one instance of ESPlayerSpriteNode in the game, so I'm relatively confident that isn't the problem)
For what it's worth, I ended up finding this post on SO, which has solved my problem:
SKSPriteNode position changes to 0,0 for no reason
It would seem to be a bug in SpriteKit, but positioning the item before attaching the physicsBody does work, and positions it correctly.
Related
I have two scenes that I made in SpriteBuilder, one is Shop and the other is UpgradesNew. Shop is a CCNode layer and UpgradesNew is a CCNode layer. I have two CCScrollViews in the MainScene that load Shop and UpgradesNew respectively.
When one button in Shop is tapped, the label in UpgradesNew should change colors. I have been trying to implement this using delegates but it's not working.
Here's what I did...
In shop.h I set the protocol:
#protocol changeColorProtocol <NSObject>
#required
-(void)changeColor;
#end
Then I set the id
#property (nonatomic, retain) id <changeColorProtocol> delegate;
Here is the button that when clicked, should use changeColor. This is in shop.m
-(void) buyDiggerShibe {
[self.delegate changeColor];
[self didLoadFromCCB];
}
Now in UpgradesNew.h I made it adopt the protocol like this
#interface UpgradesNew : CCNode <changeColorProtocol>
And in UpgradesNew.m
I set delegate to self in ViewDidLoad.
Shop *shop = [[Shop alloc]init];
shop.delegate = self;
.
-(void)changeColor {
if (hasDigger == YES) {
shovelRequires.color = [CCColor greenColor];
NSLog(#"HEY HEY HEY");
}
}
I probably have parts of the delegate placed in the wrong area because I was trying to switch them around when it wasn't working, I'm not sure where they are supposed to go. I've watched multiple delegate tutorials and it just seems overly complicated, at least with what I am trying to do.
Any ideas?
EDIT:
Tried this.
I created a property in UpgradesNew
#property (strong, nonatomic) Shop *shop;
Then I synthesized it in the implementation and allocated it like this in didLoadFromCCB, instead of creating a new object:
self.shop = [[Shop alloc]init];
shop.delegate = self;
EDIT: This is how I am creating objects.
Drag a label into a layer. Identify it then define it in header as CCLabelTTF *label; That's it, thats all I do to create any object on the layer.
To create a layer like Shop or UpgradesNew, I hit New -> File -> Layer. That creates a new CCNode. Then I set the class of the CCNode, as shown in the picture the CCNode that is highlighted has a class of MainScene. If I want to establish a #property to that CCNode I just type the name in the box right below custom class and set it as doc root var, and then put it in the header as CCNode *MainScene. I don't do anything other than that.
I don't know anything about SpriteBuilder, so it's a bit hard to address your question. You might want to add SpriteBuilder to the title of your post so people who use that framework are likely to read it.
You need to explain how the 2 "CCNode layer"s are created, and how you link them together. In order for one object to have another object as a delegate, the delegate property has to be set somewhere. Where is that setup being done? Have you set a breakpoint at the line
[self.delegate changeColor];
To make sure that self.delegate is not nil?
Have you set a breakpoint in your changeColor method, or added a log statement, to see if it's being called? My guess is that self.delegate is nil, so the messages is being dropped on the floor (it's legal to send messages to nil in Objective-C. It just doesn't do anything.)
I am trying to make my pause screen in my game. I am using the framework Cocos2d V3 RC4 in IOS and XCODE and SpriteBuilder. I read a lot of post and i think that i have two aproachs posible:
1º Push a total scene foward the main scene. (THIS WORK FINE TO ME)
In the MAIN SCENE i call this to pause the game
CCScene *pausa = [CCBReader loadAsScene:#"Pausa"];
[[CCDirector sharedDirector] pushScene:pausa];
and then in the Pause class i call this to pop the pause scene and take back the Main scene:
[[CCDirector sharedDirector] popScene];
2º Take a CNode in front of the MAIN SCENE, making it with transparency, opaque, and disableing the main scene touch, animations, actions, etc… (THIS DOESN’t WORK FOR ME AND I WHANT THIS !!!)
I doit in this way:
In the main Scene:
CCScene *pausa = [CCBReader loadAsScene:#"Pausa"];
[self addChild:pausa];
AND I TRY with ALL THIS METHODS:
// [self unscheduleAllSelectors];
// [self stopAllActions];
// [self setPaused:TRUE];
// [self setUserInteractionEnabled:FALSE];
The node is added but Have not Touch exclusively… The Node that is behind I can touch it…
I try olso with :
[[CCDirector sharedDirector] pushScene:pausa];
(in the main scene) with result obviosly bad, and i try with
[self setExclusiveTouch:TRUE];
in the pause didLoadFromCCB method but also I cant make it have a Exclusive touch. i Can STILLPRESS buttons and sprites from the back Node…
What I am doing Wrong, And how is the correct code/aproach tu use to handle a pause node like I want for method 2??
Resuming... I only want a Modal Window... (like in zk framework, in java, the Window (CNode in Cocos2d) come in front and the background keep disabled and in grey)
Thanks for read and hope someone can help
Here is my implementation from a game that I am doing
- (void) pauseGame
{
CCLOG(#"Pause game");
_contentNode.paused = YES;
_contentNode.userInteractionEnabled = NO;
_gamePausedNode = (GamePausedNode *)[self loadCCBWithNameAndPositionInCenter:#"PausedNode"];
[self addChild:_gamePausedNode];
}
gamePausedGame is a CCNode, but it could be CCSprite as well. It is not actually a CCScene, nor is it loaded by one because a modal view like this is not really a scene.
You usually want to group the CCNode objects together in one CCNode like my _contentNode so you can pause them with one click.
Update : I have edited the code to the bare minimals
CCPhysicsNode *_physics;
_physics.paused = true;// It will pause your game but not actions.
_physics.paused = false;// It will resume your spinning and falling of sprites while button is pressable like during pause game.
I've added an additional CCLayer to my "GameScene" that becomes visible ([self addChild:_congratsScreen]) whenever my character collects a given amount of objects on the screen.
Within my GameScene.h I've declared my child layer (CClayer *congratsScreen) and I'm synthesizing it on my GameScene.m. I'm allocating the child CCLayer in the GameScene's init method so it is holding the reference to the child layer in this instance variable.
On my GameScene I have a few CCParticleSystemQuad instances, and it's super simple to invoke both stopSystem and resetSystem to replay my particles animation, but if I try to do the same thing on the CCParticleSystemQuad that was initialized on the child layer, the resetSystem doesn't work after I remove the child from my GameScene and add it back again. Does something happens with the CCLayer's components once it is removed from a parent layer's scene?
I don't have the code at the moment so I will try to write some pseudo-code to illustrate how it's being done:
How it is being initialized on ChildLayer.m:
_sparkling= [CCParticleSystemQuad particleWithFile:#"sparkling.plist"];
Then, somewhere on GameScene.m I have:
- (void) showCongrats {
//pathetic way to create a modal panel
[self setTouchable = NO];
[[[self _congratsLayer] _sparkling] resetSystem];
[self addChild:_congratsLayer];
}
- (void) hideCongrats {
//let them continue playing
[self setTouchable = YES];
[[[self _congratsLayer] _sparkling] stopSystem];
[self removeChild:_congratsLayer];
}
So, it works on the first time I invoke showCongrats, the reference is good and I can manipulate the particles, but once I hide the layer, continue playing the game and show the congratulations panel again, it shows a frozen animation of the particles from the last invocation, the resetSystem no longer works. Any ideas?
I would add some breakpoints in the code and walk through it but if I had to guess I would say that when you are calling removeChild you are losing the data that you had in your init method and something funky is happening.
In my game, I use Cocos2D for physics in the gameplay and UIKit for the menu screen and the rest of the game. Now, I have to pass the data from a UIViewController to a Cocos2D scene. Is there a way to do this?
I think you can do it by assigning value to a class variable....
Code snippet will look like this...
[[CCDirector sharedDirector] replaceScene:[GameScene scene:data]];
in your GameScene.m
#implementation GameScene
#synthesize ...
+ (CCScene *) scene:(Datatype *)data
{
self.dataReceived = data;
CCScene * .....
.......
}
Something like this may help.. I did this in cocos2d-x and it works fine.. M sorry for syntax as I don't have X-code.... :)
In my game, which is using cocos2d, there is going to be many different types of enemies, which all look different, and move all in different ways. Also, there is going to be a couple of different gamemodes, which both use the same enemies. As there will be different gamemodes, I decided to make each of my enemies have their own CCSprite class. In those there will be the way that the sprites move, the animation, etc. When one of these sprite is needed in my game, they will be spawned in to the scene. The only thing is, how do I do this? How do I call for one of the sprites to be create on the screen when they are using a class of their own?
If you want to tell me another way than having these sprites having their own classes, that is fine, but keep in mind that I will be having a couple of different gamemodes. If I do the code for the sprites in the CCLayer class of that gamemode, well I will have to write the code twice, which will take time.
Thanks.
You can just subclass CCSprite and override the default initializer initWithTexture:rect:
example taken from here
#implementation MySprite
-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
{
if( (self=[super initWithTexture:texture rect:rect]))
{
// initialize your ivars here
//ivar1 = xxx;
//ivar2 = yyy;
//ivar3 = zzz;
}
return self;
}
#end
// And to create an instance of MySprite you simply do:
MySprite *sprite = [MySprite spriteWithFile...];
// or any of the supported CCSprite methods.
you can have a super class say EnemySprite that looks like this
#interface EnemySprite : CCSprite
- (void)addToLayer:(CCLayer *)layer;
- (void)removeFromLayer:(CCLayer *)layer;
#end
than create a subclass for each type of enemy for example:
#inteface BigEnemySprite : EnemySprite
#end
#implementation BigEnemySprite
- (void)addToLayer:(CCLayer *)layer {
[layer addChild:self];
// animation code for your big enemy
}
- (void)removeFromLayer:(CCLayer *)layer {
[layer removeChild:self];
// animation code
}
#end
than you can use them like
EnemySprite *enemy = [BigEnemySprite spriteFromFile:file];
[enemy addToLayer:self];