Changing sprite nodes parent after TouchesEnded - ios

I'm attempting to change the parent node of a sprite after it is dropped, but I'm getting a nil error.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *gamePiece = [self pickObject];
gamePiece.position = location;
gamePiece.physicsBody.dynamic = NO;
[self addChild:gamePiece];
_currentTouch = touch;
currentGamePiece = gamePiece;
CGPoint touchLocation = [touch locationInNode:self.scene];
if(touchLocation.y > 350)
{
_bg.position = CGPointMake(_bg.position.x, _bg.position.y-2);
}
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
if ([touch isEqual:_currentTouch])
{
currentGamePiece.position = location;
}
CGPoint touchLocation = [touch locationInNode:self.scene];
if(touchLocation.y > 350)
{
_bg.position = CGPointMake(_bg.position.x, _bg.position.y-2);
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
if ([touch isEqual:_currentTouch])
{
currentGamePiece.position = location;
currentGamePiece.physicsBody.dynamic = YES;
SKAction *removeNode = [SKAction removeFromParent];
[gamePiece runAction: removeNode];
[_bg addChild:gamePiece];
}
}
}
Maybe it has something to do with the switch to "currentGamePiece", but when I try to remove "currentGamePiece" from its parent, I get an error like "attempting to add node that already has a parent". I may just not be going about it the right way. What's the problem?

Try replacing this code...
SKAction *removeNode = [SKAction removeFromParent];
[gamePiece runAction: removeNode];
[_bg addChild:gamePiece];
with this...
[gamePiece removeFromParent];
[_bg addChild:gamePiece];
The issue is gamePiece is added to the _gb before the SKAction has a chance to remove it from its parent. The app crashes because a node can't have two parents.

Related

Sprite Kit physics causes too much shaking

In my app, once the gamepieces are stacked to a certain height, the screen starts to move down to allow more room for building. The problem is that everytime the screen moves down, the gamepieces shake so much that they topple over.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *gamePiece = [self pickObject];
gamePiece.position = location;
gamePiece.physicsBody.dynamic = NO;
[self addChild:gamePiece];
_currentTouch = touch;
currentGamePiece = gamePiece;
CGPoint touchLocation = [touch locationInNode:self.scene];
if(touchLocation.y > 350)
{
_bg.position = CGPointMake(_bg.position.x, _bg.position.y-2);
}
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
if ([touch isEqual:_currentTouch])
{
currentGamePiece.position = location;
}
CGPoint touchLocation = [touch locationInNode:self.scene];
if(touchLocation.y > 350)
{
_bg.position = CGPointMake(_bg.position.x, _bg.position.y-2);
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
if ([touch isEqual:_currentTouch])
{
currentGamePiece.position = location;
currentGamePiece.physicsBody.dynamic = YES;
}
}
}

Change SpriteNodes coordinates with User touch

