I can get the HUD layer to appear, but I can't seem to update it. I got it working in Ray's tutorial, but for some reason I can't get it working in my own app. I made a new Cocos2d project just so I can try and isolate the problem and I'm having the same issue... maybe you guys can help. ( I'm getting no errors and tried to fix the indentation as best I could for StackOverflow..)
Problem: I can't update the scoreLabel
Code:
GameHUD.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface GameHUD : CCLayer {
CCLabelTTF *scoreLabel;
}
- (void) updateScore:(int)score;
#end
GameHUD.m
#import "GameHUD.h"
#implementation GameHUD
- (id) init {
if (self = [super init]) {
scoreLabel = [CCLabelTTF labelWithString:#"00000" dimensions:CGSizeMake(240,100) hAlignment:kCCTextAlignmentRight fontName:#"Arial" fontSize:32.0f];
scoreLabel.anchorPoint = ccp(0,0);
scoreLabel.position = ccp(200,0);
scoreLabel.color = ccc3(255, 200, 100);
[self addChild:scoreLabel];
}
return self;
}
- (void)updateScore:(int)score {
scoreLabel.string = [NSString stringWithFormat:#"%i",score];
}
#end
HelloWorldLayer.h
#import "cocos2d.h"
#import "GameHUD.h"
#interface HelloWorldLayer : CCLayer
{
GameHUD *_hud;
}
#property (nonatomic,retain) GameHUD *hud;
+(CCScene *) scene;
#end
HelloWorldLayer.m
#import "HelloWorldLayer.h"
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
#implementation HelloWorldLayer
#synthesize hud = _hud;
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
GameHUD *hud = [GameHUD node];
[scene addChild:hud];
layer.hud = hud;
return scene;
}
-(id) init
{
if( (self=[super init]) ) {
// create and initialize a Label
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Layer A" fontName:#"Marker Felt" fontSize:64];
// ask director for the window size
CGSize size = [[CCDirector sharedDirector] winSize];
// position the label on the center of the screen
label.position = ccp( size.width /2 , size.height/2 );
// add the label as a child to this Layer
[self addChild: label];
// Try to update the scoreLabel in the HUD (NOT WORKING)
[_hud updateScore:74021];
}
return self;
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
[super dealloc];
}
The init is called when you invoke [HelloWorldLayer node] when the HUD is not created yet, ie, _hud is nil. Sending message to a nil object is a void operation and it doesn't crash as it is if calling functions on 'NULL` objects.
Related
I have got a SKScene name “Scene” in which I instantiate 2 objects of my class “Button” subclass of SKNode.
In order to not multiply the number of lines of code in my SKScene, I wanted to implemente the touch methods directly in my class “Button”.
Here is the code :
Scene.h
#import <SpriteKit/SpriteKit.h>
#interface Scene : SKScene
#end
Scene.m
#import "Scene.h"
#import "Button.h"
#implementation Scene
- (id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
Button *button01 = [Button node];
[button01.number setString: #"1"];
button01.position = CGPointMake(CGRectGetMidX(self.frame) - 50, 160);
Button *button02 = [Button node];
[button02.number setString: #"2"];
button02.position = CGPointMake(CGRectGetMidX(self.frame) + 50, 160);
[self addChild:button01];
[self addChild:button02];
}
return self;
}
#end
Button.h
#import <SpriteKit/SpriteKit.h>
#interface Button : SKNode
#property (strong, nonatomic) NSMutableString *number;
#end
Button.m
#import "Button.h"
SKShapeNode *button;
#implementation Button
- (id)init
{
if (self = [super init])
{
self.userInteractionEnabled = YES;
_number = [[NSMutableString alloc] init];
[_number setString: #"0"];
button = [SKShapeNode node];
[button setPath:CGPathCreateWithRoundedRect(CGRectMake(-25, -25, 50, 50), 4, 4, nil)];
button.fillColor = [SKColor clearColor];
button.lineWidth = 1.0;
button.strokeColor = [SKColor whiteColor];
button.position = CGPointMake(0, 0);
SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:#"Arial"];
label.text = _number;
label.fontSize = 20;
label.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
label.position = CGPointMake(0, 0);
label.fontColor = [SKColor whiteColor];
[button addChild:label];
[self addChild:button];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
if(CGRectContainsPoint(button.frame, location))
{
button.fillColor = [SKColor whiteColor];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
button.fillColor = [SKColor clearColor];
}
#end
The problem is when I touch inside the button01, it’s the button02 color’s which change. As if the action is only apply on the last instance of my class.
How can I fixe this.
Thanks in advance.
The issue is the following variable has global scope in your Button class:
SKShapeNode *button;
Since it is shared among the instances of Button, it contains a reference to the SKShapeNode of the last Button that was instantiated. In this case, button 2. You can resolve this issue by defining button as an instance variable:
#implementation Button
{
SKShapeNode *button;
}
...
For some reasons your Button has SKNode as a superclass. SKNode doesn't have a size property, which you can change, so it takes the whole size of it's parent node. Consider it as a layer of the scene. So wherever you touch, the latest SKScene's child (which is button02 in your case) will receive the touch.
Solution: Define Button as a subclass of SKShapeNode directly.
My app was working fine until recently, when the Sprite Scene began to crash after "return self;" it would display the error "-[BEGameScene setDelegate:]: unrecognized selector sent to instance 0x10040de10'" before "terminating with uncaught exception of type NSException" this bug seems to only happen on a device; the ios simulator works fine.
The weird thing is that the bug doesn't seem to be in my code. I took an older, still working version, duplicated it directly from finder and when I ran that it crashed too.
Could it be a problem with Xcode 6 (which I recently downloaded)?
Thanks in advance
Edit:
Sorry, here is the code
#import "BEGameScene.h"
#import "AppDelegate.h"
#import <UIKit/UIKit.h>
#import <SpriteKit/SpriteKit.h>
#interface BEMainGameViewController : UIViewController <BEGameSceneDelegate>
#property (nonatomic, assign) id <BEGameSceneDelegate> delegate;
#end
#import "BEMainGameViewController.h"
#import "BEGameScene.h"
#interface BEMainGameViewController ()
#property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *control;
#end
#implementation BEMainGameViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView *skView = (SKView *)self.view;
skView.showsFPS = NO;
skView.showsNodeCount = NO;
// Create and configure the scene.
BEGameScene *scene = [BEGameScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
scene.delegate = self;
// Present the scene.
[skView presentScene:scene];
[self.view setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"Background 1.tiff"]]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)unwindFromGame {
[self performSegueWithIdentifier:#"unwindFromGameScene" sender:self];
}
#end
#import <SpriteKit/SpriteKit.h>
#protocol BEGameSceneDelegate <NSObject>
-(void)unwindFromGame;
#end
#interface BEGameScene : SKScene <UIGestureRecognizerDelegate>
#property (weak, nonatomic) id <BEGameSceneDelegate> delegate;
#end
#import "BEMainGameViewController.h"
#import "BEGameScene.h"
#import "AppDelegate.h"
#implementation BEGameScene {
NSArray *location;
int x_flower;
int x_Trap1;
int x_Trap2;
int randNum;
int speed;
int score;
int life;
BOOL rad;
BOOL collide;
NSInteger interval;
SKSpriteNode *sprite_bee;
SKSpriteNode *sprite_flower;
SKSpriteNode *sprite_flyTrap;
SKSpriteNode *sprite_flyTrap2;
SKSpriteNode *sprite_rad;
SKSpriteNode *sprite_radGlow;
NSTimer *timer;
SKLabelNode *youLoseLabel;
SKLabelNode *instruction;
NSString *scoreText;
NSString * honey;
}
-(id)initWithSize:(CGSize)size {
life = 1;
if (self = [super initWithSize:size]) {
/* Setup your scene here */
//self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
SKSpriteNode *bgImage = [SKSpriteNode spriteNodeWithImageNamed:#"BGImage"];
bgImage.position = CGPointMake(self.size.width/2, self.size.height/2);
[self addChild:bgImage];
//Bee movement
SKTexture *texture1 = [SKTexture textureWithImageNamed:#"Bee up.tiff"];
SKTexture *texture2 = [SKTexture textureWithImageNamed:#"Bee down.tiff"];
SKTexture *texture3 = [SKTexture textureWithImageNamed:#"Flower.tiff"];
SKTexture *texture4 = [SKTexture textureWithImageNamed:#"Fly Trap.tiff"];
SKTexture *texture5 = [SKTexture textureWithImageNamed:#"radiation.png"];
SKTexture *texture6 = [SKTexture textureWithImageNamed:#"rad glow.png"];
sprite_bee = [SKSpriteNode spriteNodeWithTexture:texture1];
sprite_bee.position = CGPointMake(160, 100);
sprite_bee.zPosition = 100;
sprite_bee.size = CGSizeMake(110, 110);
SKAction *fly = [SKAction animateWithTextures:#[texture1,texture2] timePerFrame:.2];
[sprite_bee runAction:[SKAction repeatActionForever:fly]];
[self addChild:sprite_bee];
collide = NO;
//Plant creation
speed = 100;
score = 0;
x_flower = 40;
x_Trap1 = 160;
x_Trap2 = 280;
sprite_radGlow = [SKSpriteNode spriteNodeWithImageNamed:#"rad glow.png"];
sprite_radGlow.position = CGPointMake(100,3000);
sprite_radGlow.size = CGSizeMake(140, 140);
sprite_radGlow.zPosition = 50;
SKAction *glow = [SKAction sequence:#[
[SKAction fadeOutWithDuration:.5],
[SKAction fadeInWithDuration:.5],
]];
[sprite_radGlow runAction:[SKAction repeatActionForever:glow]];
[self addChild: sprite_radGlow];
sprite_flower = [SKSpriteNode spriteNodeWithTexture:texture3];
sprite_flower.position = CGPointMake(40, 300);
sprite_flower.size = CGSizeMake(100, 100);
[self addChild:sprite_flower];
sprite_flyTrap = [SKSpriteNode spriteNodeWithTexture:texture4];
sprite_flyTrap.position = CGPointMake(160, 300);
sprite_flyTrap.size = CGSizeMake(100, 100);
[self addChild:sprite_flyTrap];
sprite_flyTrap2 = [SKSpriteNode spriteNodeWithTexture:texture4];
sprite_flyTrap2.position = CGPointMake(280, 300);
sprite_flyTrap2.size = CGSizeMake(100, 100);
[self addChild:sprite_flyTrap2];
sprite_rad = [SKSpriteNode spriteNodeWithTexture:texture5];
sprite_rad.position = CGPointMake(160, 3000);
sprite_rad.size = CGSizeMake(500, 100);
[self addChild:sprite_rad];
}
return self;
}
#end
So I think your issue might be related to SKScene adding a delegate field in iOS 8.
I recommend renaming your internal delegate field to something else, like gameSceneDelgate and see if that fixes your issue.
I have two layers set up like so in one scene:
header file for scene:
#interface GameScene1 : CCScene {
GameLayer *gameLayer;
HUDLayer *hudLayer;
}
Main file for scene:
-(id)init {
self = [super init];
if (self != nil) {
gameLayer = [GameLayer node];
[self addChild:gameLayer];
hudLayer = [HUDLayer node];
[self addChild:hudLayer];
}
return self;
}
HUD layer header:
#interface HUDLayer : CCNode {
CCSprite *background;
CGSize screenSize;
}
-(void)updateMonstersSlayed:(NSString*)value;
HUD layer main:
#implementation HudLayer
-(id)init
{
self = [super init];
if (self)
{
CGSize viewSize = [[CCDirector sharedDirector] viewSize];
monstersSlayed = [CCLabelTTF labelWithString:#"Monsters Killed: 0" fontName:#"Arial" fontSize:15];
monstersSlayed.position = ccp(viewSize.width * 0.85, viewSize.height * 0.1 );
[self addChild:monstersSlayed];
}
return self;
}
-(void)updateMonstersSlayed:(NSString*)value
{
monstersSlayed.string = value;
}
Game Layer main
- (BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair collisionPlayer:(CCNode *)user collisionMonster:(CCNode *)monster
{
if (holdingWeapon)
{
HudLayer *myHud = [[HudLayer alloc] init];
[myHud updateMonstersSlayed:#"Monsters Killed: 1"];
}
}
Simply trying to get it set to where I can set text from the Game Layer to show up in a Label in the Hud Layer.
How would I accomplish this in Cocos2d 3?
There are many ways you can do this. But for the sake of simplicity the easiest way you can do this is via notifications. For example in the hud add:
#implementation HudLayer
- (void)onEnter
{
[super onEnter];
NSNotificationCenter* notiCenter = [NSNotificationCenter defaultCenter];
[notiCenter addObserver:self
selector:#selector(onUpdateMonsterText:)
name:#"HudLayerUpdateMonsterTextNotification"
object:nil];
}
- (void)onExit
{
[super onExit];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)onUpdateMonsterText:(NSNotification *)notification
{
NSDictionary* userInfo = notification.userInfo;
if (userInfo)
{
NSString* text = userInfo[#"text"];
if (text)
{
[self updateMonstersSlayed:text];
}
else
{
CCLOG(#"Monster hud text param is invalid!.");
}
}
else
{
CCLOG(#"Monster hud user info invalid!");
}
}
#end
Then anywhere in your application where you want to update text you can just post the notification. Using your physics collision began example:
- (BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair
*emphasized text*collisionPlayer:(CCNode *)user collisionMonster:(CCNode *)monster
{
if (holdingWeapon)
{
NSDictionary* userInfo = #{#"text" : #"Monsters Killed: 1"};
NSString* notiName = #"HudLayerUpdateMonsterTextNotification";
[[NSNotificationCenter defaultCenter] postNotificationName:notiName
object:self userInfo:userInfo];
}
}
Hope this helps.
You can use methods. Make a method in HUD layer class.
-(void) giveMeMyText:(NSString*)someText
{
do something with my someText
}
Don't forget to make the method visible in HUD.h -(void) giveMeMyText; Then import HUD layer class in GameScene1 #import "HUDlayer.h" and use it.
HUDLayer* myHud = [[HUDLayer alloc] init];
[myHud giveMeMyText:#"say hi!"];
You could use delegation, so your scene would implement GameLayerProtocol and the delegate of GameLayer. That way the scene would be notified of any changes the GameLayer has and act appropriately on the HudLayer.
For example:
// GameLayer class
#protocol GameLayerProtocol <NSObject>
- (void)someThingHappenedInGameLayer;
#end
#interface GameLayer : CCNode
#property (nonatomic, weak) id<GameLayerProtocol> delegate;
#end
#implementation GameLayer
- (void)someActionInGameLayer
{
[self.delegate someThingHappenedInGameLayer];
}
#end
// Scene class
#interface IntroScene : CCScene <GameLayerProtocol>
#end
#implementation IntroScene
// Implement protocol methods
- (void)someThingHappenedInGameLayer
{
//Do something with your HUDLayer here
}
#end
I'm trying to change from gamePlayscene to a GameOverScene, but when the simulator tries to changes the scene, the simulator stops but I don't receive any message in the Log.
GameOverScene.h
#interface GameOverScene : CCScene {
GameOverScene *scene;
//screen size
float winWidth;
float winHeight;
//Game vars
int score;
//Facebook vars
FBSession* session;
NSString *messageStr;
NSString *userid;
}
+ (GameOverScene *)scene;
- (id)init;
#end
GameOverScene.m
#implementation GameOverScene {
}
+ (GameOverScene *)scene
{
return [[self alloc] init];
}
// -----------------------------------------------------------------------
- (id)init
{
if( (self=[super init] )) {
NSLog(#"define tamanho da tela");
winWidth = self.contentSize.width;
winHeight = self.contentSize.height;
NSLog(#"define botao");
// Facebook login button
CCButton *fbLoginButton = [CCButton buttonWithTitle:#"Login with FB" fontName:#"Verdana-Bold" fontSize:30.0f];
fbLoginButton.position = ccp(winWidth/2, winHeight/2);
[fbLoginButton setTarget:self selector:#selector(fbLoginClicked:)];
[self addChild:fbLoginButton];
}
}
I'm calling the GameOverScene this way:
[[CCDirector sharedDirector] replaceScene:[GameOverScene scene]
withTransition:[CCTransition transitionFadeWithDuration:1.0f]];
put "return self" in the -(id)init method after the if block.
I've setup a delegate to take the old and new position of a gauge. However, I can't seem to get a response from the delegate. I could be missing something but I've done a bunch of research and it seems like everything is in order.
MeterView.h
#import <UIKit/UIKit.h>
#import "KTOneFingerRotationGestureRecognizer.h"
#protocol MeterViewDelegate;
#interface MeterView : UIView{
IBOutlet UIImageView *gauge;
IBOutlet UIImageView *needle;
float rotation, oldPos, newPos;
KTOneFingerRotationGestureRecognizer *rgest;
}
#property (nonatomic, weak) id<MeterViewDelegate> delegate;
- (IBAction)handleMovedNeedle;
#end
#protocol MeterViewDelegate <NSObject>
- (void)meterView:(MeterView*)view OldValue:(float)oldval NewValue:(float)newval;
#end
MeterView.m
#import "MeterView.h"
#import <QuartzCore/QuartzCore.h>
#implementation MeterView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(void)handleMovedNeedle{
if ([self.delegate respondsToSelector:#selector(meterView:OldValue:NewValue:)]) {
[self.delegate meterView:self OldValue:oldPos NewValue:newPos];
}
else{
NSLog(#"Delegate call FAIL, needle moved from %f, to %f", oldPos,newPos);
}
}
-(void)awakeFromNib{
gauge = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"gauge.png"]];
needle = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"needle.png"]];
needle.frame = CGRectMake(0,gauge.frame.size.height-(needle.frame.size.height/2), gauge.frame.size.width, needle.frame.size.height);
[self addSubview:gauge];
[self addSubview:needle];
rotation = 0;
rgest = [[KTOneFingerRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotate:)];
rgest.center = CGPointMake(CGRectGetMidX([needle bounds]) + needle.frame.origin.x, CGRectGetMidY([needle bounds]) + needle.frame.origin.y);
[self addGestureRecognizer:rgest];
}
- (void)rotate:(UIRotationGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
oldPos = ([(NSNumber *)[needle.layer valueForKeyPath:#"transform.rotation.z"] floatValue]/3.14)*100;
}
break;
case UIGestureRecognizerStateChanged: {
CGFloat angle = [(NSNumber *)[needle.layer valueForKeyPath:#"transform.rotation.z"] floatValue];
angle += [recognizer rotation];
if (angle >= 0 && angle <= M_PI) {
[needle setTransform:CGAffineTransformRotate([needle transform], [recognizer rotation])];
rotation += [recognizer rotation];
}
}
break;
case UIGestureRecognizerStateEnded: {
newPos = ([(NSNumber *)[needle.layer valueForKeyPath:#"transform.rotation.z"] floatValue]/3.14)*100;
[self handleMovedNeedle];
}
break;
default:
break;
}
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#import "MeterView.h"
#import "KTOneFingerRotationGestureRecognizer.h"
#interface ViewController : UIViewController<MeterViewDelegate>{
IBOutlet MeterView *secondMeter;
IBOutlet MeterView *thirdMeter;
}
#end
ViewController.m
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#define DEG2RAD(degrees) (degrees * 0.01745327) // degrees * pi over 180
#define RAD2DEG(radians) (radians * 57.2957795) // radians * 180 over pi
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
secondMeter = [[MeterView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
secondMeter.delegate = self;
thirdMeter = [[MeterView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
thirdMeter.delegate =self;
}
-(void)meterView:(MeterView *)view OldValue:(float)oldval NewValue:(float)newval{
NSLog(#"Delegate call SUCCESS, need moved from %f, to %f", oldval,newval);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This code:
secondMeter = [[MeterView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
secondMeter.delegate = self;
thirdMeter = [[MeterView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
thirdMeter.delegate =self;
creates 2 instances of your view, but it doesn't add them to a view, so they will never be seen.
So, you probably have some other view instances that are on view and don't have delegates, and these views which have delegates but aren't on display. Presumably from an XIB / storyboard as you have outlets.
Connect the views that are on display to the delegate using the outlets rather than creating new instances.
If you make the delegate property in the view an IBOutlet itself then you can connect the view controller as the delegate in the XIB / storyboard and you don't need to worry about the delegate in code at all...