Cocos2s: get CCSprite position while animating before reaching the destination - ios

I have a CCSprite that is being subjected to a position animation. It is animating perfectly.
At some point of time, between starting the animation and reaching the final position, I want to get the exact position of the CCSprite
I know that the sprite.position will return the final position
Here is the animation code:
sprite = [CCSprite spriteWithFile:#"sprite.png"];
[sprite setPosition:ccp(winSize.width/2, 170)];
[self addChild:sprite];
CCMoveTo *moveSpriteUp = [CCMoveTo actionWithDuration:2.0 position:ccp(winSize.width/2, 210)];
CCEaseOut *upEase = [CCEaseOut actionWithAction:moveSpriteUp rate:2];
CCMoveTo *moveSpriteDown = [CCMoveTo actionWithDuration:2.0 position:ccp(winSize.width/2, 170)];
CCEaseOut *downEase = [CCEaseOut actionWithAction:moveSpriteDown rate:2];
CCSequence * spriteMoveSeq = [CCSequence actions:upEase, downEase, nil];
CCRepeatForever *spriteRepeat = [CCRepeatForever actionWithAction:spriteMoveSeq];
[sprite runAction:spriteRepeat];
//1 or 1.x seconds later, the sprite's Y postion will be somewhere between 170 and 210, this is what I want to catch
Any way I can do this?

What about CCDelayTime + CCCallFunc? Something like.-
CCDelayTime *delay = [CCDelayTime actionWithDuration:kDelay];
CCCallFunc *func = [CCCallFunc actionWithTarget:self selector:#selector(getSpritePosition)];
[sprite runAction:spriteRepeat];
[self runAction:[CCSequence actionOne:delay two:func]];
In getSpritePosition you could get the sprite position (assuming sprite is accessible from your scene scope), and do whatever you need with it.

Related

SKSpriteNode resize and update SKPhysicsJointPin anchor point

I'm trying to learn SpriteKit. I have a balloon (SKSpriteNode) that after running a SKAction for resizing, the balloon will change its size. the balloon has a string attached to it. the string is just multiple SKSpriteNode attached with SKPhysicsJointPin with each other. I've inverted gravity so the balloon with attached string got upward and after 3sec timeout it gets smaller. so far it works except for the start attached point of the string is not moved to the correct position of balloon. how to solve this issue?
so i'm adding only the necessary part here otherwise the post gets too bulky. so again see balloon has skaction which resizes the balloon, and how to move the string to correct position while resizing?
Balloon is subclassed SKSpriteNode
Balloon *balloon = [Balloon spriteNodeWithImageNamed:#"balloon.png"];
[balloon setPosition:CGPointMake(200, CGRectGetMinY(scene.frame))];
[balloon setName:#"balloon"];
[balloon setUserInteractionEnabled:NO];
[balloon setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:55]];
balloon.physicsBody.dynamic = YES;
[balloon.physicsBody setRestitution:0.4f];
[balloon.physicsBody setFriction:0.0f];
CGPoint attachPoint = CGPointMake(200, CGRectGetMinY(scene.frame));
//create the rope
SKRopeNode *rope = [SKRopeNode new];
rope.name = #"ropeParent";
[scene addChild:rope];
[rope setAttachmentPoint:attachPoint toNode:balloon];
rope.ropeLength = 6;
[rope runAction:[SKAction sequence:#[[SKAction waitForDuration:4.5], [SKAction removeFromParent]]]];
[balloon runAction:[SKAction sequence:#[[SKAction waitForDuration:4], [SKAction scaleTo:balloon.yScale/2 duration:0.5], [SKAction removeFromParent]]]];
this how i create rope method for creating the length
SKSpriteNode *firstPart = [SKSpriteNode spriteNodeWithImageNamed:#"rope_part.png"];
firstPart.position = _positionOnStartNode;
firstPart.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:firstPart.size];
firstPart.physicsBody.allowsRotation = NO;
for (int i=1; i<ropeLength; i++) {
SKSpriteNode *ropePart = [firstPart copy];
ropePart.position = CGPointMake(firstPart.position.x, firstPart.position.y - (i*ropePart.size.height));
ropePart.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ropePart.size];
ropePart.physicsBody.allowsRotation = YES;
[self.scene addChild:ropePart];
}
and this is for adding joints for the rope before
SKNode *nodeA = balloon;
SKSpriteNode *nodeB = [_ropeParts objectAtIndex:0];
SKPhysicsJointPin *joint = [SKPhysicsJointPin jointWithBodyA: nodeA.physicsBody
bodyB: nodeB.physicsBody
anchor: _positionOnStartNode];
for (int i=1; i<_ropeParts.count; i++) {
SKSpriteNode *nodeA = [_ropeParts objectAtIndex:i-1];
SKSpriteNode *nodeB = [_ropeParts objectAtIndex:i];
SKPhysicsJointPin *joint = [SKPhysicsJointPin jointWithBodyA: nodeA.physicsBody
bodyB: nodeB.physicsBody
anchor: CGPointMake(CGRectGetMidX(nodeA.frame),
CGRectGetMinY(nodeA.frame))];
[self.scene.physicsWorld addJoint:joint];
}

Set position to SKSpriteNode after forcefully stop it (i.e. set velocity 0)

I am getting strange behaviour in my game. I have one SKSpriteNode. After give force to my SKSpriteNode, due to effect of force my SKSpriteNode starts travelling (i.e mySpriteNode.physicsBody.isResting == NO).
SCENARIO :
When ball stop it's simulation(i.e mySpriteNode.physicsBody.isResting == YES) and i give any position to mySpriteNode. this works perfectly.
BUT,
When mySpriteNode is travelling (i.e mySpriteNode..physicsBody.isResting == NO) and i give velocity 0 to mySpriteNode and it stops(i.e mySpriteNode..physicsBody.isResting == YES). But when i give any position to it, It doesn't make any effect.
mySpriteNode's position is remaining its place where i give 0 velocity.
Any ideas?
It is Working perfectly for me when I re-position the sprite in the action block completion.
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.xScale = 0.5;
sprite.yScale = 0.5;
sprite.position = CGPointMake(self.size.width/2.0, self.size.height/2.0);
sprite.physicsBody = [ SKPhysicsBody bodyWithRectangleOfSize:sprite.size];
SKAction *act1 = [SKAction runBlock:^{
[sprite.physicsBody applyImpulse:CGVectorMake(0, 100)];
}];
SKAction *act2 = [SKAction waitForDuration:2];
SKAction *act3 = [SKAction runBlock:^{
[sprite.physicsBody setVelocity:CGVectorMake(0, 0)];
}];
SKAction *seq = [SKAction sequence:#[act1,act2,act3]];
[sprite runAction:seq completion:^{
sprite.position = CGPointMake(self.size.width/2.0, self.size.height/2.0);
}];
self.physicsWorld.gravity = CGVectorMake(0, 0);
Try repositioning in completion block of an action.

Sprites spawning in incorrect position

I have a row of 5 sprites and they spawn every 4 seconds and move down along the Y-axis. The sprites are spawning the correct amount each time, I have 5 sprites equal spaced apart across the screen, they're all center perfectly.
However when the performSelector action is called, when it spawns them in they don't go to the right position. The move over to the left so far that I can only see half a sprite on the left side of the screen. After testing it, it seems they are all stacked on top of each other at the wrong position.
Any idea whats going on? I'd appreciate any help. Here's all the code I'm using in the scene.
-(void) addBricks:(CGSize)size {
for (int i = 0; i < 5; i++) {
//create brick sprite from image
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
//resize bricks
brick.size = CGSizeMake(60, 30);
//psoition bricks
int xPos = size.width/7.5 * (i+.5);
int yPos = 450;
brick.position = CGPointMake(xPos, yPos);
//add move action
SKAction *wait = [SKAction waitForDuration:3];
SKAction *move = [SKAction moveByX:0 y:-36.9 duration:1];
SKAction *sequence = [SKAction sequence:#[wait, move]];
SKAction *repeatMove = [SKAction repeatActionForever:sequence];
[brick runAction:repeatMove];
[self addChild:brick];
}
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:(243.0f/255) green:(228.0f/255) blue:(165.0f/255) alpha:1.0];
//add action to spawn bricks
SKAction *spawn = [SKAction performSelector:#selector(addBricks:) onTarget:(self)];
SKAction *delay = [SKAction waitForDuration:4];
SKAction *delayThenSpawn = [SKAction sequence:#[delay, spawn]];
[self runAction:[SKAction repeatActionForever:delayThenSpawn]];
[self addBricks:size];
}
return self;
}
as LearnCocos2D mentioned in your last question.. you cant use a selector when youre passing a parameter into a method.
addBricks expects size .. you arent passing size into it.
change your spawn action to
SKAction *spawn = [SKAction runBlock:^{
// make this whatever size you want, i'm just using the scene's size
[self addBricks:self.size];
}];
that should get you started

method not spawning sprite properly

Right now I have five bricks at the top of the screen all on the same y position and equally spaced apart. I would like to have five more breaks spawn outside of the top of the scene and slide down every 5 seconds. While the new bricks are sliding down I want the previous bricks to slide down to make room for the new. I want this sequence to continue on forever. I would like the new bricks to pick up the same properties of the old ones , such as when a ball hits them the react.
With the code I put in to accomplish this, I get a single brick spawning and then sliding down. Also the new brick does not act the same way as the old, it does not act like its a part of the physics world. How do I get 5 bricks spawning then sliding down and applying the same properties as the other bricks.
This code is the code I added to spawn more bricks:
-(void) spawnMoreBricks:(CGSize)size {
for (int i = 0; i < 5; i++) {
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
//resize bricks
brick.size = CGSizeMake(60, 30);
//position it
brick.position = CGPointMake(self.size.width/2, self.size.height);
SKAction *wait = [SKAction waitForDuration:5];
SKAction *spawn = [SKAction runBlock:^{[self addChild: brick];
SKAction *move = [SKAction moveByX:0 y:-80 duration:3];
[brick runAction: move];
}];
SKAction *spawnSequence = [SKAction sequence:#[wait, spawn]];
SKAction *repeatSequence = [SKAction repeatActionForever:spawnSequence];
[self runAction:repeatSequence];
}
}
This code adds the bricks into the scene right when it initializes, they react to the ball colliding with them and everything:
- (void) addBricks:(CGSize)size {
for (int i = 0; i < 5; i++) {
//create brick sprite from image
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
//resize bricks
brick.size = CGSizeMake(60, 30);
//psoition bricks
int xPos = size.width/5 * (i+.5);
int yPos = size.height - 25;
brick.position = CGPointMake(xPos, yPos);
brick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:brick.frame.size];
brick.physicsBody.dynamic = NO;
brick.physicsBody.categoryBitMask = brickCategory;
//add bricks to scene
[self addChild:brick];
}
}

How to move a sprite along a CGPath with variable velocity

I am programming a game and want to move a sprite along a fixed path (straights and curves - think railroad engine) but I want to be able to drive the animation in terms of the sprites velocity - and change that velocity in response to game events.
followPath:duration: in SKAction is close, but there seems no way to adjust the speed "on the fly" - I definitely care about sprite speed - not 'duration to follow path'.
CGPath seems like the right construct for defining the path for the sprite, but there doesn't seem to be enough functionality there to grab points along the path and do my own math.
Can anyone suggest a suitable approach?
You can adjust the execution speed of any SKAction with its speed property. For example, if you set action.speed = 2, the action will run twice as fast.
SKAction *moveAlongPath = [SKAction followPath:path asOffset:NO orientToPath:YES duration:60];
[_character runAction:moveAlongPath withKey:#"moveAlongPath"];
You can then adjust the character's speed by
[self changeActionSpeedTo:2 onNode:_character];
Method to change the speed of an SKAction...
- (void) changeActionSpeedTo:(CGFloat)speed onNode:(SKSpriteNode *)node
{
SKAction *action = [node actionForKey:#"moveAlongPath"];
if (action) {
action.speed = speed;
}
}
Try some think like this :
CGMutablePathRef cgpath = CGPathCreateMutable();
//random values
float xStart = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float xEnd = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
//ControlPoint1
float cp1X = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float cp1Y = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.height ];
//ControlPoint2
float cp2X = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float cp2Y = [self getRandomNumberBetween:0 to:cp1Y];
CGPoint s = CGPointMake(xStart, 1024.0);
CGPoint e = CGPointMake(xEnd, -100.0);
CGPoint cp1 = CGPointMake(cp1X, cp1Y);
CGPoint cp2 = CGPointMake(cp2X, cp2Y);
CGPathMoveToPoint(cgpath,NULL, s.x, s.y);
CGPathAddCurveToPoint(cgpath, NULL, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
SKAction *planeDestroy = [SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:5];
[self addChild:enemy];
SKAction *remove2 = [SKAction removeFromParent];
[enemy runAction:[SKAction sequence:#[planeDestroy,remove2]]];
CGPathRelease(cgpath);

Resources