Does anyone know if there is a way to fade (over time) between two different SKTextures on an SKSpriteNode. I am assuming that you can't do this directly and plan to use a duplicate child sprite with a higher ZPosition to realise the fade, but I just wanted to check that there was not some method using SKAction(s) that I had over looked.
The following code should address this issue assuming the new texture fits overtop of the old one (it doesn't fade out the previous texture, but simply fades in the new one on top). I've left out minor implementation details such as timing mode.
-(void) fadeTexture:(SKTexture *)newTexture ontoSpriteNode:(SKSpriteNode *)referenceSpriteNode withDuration:(CFTimeInterval)duration {
SKSpriteNode * fadeInSprite = [self fadeInSpriteWithTexture:newTexture referenceSpriteNode:referenceSpriteNode];
[[referenceSpriteNode parent] addChild:fadeInSprite];
[fadeInSprite runAction:[SKAction sequence:#[
[SKAction fadeAlphaTo:1 duration:duration],
[SKAction runBlock:^{
[fadeInSprite removeFromParent];
[referenceSpriteNode setTexture:newTexture];
}]
]]];
}
-(SKSpriteNode *) fadeInSpriteWithTexture:(SKTexture *)newTexture referenceSpriteNode:(SKSpriteNode *)referenceSpriteNode {
SKSpriteNode * fadeInSprite = [SKSpriteNode spriteNodeWithTexture:newTexture size:[referenceSpriteNode size]];
[fadeInSprite setAlpha:0];
[fadeInSprite setAnchorPoint:[referenceSpriteNode anchorPoint]];
[fadeInSprite setPosition:[referenceSpriteNode position]];
return fadeInSprite;
}
Related
I'm trying to make a node scale down to a small size, move down, and then once all that happens animate it repeatedly (at the same scaled down size with a different texture).
Here is my method that scales the node down and moves it. That part is working correctly. However once it finishes doing that I need to change the sprites texture and animate it.
- (void)shrinkAndMoveToPosition:(CGPoint)position {
SKAction *move = [SKAction moveTo:position duration:.5];
SKAction *scale = [SKAction scaleTo:.3 duration:.5];
SKAction *moveAndScale = [SKAction group:#[move, scale]];
[self runAction:moveAndScale completion:^{
NSArray *textures = #[[SKTexture textureWithImageNamed:#"ship-small_01"],
[SKTexture textureWithImageNamed:#"ship-small_02"],
[SKTexture textureWithImageNamed:#"ship-small_03"],
[SKTexture textureWithImageNamed:#"ship-small_04"]];
SKAction *animate = [SKAction animateWithTextures:textures timePerFrame:0.5];
[self runAction:[SKAction repeatActionForever:animate]];
}];
}
The problem is that whenever my completion block runs the sprite jumps back up to the size of the texture. How can I maintain my scaled down size?
I just needed to call a different animate method and pass in YES to the resize parameter:
SKAction *animate = [SKAction animateWithTextures:textures timePerFrame:0.5 resize:YES restore:NO];
I recently came across an issue where I would increase the size of a node, but the physics body would remain the same size. I tried to look up solutions to this with no success. How can I make the body scale with the size of the node?
CGPoint location = CGPointMake(randX, -self.frame.size.height - expander.size.height);
SKAction *moveAction = [SKAction moveTo:location duration:randDuration];
SKAction *expandAction = [SKAction resizeToWidth:(expander.size.width * 1.4) height:(expander.size.width * 1.4) duration:1.0];
SKAction *collapseAction = [SKAction resizeToWidth:(expander.size.width) height: (expander.size.height) duration:1.0];
SKAction *doneAction = [SKAction runBlock:(dispatch_block_t)^() {
expander.hidden = YES;
}];
SKAction *expandCollapseAction = [SKAction repeatActionForever:[SKAction sequence:#[expandAction, collapseAction]]];
SKAction *moveExpandAction = [SKAction group:#[moveAction, expandCollapseAction]];
SKAction *moveExpanderActionWithDone = [SKAction sequence: #[moveExpandAction, doneAction ]];
[expander runAction:moveExpanderActionWithDone withKey: #"expanderMoving"];
I thought this won't work, but it looks like that physics body is scaled after all. Here is the code which can reproduce physics body scaling on iOS8 :
- (void)didMoveToView:(SKView *)view {
/* Setup your scene here */
SKSpriteNode *s = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(100,100)];
s.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame)+100);
s.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:s.size];
s.physicsBody.dynamic = YES;
s.physicsBody.affectedByGravity = NO;
[self addChild:s];
[s runAction:[SKAction scaleTo:0.3f duration:5]];
[s.physicsBody applyImpulse:CGVectorMake(0,30)];
}
If I recall correctly , this wasn't worked earlier, but I just tried it and it looks like it work. Still, this is not fully tested and I am not sure if this will mess up the physics simulation. Contact detection will probably work though. I just wanted to show that actual physics bodies are scaled when sprite is scaled . Here is the result:
From the gif above it can be seen that physics body is scaled along with sprite (without re-creating another different sized physics body). The blue bounding box around the sprite is visual representation of sprite's physics body (enabled in view controller with skView.showsPhysics = YES);
this is strange you can do it add physics body before adding it to the SKNode or SKScene for e.g.
SKTexture *texture=[_gameTexture textureNamed:#"mysprite"];
SKSpriteNode *bubble=[SKSpriteNode spriteNodeWithTexture:texture];
bubble.anchorPoint=CGPointMake(0.5,0.5);
//_bubble.name=[#"bubble" stringByAppendingFormat:#"%d ",index];
bubble.name=newcolor;
// _bubble.userInteractionEnabled=YES;
_bubble.position=CGPointMake(newPosition.x, newPosition.y);
//
bubble.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:60];
bubble.physicsBody.dynamic=NO;
bubble.physicsBody.linearDamping=0;
bubble.physicsBody.restitution=0;
bubble.physicsBody.density=1;
bubble.physicsBody.allowsRotation=NO;
bubble.physicsBody.affectedByGravity=NO;
//_bubble.physicsBody.velocity=CGVectorMake([self getRandomNumberBetween:-10 to:7], [self getRandomNumberBetween:-10 to:7]);
// _bubble.physicsBody.usesPreciseCollisionDetection=YES;
bubble.physicsBody.categoryBitMask=ballCategory;
bubble.physicsBody.collisionBitMask=ballCategory | pathCategory ;
bubble.physicsBody.contactTestBitMask=ballCategory | pathCategory ;
[self addChild:_bubble];
bubble.xScale=_bubble.yScale=2;
bubble.alpha=0.5;
I'm currently trying SpriteKit for first time and using some actions. I was under the impression that every node/sprite you create you would need to remove it but for some reason they are removed by themselfs as soon as they pass the screen height. Is this a normal behavior in SpriteKit?
Here is the code.
I'm basically using an action to animate a sprite across the screen (from bottom to top in protrait-mode), the funny thing is that the node count stays at 0 no matter how many sprites I create, it actually counts them but it subtracts them as soon as they pass the screen height.
-(void) shoot
{
bullet = [SKSpriteNode spriteNodeWithImageNamed:#"bullet"];
bullet.position = CGPointMake(airPlane.position.x, airPlane.position.y + 50);
[self addChild:bullet];
SKAction *moveBullet = [SKAction moveTo:CGPointMake(airPlane.position.x, self.size.height + 20) duration:0.5];
[bullet runAction:moveBullet];
}
If I change my action to where the sprite doesn't cross the screen height then the node count keeps the count.
Change this line...
SKAction *moveBullet = [SKAction moveTo:CGPointMake(airPlane.position.x, self.size.height + 20) duration:0.5];
... to this
SKAction *moveBullet = [SKAction moveTo:CGPointMake(airPlane.position.x, self.size.height) duration:0.5];
Again Is this a normal behavior in SpriteKit?
Thanks a lot
The node count displayed on the lower right corner is just an indication of the nodes which are being rendered on the screen. They are not an indication of the total nodes in the scene.
Use the following code
[skView setValue:#(YES) forKey:#"_showsCulledNodesInNodeCount"];
At the same place where you call
skView.showsFPS = YES;
skView.showsNodeCount = YES;
Also, please have a look at this answer.
that can be a memory leak because sprite kit holds textures in the memory for further use for better result remove it from the screen when its passing out of the screen and set it manually to nil
for eg
[self enumerateChildNodesWithName:name usingBlock:^(SKNode *node, BOOL *stop) {
if(node.x>900)
{
//remove all the action
[node removeAllActions];
//remove node from parent
[node removeFromParent];
//set nil//not required set automatically to nil when node is remove from parent
node=nil;
}
}];
How do I make an object grow in one direction is SpriteKit?
Ultimately, I would like to have a node be inserted where the user touches and have each end of a line scale until it hits another object in the same category or hits the edge of the screen. I understand how to have it scale on an axis and I can then cancel the scaling after one collision is detected but I want both directions to scale independently until a collision is made for each. I am new to SpriteKit so my searches may be using the wrong terms but I have not been able to find anything relevant.
Code to add a "wall" where the user touches
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch * touch in touches) {
CGPoint location = [touch locationInNode:self];
[self addWallAtLocation:location];
}
}
-(void)addWallAtLocation:(CGPoint)location{
int odd_or_even = arc4random() % 2;
SKSpriteNode * wall = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(2, 2)];
wall.position = location;
wall.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(2, 2)];
wall.physicsBody.dynamic = NO;
wall.physicsBody.categoryBitMask = wallCategory;
wall.physicsBody.contactTestBitMask = ballCategory;
[self addChild:wall];
SKAction * grow;
if (odd_or_even == 1) {
grow = [SKAction scaleYTo:300 duration:2];
} else {
grow = [SKAction scaleXTo:300 duration:2];
}
[wall runAction:grow];
}
How I want it to work:
Touch 1 makes the horizontal line from a point which shoots out in both directions on the x-axis until both ends hit the edge of the screen. Touch 2 would then create a point which shoots out in both directions on the y-axis until the lower end hits the wall created by Touch 1 and the upper end hits the edge of the screen. The lines (scaled points) are generated where the user touches. So I do not want the part of the vertical line that is highlighted by the red box to be there.
Scaling in one direction is just a solution I see to this problem, if there is a better way of going about it I will certainly try that other solution.
EDIT FOR UPDATED QUESTION:
It sounds like you might be asking "how to scale a thing in a single axis, but then stop the scaling of that single axis in one direction only when it collides with another thing"? There isn't a simple way to do that just using actions, but you you could do some slight of hand by manipulating the anchor point of a sprite. Or, probably more appropriately, you should use 2 sprites per touch, with one action sending one north, and another action sending the other one south. Maybe like this:
-(void)someMethod{
SKSpriteNode *touch2TopPart = [SKSpriteNode node];
SKSpriteNode *touch2BottomPart = [SKSpriteNode node];
touch2TopPart.anchorPoint = CGPointMake(.5, 0);
touch2BottomPart.anchorPoint = CGPointMake(.5, 1);
touch2TopPart.name = #"touch2TopPart";
touch2BottomPart.name = #"touch2BottomPart";
SKAction *sendTopPartUp = [SKAction scaleYTo:100 duration:2];
SKAction *sendBottomPartDown = [SKAction scaleYTo:100 duration:2];
[touch2TopPart runAction:sendTopPartUp withKey:#"sendTopPartUp"];
[touch2BottomPart runAction:sendBottomPartDown withKey:#"sendBottomPartDown"];
}
-(void)whateverTheCollisonMethodIsCalledBetweenThingAandThingB
{
SKSpriteNode *somePartMovingInASingleDirection = thingAOrWhatever;
[somePartMovingInASingleDirection removeAllActions];
}
---ORIGINAL ANSWER
I'm not exactly sure what you're asking, but you already have the actions for scaling in just X or Y, so here's the simple usage for removing one of those actions after physics is simulated. You might be looking for how to detect a specific collision between two entities, but I can't be sure.
-(void)someMethod{
SKSpriteNode *thisSprite = [SKSpriteNode node];
thisSprite.name = #"spriteRunningActions";
SKAction *growX = [SKAction scaleXTo:100 duration:2];
SKAction *growY = [SKAction scaleYTo:100 duration:2];
[thisSprite runAction:growX withKey:#"actionCurrentlyScalingX"];
[thisSprite runAction:growY withKey:#"actionCurrentlyScalingY"];
}
-(void)didSimulatePhysics
{
SKSpriteNode *thatSprite = (SKSpriteNode*)[self childNodeWithName:#"spriteRunningActions"];
//stop a given action
[thatSprite removeActionForKey:#"actionCurrentScalingX"];
[thatSprite removeActionForKey:#"actionCurrentScalingY"];
}
Setting the anchor point like below made the "lines" scale in one direction.
wall.anchorPoint = CGPointMake(0, 0);
I am making a 2D game where a character stands stationary at the left hand side of the screen and objects fly towards him from the right. He needs to be able to slap these flying enemies down. I have a sprite animation that contains the "slap" animation frames. During the slap, his body moves slightly and his arm rotates from resting on the ground, to fully extended above his head and then slaps down to the ground. Here is what it looks like:
SumoSmash Animation GIF
For these purposes I have a class called SumoWarrior which is a SKSpriteNode subclass. The SumoWarrior sprite node has a child sprite node called warriorArm. My idea was to have the main sumo warrior sprite node display the animation, and to use this warriorArm sprite node only for the purposes of a physics body in the shape of the warriors arm. I need to somehow rotate this arm body to follow the sprite animation, in order to detect collisions with the flying objects.
Here is how the arm is created:
sumoWarrior.warriorArm = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:#"warriorArm"]];
sumoWarrior.warriorArm.position = CGPointMake(15, 25);
sumoWarrior.warriorArm.anchorPoint = CGPointMake(0.16, 0.7);
sumoWarrior.warriorArm.texture = nil;
sumoWarrior.warriorArm.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:[sumoWarrior createArmBody]];
sumoWarrior.warriorArm.physicsBody.mass = 9999;
sumoWarrior.warriorArm.physicsBody.categoryBitMask = CollisionPlayer;
sumoWarrior.warriorArm.physicsBody.collisionBitMask = CollisionEnemy;
sumoWarrior.warriorArm.physicsBody.contactTestBitMask = CollisionEnemy | CollisionAlly;
sumoWarrior.warriorArm.physicsBody.allowsRotation = YES;
sumoWarrior.warriorArm.physicsBody.dynamic = YES;
Is it possible to rotate and extend this arm somehow, in order for it to follow the animation precisely? Is it also possible to alter the main physics body of the warrior (which is just a polygon around the body, without the arm) so that IT also follow the animation? Or am I completely missing the way that this should be done?
I used Texture Packer for the texture and animation. I created an Texture Atlas called sumoAnimations and Texture Packer created a .h file for me which I then imported into the project.
You can get a free copy if you do not already have it.
Before I launch into the code, you might want to reconsider using the animation you have. Going by your previous comments the only relevant frames in the animation are frame 15, 16 and 17. I am not even sure about frame 17 because the sumo already has his hand down. That only give you 3 frames which by the animation you provided is equal to 0.1 seconds as each frame has a time of 0.05 seconds.
Take a look at the 3 pics I included to see what I mean. You might want to consider getting a new animation or allowing for greater time in between frames. I used 0.25 seconds per frame so you can see it more clearly. You can change it to anything you like.
As for the player being missing and being hit, you can create a clearColor sprite rect around the player (behind the arm of course) to detect contact of a missed object.
#import "MyScene.h"
#import "sumoAnimation.h"
#interface MyScene()<SKPhysicsContactDelegate>
#end
#implementation MyScene
{
SKSpriteNode *sumo;
SKSpriteNode *arm;
SKAction *block0;
SKAction *block1;
SKAction *block2;
SKAction *block3;
SKAction *block4;
SKAction *slapHappy;
SKAction *wait0;
SKAction *wait1;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
sumo = [SKSpriteNode spriteNodeWithTexture:SUMOANIMATION_TEX_SUMO_001];
sumo.anchorPoint = CGPointMake(0, 0);
sumo.position = CGPointMake(0, 0);
[self addChild:sumo];
arm = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(34, 14)];
arm.anchorPoint = CGPointMake(0, 0);
arm.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(34,14) center:CGPointMake(17, 7)];
arm.physicsBody.dynamic = NO;
slapHappy = [SKAction animateWithTextures:SUMOANIMATION_ANIM_SUMO timePerFrame:0.25];
// start the animation
block0 = [SKAction runBlock:^{
[sumo runAction:slapHappy];
}];
// time until frame 15 is reached
wait0 = [SKAction waitForDuration:3.50];
// add arm at frame 15 positon
block1 = [SKAction runBlock:^{
arm.position = CGPointMake(205, 125);
arm.zRotation = 1.3;
[self addChild:arm];
}];
// wait until next frame
wait1 = [SKAction waitForDuration:0.25]; // time in between frames
// move arm and rotate to frame 16 position
block2 = [SKAction runBlock:^{
arm.position = CGPointMake(224, 105);
arm.zRotation = 0.4;
}];
// move arm and rotate to frame 17 position
block3 = [SKAction runBlock:^{
arm.position = CGPointMake(215, 68);
arm.zRotation = -0.65;
}];
// remove arm from view
block4 = [SKAction runBlock:^{
[arm removeFromParent];
}];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[sumo runAction:[SKAction sequence:#[block0, wait0, block1, wait1, block2, wait1, block3, wait1, block4]]];
}
-(void)update:(CFTimeInterval)currentTime
{
//
}
#end
Updated based on additional comments
The pic below outlines my suggestion. Add a physics body rect for the frames the sumo is swatting. This will allow you to not have to deal with adding a body for every frame in the precise position. It will also make the swatting more effective.
Your object can still fall to the ground and have the crushed animation play. Remember that your sumo animation moves very fast and the player will not see precise locations for each frame.
Your idea of having the arm "push" the object would take a much more precise animation. Something like the arm's position changing by a single increment. Then you would have to precisely position a body on the hand. I am not saying its impossible but its certainly A LOT of work and difficult to do.