Im trying to pin the game pad controller to the bottom left on my camera node but when i add the node as a child of my camera it doesnt show up?
let gameCamera = SKCameraNode()
var joypadBackground : SKSpriteNode = SKSpriteNode(imageNamed: "a")
override func didMove(to view: SKView) {
//Set game camera
self.camera = gameCamera
joypadBackground.position = convert(CGPoint(x: 0, y: 0), to: gameCamera)
joypadBackground.size = CGSize(width: 50, height: 50)
joypadBackground.zPosition = 1000
gameCamera.addChild(joypadBackground)
}
I had a hard time with this same problem the first time I was working with SKCameraNode and creating a heads up display.
Basically you have to remember that there are two parts to the camera. Running its functionality and rendering its children. By setting the scene's camera to gameCamera you've setup the functionality, but your camera isn't in the node tree for rendering. So, if you ever have a camera that needs to render its children don't forget to add it to the scene as a child, then the camera's children will be displayed.
self.camera = gameCamera
self.addChild(gameCamera)
Hope that helps someone avoid a very common error with a very simple solution.
You don't need
convert(CGPoint(x: 0, y: 0), to: gameCamera)
You can just set the CGPoint position to (0,0) and it should be at that point relative to the camera's space.
Not sure if this helps, at all, but what I do is (generally) position a child node AFTER I've added it to its parent. This is mainly a mental reminder, to me, that the child's position is within the coordinate space of the parent. So I'd do something like this:
gameCamera.addChild(joypadBackground)
joypadBackground.position = CGPoint(x: 0, y: 0)
If you're using a mid screen origin in your SKScene, this should be in the middle of the screen.
Bottom left will be a negative x and negative y value, size of which is relative to your frame size.
Related
I have a SpriteKit game in which balls bounce around, interacting with other objects.
One of those other objects is a spinner that should rotate around its center, but NOT change its x/y position. It should be stationary except for the rotation.
According to Apple's documentation, node.physicsBody.pinned = true should do exactly what I want, making it so that:
"the node’s position is fixed relative to its parent. The node’s position cannot be changed by actions or physics forces. The node can freely rotate around its position in response to collisions or other forces."
However, that's not what's happening. What's happening is that the spinner's y-axis position changes when a ball hits it squarely -- briefly moving down and then popping back into the correct position.
My code for the spinner (please assume all variables are defined):
for i in 0..<spinners.count {
let spinnerNode = SKSpriteNode(texture: texture)
spinnerNode.position = CGPoint(x: spinners[i].minX, y: spinners[i].minY)
spinnerNode.size = CGSize(width: spinners[i].width, height: spinners[i].height)
spinnerNode.physicsBody = SKPhysicsBody(texture: spinnerNode.texture!, size: CGSize(width: spinners[i].width, height: spinners[i].height))
spinnerNode.physicsBody?.isDynamic = true
spinnerNode.physicsBody?.affectedByGravity = false
spinnerNode.physicsBody?.pinned = true
addChild(spinnerNode)
}
Why on earth is my spinner node moving vertically when a ball collides with it? Why isn't .pinned working as advertised?
Thank you for your help!
I solved the problem by setting the spinner node's mass to a value slightly greater than that of the ball nodes.
node.physicsBody?.mass = 6.0
Completely new to SpriteKit. Currently I have a UIView, and I want to add a sprite node to it (like a small UIImageView, but I want animation for it so using SpriteKit). Therefore I didn't initialize my project to be a game project, as found in almost all of tutorials for SpriteKit. I've found a note here: link and what I have now is sth like:
func initializeImage() {
let imageView = SKView()
imageView.frame = CGRect(x: self.frame.width / 2 - Constants.imageWidth / 2, y: self.frame.height - Constants.imageHeight, width: Constants.imageWidth, height: Constants.imageHeight)
// so place it somewhere in the bottom middle of the whole frame
let sheet = SpriteSheet(texture: ...)
let sprite = SKSpriteNode(texture: sheet.itemFor(column: 0, row: 0))
sprite.position = imageView.center //basically the same position as the imageView.frame's x and y value
let scene = SKScene(size: imageView.frame.size)
scene.backgroundColor = SKColor.clear
scene.addChild(sprite)
imageView.presentScene(scene)
self.frame.addSubview(imageView)
}
The SpriteSheet is similar to this: sprite sheet; it's essentially cutting an image atlas and divide it into smaller images. I tracked the process and this step is indeed giving the smaller image (the var 'sprite'). But if running I only have a black square now (should be the size as defined by Constants). If I set scene.backgroundColor to be white then it's white. May I know how I should proceed from here, as how should I make the sprite showing up?
All of your code looks good except for this:
sprite.position = imageView.center // basically the same position as the imageView.frame's x and y value
That is basically not the position you think it is. The coordinate system in SpriteKit is a) relative to the (SK)scene, not to whatever view the SKView is contained in, and b) flipped vertically relative to the UIKit coordinate system. If you want a sprite centered in the scene, you probably want to set its position based on the scene's size:
sprite.position = CGPoint(x: scene.size.width / 2, y: scene.size.height / 2)
By the way, the external SpriteSheet code might not be needed (and you're more likely to benefit from Apple's optimizations) if you slice up your sprite sheet and put it in an Xcode asset catalog.
What do I need to code in order to have an image (that is already in the assets.xcassets) displayed as the background of the GameScene.swift?
First of all you could call you scene with the scaleMode .resizeFill that modify the SKScene's actual size to exactly match the SKView :
scene.scaleMode = .resizeFill
.resizeFill – The scene is not scaled. It is simply resized so that its fits the view. Because the scene is not scaled, the images will all remain at their original size and aspect ratio. The content will all remain relative to the scene origin (lower left).
By default, a scene’s origin is placed in the lower-left corner of the view. So, a scene is initialized with a height of 1024 and a width of 768, has the origin (0,0) in the lower-left corner, and the (1024,768) coordinate in the upper-right corner. The frame property holds (0,0)-(1024,768).The default value for the anchor point is CGPointZero (so you don't need to change it), which places it at the lower-left corner.
Finally, you can use the code below to add your background image (called of example bg.jpg):
// Set background
let txt = SKTexture(imageNamed: "bg.jpg")
let backgroundNode = SKSpriteNode(texture: txt, size:size)
self.addChild(backgroundNode)
backgroundNode.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
Although this might not be the best way, but it's what I always do, and it works.
Assuming that you have an image that is exactly the same aize as your scene, you can do this:
// please declare bg as a class level variable
bg = SKSpriteNode(imageNamed: "name of your texture")
// the below two lines of code is my preference only. I want the
// background's anchor point to be the bottom left of the screen
// because IMO it's easier to add other sprites as children of the background.
bg.anchorPoint = CGPoint.zero
bg.position = CGPoint(x: self.frame.width / -2, y: self.frame.height / -2)
self.addChild(bg)
Alernatively, just do this in an sks file. It's much easier.
After that, add all your game sprites as children of bg instead of self because it is easier to manage.
I am creating a Terraria-style game in Swift. I want to have it so the player node is always in the center of the screen, and when you move right the blocks go left like in Terraria.
I am currently trying to figure out how to keep the view centered on the character. Does anyone know of a good way of accomplishing this?
Since iOS 9 / OS X 10.11 / tvOS, SpriteKit includes SKCameraNode, which makes a lot of this easier:
positioning the camera node automatically adjusts the viewport
you can easily rotate/zoom the camera by transform in the camera node
you can fix HUD elements relative to the screen by making them children of the camera node
the scene's position stays fixed, so things like physics joints don't break the way they do when you emulate a camera by moving the world
It gets even better when you combine camera nodes with another new feature, SKConstraint. You can use a constraint to specify that the camera's position is always centered on a character... or add extra constraints to say, for example, that the camera's position must stay within some margin of the edge of the world.
The below will center the camera on a specific node. It can also smoothly transition to the new position over a set time frame.
class CameraScene : SKScene {
// Flag indicating whether we've setup the camera system yet.
var isCreated: Bool = false
// The root node of your game world. Attach game entities
// (player, enemies, &c.) to here.
var world: SKNode?
// The root node of our UI. Attach control buttons & state
// indicators here.
var overlay: SKNode?
// The camera. Move this node to change what parts of the world are visible.
var camera: SKNode?
override func didMoveToView(view: SKView) {
if !isCreated {
isCreated = true
// Camera setup
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.world = SKNode()
self.world?.name = "world"
addChild(self.world)
self.camera = SKNode()
self.camera?.name = "camera"
self.world?.addChild(self.camera)
// UI setup
self.overlay = SKNode()
self.overlay?.zPosition = 10
self.overlay?.name = "overlay"
addChild(self.overlay)
}
}
override func didSimulatePhysics() {
if self.camera != nil {
self.centerOnNode(self.camera!)
}
}
func centerOnNode(node: SKNode) {
let cameraPositionInScene: CGPoint = node.scene.convertPoint(node.position, fromNode: node.parent)
node.parent.position = CGPoint(x:node.parent.position.x - cameraPositionInScene.x, y:node.parent.position.y - cameraPositionInScene.y)
}
}
Change what’s visible in the world by moving the camera:
// Lerp the camera to 100, 50 over the next half-second.
self.camera?.runAction(SKAction.moveTo(CGPointMake(100, 50), duration: 0.5))
Source: swiftalicio - 2D Camera in SpriteKit
For additional information, look at Apple's SpriteKit Programming Guide (Example: Centering the Scene on a Node).
You have to create World node that contains nodes. And you should put anchorPoint for example (0.5,0.5). Center on your player. And then you should move your player.
func centerOnNode(node:SKNode){
let cameraPositionInScene:CGPoint = self.convertPoint(node.position, fromNode: world!)
world!.position = CGPoint(x:world!.position.x - cameraPositionInScene.x, y: world!.position.y - cameraPositionInScene.y)
}
override func didSimulatePhysics() {
self.centerOnNode(player!)
}
I have added a SKNode to my SKView by using this code:
tile.fillColor = SKColor.blueColor()
tile.position = CGPoint(x: 64, y: 64)
self.addChild(tile)
However, after fiddling with window sizes in the MainMenu.xib file, the position of tile becomes inaccurate even when the SKView lines up with the window itself:
How do I fix this?
Modifying the tile's anchor point might make it easier to place. I'm not 100% clear on where precisely you want to place that tile, but if you were trying to get that tile lined up on the bottom edge, and 64 from the left edge of the parent, you could simplify things by moving the anchor point of the tile to its bottom left before placing it. By default, as rakeshbs points out, the anchor point is at the center of the tile.
tile.fillColor = SKColor.blueColor()
tile.anchorPoint = CGPointMake(0, 0)
tile.position = CGPoint(x: 64, y: 0)
self.addChild(tile)
Maybe you're already aware of the ability to modify the anchor point on a SKSpriteNode, if so please disregard; perhaps I'm not fully understanding your issue.