I am trying to fix the camera to a sprite node “players.first!” and I managed to do so using SKConstraints as follows
func setupWorld(){
let playerCamera = SKCameraNode()
let background = SKSpriteNode(imageNamed: platformType + "BG")
var cameraFollow = [SKConstraint]()
cameraFollow.append(SKConstraint.distance(SKRange(constantValue: 0), to: players.first!))
playerCamera.constraints = cameraFollow
background.zPosition = layers().backgroundLayer
background.constraints = cameraFollow
background.size = self.size
self.addChild(playerCamera)
self.camera = playerCamera
self.addChild(background)
physicsWorld.contactDelegate = self
addEmitter()
}
But this keeps the camera fixed to the exact location of the node, I want the camera to be shifted to the right of the node “players.first!” (only in X dimension) and I couldn’t manage to do so with SKConstraints, note that the node is moving fast so updating the position of the camera in the update function makes the camera jitter.
This image is explaining my issue
Constrain the camera to an empty SKNode and make it a child node of the first player which is offset to the right in the frame of the player. This can be accomplished in the scene editor or programmatically by setting this dummy node's position to something like CGPoint(x: 100, y: 0). When the player moves, this node will also move, dragging the camera along with it; and since the camera is focused on this node, the nodes in the same 'world' of the player will appropriately appear to move in the opposite direction while maintaining the look you want for the player.
EDIT: If the player rotates
If the player needs to rotate, the above configuration will result in the entire node world revolving around the fixed empty node. To prevent this, instead place an empty SKNode that acts as the fixed camera point which will be called "cameraLocation" and the player node into another empty SKNode which will be called "pseudoPlayer". Constrain the camera to "cameraLocation". Moving the "pseudoPlayer" node will then move both the camera's fixed point (so that the camera moves) and the player node while only resulting in the rotation of the player and not the entire world.
NOTE: The only potential drawback is that in order to move the player correctly through the world, you must move the "pseudoPlayer" instead.
Related
I have a moving camera in my scene, which always follows my player. But I also have some other content(On screen controls) which I want to stay in a single place on the screen, but when the camera moves, The controls are moving away to. How would I go about doing this. I have searched a lot, and found the SKConstraint, but I couldn't find any tutorials to use it in swift 3.
Should I be using the SKConstraint? If yes, how can I use it, if no, how do I keep the controls at a certain position on the screen at all times.
I also know that I could change the position of the controls in the update method, but I do not want to do that as there are many on screen controls, and I try to refrain from writing code in the update method as much as possible.
Any help would be appreciated, thanks!
You can use an SKConstraint to cause a camera to follow a character.
First, create a camera node and a hero and add them to the scene
let cameraNode = SKCameraNode()
let hero = SKSpriteNode(imageNamed: "Spaceship")
addChild(hero)
camera = cameraNode
addChild(cameraNode)
Next, create a constraint and assign it to the camera node's constraints property
let range = SKRange(constantValue:0)
let constraint = SKConstraint.distance(range, to: hero)
cameraNode.constraints = [constraint]
Lastly, if you have controls or labels that need to be at fixed locations relative to the camera, you can add them to the camera node
let label = SKLabelNode(text: "Score: 123")
// Position the label relative to the camera node
label.position = CGPoint(x: 100, y: 100)
cameraNode.addChild(label)
Context:
there is a cursor (like your mouse) SKSpriteNode
cam is a SKCameraNode and is a child to the cursor (i.e. wherever your cursor goes, so follows the camera).
cam is purposely not centered on the cursor; rather, it is offset so the cursor appears at the top of the view, and there remains empty space below
A simple schematic is given below
Goal:
The goal is two add to sprites to the lower left and lower right corners of the camera's view. The sprites will be children of the camera, so that they always stay in view.
Question
How can I position a sprite in the corner of a camera, especially given that the SKSpriteNode does not have an anchorPoint attribute (as an SKSpriteNode typically has, which let me offset the camera as a child to the cursor)?
Note: One can position the SKSpriteNodes on the GameScene and then call .move(toParent: SKNode), which gets you closers but also messes with the position and scale of the SKSpriteNodes
var cam: SKCameraNode!
let cursor = SKSpriteNode(imageNamed: "cursor")
override func didMove(to view: SKView) {
// Set up the cursor
cursor.setScale(spriteScale)
cursor.position = CGPoint(x: self.frame.midX, y: raisedPositioning)
cursor.anchorPoint = CGPoint(x:0.5, y:0.5)
cursor.zPosition = CGFloat(10)
addChild(cursor)
// Set up the camera
cam = SKCameraNode()
self.camera = cam
cam.setScale(15.0)
// Camera is child of Cursor so that the camera follows the cursor
cam.position = CGPoint(x: cursor.size.width/2, y: -(cursor.size.height * 4))
cursor.addChild(cam)
// Add another sprite here and make it child to cursor
...
the cameraNode has no size, but you can get the current screen size with the frame property
frame.size
then you can position your node accordingly, for example if you want to position the center of yournode in the left corner you set the position as this:
yournode.position.x = 0
yournode.position.y = frame.size.height
This is best solved with a "dummy node" that acts as the camera's screen space coordinates system.
Place this dummy node at the exact centre of the view of the camera, at a zPosition you're happy with, as a child of the camera.
...from SKCameraNode docs page:
The scene is rendered so that the camera node’s origin is placed in
the middle of the scene.
Attach all the HUD elements and other pieces of graphics and objects you want to stay in place, relative to the camera, to this dummy object, in a coordinate system that makes sense relative to the camera's "angle of view", which is its frame of view.
...from a little further down the SKCameraNode docs page:
The camera’s viewport is the same size as the scene’s viewport
(determined by the scene’s size property) and the scene is still
scaled by its scaleMode property when it is rendered into the view.
Whenever the camera moves, it moves the dummy object, and all the children of the dummy object move with the dummy object.
The biggest advantage of this approach is that you can shake or otherwise move the dummy object to create visual effects indicative of motion and explosions. But also a neat system for removal from view, too.
Swift 3, SceneKit: In my game, I have an SCNSphere node in the center of the screen. The sphere drops by gravity onto an SCNBox node, and a velocity of SCNVector3(0,6,0) is applied to it once it collides with the box.
A new box is created and moves forward (z+) towards my camera and towards the sphere as well. The sphere rises, peaks, and then falls back down (by gravity) towards the new box, and when it collides with the new box, a velocity of SCNVector(0,6,0) is applied to it. This process repeats continuously. A sphere that repeatedly bounces on a new approaching box, basically.
Instead of just one box, however, there will be three boxes in a row. All boxes begin in front of the sphere node and move towards it when they are created, the boxes are placed in a row, one to the left of the sphere, one directly in front of the sphere (the middle), and the third to the right of the sphere.
I want to be able to drag my finger across the screen and move my sphere so that it can land on the left and right boxes. While I'm dragging, I do not want the y-velocity or y-position to be changed at all. I just want the x-position of my sphere node to mirror the real-world x-position of my finger relative to the screen. I also do not want the sphere node to change location based on a touch alone.
For example, if the sphere's position is at SCNVector3(2,0,0), and if the user taps near SCNVector3(-2,0,0), I do not want the sphere to "teleport" to where the user tapped. I want the user to drag the sphere from its last position.
func handlePan(recognizer: UIPanGestureRecognizer) {
let sceneView = self.view as! SCNView
sceneView.delegate = self
sceneView.scene = scene
let trans:SCNVector3 = sceneView.unprojectPoint(SCNVector3Zero)
let pos:SCNVector3 = player.presentation.position
let newPos = (trans.x) + (pos.x)
player.position.x = newPos
}
I just want the x-position of my sphere node to mirror the real-world x-position of my finger relative to the screen
You can do this by using UIPanGestureRecognizer and getting the translation in the coordinate system of the view.
let myPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
let trans2D:CGPoint = myPanGestureRecognizer.translation(in:self.view)
let transPoint3D:SCNVector3 = SCNVector3Make(trans2D.x, trans2D.y, <<z>>)
For z value, refer to the unProjectPoint Discussion, which says that z should refer to the depth at which you want to un-project relative to the near and far clipping planes of your view frustum.
You can then un-project the translation to the 3D world coordinate system of the scene, which will give you the translation for the sphere node. Some partial sample code:
let trans:SCNVector3 = sceneView.unProjectPoint(transPoint3D)
let pos:SCNVector3 = sphereNode.presentationNode.position
let newPos:SCNVector3 = // trans + pos
sphereNode.position = newPosition
I have a Player SkSpriteNode that needs to pickup an Object. Once the Object is picked up, it should be attached to the player. I don't really care where the object is (to a side, above, center, etc), but it needs to stick to the player and not move from it's position.
With the code below I can get it to stick, but once the Player falls from a platform, the Object lags behind it, almost like it's lighter than the Player. I've tried pinning it, but that affects how high my Player jumps. Setting dynamic or gravity to false doesn't seem to help either.
The code below is in the Player Class.
func pickedUpObject(object: ObjectClass) {
object.removeFromParent()
object.position = CGPoint(x: -20, y: -5)
object.zPosition = 0
self.addChild(pollen)
}
I'm building a platform game, and I made the camera follow the player when he walks:
let cam = SKCameraNode()
override func didMoveToView(view: SKView) {
self.camera = cam
...
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
cam.position = Player.player.position
...
But, when the camera moves, the control buttons move as well
What should I do to keep the control buttons static?
See this note in the SKCameraNode docs:
A camera’s descendants are always rendered relative to the camera node’s origin and without applying the camera’s scaling or rotation to them. For example, if your game wants to display scores or other data floating above the gameplay, the nodes that render these elements should be descendants of the current camera node.
If you want HUD elements that stay fixed relative to the screen even as the camera moves/scales/rotates, make them child nodes of the camera.
By the way, you don't need to change the camera's position on every update(). Instead, just constrain the camera's position to match that of the player:
let constraint = SKConstraint.distance(SKRange(constantValue: 0), toNode: player)
camera.constraints = [ constraint ]
Then, SpriteKit will automatically keep the camera centered on the player without any per-frame work from you. You can even add more than one constraint — say, to follow the player but keep the camera from getting too close to the edge of the world (and showing empty space).
Add the buttons as child to the camera, like cam.addchild(yourButton)
From rickster's answer I made these constraints where the camera only moves horizontally, even if the player jumps. The order in which they are added is important. In case somebody else find them useful:
Swift 4.2
let camera = SKCameraNode()
scene.addChild(camera)
camera.constraints = [SKConstraint.distance(SKRange(upperLimit: 200), to: player),
SKConstraint.positionY(SKRange(constantValue: 0))]