Child Sprite appearing under the parent - objective-c

I am simply trying to add a sprite "light" on top of "spaceship". So as you can see below that I added light as the child of the spaceship, however, the light is appearing below the spaceship, as seen in the picture below. Can anyone tell me why this is happening, and how can I fix it?
- (void)newSpaceshipAtLocation:(CGPoint)location{
SKSpriteNode *hull = [[SKSpriteNode alloc]initWithImageNamed:#"Spaceship"];
hull.position = location;
hull.name = #"Spaceship";
hull.scale = 0.5;
SKSpriteNode *light = [self lights];
light.position = CGPointMake(hull.size.width / 5.0, hull.size.height/5.0);
[hull addChild:light];
[self addChild:hull];
}
- (SKSpriteNode *)lights{
SKSpriteNode *light = [[SKSpriteNode alloc]initWithColor:[NSColor yellowColor] size:CGSizeMake(50.0, 50.0)];
SKAction *blink = [SKAction sequence:#[
[SKAction fadeOutWithDuration:0.5],
[SKAction fadeInWithDuration:0.5],
]];
[light runAction:[SKAction repeatActionForever:blink]];
light.name = #"light";
return light;
}

Try setting the zPosition property on light. If you set it to anything higher than the hull sprite, it should work.

Related

SKNodes with SKPhysicsBody under SKAction move inaccuracy

I have two SKNode with physics (one under another):
- (SKNode*) newNode {
SKShapeNode *node = [SKShapeNode shapeNodeWithCircleOfRadius:60];
node.strokeColor = [SKColor clearColor];
node.fillColor = [SKColor greenColor];
node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:60];
node.physicsBody.categoryBitMask = 1;
node.physicsBody.collisionBitMask = 1;
node.physicsBody.contactTestBitMask = 1;
return node;
}
- (void)createSceneContents {
SKNode *n1 = [self newNode];
n1.position = CGPointMake(100,100);
[self addChild:n1];
SKNode *n2 = [self newNode];
n2.position = CGPointMake(100,300);
[self addChild:n2];
...
}
Their goal is to reach a predetermined position directly opposite them at some distance.
To achieve this, I use SKAction (this is important and can not be changed):
[n1 runAction:[SKAction moveTo:CGPointMake(800,100) duration:3]];
[n2 runAction:[SKAction moveTo:CGPointMake(800,300) duration:3]];
In addition, on the first node's movement path I place a third node - exactly the same.
- (void)createSceneContents {
...
SKNode *obstacle = [self newNode];
obstacle = CGPointMake(500,300);
[self addChild:obstacle];
}
The third node must be shifted in the direction of first node's movement by the mechanism of physics.
Click to illustration of the Scene
(the red circles marks destination position of nodes)
Then I run the app, everything goes as is conceived, except for one thing - a first node (with obstacle on the path) was not reach the target location, just a few pixels:
Click to see result
The question is simple: why? And how can I fix it?
Thank you!

SpriteKit SKSpriteNode slide

