I'm trying to subclass an SKNode class by using a custom init method.
My custom init method looks like this:
//interface
#interface BlocksLayer : SKNode
-(instancetype)initWithDirection(BlocksLayerMotionDirection)direction;
#end
//implementation
-(instancetype)initWithDirection:(BlocksLayerMotionDirection)direction{
self=[super init];
if (self) {
self.direction=direction;
[self makeLayer];
}
return self;
}
-(void)makeLayer{
//scene size has always width=0 and height=0
NSLog(#"%f",self.scene.size.width);
NSLog(#"%f",self.scene.size.height);
}
When I initialize the class with my custom method scene.width and scene.height are always zero.
Instead if I use the static node method initialization the scene.size contains valid values.
Do you have any idea what is the problem?
Is this a correct way to subclass a SKNode and is it a valid strategy to implement a non-static custom initialization method?
Many thanks,
Domenico
As far as I can tell, the [SKNode node]; method sets up the self.scene property (with most likely default values, something like 1024x768). The code you provided doesn't create the self.scene property, and therefore it doesn't have default values.
When the SKNode is added to the scene, the self.scene property will point to the SKScene that it was added to (this will always point to the root SKScene even if it is nested like SKScene->SKNode->SKNode). Then you can read the self.scene.size to get the size of the scene.
If you need to read the values before adding to the scene, you can do:
Pass in a reference to the SKScene in your custom init method and set self.scene to it.
You can pass the size in as a parameter.
You can hard code it in.
These are ranked by what I would do.
Related
I subclass the SKSpriteNode to create a SKButton in SKButton.h
#interface SKButton : SKSpriteNode
now I want to change the button image by method in SKButton.m
self = [SKSpriteNode spriteNodeWithImageNamed:image];
here self is SKButton but it is giving me error
Cannot assign to 'self' outside of a method in the init family
Incompatible pointer types assigning to 'SKButton *' from 'SKSpriteNode *'
I also tried this in SKButton.m
self = [self spriteNodeWithImageNamed:image];
It gives me error
No visible #interface for 'SKButton' declares the selector
'spriteNodeWithImagedNamed:'
spriteNodeWithImageNamed: is a class method on SKSpriteNode that returns a new SKSpriteNode using the supplied image. Essentially, what you are trying to do with this line -
self = [SKSpriteNode spriteNodeWithImageNamed:image];
is change the current object into a new object - which you can't do. Even if you could, it would be a new SKSpriteNode, not a new SKButton.
What you need to do is manipulate the texture property of your node -
self.texture=[SKTexture textureWithImageNamed:image];
In this line:
self = [SKSpriteNode spriteNodeWithImageNamed:image];
you are downcasting a SKSpriteNode to SKButton which is incorrect as a SKSpriteNode instance does not have the extra implementation provided by SKButton.
Since SKButton is a SKSpriteNode subclass it contains all its functionality so you can just do:
- (void)buttonClicked {
[self setTexture:[SKTexture textureWithImageNamed:"buttonClicked.png"]];
}
I have a class called Blade.h and I make A SKSpriteNode on that!
now I make new instance of that class on Main Class.
#import "Blade.h"
#implementation Blade
-(void) GenerateBalde
{
SKSpriteNode *blade = [SKSpriteNode spriteNodeWithImageNamed:#"blade"];
[blade runAction:[SKAction rotateByAngle:M_PI duration:1]];
[self addChild:blade];
}
#end
now, I want to have an instance of this object in my main class and I wanna detect the physic collision the Blade with another Sprites in my main class!
I know how to make an instance , but I wanna know how to detect the collision of different objects in different classes + physic!
please advice!
It would be better if your Blade was a subclass of SKSpriteNode and then in its init method use:
if (self = [super initWithImageNamed:#"blade"]){
//more code
}
return self;
Another solution is to use a property, so instead of creating a SKSpriteNode in generateBlade method use this:
In Blade.h add #property (nonatomic, retain) SKSpriteNode *bladeSprite.
In your generateBlade method, use this:
self.bladeSprite = [SKSpriteNode spriteNodeWithImageNamed:#"blade"];
[self.bladeSprite runAction:[SKAction rotateByAngle:M_PI duration:1]];
[self addChild:self.bladeSprite];
Then, in your MainScene, in update: method, you can check the collision using this code:
-(void)update:(CFTimeInterval)currentTime {
if (CGRectIntersectsRect(otherObject.frame, blade.bladeSprite.frame){
//Collisioning!
}
}
If you are using a SKSpriteNode subclass as mentioned in first lines, you could just do blade.frame instead of blade.bladeSprite.frame (you won't need to set a property).
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 have animation layers stored within SpriteBuilder. I am calling it on a touch began method
heroCharacter.m
#implementation heroCharacter{
CCNode *_heroNode;
staminaNode *_staminaReference;
}
- (void)touchBegan:(UITouch *)touches withEvent:(UIEvent *)event
//Play animation
CCBAnimationManager* animationManager = _heroNode.userObject;
[animationManager runAnimationsForSequenceNamed:#"ouch"];
}
This works fine.
I am then trying to call the animation in a custom method in another file but it then doesn't work. I have no idea why? I have tested to make sure the method is called and it is. but the actual animation isn't called. It is the same code as used in the touchBegan
-(void)sleepingHero {
//Play animation
CCBAnimationManager* animationManager = _heroNode.userObject;
[animationManager runAnimationsForSequenceNamed:#"ouch"];
No Idea how to debug this.
My custom method is being called like this in another file. Called bedroomScene.:
#implementation .....{
heroCharacter *heroHolder;
}
then in didload:
heroHolder = [[heroCharacter alloc] init];
then in another method:
[heroHolder sleepingHero];
By executing:
heroHolder = [[heroCharacter alloc] init];
in your other file, you are instantiating a new node that has no relationship whatsoever with the original _heroNode node in your first class.
What you should do is passing the actual _heroNode from your first class to your second class, so that you can reference it in the latter. There are multiple ways of doing it:
you could define a property in your second class and set it from the first (supposing the first class instantiates the second);
you could add an argument to your second class' initialization method and pass _heroNode in it;
or, you could expose _heroNode in your first class through a property (or custom accessor method) and then pass a reference to the first class into your second class.
Hope this helps.
EDIT:
you could try something like this (where you currently create your bedroomScene):
bedroomScene = [BedRoomScene scene];
bedroomScene.heroHolder = _heroNode;
For this you will need to make your heroHolder ivar into a property:
#property (nonatomic, weak) CCNode* heroHolder;
(in your BedRoomScene interface or class extension).
When is sleepingHero being called? If it is called before didLoadFromCCB is called it means that the code connections are not set up yet and you cannot reference anything created in SpriteBuilder.
Suppose you implement a custom table view and a custom view controller (which mostly mimics UITableViewControllers behaviour, but when initialized programmatically, ...
#interface Foo : MyCustomTableViewController ...
Foo *foo = [[Foo alloc] init];
... foo.view is kind of class MyCustomTableView instead of UITableView:
// MyCustomTableView.h
#protocol MyTableViewDelegate <NSObject, UITableViewDelegate>
// ...
#end
#protocol MyTableViewDataSource <NSObject, UITableViewDataSource>
// ...
#end
#interface MyCustomTableView : UITableView
// ...
#end
// MyCustomTableViewController.h
#interface MyCustomTableViewController : UIViewController
// ...
#end
How should you implement/override init methods in correct order/ways so that you could create and use an instance of MyCustomTableView both by subclassing MyCustomTableViewController programmatically or from any custom nib file by setting custom class type to MyCustomTableView in Interface Builder?
It important to note that this is exactly how UITableView (mostly UIKit for that matter) works right now: a developer could create and use either programmatically or by creating from nib, whether be it File owner's main view or some subview in a more complex hierarchy, just assign data source or delegate and you're good to go...
So far I managed to get this working if you subclass MyCustomTableViewController, where I will create an instance of MyCustomTableView and assign it to self.view in loadView method; but couldn't figure out how initWithNibName:bundle:, initWithCoder:, awakeFromNib, awakeAfterUsingCoder:, or whatever else operates. I am lost in life cycle chain and end up with a black view/screen each time.
Thanks.
It is a real mystery how the UITableViewController loads its table regardless of if one is hooked up in interface builder, however I have came up with a pretty good way to simulate that behavior.
I wanted to achieve this with a reusable view controller that contains a MKMapView, and I figured out a trick to make it happen by checking the background color of the view.
The reason this was hard is because any call to self.view caused the storyboard one to load or load a default UIView if didnt exist. There was no way to figure out if inbetween those 2 steps if the user really didn't set a view. So the trick is the one that comes from a storyboard has a color, the default one is nil color.
So now I have a mapViewController that can be used in code or in storyboard and doesn't even care if a map was set or not. Pretty cool.
- (void)viewDidLoad
{
[super viewDidLoad];
//magic to work without a view set in the storboard or in code.
//check if a view has been set in the storyboard, like what UITableViewController does.
//check if don't have a map view
if(![self.view isKindOfClass:[MKMapView class]]){
//check if the default view was loaded. Default view always has no background color.
if([self.view isKindOfClass:[UIView class]] && !self.view.backgroundColor){
//switch it for a map view
self.view = [[MKMapView alloc] initWithFrame:CGRectZero];
self.mapView.delegate = self;
}else{
[NSException raise:#"MapViewController didn't find a map view" format:#"Found a %#", self.view.class];
}
}
The strategy I've used when writing such classes has been to postpone my custom initialization code as late as possible. If I can wait for viewDidLoad or viewWillAppear to do any setup, and not write any custom code in init, initWithNibName:bundle: or similar methods I'll know that my object is initialized just like the parent class no mater what way it was instantiated. Frequently I manage to write my classes without any overrides of these init methods.
If I find that I need to put my initialization code in the init methods my strategy is to write just one version of my initialization code, put that in a separate method, and then override all the init methods. The overridden methods call the superclass version of themselves, check for success, then call my internal initialization method.
If these strategies fail, such that it really makes a difference what way an object of this class is instantiated, I'll write custom methods for each of the various init methods.
This is how I solved my own issue:
- (void)loadView
{
if (self.nibName) {
// although docs states "Your custom implementation of this method should not call super.", I am doing it instead of loading from nib manually, because I am too lazy ;-)
[super loadView];
}
else {
self.view = // ... whatever UIView you'd like to create
}
}