I'm trying to set a background texture and I want it to cover the entire screen.
I prepared the background files exactly to size in photoshop before hand. There are 2 files in my project:
background.png - 1024x768px
background#2x.png - 2048x1536px
I am running the following code:
SKTexture *backgroundTexture = [SKTexture textureWithImageNamed:#"background"];
SKSpriteNode *background = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
background.size = CGSizeMake(750, 550);
[self addChild:background];
And it is giving me this result http://d.pr/i/Ej2m - notice that the entire screen is almost filled and the background size is background.size = CGSizeMake(750, 550). Why is this?
You manually changed the sprite's size:
background.size = CGSizeMake(750, 550);
So it will display in a smaller region (750x550) than its original size (1024x768). Allow me to say: d'uh! ;)
Related
I'm trying to position the background all over the scene background, but for some reason it puts it outside of screen.
This is what I used:
SKTexture *backgroundTexture = [self.atlas textureNamed:#"back"];
SKSpriteNode *background = [SKSpriteNode spriteNodeWithTexture:backgroundTexture size:self.view.frame.size];
background.position = (CGPoint) {CGRectGetMidX(self.view.frame), CGRectGetMidY(self.view.frame)};
[self addChild:background];
This is how it puts it:
You need to add your background image to the SKScene view and as such determine the coordinates from self.
SKSpriteNode *backgroundNode = [SKSpriteNode spriteNodeWithImageNamed:#"BackgroundImage"];
backgroundNode.position = CGPointMake(self.size.width/2, self.size.height/2);
backgroundNode.zPosition = 1;
[self addChild:backgroundNode];
Actually the problem was that in the new Xcode 6.3.1 when you create a new scene it adds a SKScene view of some kind, this view is bigger then the iPhone normal screen, maybe it's iPhone 6+ screen size that's why the background image was out of screen bounds. I just had to remove this view file and not reload it from code and then it worked out.
Thanks anyway, the comment above helped me to understand that.
I've developed a game in SpriteKit for a 5s. All my sprites are positioned just where I want them when I run the Xcode simulator on a 4s, 5 or 5s:
http://tinypic.com/r/f0yanl/8
But when I run it on a 6 my sprites are not positioned correctly, and some are even placed off the entire frame/screen:
http://tinypic.com/r/akdac7/8
Notice my HUD is showing up top out of the frame, and the shadow and positioning of my "Machine" at the bottom is off, etc.
Is there a quick, simple fix to ensure the sprites maintain their position relative to my background image, when running on a 6? For now, I'm OK with the entire screen being collapsed a bit when running on a 6 (with empty borders on all four sides), but I would like to make sure everything at least looks just as clean and in order as it does on a 5.
I've used anchor points for most of my nodes, for e.g.:
+ (instancetype) machineAtPosition:(CGPoint)position {
GameMachineNode *machine = [self spriteNodeWithImageNamed:#"machine_1"];
machine.position = position;
machine.name = #"Machine";
machine.anchorPoint = CGPointMake(0.5, 0);
....
}
My "GameViewController.m" includes a resize to fill mode:
SKScene * scene = [GameTitleScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeResizeFill;
Update: figured it out. Was as simple as adding this single line of code to the initWithSize method:
background.size = self.frame.size;
Full code block for reference:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.lastUpdateTimeInterval = 0; //initializing this property
self.timeSinceEnemyAdded = 0; //initializing this property
self.addEnemyTimeInterval = 1.25;
self.totalGameTime = 0;
self.minSpeed = GameAstroDogMinSpeed;
self.restart = NO;
self.gameOver = NO;
self.gameOverDisplayed = NO;
// Setup your scene here....
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"background_1"];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
background.size = self.frame.size;
I was trying to add an image (640X1136PX) as background for a project with following code:
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"Background"];
background.position = CGPointMake(self.size.width * .5, self.size.height * .5);
background.anchorPoint = CGPointZero;
background.blendMode = SKBlendModeReplace;
[self addChild:background];
With the above code the background image is shown on left bottom showing hardly 10% of the image, if I comment the the following
background.anchorPoint = CGPointZero;
Then it shows the image centered but the 30% of the area is not covered.
Please help me on this, I am very new to SpriteKit.
Thanks
Try this approach in your Scene-class. Try to leave anchorPoint-properties with default values.
- (void)constructBackground
{
self.scaleMode = SKSceneScaleModeAspectFill;
self.background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame);
self.background.size = self.frame.size;
}
Thank you for helping me, i got fixed the issue, i am using xcode 6.0.1 and simulator is running iOS8 so we need to follow image conventions as per iOS8, when i resize the image to high resolution 1242 X 2208 px the issue was solved
#Astoria thanks for the clue.
Platform: XCode 6.0 + iOS 7.0.4
Running the following code on iPad mini retina and expect the image of tmpLbl and node to render identically because the latter is initialized with the texture of the former, via textureFromNode. In reality tmpLbl, which is an SKLabelNode, renders at retina resolution but node, which is supposed to be a copy, is blurry, appearing to be a double-scaled rendition of a half-sized texture.
I saw a post suggesting to put the source (tmpLbl in the sample below) on the scene but it makes no difference, as illustrated by the sample.
Any advice on:
1. how to make this work using any sort of approach
2. how to make this work without having the source node (tmpLbl below) ever rendered on the screen
Sample code:
- (void)didMoveToView:(SKView *)view {
SKLabelNode *tmpLbl = [SKLabelNode labelNodeWithFontNamed: #"Futura"];
tmpLbl.position = CGPointMake(100, 100);
[self addChild: tmpLbl];
tmpLbl.fontSize = 125;
tmpLbl.fontColor = [SKColor colorWithRed:0.8 green:0.0 blue:0.0 alpha:1.0];
tmpLbl.text = #"a";
SKTexture *texture = [view textureFromNode: tmpLbl];
SKNode *node = [SKSpriteNode spriteNodeWithTexture:texture];
node.position = CGPointMake(200, 130);
[self addChild:node];
return;
}
Rendering result; the left side "a" is via SKLabelNode, the right side one is via textureFromNode and spriteNodeWithTexture:
Solution to the problem: it is necessary but not sufficient for the source node-tree passed to textureFromNode to be part of the scene as suggested by others. Another condition for the returned texture to be of "retina resolution" is that textureFromNode: is called not earlier (per the results of my experiments) than the first call to SKScene update:. (Update: on XCode 5.0.1 textureFromNode: must be called in during or after the second call to update:; textureFromNode during the first call still produces non-retina texture.)
Just by moving the code in the question above to the update: function the resulting texture will have all the pixels for retina rendering. What is frustrating is that the texture will be double the size of the source node (tree) in both directions and initializing a new sprite with the returned sprite will produce a double-size sprite (that is rendered with interpolation). It is clear that the SKTexture class has an internal 'scale' property, just like UIImage. SpriteKit sets this property when creating the texture from a file with the '#2x' suffix but not when the texture is created via textureFromNode. This is very unfortunate because this forces the application developer to track the scale manually unless the texture is created from an image resource.
The updated sample below includes the following changes compared to the original in the question:
move textureFromNode: (along with the rest of the code) to update:; anyone has a better place that would still produce a retina-size rendition?
detect if the display resolution is retina and resize the new sprite to the size of the source-sprite if needed
With these changes the two 'a' images are rendered identically.
// retina resolution will be not applied if textureFromNode is called before SKScene::update():
-(void)update:(CFTimeInterval)currentTime {
static int cnt = 0;
if (cnt == 1) // can be cnt == 0 on XCode 6.0
{
double scale=1.0;
if ([[UIScreen mainScreen] respondsToSelector:#selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0)) {
// the texture returned by textureFromNode is double-size on retina displays; must explicitly make the sprite half the size of the texture:
scale=0.5;
}
SKLabelNode *tmpLbl = [SKLabelNode labelNodeWithFontNamed: #"Futura"];
tmpLbl.position = CGPointMake(100, 100);
// must be part of the scene or else retina resolution will be not applied:
[self addChild: tmpLbl];
tmpLbl.fontSize = 125;
tmpLbl.fontColor = [SKColor colorWithRed:0.8 green:0.0 blue:0.0 alpha:1.0];
tmpLbl.text = #"a";
SKTexture *texture = [self.view textureFromNode: tmpLbl];
SKNode *node = [SKSpriteNode spriteNodeWithTexture:texture size: CGSizeMake(texture.size.width*scale, texture.size.height*scale)];
node.position = CGPointMake(200, 130);
[self addChild:node];
// was added to the scene only to force retina resolution rendering:
// [tmpLbl removeFromParent];
}
++cnt;
}
Rendering result with the updated code. The right-hand-side image is not blurry anymore:
Thanks for the idea to perform textureFromNode after update: method is called.
As for a better place that would still produce a retina-size rendition, I find it clearer to call put rasterization code in dispatch_once block to avoid unnecessary counter increment.
In my GameScene (Swift):
var rasterizationToken: dispatch_once_t = 0
override func didFinishUpdate() {
dispatch_once(&rasterizationToken) {
self.rasterizeLayers() //calls textureFromNode inside
}
}
didFinishUpdate() is called after update(), therefore retina resolution remains.
Your label should be added to scene, otherwise it will have non-retina resolution in rendered texture.
Maybe it's duplicate question
Sprite Kit - Low Resolution Texture on Retina when using textureFromNode
I develop a work around for SKLabelNode in Swift 3:
extension SKLabelNode {
func pixelated() {
let v = SKView()
self.fontSize *= 4
let texture = v.texture(from: self)
let textureSize = CGSize(width: (texture?.size().width)!/4, height: (texture?.size().height)!/4)
let node = SKSpriteNode(texture: texture, color: .clear, size: textureSize)
node.position = self.position
node.anchorPoint = CGPoint(x: 0.5, y: 0.0)
self.parent?.addChild(node)
self.removeFromParent()
}
}
I am having a little trouble doing something that is supposed to be very simple.
I cannot get the floor of my tiles to display above a background picture. However I can get all my other game objects to show from my control pad, to my HUD to even coins and monsters set up in the same tile map. Basically everything appears in front the background like I expect the floor of my tilemap so it looks like im walking on air. I have tried many things like changing which layer i add the background picture or the tilemap floor too, or even tried setting it the same way i set my characters but same results. Tilemap floor is always at the back.
Adding my Set up code, Hope it is helpful too solving the problem.
I created this BG sprite since, I wanted my tilemap to scroll vertically or horzi. automatically. So the easiest way I found to do that was to make the tilemap the child of the "bg" and scroll the "bg" hence scrolling the tile map. However, I have tried setting the background as the child of the bg and setting the Z for both of them but that didnt seem to help.
Thanks in advance for any help in solving this
#implementation GameLevelScene
{
SKNode *_worldNode;
SKSpriteNode *bg;
SKSpriteNode *bkg;
}
Init
-(id)initWithSize:(CGSize)size level:(int)level {
if (self = [super initWithSize:size]) {
// [self showBackground];
NSDictionary *levelData = config[#"levels"][level];
//[show background];
if (levelData[#"tmxFile"]) {
[self showBackground];
_tileMap = [ JSTileMap mapNamed:levelData[#"tmxFile"]];
}
//self.backgroundColor = [SKColor colorWithRed:.4 green:.4 blue:.95 alpha:1.0];
// UIImage *bkgb =[UIImage imageNamed:#"land.jpg"];
// self.position=CGPointZero;
// self.anchorPoint=CGPointZero;
// self.backgroundColor=[UIColor colorWithPatternImage:bkgb];
//Above code shows no picture but it changes the color
[self setUpWorld];
[self createChar];
[self controlPadNode];
//[show background];
}
return self;
}
setUpWorld
- (void)setUpWorld
{
bg = [SKSpriteNode spriteNodeWithImageNamed:#"bg3"];
bg.userInteractionEnabled=NO;
bg.anchorPoint = CGPointZero;
bg.zPosition=0;
bg.position = CGPointZero;
bg.name = #"bg";
[self addChild:bg];
_worldNode = [SKNode node];
if (_tileMap) {
[bg addChild:_tileMap];
}
[bg addChild:_worldNode];
self.physicsWorld.contactDelegate = self;
}
create char
- (void)createChar
{
_Layer = [[TmxTileMapLayer alloc]
initWithTmxObjectGroup:[_tileMap
groupNamed:#"LevelOneObjects"]
tileSize:_tileMap.tileSize
gridSize:_bgLayer.gridSize];
[self addChild:_Layer];
}
Create Control
- (SKSpriteNode *)controlPadNode
//-(void)controlPad
{
SKSpriteNode *controlPadNode = [SKSpriteNode spriteNodeWithImageNamed:#"controller.png"];
controlPadNode.position = CGPointMake(100,50);
controlPadNode.name = #"controlPadNode";
controlPadNode.zPosition = 1.0;
[self addChild:controlPadNode];
}
background
-(void)showBackground
{
bkg = [SKSpriteNode spriteNodeWithImageNamed:#"desert_land.jpg"];
bkg.userInteractionEnabled=NO;
bkg.anchorPoint = CGPointZero;
bkg.position = CGPointZero;
bkg.zPosition=-1;
bkg.name = #"bkg";
// [self addChild:bkg];
//[_tileMap addChild:bkg];
// [_worldNode addChild:bkg];
[bg addChild:bkg];
}
Set your bkg.zposition to 1 and set your bg.zPosition to 2.
Also, the whole point of having a tile map is NOT to use a gigantic background picture but to instead use tiles.
** Update **
I just tested your code and ran a sample project myself. I assume you are using the Tiled app for your tiles. I ran variations on parents (self, worldNode, etc...) and zPositions. The bottom line is you cannot do it. Tiled does not have an alpha channel for its background color options. So either the background image is covered by the tile map or, in your case, the background image covers the tile map.
Your possible solutions are to either not use Tiled and place your tiles manually or to look for another tile app which has an alpha channel.
I noticed that where you define the wall properties of the tile if you say
SKSpriteNode* wall =
[SKSpriteNode spriteNodeWithColor:[SKColor greenColor]
size:CGSizeMake(w, h)];
the green color will be uptop of the background. Bit of a dirty fix than an answer