i just started using SpriteKit and i'm discovering the API. i have an object to move on screen when a button is pressed and held. so what i did is set an NSTimer while the user is still holding and keep calling the same method to move the object every 0.1 seconds. the object does move but while moving at some point it like doesn't show a smooth transition horizontally. so i used NSLog to look at the objects position and i released that it's subtracting 10 points rather than 0.5. here is the code:
SKAction *moveLeft = [SKAction moveToX:((CGFloat)[[self childNodeWithName:#"Hero"] frame].origin.x)-0.5 duration:0.1];
[[self childNodeWithName:#"Hero"] runAction:moveLeft];
how can i fix that? besides, i realised in the console that the coordinate is like 150.000000 not like 150 or 150.0. could that be the problem?
Another question for the ones who worked with SpriteKit before!!
in a gameplay scene and you want to add a button you add it in the scene class or in the view controller?
i found a solution, i'm using this right now:
SKAction *moveLeft = [SKAction moveByX:1 y:0 duration:0];
it gives more precise results.
It sounds like you are compounding SKAction tweens, setting a new one before the old finishes. It's also possible you are creating timers and not killing them ? Can't be sure without seeing the implementation.
One way to solve the problem is to set a SKAction in the desired direction when the touch begins, and then removing the action when the touch ends. So you will only ever have one SKAction running at a time.
Without seeing your code that creates the NSTimer and how it handles them it's hard to determine exactly what is going on, so I'm basing my answer on what information you have given us. This is one possible scenario.
Related
I am developing a game that involves moving tokens around.
The user makes one move, and the tokens move according to predetermined rules. I have developed this using sprite-kit.
My problem is, once the user makes one move, the algorithm removes token SKSpriteNodes and adds token SKSpriteNodes as needed, but the user only sees the end result.
I would like them to see each token movement individually, at a pace that the user chooses (quick animation or slow animation). I have it working, but I feel that there must be a better solution.
My solution is to keep track of how many seconds I need to delay the removal of a token, and pass that into the function such that the function is called with .5 second delay for the first token, 1 delay for the second, 1.5 for the third, and so on.
The removeToken code is shown below, and the addToken code is similar.
(void)removeTokenIn:(NSTimeInterval)seconds {
Token *tokenToRemove = (Token *)[self childNodeWithName:
[NSString stringWithFormat:#"token%i",nbrTokens]];
SKAction *waitAction = [SKAction waitForDuration:seconds];
[tokenToRemove runAction:waitAction completion:^{
[tokenToRemove removeFromParent];
}];
nbrTokens--;
}
Is there a better solution?
You can do this in a single SKAction, which is neater in code, easier to debug and can easily be extended to add more sophisticated effects:
Use a single sequence SKAction that contains: a waitForDuration SKAction and a removeFromParent SKAction.
You could then consider adding things like making them fade out rather than disappear (add a fadeOutWithDuration SKAction), sound effects (play SKAction) etc.
I was wondering what the appropriate way was to resize an SKSpriteNode WITH its SKPhysicalBody in SpriteKit. At first I thought I could use SKActions to do resizeToX/Y and that would work. I tried doing this like:
SKAction *action = [SKAction resizeToY:whateverFloat completion:^{
SKPhysicalBody *newBody = [SKPhysicalBody bodyWithRectangleOfSize:spriteNode.size];
spriteNode.physicalBody = newBody;
}];
NOTE : I typed this from memory, its not the exact code, so please ignore syntax errors. I also tried moving around the variables just in case to see what happens. For example, I tried instantiating newBody outside the completion block. I tried assigning the newBody outside the completion block. I tried to update the categoryBitMask and all the same contactBitMasks again. Nothing works.
The closest I got it to working was when I didn't add a new physicalBody, the SKSpriteNode would resize, but its physicalBody would stay the same. If I add a new physicalBody, the whole SKSpriteNode disappears. I don't understand.
After searching online for a solution using SKAction, I found many different threads saying "SKActions don't work well with physics. Avoid mixing them." If I have to avoid mixing them, then how do I grow and shrink an SKSpriteNode the correct way and update its physical body while getting rid of its old one? The physics and collision logic from SpriteKits physics stuff is very important in my game.
I've been mucking around with sprite-kit but found out the only way to affect a sprites coordinate is with sprite.position, the issue here is I only want to affect one sprite coordinate, let's say y. How would I do this?
You should probably read up on SKActions:
SKAction *moveSprite = [SKAction moveToY:200 duration:0]; // or wherever...
[yourSpriteInstance runAction:moveSprite];
There is also a moveToX:duration:, moveTo:duration:, moveBy:duration: to mention a few of the options..
Like #sangony writes in the comments you can of course access the node's position directly through its position property:
sprite.position = CGPointMake(sprite.position.x, sprite.position.y + 200);
This will work just fine.
That being said, SKActions is the way to go about this in most real-life scenarios: E.g. if you want to have the sprite change its position after 3 seconds, then wait another 2 seconds before it begins rotating. It really is one of the cornerstones of the SpriteKit framework...
I basically want the action to be running and then in the middle of the action create a slow motion effect and then later bring it out of slow motion. Does anyone have any good feedback of how this might be done? I've thought about creating the action manually and using an update method, but I feel like that might be overkill. I was hoping for a simpler solution.
The other thought I have is to stop the action and then start it again at a slower duration, but I don't think it will stay on the same path and it will probably look weird.
This is the code I'm using to create the action.
CGMutablePathRef cgpath = CGPathCreateMutable();
CGPathMoveToPoint(cgpath,NULL, mysprite.position.x, mysprite.position.y);
CGPathAddCurveToPoint(cgpath, NULL, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
[mysprite runAction:[SKAction sequence:#[[SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:3]]]];
CGPathRelease(cgpath);
Each node has a speed property:
A speed modifier applied to all actions executed by a node and its
descendants.
Discussion
The default value is 1.0, which means that all actions run
at their normal speed. If you set a different speed, time appears to
run faster or slower for all actions executed on the node and its
descendants. For example, if you set a speed value of 2.0, actions run
twice as fast.
You could set this to a value smaller than 1 to make the action run slower. You can even animate the speed to gradually slow down:
[mySprite runAction:[SKAction speedTo:0.5 duration:1.0]];
For my application, I am trying to implement a character who displays a walking animation when walking and displays a jumping animation when in the air.
Using Cocos2D, I've created a simple animation. The problem is that the I am using CCRepeatForever and I cannot seem to stop the animation or switch it once I set it off.
Steffen Itterheim's book discussed some animation in the chapter regarding sprites. The way he created an animation was to add multiple sprite frames to a CCAnimation object and then run it with CCRepeatForever.
I'm currently looking at two possiblities:
1) Create multiple sprites and add them to a CCArray and then loop through the multiple sprites.
2) Find an alternate solution to CCRepeatForever and find a way to stop the animation or switch to a different animation.
The problem with number 1 is that I cannot figure out a way to render a selective sprite to the stage. It seems to be that the only way to add a sprite to the screen is to use [self addChild:mySprite]; This is limiting and problematic if I want to switch between multiple sprites. Is there a way to selectively render a sprite?
The problem for number 2 is that there seems to be no alternate to CCRepeatForever and the other animation classes are not sufficient for running the animation.
Thank you!
Assuming you assign a tag to the action like the following.
CCSprite *sprite = [CCSprite spriteWithFile:#"image.png"];
CCRotateBy *spinAction = [CCRotateBy actionWithDuration:1 angle:90];
CCRepeatForever *spinForever = [CCRepeatForever actionWithAction:spinAction];
[spinForever setTag:ANIMATION_TAG];
[sprite runAction:spinForever];
you can stop the animation by calling
[sprite stopActionByTag:ANIMATION_TAG];