Sprite kit deforming textures? - ios

I am trying to animate a stickman walking. However, when I animate its texture atlas, the second image deforms and is stretched along the x-axis. Here's the code for setting up the scene:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* GROUND */
self.backgroundColor = [SKColor colorWithRed:0 green:1 blue:1 alpha:1.0];
CGFloat groundHeight = (size.height / 2) - 50;
CGSize groundSize = CGSizeMake(size.width, groundHeight);
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithColor:[UIColor lightGrayColor] size:groundSize];
ground.position = CGPointMake(CGRectGetMidX(self.frame), (groundHeight / 2));
[self addChild:ground];
/* STICKMAN */
SKTextureAtlas *stickmanTextureAtlas = [SKTextureAtlas atlasNamed:#"Stickman"];
stickmanFrames = #[[stickmanTextureAtlas textureNamed:#"stickman1"],
[stickmanTextureAtlas textureNamed:#"stickman2"]];
stickmanSprite = [SKSpriteNode spriteNodeWithTexture:stickmanFrames[0]];
CGPoint stickmanPosition = CGPointMake(200, 100);
stickmanSprite.position = stickmanPosition;
[self addChild:stickmanSprite];
[stickmanSprite runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:stickmanFrames
timePerFrame:0.5f
resize:NO
restore:YES]] withKey:#"walkingInPlaceStickman"];
}
return self;
}
Why is this happening? Even without scaling the image using its CGSize this still happens. The images' actual sizes in pixels are the same.

Related

Strange behavior of sprite Kit

I am setting background color in this code:
- (void)randomBackground {
int random = arc4random() % 3;
NSLog(#"%d", random);
switch (random) {
case 0:
self.background = [SKSpriteNode spriteNodeWithImageNamed:#"image1"];
break;
case 1:
self.background = [SKSpriteNode spriteNodeWithImageNamed:#"image2"];
break;
case 2:
self.background = [SKSpriteNode spriteNodeWithImageNamed:#"image3"];
break;
}
self.background.position = CGPointMake(self.size.width / 2.0, self.size.height / 2.0);
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
NSLog(#"Size: %#", NSStringFromCGSize(size));
}
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
return self;
}
-(void)didMoveToView:(nonnull SKView *)view {
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
[self randomBackground];
self.player = [SKSpriteNode spriteNodeWithImageNamed:#"ninja"];
self.player.position = CGPointMake(self.player.size.width / 2.0, self.player.size.height / 2.0);
[self removeAllChildren];
[self addChild:self.background];
[self addChild:self.player];
}
After my player looses, I present another scene, and than present this scene again:
SKAction *loseAction = [SKAction runBlock:^{
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
GameOverScene *gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
[self.view presentScene:gameOverScene transition:reveal];
}];
[hero runAction:[SKAction sequence:#[move, loseAction, moveDone]]];
Here is how I present main scene again:
[self runAction:
[SKAction sequence:#[[SKAction waitForDuration:0.5],
[SKAction runBlock:^{
[self.parentScene removeAllChildren];
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
GameScene *myScene = [GameScene sceneWithSize:self.size];
[self.view presentScene:myScene transition:reveal];
}]
]]];
The problem is that sometimes, when a specific background is loaded, the whole content seems to be covered by it, however, when I nslog the children of scene, the background is on the first place of an array.
As the result, I can see only background. Strangely enough, this happens only on certain backgrounds, while on other backgrounds it is OK and the content is displayed properly.
Any ideas how to fix it?
The order of initialization is actually very interesting, but its a completely different topic. To fix your issue, you need to specify each node's position on the z-axis, through the property zPosition. For example, if you write:
background.zPosition = -1;
myNode.zPosition = 1;
myNode will always present itself on top of background.

Physics Body edges IOS8, Sprite Kit

SO following an IOS game tutorial, its IO7 as not many for 8 are out yet.
The following code is supposed to be a ball bouncing around but only bounces on the top and bottom of the screen, if it hits the side edges the ball goes of screen
#import "GameScene.h"
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
self.backgroundColor =[SKColor whiteColor];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.gravity = CGVectorMake(0, 0);
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
ball.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.frame.size.width/2];
//ball.physicsBody.friction = 0;
ball.physicsBody.linearDamping = 0;
ball.physicsBody.restitution = 1;
[self addChild:ball];
CGVector myVector = CGVectorMake(0, 5);
[ball.physicsBody applyImpulse:myVector];
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
There has to be something simple here but i can't seem to find an answer on stack overflow
Use NSLog to check the size of your view: NSLog(#"%f,%f",self.size.width,self.size.height);
There is a problem with SpriteKit that causes the view dimensions to be off of what you expect. If this is what is happening in your case, then make sure to change the size of your view to the correct one:
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
self.size = self.view.frame.size;
self.backgroundColor =[SKColor whiteColor];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.gravity = CGVectorMake(0, 0);
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
ball.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.frame.size.width/2];
//ball.physicsBody.friction = 0;
ball.physicsBody.linearDamping = 0;
ball.physicsBody.restitution = 1;
[self addChild:ball];
CGVector myVector = CGVectorMake(50, 20);
[ball.physicsBody applyImpulse:myVector];
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}

Issues adding SKSpriteNode in SKScene initWithSize method in landscape orientation

When trying to add a SKSpriteNode at position 0,0 in my scenes initWithSize method, it is offset on the x axis. This is due to the positions still holding values for "portrait" orientation.
I've followed the advice in the following article by adding my scene in the "viewWillLayoutSubviews" method of the view controller, but I'm still experiencing an offset issue.
Here is the initWithSize method:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.userInteractionEnabled = YES;
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.physicsWorld.gravity = CGVectorMake(0.0f, -1.0f);
self.physicsWorld.contactDelegate = self;
SKSpriteNode *n1 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(size.width,20)];
n1.position = CGPointMake(0, 0);
n1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:n1.size];
n1.physicsBody.contactTestBitMask = catCategory;
n1.physicsBody.dynamic = NO;
n1.physicsBody.categoryBitMask = floorCategory;
[self addChild:n1];
}
return self;
}
Following Ray Wanderlich's article I was able to achieve success by using an offset, and applying it to the position when adding the sprite, but there HAS to be a better way, right?
-(id)initWithSize:(CGSize)size {
float center = 240.0;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
center = 284.0;
}
if (self = [super initWithSize:size]) {
self.userInteractionEnabled = YES;
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.physicsWorld.gravity = CGVectorMake(0.0f, -1.0f);
self.physicsWorld.contactDelegate = self;
SKSpriteNode *n1 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(size.width,20)];
n1.position = CGPointMake(center, 0);
n1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:n1.size];
n1.physicsBody.contactTestBitMask = catCategory;
n1.physicsBody.dynamic = NO;
n1.physicsBody.categoryBitMask = floorCategory;
[self addChild:n1];
}
return self;
}
I'd like to find a comprehensive solution that doesn't rely on a hard-coded offset, and will work with landscape orientation regardless of if its an iPad or iPhone.
Thanks.

