I want to add an animation to button in sprite Kit, such as the play button found in Candy Crush Saga.
Link for Candy Crush Saga: https://www.youtube.com/watch?v=KAMUWIqYN24
Do I use an image sequence? Or do I scale the size of the button?
I think it's better to use SKAction to scale the button by using those methods assuming your button is a SKSpriteNode
+ scaleXTo:y:duration:
+ scaleYTo:duration:
for example
SKAction * yScale=[SKAction scaleYTo:0.4 duration:.5]
SKAction * xScale=[SKAction scaleXTo:0.5 duration:.5]
and you repeat the action for ever
[buttonSprite runAction:[SKAction repeatActionForever:[SKAction sequence:#[xScale, yScale]]]];
EDIT: you need to give a name to your SKSpriteNode button like this
buttonSprite = #"NAME";//to identify the button in touchesBegan
And the implement
- (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:#"NAME"]) {
//DO SOMETHING
}
}
Related
I am currently working on a iOS game using SpriteKit and Objective-C. I've recently been trying to optimize my draw calls, but I've run into an issue where I am either misunderstand the batch draw operation and/or I am receiving some unexpected results. Additionally I have read the Apple documentation regarding batch drawing, I'm just afraid I may have misinterpreted some of the guidelines.
So here is the rundown: (assume that all atlases are preloaded and that self is a SKScene)
This works as you would expect. The first touch adds +1 to both the node and draw count. Subsequent touches add to the node count but not the draw count.
/*Code Block 1:*/
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
SKSpriteNode *touch1 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas1"] textureNamed:#"texture1"]];
[touch1 setPosition:location];
[self addChild:enemyTouch];
}
When I start adding two sprites from different atlases to the parent I start to get too many draw calls. What I expected: on the first touch I would get +2 nodes and +2 draw calls and subsequent touches produce +2 nodes and +0 draw calls. What I got: on the first touch I got +2 nodes and +2 draw calls and subsequent touches produced +2 nodes and +2 draw calls.
/*Code Block 2:*/
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
SKSpriteNode *touch1 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas1"] textureNamed:#"texture1"]];
[touch1 setPosition:location];
[self addChild:touch1];
SKSpriteNode *touch2 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas2"] textureNamed:#"texture2"]];
[touch2 setPosition:location];
[self addChild:touch2];
}
I tried one additional step of adding a child SKNode *extraLayer to the scene and adding one sprite to the scene and one sprite to the extraLayer. This produced the desired results of +2 nodes and +0 draw calls I wanted above.
/*Code Block 3:*/
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
SKSpriteNode *touch1 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas1"] textureNamed:#"texture1"]];
[touch1 setPosition:location];
[self addChild:touch1];
SKSpriteNode *touch2 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas2"] textureNamed:#"texture2"]];
[touch2 setPosition:location];
[self.extraLayer addChild:touch2];
}
The question: Is this in fact the way batching draw calls needs to be handled? All sprites whose textures come from common atlases need to be children of the same parent, but additionally that parent has no children who's textures come from a different atlas? Or is there a way of making the method of block 2 batch correctly?
I have GamePlayScene that when ended, removes all nodes and displays the Score and an option to restart. I also have a button on that scene that takes you to another scene to display upgradable items. I would like to make a function that adds a back button on the "upgrade items" scene that returns me to the ended GamePlay scene. The code that I have right now returns me back to the GamePlay scene but restarts the game instead of displaying the Game Over text. I've tried searching online but I can't manage to find a good solution. My Code:
GamePlayScene.m
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
...
else if (self.restart) {
[[GameState sharedInstance] saveState];
if ([node.name isEqualToString:#"UpgradesButton"]){
UpgradeShipScene *upgradeScene = [UpgradeShipScene sceneWithSize:(self.frame.size)];
SKTransition *transition = [SKTransition fadeWithDuration:0.5];
[self.view presentScene:upgradeScene transition:transition];
}
...
}
UpgradesScene.m
-(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:#"BackButton"]){
for (SKNode *node in [self children]){
[node removeFromParent];
}
GamePlayScene *scene = [GamePlayScene sceneWithSize:self.view.bounds.size];
SKTransition *transition = [SKTransition fadeWithDuration:0.5];
[self.view presentScene:scene transition:transition];
}
}
You can use a SKNode as a "fake" SKScene, and then use a SKAction to animate it (to present or hide).
I have a SKSpriteNode, which is a ball. My view don't have gravity. I'm able to move the ball by applying an impulse over it in the start and I want to make it to move to the user touch point which we can get in -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event method without affecting its motion.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInView:self.view];
SKAction *moveToPoint = [SKAction moveByX:location.x y:location.y duration:2];
[ball runAction:moveToPoint];
}
}
Its not seems to be working. Help me guys.
If you want to send the node directly to a point use the method:
[SKAction moveTo:location duration:2];
The SKAction moveBy: method moves the node by the x and y values provided, whereas the moveTo method to the point in the node's parent.
EDIT:
Also, change the line:
CGPoint location = [touch locationInView:self.view];
to
CGPoint location = [touch locationInNode:self];
A UIView has a different coordinate system than a SKScene.
EDIT 2: You can remove affect of physics while touch exists
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
ball.physicsBody.dynamic = NO;
CGPoint location = [touch locationInNode: self];
SKAction *moveToPoint = [SKAction moveTo:location duration:2];
[ball runAction:moveToPoint];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
ball.physicsBody.dynamic = YES;
}
Is there a property of SKSpriteNode that lets you manually set its touchable area?
I have a sprite textured with a PNG, it only seems to detect touches on the opaque portion of the PNG. So a small circle inside a large blank canvas actually has a tiny touchable area.
Create an SKNode that is the size you would like the touchable area to be. Add your textured sprite as a child of the new SKNode. Check to see if the new SKNode was touched instead of if the textured sprite was touched.
-(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:#"childName"])
{
NSLog(#"You touched to child of sprite");
}
}
-(void)sprite
{
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"spriteImageName"];
[self addChild:sprite];
SKSpriteNode *spriteChild = [SKSpriteNode spriteNodeWithImageNamed:#"childImageName"];
spriteChild.name = #"childName";
[sprite addChild:spriteChild];
}
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));
}
...
}