as you can see below, i try to run an action which increases the size of my ScoreLabel, whenever the score increases. When i run my app, i realized, that the size of my label doesn't set back to his original form. I tried many ways, but couldnt realize it
I hope for you quick answers
-(void)update:(CFTimeInterval)currentTime{
/* Called before each frame is rendered */
// Update wird vor jeden Frame aufgerufen
// Score Counter
if( [speerArray count] > 1) {
SKSpriteNode *sprite = [speerArray objectAtIndex:1];
if (sprite.position.y < superhero.position.y && [sprite.name isEqualToString:#"speer"] && sprite.position.y > 0) {
SKAction* scoreAction = [SKAction scaleBy:2 duration:1];
score ++;
[scoreLabel runAction:scoreAction];
scoreLabel.text = [NSString stringWithFormat:#"%d", score/2];
sprite.name = #"afterBird";
}
}
Using the following lines might be what you are looking for:
[scoreLabel removeAllActions];
SKAction* scoreAction = [SKAction scaleBy:2 duration:1];
SKAction* revertAction = [SKAction scaleTo:1 duration:1];
SKAction* completeAction = [SKAction sequence:#[scoreAction, revertAction]];
[scoreLabel runAction:completeAction];
You can put in a waitForDuration: action as well, if required.
Related
So I have a method that draws squares and at a random chance, it will draw only triangles for 5 seconds, then return to drawing squares. How do I run the drawTriangle() continuously for a certain amount of time, then break out of it?
drawShape(){
drawSquare();
if (arc4random_uniform(50) == 0){
for 5 seconds{ << how do I implement that part?
stopDrawSquare();
drawTriangle();
}
}
}
SKAction *draw = [SKAction sequence:#[
[SKAction waitForDuration:1 withRange:1],
[SKAction performSelector:#selector(drawShape) onTarget:self]
]];
[self runAction:[SKAction repeatActionForever:draw] withKey:#"drawShape"];
Create 2 ivars:
BOOL ready;
BOOL drawTriangle;
Set ready to default true and drawTriangle to default false.
Add this code to your update method (or wherever you decide it should go)
if(ready == true) {
if (arc4random_uniform(50) == 0) {
drawTriangle = true;
ready = false;
SKAction *wait0 = [SKAction waitForDuration:5.0];
SKAction *block0 = [SKAction runBlock:^{
ready = true;
}];
[self runAction:[SKAction sequence:#[wait0, block0]]];
} else {
drawTriangle = false;
}
}
if(drawTriangle == true) {
NSLog(#"draw triangle");
} else {
NSLog(#"draw square");
}
I made a game with SpriteKit. I structured my game by coding a GameScene (SKScene) and a separate enemy class.
I want the spawning enemies to shoot a particle e.g. every 2 seconds (just moving an SKEmitterNode by the y axis) I tried to do it with a timer, but it doesn't really work.
I call my Enemy class from the gaming Scene with this code:
GameScene.m
-(void)enemiesLevel1{
EnemyClass* wave1 = [[EnemyClass alloc] init];
[wave1 enemiesLevel1:self];
}
And I am basically calling this method from EnemyClass.m
-(void)enemiesLevel1:(SKScene *)scene
{
enemy = [SKSpriteNode spriteNodeWithImageNamed:Enemy];
//Enemy Path
(...)
SKAction *followPath2 = [SKAction followPath:pathRef2
asOffset:NO
orientToPath:YES
duration: pathSpeed];
SKAction *forever = [SKAction repeatActionForever:followPath2];
//PhysicsBody Eigenshaften
enemy.physicsBody =[SKPhysicsBody bodyWithCircleOfRadius:enemy.size.width];
enemy.physicsBody.dynamic = YES;
enemy.physicsBody.categoryBitMask = enemyCategory;
[enemy runAction:forever];
[scene addChild:enemy];
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(weaponParticle)
userInfo:nil
repeats:YES];
}
-(void)weaponParticle{
screenHeight = self.frame.size.height;
screenWidth = self.frame.size.width;
//Schuss-Particles
enemyParticlePath = [[NSBundle mainBundle] pathForResource:#"ShootFire" ofType:#"sks"];
enemyParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:enemyParticlePath];
enemyParticle.physicsBody =[SKPhysicsBody bodyWithCircleOfRadius:0.2];
enemyParticle.physicsBody.categoryBitMask = shootCategory;
enemyParticle.physicsBody.contactTestBitMask = playerCategory;
//Schuss-Action
moveDown = [SKAction moveByX:0.0 y:-screenHeight duration:1.0];
remove = [SKAction removeFromParent];
weaponShot = [SKAction sequence:#[moveDown, remove]];
enemyParticle.position = CGPointMake(enemy.position.x, enemy.position.y+10);
[self addChild:enemyParticle];
[enemyParticle runAction: weaponShot];
}
The enemies are spawning 1 by 1 just how I wanted, but they can't shoot. Can anyone help me out?
Instead of scheduling NSTimer try something like this:
SKAction *shoot = [SKAction runBlock:^{
// add code that shoots here
}];
SKAction *wait = [SKAction waitForDuration:0.5];
[enemy runAction:[SKAction repeatActionForever:[SKAction sequence:#[shoot, wait]]]];
I want that every object from the NSMuttableArray to appear for 4 sec. and than disappear.And on this way to iterate all items from the array.Instead the result i got is that all items appears and after the period disappear together.
-(void)showTreasures{
for (int i = 0; i < _treasures.count; i++)
{
SKSpriteNode *obj = [_treasures objectAtIndex:i];
SKAction *show = [SKAction runBlock:^{
obj.hidden = NO;
}];
SKAction *wait = [SKAction waitForDuration:4];
SKAction *hide = [SKAction runBlock:^{
obj.hidden = YES;
}];
SKAction *sequence = [SKAction sequence:#[show, wait, hide]];
[obj runAction:sequence completion:^{
NSLog(#"Item %d", i);
}];
}
}
I think it's good job for recursive method, method which call itself.
You can created array of all of your objects (SKSpriteNode) and pass it to the method which takes first (or last) object and run appropriate action, remove the object and call the method again:
NSMutableArray *arrOfObject = //arrat with all of the sprites you want to show
[self runShowAction:arrOfObject];
-(void)runShowAction:(NSMutableArray*)array {
//if no object in the array return
if(array.count <= 0) return;
SKSpriteNode *obj = [array firstObject];
//Run your code here
//...
//On completion remove object from array and run this method again
[obj runAction:sequence completion:^{
NSLog(#"Item %d", i);
[array removeObject:obj];
[self runShowAction:array];
}];
}
Why don't you use an array to build up a list of all the actions, then run a sequence of all of those actions?
-(void)showTreasures
{
NSMutableArray *actions = [NSMutableArray array];
for (int i = 0; i < _treasures.count; i++)
{
SKSpriteNode *obj = [_treasures objectAtIndex:i];
SKAction *show = [SKAction runBlock:^{
obj.hidden = NO;
}];
SKAction *wait = [SKAction waitForDuration:4];
SKAction *hide = [SKAction runBlock:^{
obj.hidden = YES;
}];
SKAction *finish = [SKAction runBlock:^{
NSLog(#"Item %d", i);
}];
[actions addObjectsFromArray:#[show, wait, hide, finish]];
}
SKAction *sequence = [SKAction sequence:actions];
[obj runAction:sequence completion:^{
NSLog(#"Finished all items");
}];
}
I have a sprite kit game for Mac where I have multiple sprite nodes moving across the screen. How do I make it so I can removeFromParent a certain node?
For example, I have a loop that periodically adds a new node every second:
-(void)spawnNew {
xPos = 100;
yPos = 1000;
CGPoint location = CGPointMake(xPos, yPos);
orb = [SKSpriteNode spriteNodeWithImageNamed:#"orb"];
orb.position = location;
orb.scale = 0.3;
orb.name = #"orbSprite";
CGFloat dirX = 0;
CGFloat dirY = -30;
CGVector vector1 = CGVectorMake(dirX, dirY);
SKAction *action = [SKAction moveBy:vector1 duration:0.1];
[orb runAction:[SKAction repeatAction:action count:100000]];
[self addChild:orb];
}
How would I make it so that I can get the position or remove certain orbs, not the entire group? For example, if the position of a sprite is equal to x=100, y=200 then I would remove it from parent. Currently if I try to do this and check for an orb's position, it just checks the position of the newest orb to be added, not all of them which I would want.
Thanks, any help would be appreciated.
This might not be the most efficient answer but it does show how you might do this. keep in mind that generally with Sprite Kit your going to end up with a lot of nodes so grouping them under a parent SKNode is a good idea, it also lets to specify a point in the tree so you can easily get the children below it. The NSLog statements are just for testing/debug, I left then in as they may help if you try the code.
- (void)addOrbs {
SKNode *orbRoot = [SKNode node];
[orbRoot setName:#"ORB_ROOT"];
[self addChild:orbRoot];
for(int counter = 0; counter<5; counter++) {
SKSpriteNode *newOrb = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
[newOrb setPosition:CGPointMake(100, 100 + (100*counter))];
NSLog(#"ORB POS: %#", NSStringFromCGPoint([newOrb position]));
[newOrb setName:#"orbSprite"];
[orbRoot addChild:newOrb];
}
}
.
- (void)removeOrbAtPoint:(CGPoint)point {
SKNode *orbRoot = [self childNodeWithName:#"//ORB_ROOT"];
NSArray *allOrbs = [orbRoot children];
NSLog(#"ORBS: %#", allOrbs);
for(SKNode *eachOrb in allOrbs) {
if([eachOrb position].x == 100 && [eachOrb position].y == 200) {
NSLog(#"REMOVING: %# WITH POSITION: %#", [eachOrb name], NSStringFromCGPoint([eachOrb position]));
[eachOrb removeFromParent];
}
}
}
An alternate version of remove using fast block enumeration:
- (void)removeOrbAtPoint_v2:(CGPoint)point {
SKNode *orbRoot = [self childNodeWithName:#"//ORB_ROOT"];
NSArray *allOrbs = [orbRoot children];
NSLog(#"ORBS: %#", allOrbs);
[orbRoot enumerateChildNodesWithName:#"orbSprite" usingBlock:^(SKNode *node, BOOL *stop) {
if([node position].x == 100 && [node position].y == 200) {
NSLog(#"REMOVING: %# WITH POSITION: %#", [node name], NSStringFromCGPoint([node position]));
[node removeFromParent];
}
}];
}
A different approach - You can add the removeFromParent into an action sequence. This will automatically remove it when it gets to a defined point, with no additional code.
Would changing your moveBy: action to a moveTo: work?
SKAction *moveToFinish = [SKAction moveTo:finishPoint duration:5.0];
SKAction *removeMe = [SKAction removeFromParent];
SKAction *actionSeq = [SKAction sequence:#[moveToFinish,removeMe]];
[sprite runAction:actionSeq];
My finishPoint is generated by a random statement to give it some variety.
Loop through all the nodes in the node or scene you are adding the orbs to.
for(SKNode * child in self.children) {
...
}
See if they are within this box.
for(SKNode * child in self.children) {
if(CGRectIntersectsRect(child.calculateAccumulatedFrame, CGRectMake(10, 50, 100, 200))) {
...
}
}
Remove the child (CGRectMake takes x,y,width,height as parameters)
for(SKNode * child in self.children) {
if(CGRectIntersectsRect(child.calculateAccumulatedFrame, CGRectMake(10, 50, 100, 200))) {
[child removeFromParent];
}
}
I don't really know where to start. I have an image of a circle stored in an SKSpriteNode and a physicsBody that mirrors the size when it is created.
I am using an SKAction to scale down the size of the image though, and the physicsBody remains the same size. How can I scale down the physicsBody?
My code:
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"whiteball"];
sprite.size = CGSizeMake(20, 20);
sprite.position = CGPointMake(dx, CGRectGetHeight(self.frame)-dy);
sprite.name = #"ball";
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
[self addChild:sprite];
SKNode *ballNode = [self childNodeWithName:#"ball"];
if (ballNode != Nil){
ballNode.name = nil;
SKAction *delay = [SKAction waitForDuration:3];
SKAction *scale = [SKAction scaleTo:0 duration:1]; // I want this to scale the physicsBody as well as the spriteNode...
SKAction *remove = [SKAction removeFromParent];
//put actions in sequence
SKAction *moveSequence = [SKAction sequence:#[delay, scale, remove]];
//run action from node (child of SKLabelNode)
[ballNode runAction:moveSequence];
}
If you set the size of the physicsbody to the size of the circle, you may achieve what you seek.
_circle = [SKSpriteNode spriteNodeWithImageNamed:#"cirlce"];
_circle.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_circle.size.width/2];