Sprite Kit - Sprite and Edge have large gap between them when colliding [See screenshot]

As you can see in the screenshot below, there is a large gap between both the circle (a SKShapeNode) and the line (also a SKShapeNode).
Naturally, both of these items should be touching when they collide.
Here is my code:
- (void)createSceneContents
{
[self setBackgroundColor:[SKColor blackColor]];
// Floor
//
SKShapeNode *floorShapeNode = [SKShapeNode node];
CGFloat threeQuartersDown = CGRectGetMidY(self.frame) * 0.5f;
CGPoint floorFromPoint = CGPointMake(CGRectGetMinX(self.frame), threeQuartersDown);
CGPoint floorToPoint = CGPointMake(CGRectGetMaxX(self.frame), threeQuartersDown);
CGMutablePathRef floorLinePath = CGPathCreateMutable();
CGPathMoveToPoint(floorLinePath, NULL, floorFromPoint.x, floorFromPoint.y);
CGPathAddLineToPoint(floorLinePath, NULL, floorToPoint.x, floorToPoint.y);
[floorShapeNode setPath:floorLinePath];
[floorShapeNode setPhysicsBody:[SKPhysicsBody bodyWithEdgeFromPoint:floorFromPoint toPoint:floorToPoint]];
[floorShapeNode setStrokeColor:[SKColor whiteColor]];
[floorShapeNode setLineWidth:0.5f];
[self addChild:floorShapeNode];
// Player
//
[self setPlayerNode:[BZPlayerNode node]];
[self.playerNode setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:(CGRectGetWidth(self.playerNode.frame) / 2.0f)]];
[self.playerNode.physicsBody setDynamic:YES];
[self.playerNode setPosition:CGPointMake(CGRectGetMidX(self.frame) - CGRectGetMidX(self.playerNode.frame), CGRectGetMidY(self.frame) - CGRectGetMidX(self.playerNode.frame) + 100.0f)];
[self addChild:self.playerNode];
}
And the +node method for the SKShapeNode:
+ (instancetype)node
{
BZPlayerNode *playerNode = [super node];
if (playerNode) {
[playerNode setFillColor:[SKColor whiteColor]];
[playerNode setPath:CGPathCreateWithEllipseInRect(CGRectMake(0.0f, 0.0f, 80.0f, 80.0f), NULL)];
}
return playerNode;
}
Does anyone know where I’ve gone wrong?
Thanks

Moving a camera in SpriteKit

