SpriteKit change Image after swipe gesture - ios

I am trying to code a game. I got an object, that can jump and slide.
I want to hold the animation of 'run' while jumping, but while sliding, I want to change the image. My problem: the image won't show off , if I just change the image to 'slide7'.
Nothing happens.
The slide animation should appear only for about 4 seconds, than go again into the run animation. Any suggestions?
My Code :
-(void)Mensch{
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
Mensch = [SKSpriteNode spriteNodeWithTexture:MenschTexture1];
Mensch.size = CGSizeMake(45, 45);
Mensch.position = CGPointMake(self.frame.size.width / 5, Boden.position.y + 73);
Mensch.zPosition = 2;
Mensch.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Mensch.size];
Mensch.physicsBody.dynamic = YES;
Mensch.physicsBody.allowsRotation = NO;
Mensch.physicsBody.usesPreciseCollisionDetection = YES;
Mensch.physicsBody.restitution = 0;
Mensch.physicsBody.velocity = CGVectorMake(0, 0);
[Mensch runAction:Run];
[self addChild:Mensch];
}
- (void)didMoveToView:(SKView *)view
{
UISwipeGestureRecognizer *recognizerDown = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(handleSwipeDown:)];
recognizerDown.direction = UISwipeGestureRecognizerDirectionDown;
[[self view] addGestureRecognizer:recognizerDown];
}
-(void)handleSwipeDown:(UISwipeGestureRecognizer *)sender
{
[Mensch removeAllActions];
Mensch = [SKSpriteNode spriteNodeWithImageNamed:#"slide7.png"];
NSLog(#"Slide");
}

You are replacing the Mensch object. More specifically, you are replacing the pointer you have to the object, but that doesn't stop it from being displayed in the scene.
You can either:
Replace the SKTexture inside the sprite, which should be displayed immediately. This means Mensch.texture = [SKTexture textureWithImageNamed:#"slide7.png"];
Replace the Sprite. This entails removing the current sprite ([sprite removeFromParent]) and adding the new one ([self addChild:newSprite]).
I think you want the first one, since you don't have to recreate the Mensch object.
Might I add that you are performing a lot of Mensch-specific logic in this object? It would be better (from an Object Oriented standpoint) to move this creation code to an SKSpriteNode subclass Mensch.
#interface Mensch : SKSpriteNode
- (void) displayRunAnimation;
- (void) displaySlideAnimation;
#end
#implementation : Mensch
- (instancetype) init {
self = [super init];
if (self) {
// your initialization code here, including physics body setup
[self displayRunAnimation];
}
return self;
}
- (void) displayRunAnimation {
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
// if you plan to call this often, you want to cache this SKAction, since creating it over and over is a waste of resources
[self runAction:Run];
}
- (void) displaySlideAnimation {
[self removeAllActions];
self.texture = [SKTexture textureWithImageNamed:#"slide7.png"];
NSLog(#"Slide");
// re-start the runAnimation after a time interval
[self performSelector:#selector(displayRunAnimation) withObject:nil afterDelay:4.0];
}

Related

How can I change Sprite Image every half a second?

I have been trying to achieve this from this morning but haven't figured it out. I have a SKSpriteNode which is an Image that displays the name of my game on the main screen. What I am trying to achieve is to change those images every half a second. I have made multiple images of the name with different colors so I can change images every half a second.That will give the effect of the title changing colors like a typical arcade game. This is what I have done so far...
- (void)viewDidLoad
{
[super viewDidLoad];
_images = [NSArray arrayWithObjects:#"YellowLabel.png", #"BlueLabel.png", #"GreenLabel.png", #"RedLabel.png", #"WhiteLabel.png", nil];
NSTimer *myTimer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(doAnimation) userInfo:nil repeats:YES];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [TitleScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
SKSpriteNode *labelNode = [SKSpriteNode spriteNodeWithImageNamed:#"WhiteLabel.png"];
labelNode.position = CGPointMake(160, 400);
// Present the scene.
[skView presentScene:scene];
[self doAnimation];
[scene addChild:labelNode];
}
And I also have this additional method:
-(void)doAnimation {
SKSpriteNode *labelNode = [SKSpriteNode spriteNodeWithImageNamed:#"WhiteLabel.png"];
static int counter = 0;
if ([_images count] == counter+1) {
counter = 0;
}
labelNode = [SKSpriteNode spriteNodeWithImageNamed:[_images objectAtIndex:counter]];
}
Thanks for any help!
Here's one way you could do this with SKAction :
SKAction *actionWait = [SKAction waitForDuration:.5];
SKAction *actionBlock = [SKAction runBlock:^(void)
{
// do whatever you want to do every half second here
}];
SKAction *actionSequence = [SKAction sequence:#[actionWait, actionBlock]];
SKAction *actionRepeat = [SKAction repeatActionForever:actionSequence]
[self runAction:actionRepeat];
Another way, could be like this, since I think you are just animating textures :
// create an NSArray called anim that includes all your SKATextures
SKAction *actionAnimate = [SKAction animateWithTextures:anim timePerFrame:.5 resize:YES restore:NO];
SKAction *actionRepeat = [SKAction repeatActionForever:actionAnimate];
[self runAction:actionRepeat];

How to display a parent node with all the children in Sprite Kit?

I am having trouble with adding all of my sprite nodes to a single parent node and then displaying all of the children inside of that parent node on the screen.
My current and working method is simply taking the children that were added to the sprites' methods and calling it within the - (id)initWithSize:(CGSize)size method.
So it current looks and works like this:
Code within the MenuScene.m:
- (id)initWithSize:(CGSize)size{
if(self = [super initWithSize:size]){
_skyColor = [SKColor colorWithRed:25.0/255.0 green:171.0/225 blue:1 alpha:1];
[self setBackgroundColor:_skyColor];
[self nameMovement];
[self playButtonPushed];
[self groundMovement];
[self cloudMovement];
}
return self;
}
The following code works well, but I would rather only having one node holding all of the children that are held by the groundMovement, cloudMovement..etc methods.
I have tried making a new SKNode called _moving, and then doing this to the rest of the methods that hold sprites with movement.
Below is the groundMovement method where I have tried to implement the parent node I had created called _moving (the only change I have made is at the bottom). Also when I did this I initialised the SKNode in the new initWithSize by adding _moving = [SKNode node];
new initWithSize with SKNode declared and the parent node attempted to be drawn (The SKNode *_moving is in the .h file):
- (id)initWithSize:(CGSize)size{
if(self = [super initWithSize:size]){
_skyColor = [SKColor colorWithRed:25.0/255.0 green:171.0/225 blue:1 alpha:1];
[self setBackgroundColor:_skyColor];
SKNode *_moving = [SKNode node];
[self addChild:_moving];
}
return self;
}
groundMovement method:
- (void)groundMovement{
//Ground texture declaration
groundTexture = [SKTexture textureWithImageNamed:#"Ground"];
groundTexture.filteringMode = SKTextureFilteringNearest;
//SKAction Declarations
groundSpriteMovement = [SKAction moveByX:-groundTexture.size.width/2 y:0 duration:0.04 * 100];
resetgroundSpriteMovement = [SKAction moveByX:groundTexture.size.width/2 y:0 duration:0];
repeatForevergroundSpriteMovement = [SKAction repeatActionForever:[SKAction sequence:#[groundSpriteMovement, resetgroundSpriteMovement]]];
//Ground Sprite declaration
groundSprite = [SKSpriteNode spriteNodeWithTexture:groundTexture];
groundSprite.anchorPoint = CGPointMake(0, 0);
groundSprite.position = CGPointMake(0, 0);
[groundSprite runAction:repeatForevergroundSpriteMovement];
[_moving addChild:groundSprite];
}

SpriteKit Objective Jump

I got a problem with my code. I added the swipe gesture to jump, but my objective won't jump at all. The NSLog for the Swipe gesture works, so it must be something wrong with my objective code.
-(void)Mensch{
SKTexture * MenschTexture1 = [SKTexture textureWithImageNamed:#"Mensch1"];
MenschTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * MenschTexture2 = [SKTexture textureWithImageNamed:#"Mensch2"];
MenschTexture2.filteringMode = SKTextureFilteringNearest;
SKAction * Run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[MenschTexture1, MenschTexture2] timePerFrame:0.4]];
Mensch = [SKSpriteNode spriteNodeWithTexture:MenschTexture1];
Mensch.size = CGSizeMake(45, 45);
Mensch.position = CGPointMake(self.frame.size.width / 5, Boden.position.y + 73);
Mensch.zPosition = 2;
Mensch.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Mensch.size];
Mensch.physicsBody.dynamic = NO;
Mensch.physicsBody.allowsRotation = NO;
Mensch.physicsBody.usesPreciseCollisionDetection = YES;
Mensch.physicsBody.restitution = 0;
[Mensch runAction:Run];
[self addChild:Mensch];
}
- (void)didMoveToView:(SKView *)view
{
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
recognizer.direction = UISwipeGestureRecognizerDirectionUp;
[[self view] addGestureRecognizer:recognizer];
}
- (void)handleSwipe:(UISwipeGestureRecognizer *)sender
{
Mensch.physicsBody.velocity = CGVectorMake(0, 0);
[Mensch.physicsBody applyImpulse:CGVectorMake(0, 20)];
NSLog(#"Jump");
}
You need to set
Mensch.physicsBody.dynamic = YES;
According to Apple's documentation about the dynamic property of SKPhysicsBody:
The default value is YES. If the value is NO, then the physics body
ignores all forces and impulses applied to it. This property is
ignored on edge-based bodies; they are automatically static.

SpriteKit Animation Issue

I am having an issue with the sprite animation in my game.
In the current code posted below, the sprite will show on the screen but does not do the walking animation; it is only a static frame of one of the images in the walk cycle.
The init function:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize: size]) {
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// Create the game nodes
// Background
_backgroundNode = [self createBackgroundNode];
[self addChild: _backgroundNode];
// Foreground
_foregroundNode = [SKNode node];
[self addChild: _foregroundNode];
// Add the Player
_thePlayer = [self createPlayer];
[_foregroundNode addChild:_thePlayer];
[self walkingQueen];
}
return self;
}
Function creating the player / filling in the animation array:
-(SKSpriteNode *)createPlayer
{
SKSpriteNode *playerNode = [SKSpriteNode node];
//SKSpriteNode *_player = [SKSpriteNode node];
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *queenAnimatedAtlas = [SKTextureAtlas atlasNamed: #"QueenSprites"];
SKTexture *walk1 = [queenAnimatedAtlas textureNamed:#"queen7"];
SKTexture *walk2 = [queenAnimatedAtlas textureNamed:#"queen8"];
SKTexture *walk3 = [queenAnimatedAtlas textureNamed:#"queen9"];
walkFrames[0] = walk1;
walkFrames[1] = walk2;
walkFrames[2] = walk3;
_queenWalkingFrames = walkFrames;
/*
int numImages = 9;
for (int i = 7; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"queen%d", i];
SKTexture *temp = [queenAnimatedAtlas textureNamed: textureName];
[walkFrames addObject: temp];
}
*/
//_queenWalkingFrames = walkFrames;
SKTexture *temp = _queenWalkingFrames[0];
SKSpriteNode *_player = [SKSpriteNode spriteNodeWithTexture:temp];
_player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
//[self addChild:_player];
//[self walkingQueen];
[playerNode addChild:_player];
[self walkingQueen];
return playerNode;
}
And the function for starting the walk animation:
-(void)walkingQueen
{
for (int i = 0; i < _queenWalkingFrames.count; i++) {
if (_queenWalkingFrames[i] == NULL) {
NSLog(#"ERROR!");
}
}
[_thePlayer runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:_queenWalkingFrames
timePerFrame:0.1f
resize:NO
restore:YES]] withKey:#"walkingInPlaceQueen"];
//return;
}
I played around with the code a bit and the animation DOES work IF I ignore the concept of layers (background / foreground layers) and stick the creation of the player into the init function (basically taking what's inside the createPlayer() and sticking it inside initWithSize()).
I do need to have the layers for my game and everything seems like it should work; had no idea that moving a few lines of code into its own function block would create such an issue. The check inside the walkingQueen() function did not return any errors so I assume my array has been filled with the sprite images.
_thePlayer = [self createPlayer];
This means _thePlayer is nil until after the createPlayer method returns. Thus in the walkingQueen method _thePlayer is nil and the actions don't run.
Replace this:
SKSpriteNode *playerNode = [SKSpriteNode node];
with
_thePlayer = [SKSpriteNode node];
and use _thePlayer instead of playerNode. You can also skip returning and assigning it, it's an ivar after all.
Re-wrote the createPlayer() function. Correct implementation is much simpler and as follows:
-(SKSpriteNode *)createPlayer
{
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *queenWalkingAtlas = [SKTextureAtlas atlasNamed:#"QueenSprites"];
SKTexture *walk1 = [queenWalkingAtlas textureNamed:#"queen7"];
SKTexture *walk2 = [queenWalkingAtlas textureNamed:#"queen8"];
SKTexture *walk3 = [queenWalkingAtlas textureNamed:#"queen9"];
walkFrames[0] = walk1;
walkFrames[1] = walk2;
walkFrames[2] = walk3;
_queenWalkingFrames = walkFrames;
SKTexture *temp = _queenWalkingFrames[0];
_thePlayer = [SKSpriteNode spriteNodeWithTexture:temp];
_thePlayer.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
return _thePlayer;
}
Basically doing as the post above suggested, get rid of the ivars, use the function ONLY to fill the array with the appropriate images. Then, in the init function, call createPlayer(), add it to the foreground layer, and then call walkingQueen().

SpriteKit Add Child not working

I had a general SKScene where I created a SKSpriteNode, added it via
[self addChild:_charecter];
And all was well..
I tried making a new class for the SKSpriteNode so that I could add some custom methods to it. Here is it's init method:
- (instancetype)init
{
if (self = [super init]) {
SKTexture *run1 = [SKTexture textureWithImageNamed:#"run1"];
SKTexture *run2 = [SKTexture textureWithImageNamed:#"run2"];
SKTexture *run3 = [SKTexture textureWithImageNamed:#"run3"];
_charecterSprites = [NSArray arrayWithObjects:run1, run2, run3, nil];
self.texture = run1;
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:1.0]; //CHANGE THIS LATER
}
return self;
}
The problem is that now when I add the _charecter to the view, it doesn't appear. I'm adding him in the SKScene as follows:
_charecter = [[RUNPlayer alloc] init];
_charecter.position = CGPointMake(CGRectGetMidX(self.frame), CHARECTER_Y_AXIS);
self.physicsWorld.gravity = CGVectorMake(0, -10.0/150.0);
[self addChild:_charecter];
Any idea why my SKSpriteNode isn't visible?
Thanks
Trying using one of the SKSpriteNode initializers instead of init
- (instancetype)initWithImageNamed:(NSString *)name
{
if (self = [super initWithImageNamed:name]) {
SKTexture *run1 = [SKTexture textureWithImageNamed:#"run1"];
SKTexture *run2 = [SKTexture textureWithImageNamed:#"run2"];
SKTexture *run3 = [SKTexture textureWithImageNamed:#"run3"];
_charecterSprites = [NSArray arrayWithObjects:run1, run2, run3, nil];
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:1.0]; //CHANGE THIS LATER
}
return self;
}
From what I understand, when you use initWithImageNamed:, the sprite node's size is set to the texture's size. Your sprite could of been on screen but had a size of (0,0). Let us know if this works!
On a side note, check out Ray's tutorial on texture atlases to replace _characterSprites http://www.raywenderlich.com/45152/sprite-kit-tutorial-animations-and-texture-atlases

Resources