so I have a base game setup that can be found at the bitbucket link below:
Game link
I\'m currently having a hard time understanding how to translate the cam
Applications like this are exactly what SKConstraint is for.
You can see a demo of this exact feature — constraining a camera so that it follows the player, but doesn't show too much empty space around the edge of the level — in the WWDC15 session Deeper into GameplayKit with DemoBots.* (The link there should jump to about 7:27 in the talk where discussion of this feature begins.)
The gist of what's in the video, with some snippets from the DemoBots sample code:
Use a distance constraint to keep the camera centered on the player (automatically, without having to set camera.position
directly on every update()
).
// Constrain the camera to stay a constant distance of 0 points from the player node.
let zeroRange = SKRange(constantValue: 0.0)
let playerBotLocationConstraint = SKConstraint.distance(zeroRange, toNode: playerNode)
Use a position constraint to keep the camera within a certain range of the edge of the level. Calculate that range by taking the frame of the level and insetting that rect by the distance the camera should keep from the edge of the level.
// get the scene size as scaled by `scaleMode = .AspectFill`
let scaledSize = CGSize(width: size.width * camera.xScale, height: size.height * camera.yScale)
// get the frame of the entire level contents
let boardNode = childNodeWithName(WorldLayer.Board.nodePath)!
let boardContentRect = boardNode.calculateAccumulatedFrame()
// inset that frame from the edges of the level
// inset by `scaledSize / 2 - 100` to show 100 pt of black around the level
// (no need for `- 100` if you want zero padding)
// use min() to make sure we don't inset too far if the level is small
let xInset = min((scaledSize.width / 2) - 100.0, boardContentRect.width / 2)
let yInset = min((scaledSize.height / 2) - 100.0, boardContentRect.height / 2)
let insetContentRect = boardContentRect.insetBy(dx: xInset, dy: yInset)
// use the corners of the inset as the X and Y range of a position constraint
let xRange = SKRange(lowerLimit: insetContentRect.minX, upperLimit: insetContentRect.maxX)
let yRange = SKRange(lowerLimit: insetContentRect.minY, upperLimit: insetContentRect.maxY)
let levelEdgeConstraint = SKConstraint.positionX(xRange, y: yRange)
levelEdgeConstraint.referenceNode = boardNode
Apply both constraints to your SKCameraNode
.
camera.constraints = [playerBotLocationConstraint, levelEdgeConstraint]
For a deeper look, download Apple's DemoBots sample code project, which has a lot of comments and supporting code that I trimmed from the above snippets to keep this post from getting excessively long. Everything for the camera constraint is in func setCameraConstraints()
in LevelScene.swift
.
* Despite the session name, it's about a lot more than just GameplayKit... it shows how to leverage many the technologies introduced in iOS 8 / OS X 10.11 / Xcode 7 to build something resembling a full-scale game: App Thinning, new SpriteKit features, ReplayKit, and a lot more.
I didn't use your code. I made a sample project and got this working.
heres my code
import SpriteKit
class GameScene: SKScene {
let world = SKSpriteNode(imageNamed: "world.jpg")
let player = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(10, 10))
var cam: SKCameraNode!
override init(size: CGSize) {
super.init(size: size)
print(world.size)
addChild(world)
addChild(player)
world.zPosition = 1
player.zPosition = 2
cam = SKCameraNode()
self.camera = cam
addChild(cam)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
player.position = location
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
player.position = location
}
}
func clampCamera(){
func clamp(inout input: CGFloat, num1: CGFloat, num2: CGFloat) {
if input < num1 {
input = num1
}
else if input > num2 {
input = num2
}
}
let lBoundary = -world.size.width/2 + size.width/2
let rBoundary = world.size.width/2 - size.width/2
let bBoundary = -world.size.height/2 + size.height/2
let tBoundary = world.size.height/2 - size.height/2
clamp(&camera!.position.x, num1: lBoundary, num2: rBoundary)
clamp(&camera!.position.y, num1: bBoundary, num2: tBoundary)
}
override func update(currentTime: NSTimeInterval) {
camera!.position = player.position
clampCamera()
}
}
this is the same image i used as my "world" http://i.imgur.com/XhZbh8q.jpg