//UPDATE
The updated code has been added that works as I expected. See didSimulatePhysics method in the updated code below. In my case, I only care about moving a character left or right on the x axis where 0 on the x axis is the absolute left and right on the x axis is a configurable value. The Apple adventure game really helped a lot too.
//ORIGINAL POST BELOW
I'm working with Apple SpriteKit and I'm struggling to implement a camera as I would like it to behave. What I've done in the code is load a sprite character, two buttons, and a red box that is off to the right outside of the view at the start. What I'd like to be able to do is move the character with the buttons, and once the player reaches the middle or end of the screen, the camera will then re-adjust to uncover what couldn't be seen in the view. So moving to the right should eventually show the red box that is off outside of the view initially once the player gets there. However, with the code I'm using below, I'm unable to get the camera to follow and adjust the coordinates to the main character at all. I've looked at Apple's advanced scene processing doc as well as a few other stack overflow posts but can't seem to get it right. If anyone could offer some advice it would be appreciated.
#define cameraEdge 150
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
//320 568
self.backgroundColor = [SKColor whiteColor];
myWorld = [[SKNode alloc] init];
[self addChild:myWorld];
mainCharacter = [SKSpriteNode spriteNodeWithImageNamed:#"0"];
mainCharacter.physicsBody.dynamic = YES;
mainCharacter.name = #"player";
mainCharacter.position = CGPointMake(20, 20);
CGRect totalScreenSize = CGRectMake(0, 0, 800, 320);
SKSpriteNode *box = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(60, 60)];
SKSpriteNode *boxTwo = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(60, 60)];
SKSpriteNode *boxThree = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(60, 60)];
boxThree.position = CGPointMake(40, 50);
[myWorld addChild:boxThree];
boxTwo.position = CGPointMake(1100, 50);
box.position = CGPointMake(650, 50);
[myWorld addChild:box];
[myWorld addChild:boxTwo];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:totalScreenSize];
self.physicsWorld.gravity = CGVectorMake(0, -5);
mainCharacter.name = #"mainCharacter";
mainCharacter.physicsBody.linearDamping = 0;
mainCharacter.physicsBody.friction = 0;
mainCharacter.physicsBody.restitution = 0;
mainCharacter.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mainCharacter.size];
[myWorld addChild:mainCharacter];
[self addChild:[self buildLeftButton]];
[self addChild:[self buildRightButton]];
}
return self;
}
- (void)didSimulatePhysics
{
SKSpriteNode *hero = mainCharacter;
if(hero)
{
CGPoint heroPosition = hero.position;
CGPoint worldPosition = myWorld.position;
NSLog(#"%f", heroPosition.x);
CGFloat xCoordinate = worldPosition.x + heroPosition.x;
if(xCoordinate < cameraEdge && heroPosition.x > 0)
{
worldPosition.x = worldPosition.x - xCoordinate + cameraEdge;
self.worldMovedForUpdate = YES;
}
else if(xCoordinate > (self.frame.size.width - cameraEdge) && heroPosition.x < 2000)
{
worldPosition.x = worldPosition.x + (self.frame.size.width - xCoordinate) - cameraEdge;
self.worldMovedForUpdate = YES;
}
myWorld.position = worldPosition;
}
}
-(SKSpriteNode *)buildLeftButton
{
SKSpriteNode *leftButton = [SKSpriteNode spriteNodeWithImageNamed:#"left"];
leftButton.position = CGPointMake(20, 20);
leftButton.name = #"leftButton";
leftButton.zPosition = 1.0;
return leftButton;
}
-(SKSpriteNode *)buildRightButton
{
SKSpriteNode *leftButton = [SKSpriteNode spriteNodeWithImageNamed:#"right"];
leftButton.position = CGPointMake(60, 20);
leftButton.name = #"rightButton";
leftButton.zPosition = 1.0;
return leftButton;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"leftButton"])
{
[mainCharacter.physicsBody applyImpulse:CGVectorMake(-120, 0)];
}
else if([node.name isEqualToString:#"rightButton"])
{
[mainCharacter.physicsBody applyImpulse:CGVectorMake(120, 10)];
}
}
If you want the view to always be centered on your player's position, modify your code with these points in mind:
1) Create a SKNode and call it myWorld, worldNode or any other name like that.
2) Add the worldNode [self addChild:worldNode];
3) Add all other nodes to the worldNode, including your player.
4) In the didSimulatePhysics method, add this code:
worldNode.position = CGPointMake(-(player.position.x-(self.size.width/2)), -(player.position.y-(self.size.height/2)));
Your view will now always be centered on your player's position.
Update May 2015:
If you are using a map created with Tiled Map Editor, you can use the free SKAToolKit framework. Features include player camera auto follow, test player, test HUD and sprite buttons.

Resources