SpriteKit: textureFromNode method not working in initWithSize method - objective-c

This doesn't work – the newNode's texture is nil:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKSpriteNode *node = [SKSpriteNode spriteNodeWithColor:[UIColor orangeColor] size:CGSizeMake(100, 100)];
SKTexture *texture = [self.view textureFromNode:node];
SKSpriteNode *newNode = [SKSpriteNode spriteNodeWithTexture:texture];
[self addChild:newNode];
}
return self;
}
This does work:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKSpriteNode *node = [SKSpriteNode spriteNodeWithColor:[UIColor orangeColor] size:CGSizeMake(100, 100)];
SKAction *createNewNode = [SKAction runBlock:^{
SKTexture *texture = [self.view textureFromNode:node];
SKSpriteNode *newNode = [SKSpriteNode spriteNodeWithTexture:texture];
[self addChild:newNode];
}];
[self runAction: createNewNode];
}
return self;
}
Can someone please explain why!? Thanks!
In case you're wondering why the node is not added the scene: The Apple documentation for textureFromNode states: "The node being rendered does not need to appear in the view’s presented scene."

My guess is: at the point where you are creating the sprite node (inside the scene's initWithSize:) the node graph is not fully set up yet. The scene isn't connected with (presented by) the view, and therefore it is not set up for rendering.
It's quite possible that SKSpriteNode textures for sprites using a color are only created after the scene is presented. This would explain why running the block action works.
You can probably fix this also by simply moving the code from initWithSize: to the scene's didMoveToView method, which is called after the scene was presented on the view.

Related

CGRectContainsPoint using shape opposed to frame

So we have our detection here
-(void)checkInFOVWithPlayer:(Player *)player andEnemy:(Player *)enemy {
SKNode *fovNode = [player childNodeWithName:player.playersFOVName];
SKNode *node = [self childNodeWithName:#"enemy"];
CGPoint newPosition = [self convertPoint:node.position toNode:fovNode.parent];
if (CGRectContainsPoint(fovNode.frame, newPosition)) {
[self playerAimAtEnemy:enemy withPlayer:player];
}
}
And our implementation for the field of vision
SKShapeNode *fov = [SKShapeNode node];
UIBezierPath *fovPath = [[UIBezierPath alloc] init];
[fovPath moveToPoint:CGPointMake(0, 0)];
[fovPath addLineToPoint:CGPointMake(fovOpposite *-1, fovDistance)];
[fovPath addLineToPoint:CGPointMake(fovOpposite, fovDistance)];
[fovPath addLineToPoint:CGPointMake(0, 0)];
fov.path = fovPath.CGPath;
fov.lineWidth = 1.0;
fov.strokeColor = [UIColor clearColor];
fov.antialiased = NO;
fov.fillColor = [UIColor greenColor];
fov.alpha = 0.2;
fov.name = #"playerFOV";
[_playerImage addChild:fov];
Now, this works. However, the detection range for the "field of vision" is not actually the boundaries of the BezierPath, it's in fact the CGRect that creates the image.
So, the detection will run, even if it's outside of the visual field of vision.
I'm curious as to whether there's an easy fix for this, as I don't really want to go down physics body paths if I don't need to.
Finally I figured out what was required.
I had to redraw the path and optimise it for each frame, after that;
if (CGPathContainsPoint(fovPath.CGPath, nil, newPosition, NO)) {}
Ended my problems.

SpriteKit SKCropNode Masks everything

I am trying to mask a node with SKCropNode class. I am doing it with my own init method in SKCropNode inherited class:
- (id)init {
self = [super init];
if (self) {
self.maskNode = [[SKSpriteNode alloc] initWithColor:[SKColor blackColor] size:(CGSize){50,50}];
SKSpriteNode *contentNode = [[SKSpriteNode alloc] initWithImageNamed:#"Spaceship"];
[self addChild: contentNode];
self.userInteractionEnabled = YES;
}
return self;
}
It works as expected:
Now, I want to add background to the scene and a cropped spaceship with following code:
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
//
//background
//
SKSpriteNode * background = [SKSpriteNode spriteNodeWithImageNamed:#"background_game"];
background.position = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view.frame) - CGRectGetMinY(view.frame));
[self addChild:background];
TurretCropNode * cropNode = [[TurretCropNode alloc] init];
cropNode.position = (CGPoint){view.frame.size.width/2,view.frame.size.height/2};
[self addChild:cropNode];
}
I have just added few lines with adding background at the beginning (cropped node lines remain the same)
Here what I got now:
Is it an expected behavior? Why cropped spaceship disappeared?
I am guessing that the image as shown below is your designated background. Have you checked your nodes zPosition?
If you want to show your spaceship infront of your background, you will have to adjust the zPosition to be higher than that of the background sprite.

UIImageView animating on UIBezierPath not detecting collision

I have a UIImageView travelling on a UIBezierPath:
- (void)viewDidLoad {
[super viewDidLoad];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(60, 60)];
[path addLineToPoint:CGPointMake(70, 70)];
[path addLineToPoint:CGPointMake(80, 120)];
[self.thingy setImage:[UIImage imageNamed:#"titles.png"]];
[self.thingy.layer setPosition:CGPointMake(60, 60)];
[self.view.layer addSublayer:self.thingy.layer];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = path.CGPath;
anim.rotationMode = kCAAnimationRotateAuto;
anim.duration = 8.0;
[self.thingy.layer addAnimation:anim forKey:#"move"];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(tester)
userInfo:nil
repeats:YES];
}
Goal: I want to detect whether thingy collides with a barrier. Here is tester:
- (void) tester {
NSLog(#"calling method");
if (CGRectIntersectsRect(self.thingy.frame, self.barrier.frame)) {
NSLog(#"collided");
}
}
barrier and thingy are both UIImageViews declared as weak and nonatomic in the header file.
Problem: tester is always being called every second, but when the two UIImageViews collide nothing happens. Any help to try and make this work?
Since thingy is being animated, you won't get accurate values from its frame. If I recall correctly, once the animation starts, the view will immediately have the final frame from the end of the animation. If you want to test thingy's frame while it is in motion, you have to test the frame of thingy's layer's presentationLayer. Something like this:
- (void) tester {
NSLog(#"calling method");
if (CGRectIntersectsRect(((CALayer *)[self.thingy.layer presentationLayer]).frame, self.barrier.frame)) {
NSLog(#"collided");
}
}
Note: If your barrier is in motion too, you'll have to use its presentationLayer as well.

Draw a frame in Spritekit

I want to draw a frame like in the picture below, where the white part inside the circle is clear(transparent) so that i can see the scene in the background, and the gray part is non-transparent.
I've looked into the CropNodes but I didn't find an answer. Everything should be added to the SKViewat the end.
this method works you can filter it more i am using shape node you can your texture shape node having some memory leaks in sprite kit
#import "milkhuntMyScene.h"
#implementation milkhuntMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
[self maskMe];
}
return self;
}
-(void)maskMe
{
//replace tomask to 1 pixel background and 778 width or acc to ur game
// SKSpriteNode *whiteArea = [SKSpriteNode spriteNodeWithImageNamed:#"pixel1.png"];
//[self addChild: whiteArea];
//whiteArea.xScale=1024;
SKSpriteNode *toMask = [SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size: CGSizeMake(2024, 778*2)];
SKSpriteNode *mask = [SKSpriteNode spriteNodeWithImageNamed:#"maskarea.png"];
mask.position=CGPointMake(400, 300);
//
CGRect circle = CGRectMake(0, 0,mask.frame.size.width,mask.frame.size.height);
SKShapeNode *shapeNode = [[SKShapeNode alloc] init];
shapeNode.path = [UIBezierPath bezierPathWithOvalInRect:circle].CGPath;
shapeNode.fillColor = SKColor.yellowColor;
shapeNode.strokeColor = [SKColor blueColor];
shapeNode.lineWidth=20;
shapeNode.position=CGPointMake(mask.position.x/2, 100);
[self addChild:shapeNode];
//
SKCropNode *cropNode = [SKCropNode node];
[cropNode addChild: toMask];
[cropNode setMaskNode: mask];
[self addChild: cropNode];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end

How do I get the NAME of a UIColor/SKColor (i.e. [UIColor redColor] --> #"red"

I kinda asked the question in the title but suppose I have a UIColor (i.e. [UIColor redColor]. How do i get an NSString to equal #"red" OR how can i find out is a color is the same as [UIColor redColor]?
For example I have tried,
SKSpriteNode *player = [SKSpriteNode spriteNodeWithImageNamed:#"image"];
[player runAction:[SKAction colorWithColor:[SKColor blueColor] withColorBlendFactor:1 duration:0.01];
[self addChild:player];
Then later:
if (player.color == [SKColor blueColor]) { //Also I have tried 'isEqual' and that didn't work either!
}
PLEASE HELP ME! Thanks in advance!
I assume you do not know about userData as rmaddy already suggested so I will give you some sample code. First though, this is what Apple says about the userData:
You use this property to store your own data in a node. For example, you might store game-specific data about each node to use inside your game logic. This can be a useful alternative to creating your own node subclasses to hold game data.
Sprite Kit does not do anything with the data stored in the node. However, the data is archived when the node is archived.
#implementation MyScene
{
SKSpriteNode *frank;
SKSpriteNode *sally;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
frank = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(50, 50)];
frank.position = CGPointMake(100, 100);
frank.userData = [NSMutableDictionary dictionary];
[frank.userData setValue:#"blue" forKey:#"color"];
[self addChild:frank];
sally = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(50, 50)];
sally.position = CGPointMake(200, 100);
sally.userData = [NSMutableDictionary dictionary];
[sally.userData setValue:#"red" forKey:#"color"];
[self addChild:sally];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKNode *node = [self nodeAtPoint:location];
NSMutableDictionary *tempDictionary = node.userData;
NSString *tempString = [NSString stringWithFormat:#"%#",[tempDictionary objectForKey:#"color"]];
if([tempString isEqualToString:#"blue"])
NSLog(#"My color is blue");
if([tempString isEqualToString:#"red"])
NSLog(#"My color is red");
}