Why the texture in my SKSpriteNode gets removed when it's done animating? - ios

I have a SKSpriteNode, I'm Trying to animate it in my touchesbegan and the animation works fine but when it's done animating, the default texture for my SKSpriteNode is gone. My R.atlas contains images named R1,R2..R7 ,What am I doing wrong? Thanks a lot.
#interface MyScene()
{
SKSpriteNode * rightTubeNode;
}
#property NSArray* rightTubeAnimationArray;
#end
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKTextureAtlas *rightTubeAtlas = [SKTextureAtlas atlasNamed:#"R"];
rightTubeTexture = [rightTubeAtlas textureNamed:#"R1"];
NSMutableArray *rightAnimFrames = [NSMutableArray array];
for (int i = 1; i <= rightTubeAtlas.textureNames.count; ++i){
NSString *texture =[NSString stringWithFormat:#"R%d",i];
[rightAnimFrames addObject:[rightTubeAtlas textureNamed:texture]];
}
self.rightTubeAnimationArray = rightAnimFrames;
rightTubeNode = [self createRightTubeNode];
rightTubeNode.position = CGPointMake(self.frame.size.width/2+rightTubeNode.frame.size.width/2,50);
rightTubeNode.zPosition = 0.1;
[self addChild:rightTubeNode];
}
return self;
}
- (SKSpriteNode *)createRightTubeNode
{
SKSpriteNode *rightTube = [SKSpriteNode spriteNodeWithTexture:rightTubeTexture];
rightTube = [SKSpriteNode spriteNodeWithTexture:rightTubeTexture];
rightTube.name = #"rightTubeNode";
return rightTube;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
CGPoint nodesLocation = CGPointMake(location.x,self.frame.size.height/2);
if (nodesLocation.x>self.frame.size.width/2) {
SKNode *archerNode = [self childNodeWithName:#"rightTubeNode"];
if (archerNode != nil){
SKAction *animate = [SKAction animateWithTextures:self.rightTubeAnimationArray
timePerFrame: 0.02];
[archerNode runAction:animate];
}
}
}
}

Re-adding the R.atlas (with checking the Create groups options box) without any further changes did the trick, Thanks to #Whirlwind

Related

How to make several sprite nodes each with a special path to follow

I am trying to make a game like flight control where there are x amount of cars and each one has its own path to follow. My idea was to make a car class of subtype SKNode and have a CGMutablePathRef instance variable for each object. I can make the CGMutablePathRef with touchsBegins, touchsMoved, and touchesEnded and figured I could then have each object follow its specific path with the SKAction followPath. Is this a good approach? If not how would you do it? This is what I have so far for the Vehicle class:
Vehicle.h
#interface Vehicle: SKNode
#property (nonatomic, assign) CGMutablePathRef pathToFollow;
#property (nonatomic) CGFloat speed;
#property (nonatomic) int direction;
- (instancetype)initWith:(CGMutablePathRef)pathPassed;
#end
Vehicle.m
#implementation Vehicle : SKNode
#synthesize pathToFollow;
#synthesize speed;
#synthesize direction;
- (id)init
{
self = [super init];
if (self) {
SKSpriteNode *vehicle = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[self addChild:vehicle];
[self setDirection:4];
[vehicle setName:#"car"];
[self setScale:.5];
}
return self;
}
- (id)initWith:(CGMutablePathRef)pathPassed {
self = [super init];
if (self) {
SKSpriteNode *vehicle = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[self addChild:vehicle];
[self setPathToFollow:pathPassed];
[self setDirection:4];
[vehicle setName:#"car"];
[self setScale:.1];
}
return self;
}
#end
EDIT: Adding GameScene in hopes that it will help. I am having a problem with my implementation now where the Vehicle object is following the path but only after it jumps up to the coordinates of the first point + the nodes position on the scene (ex: position = (200, 200), first point = (123, 456) it will go to (323, 656) then follow the path). It draws the line where I want it but then it follows from way up on this coordinate. Thanks again for the help!
GameScene.m
-(void)didMoveToView:(SKView *)view {
_car = [[Vehicle new] init]; // init the Vehicle object then set position
[_car setPosition:CGPointMake(self.scene.size.width/2, self.scene.size.height/2)];
[self addChild_car]; // add the child to the scene
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// get the node that was touched
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
_nodeTouchedFirst = [self nodeAtPoint:positionInScene];
if ([_nodeTouchedFirst.name isEqualToString:#"car"]) {
if (lineNode != NULL) { // remove the previous path
[lineNode removeFromParent];
[CGPathRelease(pathToDraw);
}
//Start a new path and display it on the screen
pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode = [SKShapeNode];
lineNode.path = pathToDraw;
lineNode.strokeColor = [SKColor redColor];
[self addChild:lineNode];
}
} // touchesBegan
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
if ([_nodeTouchedFirst.name isEqualToString:#"car"]) {
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode.path = pathToDraw;
}
} // touchesMoved
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([_nodeTouchedFirst.name isEqualToString:#"car"]) {
SKAction *followPath = [SKAction followPath:pathToDraw asOffset:YES orientToPath:NO duration:2];
[_nodeTouchedFirst runAction:followPath];
lineNode.strokeColor = [SKColor grayColor];
}
}
Some observations that may or may not help:
You're setting the position of _car and adding it to the scene before you give it a path to follow.
You're referencing both _nodeTouchedFirst and nodeTouchedFirst, "car" and "car1".
You're following a path with asOffset:YES, orientToPath:NO, duration:2 (rather than asOffset:NO, orientToPath:YES, speed:2).
direction doesn't correspond to the SKNode zRotation property.

Having issues with touchesBegan method

I have my sound on logo showing up on the screen and the sound off logo appears when i test it. what I want it to do is change from sound on to sound off when I tap on it. The buttons do acknowledge the tap but nothing happens. I don't have much experience in change node images when they're tapped so I don't really know if I used the most efficient code. Can someone look over my code and see what's wrong? Thanks in advance!
#implementation MyScene
{
SKSpriteNode *soundLogo;
SKSpriteNode *soundOff;
}
-(void) addSoundOff:(CGSize)size {
soundOff = [SKSpriteNode spriteNodeWithImageNamed:#"soundOff"];
//resize sprite
soundOff.size = CGSizeMake(soundOff.size.width/2.25, soundOff.size.height/2.25);
//position it
soundOff.position = CGPointMake(65, 25);
//name sound off
soundOff.name = #"soundOff";
soundOff.zPosition = 0;
soundOff.alpha = 0;
//[self addChild:soundOff];
}
-(void) addSoundOn:(CGSize)size {
soundLogo = [SKSpriteNode spriteNodeWithImageNamed:#"soundLogo"];
//resize sprite
soundLogo.size = CGSizeMake(soundLogo.size.width/2.25, soundLogo.size.height/2.25);
//position sprite
CGPoint myPoint = CGPointMake(65, 25);
soundLogo.position = myPoint;
//name sound logo
soundLogo.name = #"soundOn";
//add action
//soundLogo.alpha = 1;
soundLogo.zPosition = 100;
[self addChild:soundLogo];
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"soundOn"]) {
soundOff.zPosition = 100;
soundOff.alpha = 1;
soundLogo.alpha = 0;
soundLogo.zPosition = 0;
// [node removeFromParent];
// [node addChild:soundOff];
NSLog(#"sound on is pressed");
}
if ([node.name isEqualToString:#"soundOff"]) {
soundLogo.zPosition = 100;
soundLogo.alpha = 1;
soundOff.alpha = 0;
soundOff.zPosition = 0;
// [node removeFromParent];
// [node addChild:soundLogo];
NSLog(#"sound off is pressed");
}
}
It seems nothing wrong in your addSoundOff and addSoundOn functions. What you can do as one of the solutions to change alpha and zPosition properties of SKSpriteNode . And also createsoundOff and soundLogo as instance variables under the implementation. So here is the all code:
#import "MyScene.h"
#implementation MyScene
{
SKSpriteNode *soundLogo;
SKSpriteNode *soundOff;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
[self addSoundOn:self.size];
[self addSoundOff:self.size];
}
return self;
}
-(void) addSoundOff:(CGSize)size {
soundOff = [SKSpriteNode spriteNodeWithImageNamed:#"soundOff"];
//resize sprite
soundOff.size = CGSizeMake(soundOff.size.width/2.25, soundOff.size.height/2.25);
//position it
soundOff.position = CGPointMake(65, 25);
//name sound off
soundOff.name = #"soundOff";
soundOff.alpha = 0;
soundOff.zPosition = 0;
[self addChild:soundOff];
}
-(void) addSoundOn:(CGSize)size {
SKTexture *soundOn = [SKTexture textureWithImageNamed:#"soundLogo"];
soundLogo = [SKSpriteNode spriteNodeWithTexture:soundOn];
//resize sprite
soundLogo.size = CGSizeMake(soundLogo.size.width/2.25, soundLogo.size.height/2.25);
//position sprite
CGPoint myPoint = CGPointMake(65, 25);
soundLogo.position = myPoint;
//name sound logo
soundLogo.name = #"soundOn";
//add action
soundLogo.zPosition = 100;
[self addChild:soundLogo];
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//sound logo pressed to turn sound on/off
if ([node.name isEqualToString:#"soundOn"]) {
soundOff.alpha = 1;
soundOff.zPosition = 100;
soundLogo.alpha = 0;
soundLogo.zPosition = 0;
NSLog(#"sound on is pressed");
}
if ([node.name isEqualToString:#"soundOff"]) {
soundOff.alpha = 0;
soundOff.zPosition = 0;
soundLogo.alpha = 1;
soundLogo.zPosition = 100;
NSLog(#"sound off is pressed");
}
}
#end

sprite kit: verify if touch is inside a node

I'm trying to figure out how to detect if I touch a node in sprite kit. I thought it was function like this in UIView:
[myView pointInside:point withEvent:nil];
But so far I can not have found an alternative to this. What I'm trying to accomplish is to know I have touch the sprite node in the screen.
I'll really appreciate if you can help me to I acomplish this
Here is what I have in my code. I'm adding animated node:
-(void)addMyNodeAnimated
{
NSMutableArray *myNodeArray = [NSMutableArray array];
NSArray *animatedFrames = [NSArray new];
SKTextureAtlas *AnimatedAtlas = [SKTextureAtlas atlasNamed:#"pc2"];
int numImages = (int)AnimatedAtlas.textureNames.count;
for (int i=1; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"%#-%d", #"pc2", i];
SKTexture *temp = [AnimatedAtlas textureNamed:textureName];
[myNodeArray addObject:temp];
}
animatedFrames = myNodeArray;
SKTexture *temp = animatedFrames[0];
SKSpriteNode *animationNode = [SKSpriteNode spriteNodeWithTexture:temp];
animationNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
animationNode.userInteractionEnabled = YES;
animationNode.name = #"AnimationNode";
[self addChild:animationNode];
[animationNode runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:animatedFrames
timePerFrame:0.1f
resize:NO
restore:YES]] withKey:#"AnimationRuning"];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint _touchLocation = [[touches anyObject] locationInNode:self];
SKNode *node = [self nodeAtPoint:_touchLocation];
if (node != nil)
{
NSLog(#"node name %#", node.name);
}
}
When I touch the node in the screen the node returns null.
Any of know why is this?
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"yourSpriteName"] && [node.name isEqualToString:#"yourEffectName"]) {
//Whatever you want.
}
}

How can i remove a SKSpriteNode from parent, when the background is the same color as the sprite?

Im making a game where the colour of a square will change every second and the background will also change colour every second, the user has to tap the square when it is the same colour as the background and the score will increase. But i cant work out how to do this.
This is the code i have so far:
#import "MyScene.h"
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
[self performSelector:#selector(backgrounds) withObject:nil ];
[self performSelector:#selector(createSquare) withObject:nil afterDelay:0];
[self performSelector:#selector(createPSquare) withObject:nil afterDelay:0];
}
return self;
}
-(void) backgrounds {
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"blueOutline"];
background.name = #"blueOutline";
background.size = CGSizeMake(320, 480);
background.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
[self addChild:background];
//meathod sequence at interval
}
-(void) createSquare {
SKSpriteNode *blueSprite = [SKSpriteNode spriteNodeWithImageNamed:#"blue"];
blueSprite.name = #"blueSprite";
blueSprite.size = CGSizeMake(50, 50);
blueSprite.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
[self addChild:blueSprite];
//meathod sequence at interval
}
-(void) createPSquare {
SKSpriteNode *pinkSprite = [SKSpriteNode spriteNodeWithImageNamed:#"pink"];
pinkSprite.name = #"pinkSprite";
pinkSprite.size = CGSizeMake(50, 50);
pinkSprite.position = CGPointMake(CGRectGetMidX(self.frame)+10,CGRectGetMidY(self.frame));
[self addChild:pinkSprite];
//meathod sequence at interval
}
-(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];
if ([node.name isEqualToString:#"blueSprite"]) {
[node runAction:[SKAction removeFromParent]]; //Removes Sprite from parent
}
if ([node.name isEqualToString:#"pinkSprite"]) {
[node runAction:[SKAction removeFromParent]]; //Removes Sprite from parent
}
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
I suggest you generalize the code that creates the squares and backgrounds to simplify adding more colors to your game. Here's an example of how to do that:
Define a type that identifies the type of sprite node to create
typedef NS_ENUM (NSInteger, SpriteType) {
SpriteTypeBackground,
SpriteTypeSquare
};
This method adds a square and a background to the scene each with a randomly selected color
- (void) addSquareAndBackground {
_background = [self createSpriteWithType:SpriteTypeBackground];
_square = [self createSpriteWithType:SpriteTypeSquare];
}
This removes the square and background from the scene
- (void) removeSquareAndBackground {
[_background removeFromParent];
[_square removeFromParent];
}
This creates either a square or a background sprite based on the specified type
-(SKSpriteNode *) createSpriteWithType:(SpriteType)type {
// Select a color randomly
NSString *colorName = [self randomColorName];
SKSpriteNode *sprite;
if (type == SpriteTypeBackground) {
NSString *name = [NSString stringWithFormat:#"%#Outline",colorName];
sprite = [SKSpriteNode spriteNodeWithImageNamed:name];
sprite.name = name;
sprite.size = CGSizeMake(320, 480);
}
else {
sprite = [SKSpriteNode spriteNodeWithImageNamed:colorName];
sprite.name = [NSString stringWithFormat:#"%#Sprite",colorName];
sprite.size = CGSizeMake(50, 50);
}
sprite.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
[self addChild:sprite];
return sprite;
}
Randomly select a color name
// Set the total number of colors here
#define kNumberOfColors 2
- (NSString *) randomColorName {
NSString *colorName;
switch (arc4random_uniform(kNumberOfColors)) {
case 0:
colorName = #"blue";
break;
case 1:
colorName = #"pink";
break;
// Add more colors here
default:
break;
}
return colorName;
}
Add this to your touchesBegan method to test for a color match
if (node == _square) {
// Extract the color name from the node name
NSArray *squareNameParts = [node.name componentsSeparatedByCharactersInSet:[NSCharacterSet uppercaseLetterCharacterSet]];
// Extract the color name from the node name
NSArray *backgroundNameParts = [_background.name componentsSeparatedByCharactersInSet:[NSCharacterSet uppercaseLetterCharacterSet]];
// Compare if the colors match
if ([backgroundNameParts[0] isEqualToString: squareNameParts[0]]) {
NSLog(#"Score");
}
}
All that's left is to create an SKAction that calls addSquareAndBackground, waits for one second, and then calls removeSquareAndBackground. Lather, rinse, repeat!
EDIT: Add this above your #implementation MyScene statement:
#interface MyScene()
#property SKSpriteNode *background;
#property SKSpriteNode *square;
#end

How to remove a child SKSpritenode from SKNode?

i will explain a part of my code. I have Spritenodes (images) who are moving down on the screen.
SKTexture* Squaretexture = [SKTexture textureWithImageNamed:#"squaregreen"];
SquareTexture.filteringMode = SKTextureFilteringNearest;
Square = [SKSpriteNode spriteNodeWithTexture:SquareTexture];
Square.name = #"square";
.
.
.
[_objects addChild:Square];
_objects is a SKNode and Square is a SKSpriteNode. Now there is my code: every one second there is one square, who came from "over the screen" and is moving to the bottom. (Also there are more then one squares on the screen).
Now I want this: When I touch a square it should be "deleted" or hidden, but only the one who i touch. With my code, when i touch all squares are deleted or nothing. I tried with removefromparent and removechild, but i couldn't solve it.
-(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(#"Point in myView: (%f,%f)", location.x, location.y);
if ([node.name isEqualToString:#"Square"]) {
[Square removeFromParent];
[Square removeAllChildren];
}
}
Do you have a suggestion how can I do it?
Thanks for Answers.
Mehmet
You almost had it right. The trick is that you need to have a unique identifier for each object (sprite) that you create and then store those objects in an array for later use.
The code below creates 5 sprites and gives them unique names: Sprite-1, Sprite-2, etc...
Whenever a touch is registered, it extracts the touched node's name, searches the array for the matching object, removes the object from the view and lastly removes the object from the array.
Note that my sample code is based on landscape view.
#import "MyScene.h"
#implementation MyScene
{
NSMutableArray *spriteArray;
int nextObjectID;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
spriteArray = [[NSMutableArray alloc] init];
nextObjectID = 0;
// create 5 sprites
for (int i=0; i<5; i++)
{
SKSpriteNode *mySprite = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(30, 30)];
nextObjectID ++; // increase counter by 1
mySprite.name = [NSString stringWithFormat:#"Sprite-%i",nextObjectID]; // add unique name to new sprite
mySprite.position = CGPointMake(50+(i*70), 200);
[spriteArray addObject:mySprite];
[self addChild:mySprite];
}
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode: self];
SKNode *node = [self nodeAtPoint:location];
NSLog(#"touched node name: %#",node.name);
NSLog(#"objects in spriteArray: %lu",(unsigned long)[spriteArray count]);
NSMutableArray *discardedItems = [NSMutableArray array];
for(SKNode *object in spriteArray)
{
if([object.name isEqualToString:node.name])
{
[object removeFromParent];
[discardedItems addObject:object];
}
}
[spriteArray removeObjectsInArray:discardedItems];
NSLog(#"objects in spriteArray: %lu",(unsigned long)[spriteArray count]);
}
-(void)update:(CFTimeInterval)currentTime
{
//
}
#end

Resources