I am trying to scale an SKSpriteNode object without smoothing/anti-aliasing (I'm using pixel-art so it looks better pixelated).
Is there a property I need to set to do this? This is the code I am using to render the sprite:
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:#"objects"];
SKTexture *f1 = [atlas textureNamed:#"hero_1.png"];
SKSpriteNode *hero = [SKSpriteNode spriteNodeWithTexture:f1];
[self addChild: hero];
hero.scale = 6.0f;
The image is scaled correctly but blurry/smoothed out. This is hero_1.png .
Try,
SKTexture *texture = [SKTexture textureWithImageNamed:#"Fak4o"];
texture.filteringMode = SKTextureFilteringNearest;
SKSpriteNode *newHero = [SKSpriteNode spriteNodeWithTexture:texture];
newHero.position = CGPointMake(200, 200);
[newHero setScale:50];
[self addChild:newHero];
SKTextureFilteringNearest
Each pixel is drawn using the nearest point in the texture. This mode
is faster, but the results are often pixelated.
Swift:
sprite.texture!.filteringMode = .Nearest
Just a note, if you create a SKSpriteNode instead of SKTexture, you can set the SKTextureFilteringNearest like in the following example:
SKSpriteNode *asteroid = [SKSpriteNode spriteNodeWithImageNamed:#"o1"];
asteroid.size = CGSizeMake(36, 24);
asteroid.position = CGPointMake(startX, startY);
asteroid.name = #"obstacle";
// >>>
asteroid.texture.filteringMode = SKTextureFilteringNearest;
// <<<
[self addChild:asteroid];
try this:
hero.xscale = 6.0f;
hero.yscale = 6.0f;
Related
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];
}
For some odd reason textures from my texture atlas are not loading. I have no idea why.
Below is how I typically declare/code a texture
-(SKSpriteNode *)background {
SKSpriteNode *background;
NSArray *backgroundIpad;
background = [SKSpriteNode spriteNodeWithImageNamed:#"dodgR - main background"];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
background.size = CGSizeMake(1136, 640);
NSArray *backgroundIphone = #[[SKTexture textureWithImageNamed:#"dodgR - animation 1.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 2.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 3.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 4.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 5.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 6.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 7.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 8.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 9.jpg"],
[SKTexture textureWithImageNamed:#"dodgR - animation 10.jpg"]];
SKAction *backgroundIphoneAnimation = [SKAction animateWithTextures:backgroundIphone timePerFrame:0.05];
SKAction *backgroundIphoneRepeat = [SKAction repeatActionForever:backgroundIphoneAnimation];
[background runAction:backgroundIphoneRepeat];
background.name = #"background";
return background;
}
The name of my texture atlas is sprites.atlas
Any help will be much appreciated, thanks
Your code above only works when using images not in a texture atlas because it doesn't actually make use of a texture atlas.
Images can't be pulled out of an Atlas (that i know of) without using SKTextureAtlas. So you can't grab those images directly.
SWIFT:
let atlas = SKTextureAtlas(named: "BackgroundImages") // atlas name
var backgroundFrames = [SKTexture]()
let imageCount = atlas.textureNames.count
for var i=1; i<= imageCount/2; i++ {
let textureName = "dodgR - animation \(i)"
backgroundFrames.append(atlas.textureNamed(textureName))
}
OBJECTIVE-C (not tested)
SKTextureAtlas *atlas = [SKTextureAtlas named:#"BackgroundImages"]; // atlas name
NSMutableArray *backgroundFrames = [NSMutableArray new];
int imageCount = atlas.textureNames.count;
for (int i = 1; i <= imageCount/2; i++) {
NSString *textureName = #"dodgR - animation \(i)";
[bacgroundFrames addObject:[atlas textureNamed:textureName]];
}
BONUS: Xcode 7 now allows you to create your sprite atlases right inside the assets folder.
I am having an issue with the sprite animation in my game.
In the current code posted below, the sprite will show on the screen but does not do the walking animation; it is only a static frame of one of the images in the walk cycle.
The init function:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize: size]) {
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// Create the game nodes
// Background
_backgroundNode = [self createBackgroundNode];
[self addChild: _backgroundNode];
// Foreground
_foregroundNode = [SKNode node];
[self addChild: _foregroundNode];
// Add the Player
_thePlayer = [self createPlayer];
[_foregroundNode addChild:_thePlayer];
[self walkingQueen];
}
return self;
}
Function creating the player / filling in the animation array:
-(SKSpriteNode *)createPlayer
{
SKSpriteNode *playerNode = [SKSpriteNode node];
//SKSpriteNode *_player = [SKSpriteNode node];
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *queenAnimatedAtlas = [SKTextureAtlas atlasNamed: #"QueenSprites"];
SKTexture *walk1 = [queenAnimatedAtlas textureNamed:#"queen7"];
SKTexture *walk2 = [queenAnimatedAtlas textureNamed:#"queen8"];
SKTexture *walk3 = [queenAnimatedAtlas textureNamed:#"queen9"];
walkFrames[0] = walk1;
walkFrames[1] = walk2;
walkFrames[2] = walk3;
_queenWalkingFrames = walkFrames;
/*
int numImages = 9;
for (int i = 7; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"queen%d", i];
SKTexture *temp = [queenAnimatedAtlas textureNamed: textureName];
[walkFrames addObject: temp];
}
*/
//_queenWalkingFrames = walkFrames;
SKTexture *temp = _queenWalkingFrames[0];
SKSpriteNode *_player = [SKSpriteNode spriteNodeWithTexture:temp];
_player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
//[self addChild:_player];
//[self walkingQueen];
[playerNode addChild:_player];
[self walkingQueen];
return playerNode;
}
And the function for starting the walk animation:
-(void)walkingQueen
{
for (int i = 0; i < _queenWalkingFrames.count; i++) {
if (_queenWalkingFrames[i] == NULL) {
NSLog(#"ERROR!");
}
}
[_thePlayer runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:_queenWalkingFrames
timePerFrame:0.1f
resize:NO
restore:YES]] withKey:#"walkingInPlaceQueen"];
//return;
}
I played around with the code a bit and the animation DOES work IF I ignore the concept of layers (background / foreground layers) and stick the creation of the player into the init function (basically taking what's inside the createPlayer() and sticking it inside initWithSize()).
I do need to have the layers for my game and everything seems like it should work; had no idea that moving a few lines of code into its own function block would create such an issue. The check inside the walkingQueen() function did not return any errors so I assume my array has been filled with the sprite images.
_thePlayer = [self createPlayer];
This means _thePlayer is nil until after the createPlayer method returns. Thus in the walkingQueen method _thePlayer is nil and the actions don't run.
Replace this:
SKSpriteNode *playerNode = [SKSpriteNode node];
with
_thePlayer = [SKSpriteNode node];
and use _thePlayer instead of playerNode. You can also skip returning and assigning it, it's an ivar after all.
Re-wrote the createPlayer() function. Correct implementation is much simpler and as follows:
-(SKSpriteNode *)createPlayer
{
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *queenWalkingAtlas = [SKTextureAtlas atlasNamed:#"QueenSprites"];
SKTexture *walk1 = [queenWalkingAtlas textureNamed:#"queen7"];
SKTexture *walk2 = [queenWalkingAtlas textureNamed:#"queen8"];
SKTexture *walk3 = [queenWalkingAtlas textureNamed:#"queen9"];
walkFrames[0] = walk1;
walkFrames[1] = walk2;
walkFrames[2] = walk3;
_queenWalkingFrames = walkFrames;
SKTexture *temp = _queenWalkingFrames[0];
_thePlayer = [SKSpriteNode spriteNodeWithTexture:temp];
_thePlayer.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
return _thePlayer;
}
Basically doing as the post above suggested, get rid of the ivars, use the function ONLY to fill the array with the appropriate images. Then, in the init function, call createPlayer(), add it to the foreground layer, and then call walkingQueen().
I applied a mask to SKCropNode And I see what i need now but
the cropped node has the size of the full image i just want to access the cropped part not the full image can i get that into SKSpriteNode??
Here is my code
SKSpriteNode *pic = [SKSpriteNode spriteNodeWithImageNamed:#"test.png"];
pic.name = #"PictureNode";
SKSpriteNode *mask = [SKSpriteNode spriteNodeWithImageNamed:#"2.png"];
mask.size=CGSizeMake(50, 50);
mask.position=CGPointMake(0, 50);
SKCropNode *cropNode = [SKCropNode node];
cropNode.position=CGPointMake(160, 70);
[cropNode addChild:pic];
[cropNode setMaskNode:mask];
[self addChild:cropNode];
and here are the images and result :
Thanks in advance
As far as I understood, you are trying to get cropped part as a SKSpriteNode, if it is the case, you can manage it as follows:
SKTexture *oldWholeTexture = [self.scene.view textureFromNode:cropNode];
SKTexture *newTexture = [SKTexture textureWithRect:CGRectMake(140.f/364.f,56.f/365.f,80.f/364.f,125.f/365.f) inTexture: oldWholeTexture];
SKSpriteNode *newNode = [SKSpriteNode spriteNodeWithTexture:newTexture];
CGRect parameter of textureWithRect takes unit values, so I normalized my values according my texture size. Bottom left is (0,0).
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.