Centering the Camera on a Node in Swift Spritekit

Centering the camera on a node in swift spritekit

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).

SKCameraNode not centered

Set the scene anchor point to (0.5,0.5)

How to do a camera movement in spriteKit

In the example from Apple's Documentation, which you are following the camera node isn't an SKSprite, it's an SKNode. I think that will fix your problem.

To answer the question from the title, what you're essentially doing is attaching a world node to the scene. Inside this node, all the sprites are placed. As a child to the world node you add another node for the camera.

This gives you three distinct coordinate systems. Imagine, three sheets of paper, the bottom most one is your world, ie the layer with all the sprites. On top of that is a small piece of paper that represents the camera. Above all of this you have a transparent box that represents your viewing area.

The way it's set up it's impossible to move the top most transparent viewing layer. Instead, what you're doing is moving the point that's sits on top of the world layer and then sliding the world layer to that point.

Now imagine, in the paper scenario, this is a 2D scrolling world where you can only go left and right. Now take the camera point and put it all the way to the right most side of the viewing area. Now, take the world layer and drag it to the left until the camera is in the center of the non-moveable viewing area. That is more or less, what's happening.

Define map bounds and center on player node?

The example in Apple's documentation is in the Advanced Scene Processing section. Apple suggests making a "World" SKNode as a child of the Scene, and a "Camera" SKNode as a child of the world.

They suggest constantly moving the world so that it centers on the Camera during the didSimulatePhysics step. This method allows you to perform actions or simulate physics on the Camera itself, if you so choose. If you center the camera prior to this step, you won't be able to use physics to affect the Camera Node.

If you specifically only want left and right scrolling, simply restrict the movement to the X-axis.

Edit:
The current problem is because of your creation of the world and the physicsBody from a Rect that has its position predetermined. This is causing trouble with your anchorPoint setting (the world & physicsBody are being created with their lower left corners starting at the Scene's anchor point).

This can be fixed by creating the World and Player without using a Rect with position set. SKShapeNode's shapeNodeWithRectOfSize works, as does SKSpriteNode's spriteNodeWithColor:size: PhysicsBody is a bit trickier, and should likely use bodyWithEdgeLoopFromPath: world.path

EDIT: For future persons interested in creating a side-scroller with a camera always focused on the player, this is probably the simplest way to get one to work:

var player = SKShapeNode()
var world = SKShapeNode()

override func didMoveToView(view: SKView) {
self.anchorPoint = CGPointMake(0.5, 0.5)
self.size = CGSizeMake(view.bounds.size.width, view.bounds.size.height)

// Add world
world = SKShapeNode(rectOfSize: CGSizeMake(300, 300))
world.physicsBody = SKPhysicsBody(edgeLoopFromPath: world.path)
world.fillColor = SKColor.blackColor()
self.addChild(world)

// Add player
player = SKShapeNode(rectOfSize: CGSize(width: 50, height: 50))
player.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 50, height: 50))
player.fillColor = SKColor.blackColor()
world.addChild(player)
}

override func update(currentTime: CFTimeInterval) {
world.position.x = -player.position.x
world.position.y = -player.position.y
}

How to make camera follow SKNode in Sprite Kit?

Something like this should help. I call the following from my didSimulatePhysics.

-(void)centerOnNode:(SKNode*)node {
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
cameraPositionInScene.x = 0;
node.parent.position = CGPointMake(node.parent.position.x - cameraPositionInScene.x, node.parent.position.y - cameraPositionInScene.y);
}

Position sprite inside camera view

Actually the frame property isn't directly from SKCameraNode, its from its SKNode parent class and here is what it does (from Apple Developper documentation):

The frame property provides the bounding rectangle for a node’s visual
content, modified by the scale and rotation properties.

A camera node doesn't draw any content, it is just a representation of a camera you can use as a point of view; therefore the frame property isn't useful here.

From the documentation:

The scene is rendered so that the camera node’s origin is placed in
the middle of the scene.

Therefore you should rather use the position property of your camera node to place your object as you want.

Centering Camera on Velocity Applied Node Not Working

I think this should still work with physics unless I am not truly understanding the question. We did something similar with our SKATiledMap with that Auto Follow Feature. What you need to do is make sure the player is added to a node you can move (usually a map) as a child and then in the update function you do something like this...(sorry it isn't in swift)

-(void)update
{
if (self.autoFollowNode)
{
self.position = CGPointMake(-self.autoFollowNode.position.x+self.scene.size.width/2, -self.autoFollowNode.position.y+self.scene.size.height/2);

//keep map from going off screen
CGPoint position = self.position;

if (position.x > 0)
position.x = 0;

if (position.y > 0)
position.y = 0;

if (position.y < -self.mapHeight*self.tileWidth+self.scene.size.height)
position.y = -self.mapHeight*self.tileWidth+self.scene.size.height;
if (position.x < -self.mapWidth*self.tileWidth+self.scene.size.width)
position.x = -self.mapWidth*self.tileWidth+self.scene.size.width;

self.position = CGPointMake((int)(position.x), (int)(position.y));
}
}

Map being the node that the player is added to. Hopefully that helps. Also here is the link to the git hub project we have been working on. https://github.com/SpriteKitAlliance/SKAToolKit

ARKit - Center SCNode on camera

To keep your node always in the center of view, you have to calculate the center position of world position.

extension ARSCNView {
// create real world position of the point
func realWorldPosition(for point: CGPoint) -> SCNVector3? {
let result = self.hitTest(point, types: [.featurePoint])
guard let hitResult = result.last else { return nil }
let hitTransform = SCNMatrix4(hitResult.worldTransform)

// m4x -> position ;; 1: x, 2: y, 3: z
let hitVector = SCNVector3Make(hitTransform.m41, hitTransform.m42, hitTransform.m43)

return hitVector
}
}

// MARK: - ARSCNViewDelegate
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
let center = self.view.center
guard let realWorldPosition = self.sceneView.realWorldPosition(for: center) else { return }
self.NodeCompass.position = realWorldPosition
}
}


Related Topics



Leave a reply



Submit