I'm currently programming a small game with SpriteKit in Xcode for the iOS platform. I wrote a code which adds some SpriteNodes on 2 different "lines", so half of the nodes are moving with the y-coordinate 100 and the others with y = 200, and they're all moving from the left side to the right which an infinity loop. Now I want that the user can touch on one SpriteNode, then moves his finger to another SpriteNode, but it has to be located on another line, then removes his finger and the SpriteNode with the TOUCHBEGAN should change its y-coordinate with the TOUCHEND Node. How can I accomplish this?
-(void)add
{
SKSpriteNode *sprite2 = [SKSpriteNode spriteNodeWithImageNamed:#"test1.png"];
sprite2.position = CGPointMake(-40, self.frame.size.height / 2);
sprite2.size = CGSizeMake(100, 32);
SKSpriteNode *sprite3 = [SKSpriteNode spriteNodeWithImageNamed:#"test.png"];
sprite3.position = CGPointMake(-40, (self.frame.size.height / 2) + 90);
sprite3.size = CGSizeMake(32, 100);
[self addChild:sprite1];
[self addChild:sprite2];
SKAction *actionMove1 = [SKAction moveTo:CGPointMake(400, (self.frame.size.height / 2) - 90) duration:12];
SKAction *actionMove2 = [SKAction moveTo:CGPointMake(200, (self.frame.size.height / 2)) duration:12];
SKAction *actionMoveDone = [SKAction removeFromParent];
[sprite1 runAction:[SKAction sequence:#[actionMove1, actionMoveDone]]];
[sprite2 runAction:[SKAction sequence:#[actionMove2, actionMoveDone]]];
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast
{
self.lastSpawnTime += timeSinceLast;
if (self.lastSpawnTime > 2)
{
self.lastSpawnTime = 0;
[self add];
}
}
- (void)update:(CFTimeInterval)currentTime
{
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTime;
self.lastUpdateTime = currentTime;
if (timeSinceLast > 2)
{
timeSinceLast = 1.0 /60.0;
self.lastUpdateTime = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
You have to subclass SkSpriteNode:
to detect TouchEvents you have to set "userInteractionEnabled" to "YES"
to react on TouchEvents implement the touch method which fits for you needs:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{}
In this method you can change the coordinates of your sprite
add these function like that in your sksence
-(void)touchesEnded:(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:#"plane"])
{
NSLog(#"_______touch ended");
//x and y position of object at Scene
NSLog(#"%f",node.position.x);
NSLog(#"%f",node.position.y);
NSLog(#"%#",node);
}
}
-(void)touchesMoved:(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:#"plane"])
{
NSLog(#"_______touch mobing");
}
}
-(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:#"plane"])
{
NSLog(#"______touch begin");
//x and y position of object at Scene
NSLog(#"%f",node.position.x);
NSLog(#"%f",node.position.y);
NSLog(#"%#",node);
}
}

Button pressed animation in Sprite-Kit

In a Sprite-Kit game I want to animate Buttons when they were pressed. Right now the code reacts directly and to not wait till the animation is executed. Furthermore I want the button to animate back when the user wipes his finger out of the button.
Here my code:
-(void)addStartButton:(CGSize)size {
self.start = [SKSpriteNode spriteNodeWithImageNamed:#"startButton1"];
self.start.position = CGPointMake(size.width/2, size.height/2);
self.start.name = #"start";
[self addChild:self.start];
}
-(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:#"start"]) {
self.start.texture = [SKTexture textureWithImageNamed:#"startButton2"];
MyScene *myScene = [MyScene sceneWithSize:self.size];
[self.view presentScene:myScene transition:[SKTransition pushWithDirection:SKTransitionDirectionLeft duration:0.5]];
}
}
This will give you 2 second delay before switching to another scene:
-(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:#"start"]) {
SKAction *changeTexture = [SKAction runBlock:^{
self.start.texture = [SKTexture textureWithImageNamed:#"startButton2"];
}];
SKAction *wait = [SKAction waitForDuration:2.f];
SKAction *presentAnotherScene = [SKAction runBlock:^{
MyScene *myScene = [MyScene sceneWithSize:self.size];
[self.view presentScene:myScene transition:[SKTransition pushWithDirection:SKTransitionDirectionLeft duration:0.5]];
}];
[self runAction:[SKAction sequence:#[changeTexture,wait,presentAnotherScene]]];
}
}
Furthermore I want the button to animate back when the user wipes his finger out of the button.
This seems pointless, since you are transitioning to another scene when user presses the button.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"start"]) {
startButton.texture = [SKTexture textureWithImageNamed:#"startButton2"];
}else{startButton.texture = [SKTexture textureWithImageNamed:#"startButton1"];}
-(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:#"start"]) {
startButton.texture = [SKTexture textureWithImageNamed:#"startButton2"];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"start"]) {
//Start Button Actions
}

move Sprite with touches moved

i am moving a sprite using the touches moved method. currently the sprite jumps to the point on which the screen is touched but I want the sprite only to move when it is touched directly.
my code:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
CGPoint newPosition = CGPointMake(location.x, self.size.height/2);
self.sprite.position = newPosition;
}
}
check if the touch location is inside the sprite, like this:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
if(CGRectContainsPoint(self.sprite.boundingBox,positionInScene)) {
CGPoint newPosition = CGPointMake(positionInScene.x, self.size.height/2);
self.sprite.position = newPosition;
}
}

touchesMoved on a specific moving object

Im using Spritekit to create a game for iOS 7. The game has circles moving across the screen using this code
_enemy = [SKSpriteNode spriteNodeWithImageNamed:#"greeny"];
_enemy.position = CGPointMake(CGRectGetMidX(self.frame)+300,
CGRectGetMidY(self.frame));
_enemy.size = CGSizeMake(70, 70);
_enemy.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:35];
_enemy.physicsBody.mass = 0;
_enemy.physicsBody.categoryBitMask = Collisiongreen;
[_enemies addObject:_enemy];
NSLog(#"Enemies%lu",(unsigned long)_enemies.count);
[self addChild:_enemy];
[_enemy runAction:[SKAction moveByX:-900 y:0 duration:4]];
[self runAction:[SKAction playSoundFileNamed:#"Spawn.wav" waitForCompletion:NO]];
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:1.4],
[SKAction performSelector:#selector(move)
onTarget:self],
]]];
I would like to have it that when the user touches one of the objects moving across the screen that they can move it with their finger either up or down (thats why I'm using touches moved)
The code i have set up right now is
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if(_dead)
return;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
if(CGRectContainsPoint(_enemy.frame, location)){
[_enemy runAction:[SKAction moveTo:[[touches anyObject] locationInNode:self] duration:0.01]];
}
My problem is that if i touch it the object will move a few pixels but will stop right after. How can I get it to move with my finger and if i let go it will continue moving left like programed before? Thanks!!
So the trick here is that in the touchesMoved method, you want the enemy's postion set to the location of your touch. Use the touchesEnded method to execute a method that makes the enemy continue in the direction of your last touch. This is a rough non-tested example.
You need to store the difference of the current location with the previous location.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if(_dead)
return;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
if(CGRectContainsPoint(_enemy.frame, location)){
someBool = False;
[_enemy setPosition:location];
changeInX = location.x - lastX;
changeInY = location.y - lastY;
lastX = location.x;
lastY = location.y;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
someBool = True;
}
- (void)update:(NSTimeInterval)currentTime
{
...
if (someBool) {
enemy.position = CGPointMake((enemy.position.x + changeInX), (enemy.position.y + changeInY));
}
...
}

Resources