The Child SKSpriteKITnodes in the coordinate system doesn't work well I am trying to move the SKSprite nodes from Top To Bottom But they Get stop in the middle ?
ColorCoin *coin = [[ColorCoin alloc] initWithImageNamed: coinName] ;
SKAction *move = [SKAction moveToY:0 duration: 10];
CGFloat screenWidth = [self dropNode].size.width;
int x = arc4random() % (int) _dropNode.calculateAccumulatedFrame.size.width;
coin.zPosition =0.0;
coin.position = CGPointMake( x, _dropNode.calculateAccumulatedFrame.size.height-30);
coin.name = coinName;
[coin runAction: move];
[[self dropNode] addChild:coin];
The anchorPoint of an SKSpriteNode is (0.5, 0.5) by default. Therefore, when [self dropNode].position.y == 0 it's correct the the child node will be in the middle of its parent SKSpriteNode.
If you want the coin to move to the bottom of dropNode you can either set the anchorPoint of dropNode to (x, 0) (where x is any value in the range 0-1). Or you can change move to:
SKAction *move = [SKAction moveToY:-screenWidth / 2 duration: 10];
Related
I want to rotate this arrow across a fixed point in clock. The code below doesn't do what i intend to do, it rotates the whole arrow towards a center of a circle. I also tried to animate it across a circular path but it didn't give me the effect i want ...
i want this arrow to rotate around a circle while the arrow head is pointed towards the center of this circle
-(void) addArrow2:(CGSize)size{
_Arrow2 = [SKSpriteNode spriteNodeWithImageNamed:#"arrow2"];
_Arrow2.position = CGPointMake(self.frame.size.width/2 , self.frame.size.height/2 +30);
SKAction *rotate = [SKAction rotateByAngle:M_2_PI duration:1];
SKAction *forever = [SKAction repeatActionForever:rotate];
_Arrow2.yScale = 0.45;
_Arrow2.xScale = 0.45;
[self addChild:_Arrow2];
[_Arrow2 runAction:forever];
}
I suppose your got this kind of arrow →. Change the anchorPoint as (1.0, 0.5) and make the arrow follow a circle path. Make some change to fit your need.
- (void)addArrow2
{
_Arrow2 = [SKSpriteNode spriteNodeWithImageNamed:#"arrow2"];
_Arrow2.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2+30);
_Arrow2.anchorPoint = CGPointMake(1.0, 0.5);
_Arrow2.yScale = 0.45;
_Arrow2.xScale = 0.45;
// Make a circle path
CGMutablePathRef circle = CGPathCreateMutable();
CGFloat centerX = self.frame.size.width/2;
CGFloat centerY = self.frame.size.height/2;
CGFloat radius = 25.0;
CGFloat startAngle = 0.0;
CGFloat endAngle = 2*M_PI;
CGPathAddArc(circle, NULL, centerX, centerY, radius, startAngle, endAngle, true);
CGPathCloseSubpath(circle);
SKAction *followPath = [SKAction followPath:circle asOffset:NO orientToPath:YES duration:2.0];
// Infinite rotation
SKAction *forever = [SKAction repeatActionForever:followPath];
[_Arrow2 runAction:forever];
[self addChild:_Arrow2];
}
Animation preview:
I'm working on a game that has 3 clouds in the background which are each SKSpriteNodes. In order to create a parallax effect I'm moving them down the screen as the character is moving up (the screen is moving up as well).
I removing them in the update method after they reach the bottom of the screen. I would like to add them back at a Y position 20px above the viewable scene so that would be 500x on an iPhone 4 and 588px on a iPhone 5. Then just repeat over and over.
Then only way I can think of to do this is to chain SKActions and run a sequence, but I don't think that is the best way. I'm trying to find a way to add them back after they have been removed in the update method.
-(id)initWithSize:(CGSize)size {
...
//Moving Clouds
[self performSelector:#selector(createClouds) withObject:nil afterDelay:.2];
}
-(void)createClouds {
SKSpriteNode *cloud1 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud1"];
cloud1.position = CGPointMake(40, 200);
cloud1.xScale = .5;
cloud1.yScale = .5;
cloud1.name = #"Cloud1";
cloud1.zPosition = 0;
SKAction *moveCloud1 = [SKAction moveToY:-20 duration:15];
[cloud1 runAction:[SKAction repeatActionForever:moveCloud1]];
[self addChild:cloud1];
SKSpriteNode *cloud2 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud2"];
cloud2.position = CGPointMake(300, 320);
cloud2.xScale = .5;
cloud2.yScale = .5;
cloud2.name = #"Cloud2";
cloud2.zPosition = 0;
SKAction *moveCloud2 = [SKAction moveToY:-40 duration:15];
[cloud2 runAction:[SKAction repeatActionForever:moveCloud2]];
[self addChild:cloud2];
SKSpriteNode *cloud3 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud3"];
cloud3.position = CGPointMake(200, 450);
cloud3.xScale = .5;
cloud3.yScale = .5;
cloud3.name = #"Cloud3";
cloud3.zPosition = 0;
SKAction *moveCloud3 = [SKAction moveToY:-200 duration:20];
[cloud3 runAction:[SKAction repeatActionForever:moveCloud3]];
[self addChild:cloud3];
}
-(void)update:(CFTimeInterval)currentTime {
....
// Remove Cloud from parent
[self enumerateChildNodesWithName:#"Cloud1" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x < 0 || node.position.y < 0) {
[node removeFromParent];
//Add node back to the scene at a Y position above the screen then move
// the cloud downwards again. Repeat Forever.
// Exp: Move Cloud 1 to X position 40, Y position 500 (20 pixels above the top of the screen for an iPhone 4s)
}
else {
// Do something?
}
}];
You can add an SKAction to move your cloud back to the top:
SKSpriteNode *cloud1 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud1"];
cloud1.position = CGPointMake(40, 200);
cloud1.xScale = .5;
cloud1.yScale = .5;
cloud1.name = #"Cloud1";
cloud1.zPosition = 0;
SKAction *moveCloud1 = [SKAction moveToY:-20 duration:15];
// SKAction to move cloud back to top of screen
SKAction *moveToTop = [SKAction moveToY:CGRectGetHeight(self.frame)+20 duration:0];
SKAction *cloud1Action = [SKAction sequence:#[moveCloud1, moveToTop]];
[cloud1 runAction:[SKAction repeatActionForever:cloud1Action]];
You won't need the code in the update method.
I think you can simply change the position
Also, use CGRectGetMinY to use relative positioning, easier to manage different screen sizes.
And check only the Y position too, because X won't change if they're moving vertically.
This is my idea:
-(void)update:(NSTimeInterval)currentTime {
for(int i = 1; i<=3; i++){
NSString* cloud = [NSString stringWithFormat:#"Cloud%d",i];
[self enumerateChildNodesWithName:cloud usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0) {
node.position = CGPointMake(node.position.x,CGRectGetMaxY(self.frame)+20);
}
}];
}
}
I'm using a sprite kit to create a game.
I have a player node which I control by touch but then I have an enemy node that moves on its own. I need the enemy to be able to shoot projectiles on its own. I'm guessing I would need a method for this and to call it. I need the direction of the projectiles to shoot wherever my player is. Any suggestions?
Here is the method I have:
-(void)monstershoot
{
SKSpriteNode * projectile = [SKSpriteNode spriteNodeWithImageNamed:#"projectile"];
projectile.position = _enemy.position;
CGPoint offset = rwSub(_player.position, projectile.position);
if (offset.x <= 0) return;
[_background addChild:projectile];
CGPoint direction = rwNormalize(offset);
CGPoint shootAmount = rwMult(direction, 1000);
CGPoint realDest = rwAdd(shootAmount, projectile.position);
// 9 - Create the actions
float velocity = 480.0/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[projectile runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
But nothing happens when I call it.
It will do "nothing" in two cases: the image doesn't exist or the enemy is right of the player. Replace this line
if (offset.x <= 0) return;
by a check if the length of the offset vector is 0. You could write a method to calculate the length by using
sqrtf(powf(offset.x,2) + powf(offset.y,2))
Further you should use the length of shootAmount to calculate
float realMoveDuration = 1000 / velocity;
which will give you the same (and correct) speed for every projectile. But otherwise I see nothing wrong with your code.
I'm making a simple iOS game where the goal is to fire arrows from a bow at the bottom of the screen to destroy monsters coming from the top of the screen. I have this bit of code, but it only makes monsters spawn and move left from the right side of the screen. Here's the code:
- (void) spawnMonster
{
SKSpriteNode *monster = [SKSpriteNode spriteNodeWithImageNamed: #"Monster"];
monster.xScale = 0.5;
monster.yScale = 0.5;
int minY = monster.size.height / 2;
int maxY = self.frame.size.height - monster.size.height / 2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
monster.position = CGPointMake(self.frame.size.width + monster.size.width / 2, actualY);
[self addChild: monster];
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
SKAction *actionMove = [SKAction moveTo: CGPointMake(-monster.size.width / 2, actualY) duration: actualDuration];
SKAction *actionMoveDone = [SKAction removeFromParent];
[monster runAction: [SKAction sequence: #[actionMove, actionMoveDone]]];
}
Suggestions? Thanks.
If you want objects falling from the top then you should set the arc4random function to the monster sprite's X and not Y. The Y should always be the top of your view. Remember x is the left/right axis and y is the up/down axis.
Hi I need to spawn balloons which are my sprites along the bottom of the x axis out of the screen randomly. I used this code from Ray Wenderliches website but when i click the button that starts the spawning they don't spawn?
#implementation MyScene2
- (void)addMonster {
// Create sprite
SKSpriteNode * monster = [SKSpriteNode spriteNodeWithImageNamed:#"balloon11.png"];
// Determine where to spawn the monster along the X axis
int minX = monster.size.height / 2;
int maxX = self.frame.size.height - monster.size.height / 2;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
// Create the monster slightly off-screen along the right edge,
// and along a random position along the X axis as calculated above
monster.position = CGPointMake(self.frame.size.width + monster.size.width/2, actualX);
[self addChild:monster];
// Determine speed of the monster
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(-monster.size.width/2, actualX)
duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[monster runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 1) {
self.lastSpawnTimeInterval = 0;
[self addMonster];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
Your main problem lies in this lines of code:
monster.position = CGPointMake(self.frame.size.width + monster.size.width/2 , actualX);
That method works like this: CGPointMake( x_coord , y_coord );.
Right now, you are setting the x value to width of the frame plus the width of the sprite, so this will cause the sprite to be spawned off the screen. You are giving the y coordinate the randomized value. Instead, use
CGPointMake(actualX , self.frame.size.height);
Now there is another problem with this code. These coordinates are based on the view, but monster.position is based on the SKScene. The SKScene origin usually does not match up with the origin for the view. So you have to do it like this:
monster.position = [CGPointMake(actualX , self.frame.size.height); locationInNode: self];
Also, I noticed that you are using frame.size.width for intended Y axis coordinates and frame.size.height for intended X axis coordinates. Width is for x and height is for y, so make sure you are using those properly.