I am very new to Sprite Kit game development. I am currently developing my first game - a simple game where the player must navigate a simple object past obstacles. If the object collides with an obstacle - GAME OVER.
I got that to work fine but I have been stuck for over a week on an animation problem. I've literally searched for an answer for days now ... without success. So basically ... when my object is being navigated past the obstacles I have one animation pattern (that works fine). Once it hits an obstacle however, I want the animation pattern to change (into an explosion). And for some reason that is NOT HAPPENING! :( It is basically completely IGNORING the two lines of code ([self stopBeeAnimation]; and [self defeatedAnimation];) I added and the game is over right away.
Any help I might get on this, is really appreciated (this is driving me nuts) :)
Thank you so much.
Here my code:
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKSpriteNode *firstSprite;
SKSpriteNode *secondSprite;
firstSprite = (SKSpriteNode *)contact.bodyA.node;
secondSprite = (SKSpriteNode *)contact.bodyB.node;
if ((contact.bodyA.categoryBitMask == beeCategory) && (contact.bodyB.categoryBitMask = obstacleCategory)) {
[self stopBeeAnimation];
[self defeatedAnimation];
[obstacleArray removeAllObjects];
[bee.physicsBody setAffectedByGravity:NO];
[timer invalidate];
scoreLabel.fontSize = 30;
scoreLabel.text = [NSString stringWithFormat:#"GAME OVER %d", score/2];
[self removeFromParent];
SKTransition *transition = [SKTransition fadeWithDuration:5];
[self.scene.view presentScene:[[NPMyScene alloc]initWithSize:self.size] transition:transition];
}}
and:
-(void) defeatedAnimation {
SKAction *defeatedAnimation;
NSMutableArray *textures2 =[NSMutableArray arrayWithCapacity:5];
for (int a = 1; a < 6; a++) {
NSString *textureName2 = [NSString stringWithFormat:#"defeated%d", a];
SKTexture *texture2 = [SKTexture textureWithImageNamed:textureName2];
[textures2 addObject:texture2];
}
defeatedAnimation = [SKAction animateWithTextures:textures2 timePerFrame:0.2];
}
Look here:
SKTransition *transition = [SKTransition fadeWithDuration:5];
[self.scene.view presentScene:[[NPMyScene alloc]initWithSize:self.size] transition:transition];
You're presenting a different scene (or maybe another instance of the same scene class?) in the view after trying to run your animations. Any animation changes in the current scene won't be visible because the new scene replaces the current scene.
But even before that...
- (void)defeatedAnimation {
// ...
defeatedAnimation = [SKAction animateWithTextures:textures2 timePerFrame:0.2];
}
You're creating an SKAction but not doing anything with it. For an action to take effect you need to run it on a node with runAction:.
Related
I have been struggling with this for a while now and cant seem to find the issue.
I have an SKScene which i will refer to as self, and an SKNode that is called chapterScene that gets added to self. I have a boundary set up that contains a movable character. Here is how I have set this up
ViewController.m (the controller that presents the SKScene subclass OLevel
- (void)viewDidLoad {
[super viewDidLoad];
// Configure the view.
SKView *skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
scene = [OLevel sceneWithSize:self.view.frame.size];
scene.scaleMode = SKSceneScaleModeAspectFit;
// Present the scene.
[skView presentScene:scene];
// Do things after here pertaining to initial loading of view.
}
Here is my OLevel.m
- (id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
NSLog(#"Creating scene");
[self setUpScene];
}
return self;
}
- (void)setUpScene {
NSLog(#"Setting up scene");
//self.speed = 0.9f;
#pragma 1 Set up scene
// Set up main chapter scene
self.anchorPoint = CGPointMake(0.5, 0.5); //0,0 to 1,1
chapterScene = [SKNode node];
chapterScene.position = CGPointZero;
chapterScene.name = #"chapterScene";
[self addChild:chapterScene];
// Set up physics boundary
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
self.physicsWorld.contactDelegate = self;
.
.
.
}
The main point here, is that ultimately I have set up my scene and its child nodes correctly (as I expected until of late). When I run on the simulator (iPhone 6), I am using the - (void)didBeginContact:(SKPhysicsContact *)contact method to monitor and collisions. Whenever contact does begin, I am logging the following
CGPoint contactPoint = contact.contactPoint;
NSLog(#"non conv: %f, %f", contactPoint.x, contactPoint.y);
CGPoint sceneContactPoint = [self convertPoint:contactPoint toNode:chapterScene];
NSLog(#"1 conv pt: %f, %f", sceneContactPoint.x, sceneContactPoint.y);
I am also logging the characters position as well to make sure that this converted point is correct.
When I run this on the simulator, and the moving node character hits the wall, I get this:
2016-02-25 20:02:31.102 testGame[43851:14374676] non converted point: 0.143219, 29.747963
2016-02-25 20:02:31.102 testGame[43851:14374676] 1 conv pt: -140.206223, 615.699341
2016-02-25 20:02:31.102 testGame[43851:14374676] Player hit the wall
2016-02-25 20:02:31.103 testGame[43851:14374676] player pos: -140.206238, 590.749268
which is quite correct.
HOWEVER, for whatever reason I can not seem to find, this is the exact same code running on my iPhone 5C...
2016-02-25 20:04:50.062 testGame[2907:1259447] non converted point: 160.337631, 310.808350
2016-02-25 20:04:50.063 testGame[2907:1259447] 1 conv pt: 70.996162, 900.004272
2016-02-25 20:04:50.064 testGame[2907:1259447] Player hit the wall
2016-02-25 20:04:50.065 testGame[2907:1259447] player pos: -89.003845, 593.984009
I really hope this is a simple fix that I am missing. If anyone can help me I would greatly appreciate it. thanks
UPDATE
It appears that all that is happening is that when I run it on the simulator, the point is being referenced from the center of the screen (0,0) whereas on the device, the reference point is the true origin, the top left corner being (0,0), with the center being, in the iPhone 5c's case, (160, 284). Still not sure how to correct this...or why it is even happening.
So far this is the only solution I can think of...
if (!TARGET_OS_SIMULATOR) {
contactPoint = CGPointMake(sceneContactPoint.x - screenBounds.size.width/2.0f, sceneContactPoint.y - screenBounds.size.height/2.0);
}
else {
contactPoint = CGPointMake(sceneContactPoint.x, sceneContactPoint.y);
}
But this is embarrassing. Either this is a bug with Xcode or Apple, or there is reason this is happening and a different solution.
I have been writing a game with a helicopter for a while and now I am trying to offer the user the option between two helis. I used this code to animate the original one, with no problems whatsoever:
heliAtlas = [SKTextureAtlas atlasNamed:#"APACHE"];
NSArray *heliAtlasArray = [heliAtlas textureNames];
NSArray *heliAtlasArraySorted = [heliAtlasArray sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableArray *heliTextures = [NSMutableArray array];
for (NSString *filename in heliAtlasArraySorted) {
SKTexture *texture = [heliAtlas textureNamed:filename];
[heliTextures addObject:texture];
}
SKAction *animateHeli = [SKAction animateWithTextures:heliTextures timePerFrame:.016];
SKAction *repeatAnimation = [SKAction repeatActionForever:animateHeli];
Now, I have used the exact same code for the second heli,but when I tried to use the heli, the animation looked glitched. I slowed down the animation, and I realized that it shows the first half of the animation, and then animates through the atlas style photos (with two side-by side helis cut in half or upside down, etc).
Why are my two atlases acting differently?
I found out that an atlas has an image limit of 50. I've removed images until there were 50 left and it worked just fine from there.
I´m trying a pretty simple thing for testing and can´t get it to work, sorry.
What I want to achieve is that the changes in the textfield label are seen on the screen as well as the color changes.
I already tried several things without success and I´m pretty sure I haven´t got the clue yet.
Any ideas what I´m doing wrong?
I have this right now:
- (IBAction) writeTextToLabel:(id)sender
{
NSAnimation *animation;
[animation setDuration:10];
int n = 100;
NSString *string1 = #"";
[animation startAnimation];
for (int i = 1 ; n >= i ; i++) {
[label setTextColor:[self ccRandom:1.0 :0.0]];
string1 = [NSString stringWithFormat:#"Hello World %.3i\n",i];
[label setStringValue:string1];
}
[animation stopAnimation];
}
You can use AnimTextFieldUsingNSAnimationContext by ipmcc
This code makes fadeIn/fadeOut effect of changing text.
But you can add changing color code after [self setStringValue: aString]; to change your color.
Don't forget to include Quartz.framework to your project.
I am working on a Mac app for Mac OS X 10.6+ and need to redraw the contents of a CAOpenGLLayer while an animation is occurring. I think I've read up on all the necessary parts, but it is just not working for me. I set up the animation as follows:
[CATransaction setAnimationDuration:SLIDE_DURATION];
CABasicAnimation *drawAnim = [CABasicAnimation animationWithKeyPath:#"drawIt"];
[drawAnim setFromValue:[NSNumber numberWithFloat:0]];
[drawAnim setToValue:[NSNumber numberWithFloat:1]];
[drawAnim setDuration:SLIDE_DURATION];
[chartView.chartViewLayer addAnimation:drawAnim forKey:#"drawIt"];
chartFrame.origin.x += controlFrame.size.width;
chartFrame.size.width -= controlFrame.size.width;
chartView.chartViewLayer.frame = CGRectMake(chartFrame.origin.x,
chartFrame.origin.y,
chartFrame.size.width,
chartFrame.size.height);
The drawIt property is a custom property whose only purpose is to be called to draw the layer during the successive animation frames. It order to get this to work, you have to add this to the chartViewLayer's class:
+ (BOOL)needsDisplayForKey:(NSString *)key
{
if ([key isEqualToString:#"drawIt"])
{
return YES;
}
else
{
return [super needsDisplayForKey:key];
}
}
So this seems to all be working fine. However, I need to get the current (animated) size of the layer before drawing it. I've found various conflicting information on how to get this out of the presentation layer. Here is what I've tried when the layer's:
drawInCGLContext:(CGLContextObj)glContext
pixelFormat:(CGLPixelFormatObj)pixelFormat
forLayerTime:(CFTimeInterval)timeInterval
displayTime:(const CVTimeStamp *)timeStamp
method is called during the animation. I've tried getting the size using KVC, and by querying either the frame or the bounds.
CALayer *presentationLayer = [chartViewLayer presentationLayer];
//bounds.size.width = [[[chartViewLayer presentationLayer]
// valueForKeyPath:#"frame.size.width"] intValue];
//bounds.size.height = [[[chartViewLayer presentationLayer]
// valueForKeyPath:#"frame.size.height"] intValue];
//bounds.size = presentationLayer.bounds.size;
bounds.size = presentationLayer.frame.size;
NSLog(#"Size during animation: %f, %f", bounds.size.width, bounds.size.height);
In all cases, the returned value is the end result of the animation rather than the transitional values. My understanding is that using the presentationLayer should give the transitional values.
So is this just broken or am I missing some critical step? Thanks for any help.
I have a CAShapeLayer and it has to do a simple task of moving on the screen, guided by the user's finger.
The problem is that the movement is too slow. The layer does move, but there is a lag and it feels slow.
I have another test app where an UIImage is moved and there is no lag at all and the image moves instantly.
What can I do to overcome this?
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
currentPoint = [[touches anyObject] locationInView:self];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint activePoint = [[touches anyObject] locationInView:self];
CGPoint newPoint = CGPointMake(activePoint.x - currentPoint.x,activePoint.y - currentPoint.y);
curLayer.position = CGPointMake(shapeLayer.position.x+newPoint.x,shapeLayer.position.y+newPoint.y);
currentPoint = activePoint;
}
Thanks!
Keep in mind that when you set the position on a layer (assuming it's not the root layer of a UIView on which actions are disabled by default), it implicitly animates to the new position, which takes 0.25 seconds. If you want to make it snappier, temporarily disable actions on the layer like this:
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint activePoint = [[touches anyObject] locationInView:self];
CGPoint newPoint = CGPointMake(activePoint.x -
currentPoint.x,activePoint.y - currentPoint.y);
[CATransaction begin];
[CATransaction setDisableActions:YES];
curLayer.position = CGPointMake(shapeLayer.position.x +
newPoint.x, shapeLayer.position.y + newPoint.y);
[CATransaction commit];
currentPoint = activePoint;
}
This should cause it to jump to the new position rather than animate. If that doesn't help, then let me take a look at your layer init code so I can see what properties and dimensions it has. Properties such as cornerRadius, for example, can affect performance.
Try setting shouldRasterize to YES on your CAShapeLayer, particularly if it is usually drawn at the same scale. If your app runs on high-DPI devices, you may also need to set rasterizationScale to match the layer’s contentsScale.
While rasterizing your shape can make it faster to move the shape around, you’ll probably want to temporarily disable rasterization while you’re animating the layer’s path or size.