I'm trying determine why slide is happened. I have created 2 nodes, and one of them is moving using SKAction. But when one of them above on other, it does not moving with node under them.
It's a little bit hard to explain, so I have created the video:
https://www.youtube.com/watch?v=F4OuTBVd5sM&feature=youtube_gdata_player
Thanks for your replies!
Parts of code:
CGPoint positionPoint = [valuePoint CGPointValue];
SKSpriteNode *slab = [SKSpriteNode spriteNodeWithTexture:plitaTexture];
slab.name = #"plita";
slab.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:plitaTexture.size];
slab.position = positionPoint;
slab.physicsBody.dynamic = NO;
CGFloat duration = 3.0;
CGFloat firstDuration = duration/(self.size.width/slab.position.x);
SKAction *moveLeftFirstTime = [SKAction moveTo:CGPointMake(0.0 + plitaTexture.size.width/2, positionPoint.y) duration:firstDuration];
SKAction *moveLeft = [SKAction moveTo:CGPointMake(0.0 + plitaTexture.size.width/2, positionPoint.y) duration:5];
SKAction *moveRight = [SKAction moveTo:CGPointMake(self.size.width - plitaTexture.size.width/2, positionPoint.y) duration:5];
SKAction *movingRightLeft = [SKAction sequence:#[moveRight, moveLeft]];
SKAction *moving = [SKAction sequence:#[moveLeftFirstTime,[SKAction repeatActionForever:movingRightLeft]]];
[slab runAction:moving];
[self addChild:slab];
Rabbit init code:
-(instancetype)init {
if (self = [super initWithImageNamed:#"eating-rabbit1"]) {
self.physicsBody = [SKPhysicsBody bodyWithTexture:self.texture size:self.texture.size];
self.physicsBody.allowsRotation = NO;
self.physicsBody.dynamic = YES;
self.physicsBody.friction = 1;
}
return self;
}
The main character is sliding because you are moving the slab by changing its position over time. Imagine you are standing on top of a train that is moving, friction will cause you to move along in the direction of the train. However, if the train magically moved from point A to point B by disappearing and reappearing 1 cm at a time, you will remain at point A. That is what's happening to your main character.
To correct this issue, you will need to move the slabs using physics. Here is an example of how to do that:
slab.physicsBody.affectedByGravity = NO;
slab.physicsBody.dynamic = YES;
// These will help prevent the slab from twisting due to the weight of the main character
slab.physicsBody.angularDamping = 1.0;
slab.physicsBody.mass = 1e6;
// Air resistance will slow slab's movement, set the damping to 0
slab.physicsBody.linearDamping = 0;
Define a set of SKActions that will move the slabs left and right. Adjust the speed and duration variables to achieve the desired motion.
SKAction *moveLeft = [SKAction customActionWithDuration:0 actionBlock:^(SKNode *node, CGFloat elapsedTime){
node.physicsBody.velocity = CGVectorMake(-speed, 0);
}];
SKAction *moveRight = [SKAction customActionWithDuration:0 actionBlock:^(SKNode *node, CGFloat elapsedTime){
node.physicsBody.velocity = CGVectorMake(speed, 0);
}];
SKAction *wait = [SKAction waitForDuration:duration];
SKAction *action = [SKAction sequence:#[moveRight, wait, moveLeft, wait]];
[slab runAction:[SKAction repeatActionForever:action]];
You need to set the slab's physicsBody to be dynamic.
slab.physicsBody.dynamic = YES;
slab.physicsBody.affectedByGravity = NO;
A non-dynamic physicsBody is rendered static and ignores any forces and impulses applied on it. Look up the property in the documentation here.

Why does this code not properly center scene when doing UIPanGestureRecognizer?

I am a beginner developing a program for the AppStore using Xcode's sprite kit. I made a 'test' program so I can try out new things before adding it to my game. Right now I am fiddling around with swiping a scene - I have an SKNode (called "background") where I am adding several children as SKSpriteNodes. One sprite node is visible on the initial scene (the center, position 160,240), and two more that are not visible: to the left of the scene (position -160,240), and to the right of the scene (position 480,240).
I would like my game to be able to swipe left or right, and when it swipes left or right, the view will auto-center itself (with animation) to one of the three SKSpriteNodes. My code using the UIPanGestureRecognizer to move the background node works properly, and my code for auto-centering the view works MOSTLY (background position set to 0,0 or -320,0 or +320,0), but sometimes it has a strange offset and doesn't completely center itself (for example, the background position will be 7,0 or -34,0 when I pan right or left). What am I doing wrong?
P.S: I am using code from RayWenderlich's "iOS Games" for the SKTMoveEffect. I also want to note that if I make the function f(t)=t, there is no problem (at least in my several tests), but f(t)=t^2 or anything else seems to have an issue; if it helps to see the code for this I can post it too
#implementation LTMyScene
{
SKNode *background;
SKSpriteNode *spaceship1, *spaceship2;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
background=[SKNode node];
[self addChild:background];
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
myLabel.text = #"Hello, World!";
myLabel.fontSize = 30;
myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[background addChild:myLabel];
spaceship1=[SKSpriteNode spriteNodeWithImageNamed:#"Spaceship.png"];
spaceship1.position=CGPointMake(-self.size.width/2, self.size.height/2);
spaceship1.anchorPoint=CGPointMake(0.5, 0.5);
[background addChild:spaceship1];
spaceship2=[SKSpriteNode spriteNodeWithImageNamed:#"Spaceship.png"];
spaceship2.position=CGPointMake(self.size.width*3/2, self.size.height/2);
spaceship2.anchorPoint=CGPointMake(0.5, 0.5);
[background addChild:spaceship2];
}
return self;
}
- (void)didMoveToView:(SKView *)view
{
UIPanGestureRecognizer *swipe = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(dragPlayer:)];
[[self view] addGestureRecognizer:swipe];
}
-(void)dragPlayer: (UIPanGestureRecognizer *)gesture {
[[[self view] layer] removeAllAnimations];
CGPoint trans = [gesture translationInView:self.view];
SKAction *moveAction = [SKAction moveByX:trans.x y:0 duration:0];
[background runAction:moveAction];
[gesture setTranslation:CGPointMake(0, 0) inView:self.view];
if([gesture state] == UIGestureRecognizerStateEnded) {
CGFloat finalX=0;
if (abs(background.position.x)<self.size.width/2) {
finalX=0;
} else if (abs(background.position.x)<self.size.width*3/2) {
finalX=self.size.width*background.position.x/abs(background.position.x);
}
NSLog(#"%f",finalX);
SKTMoveEffect *upEffect =
[SKTMoveEffect effectWithNode:background duration:0.5
startPosition:background.position
endPosition:CGPointMake(finalX, 0)];
upEffect.timingFunction = ^(float t) {
// return powf(2.0f, -3.0f * t) * fabsf(cosf(t * M_PI * 1.0f)) //has bounce
// return (-1.0f*t*t+1) //no bounce ... for parabola this is only solution with (1,0) and (0,1) as intercepts and vertex at (1,0)
return (t*t)
;};
SKAction *upAction = [SKAction actionWithEffect:upEffect];
[background runAction:upAction];
}
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
NSLog(#"%f,%f",background.position.x,background.position.y);
}
#end
You have to remember that you are moving the larger background node with other nodes as its children. Keeping that in mind, the code you need to center on a specific node is:
_worldNode.position = CGPointMake(-(myNode.position.x-(self.size.width/2)), -(myNode.position.y-(self.size.height/2)));
The above assumes that your main background "canvas" is called _worldNode and your target node is a child of _worldNode

Moving Sprite in a Circle

I am trying to move a Sprite ("Spaceship") in a circle, where the click was performed.
Here is the code that is triggered when a click is performed:
- (void)newSpaceShipAt: (CGPoint)location
{
SKSpriteNode *hull = [[SKSpriteNode alloc]initWithImageNamed:#"Spaceship"];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, location.x, location.y, self.size.width / 5.0, 0, 360, YES);
SKAction *move = [SKAction sequence:#[
[SKAction followPath:path duration:1],
]];
[hull runAction:[SKAction repeatActionForever:move]];
hull.name = #"Spaceship";
hull.scale = 0.5;
hull.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:MAX(hull.size.width / 2.0, hull.size.height / 2.0)];
hull.physicsBody.dynamic = NO;
[self addChild:hull];
}
I was the Spaceship to move in the circle indefinitely about the point "location", which is where the click was performed. However, what happens is that after one successful revolution, the spaceship moves "location" relative from the click. For example, if the click was performed on (50, 100) then after one revolution it will move to (100, 200), relative to the scene, and it will carry on.
What can I do to fix this problem? Or if I my approach is totally wrong, what would be a better one?
Also, what is the best way to draw a trail (a simple line), to the movement of the sprite?
From the documentation of followPath:duration:...
Discussion
Calling this method is equivalent to calling the
followPath:asOffset:orientToPath:duration: method, passing in YES to
both the offset and orient parameters.
The behaviour of this method will tell the hull to orient to the path and also tell it to use the path as an offset. i.e. it will move 50,100 each time it runs.
Use this method instead...
[SKAction followPath:path asOffset:NO orientToPath:YES duration:1.0];
That should solve your problem.
Also, get rid of that sequence.
Do this...
SKAction *moveAction = [SKAction followPath:path asOffset:NO orientToPath:YES duration:1.0];
[hull runAction:[SKAction repeatActionForever:moveAction]];

Changing sprite Images in Sprite-Kit

Is there a way to change the image of a sprite which has already been initialized with another image?
I tried:
if ([node.name isEqualToString:#"NameX"]) {
SKAction *fadeOut = [SKAction fadeOutWithDuration:0.3];
SKAction *fadeIn = [SKAction fadeInWithDuration:0.3];
[self.sprite runAction:fadeOut];
[self runAction:fadeOut completion:^{
self.sprite = [SKSpriteNode spriteNodeWithImageNamed:#"NameY"];
[self.sprite runAction:fadeIn]
}];
}
There is. Internally, the spriteNodeWithImageNamed: class method just uses the image name you pass it to set the node's texture property. That being said, if at any point you want to arbitrarily change the node's texture, you can just set it directly.
[self.sprite setTexture:[SKTexture textureWithImageNamed:#"someOtherImage"]];
There are also some SKActions for doing this, in case you want the node to resize or animate between different textures.
[self.sprite runAction:[SKAction setTexture:[SKTexture textureWithImageNamed:#"someOtherImage"] resize:YES]];
[self.sprite runAction:[SKAction animateWithTextures:#[tex1,tex2,tex3] timePerFrame:0.5 resize:YES restore:YES]];
You must create texture array such as this:
[SKAction animateWithTextures:[NSArray arrayWithObjects:
[SKTexture textureWithImageNamed:#"im1.png"],
[SKTexture textureWithImageNamed:#"im2.png"],
[SKTexture textureWithImageNamed:#"im3.png"],
[SKTexture textureWithImageNamed:#"im4.png"], nil] timePerFrame:0.5 resize:YES restore:YES];