Centering the camera on the character - ios

I am a beginner and I have started my first project. It should be a little game where you can run to the left and to the right. But I don't know how to set the camera settings.
I'd be very thankful if you could help me, tell me how I can improve my code and what is wrong.
Here is my Code:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var ground:SKSpriteNode = SKSpriteNode()
var hero: MPCharacter!
var tree:MPTree!
override func didMoveToView(view: SKView) {
backgroundColor = UIColor(red: 155.0/255.0, green: 201.0/255.0, blue: 244.0/255.0, alpha: 1.0)
self.anchorPoint = CGPointMake(0.5, 0.5)
// --> hero <--
hero = MPCharacter()
hero.position = CGPointMake(frame.size.width/2, view.frame.size.height/2)
hero.zPosition = 100
hero.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(hero.size.width, hero.size.height))
hero.physicsBody!.dynamic = true
addChild(hero)
// --> ground <--
ground = SKSpriteNode(imageNamed: "ground")
ground.size = CGSizeMake(frame.size.width, frame.size.height/3)
ground.position = CGPointMake(frame.size.width/2, frame.size.height/2 - ground.size.height)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(frame.size.width, ground.size.height - 25))
ground.physicsBody!.dynamic = false
addChild(ground)
// --> Tree <--
tree = MPTree()
tree.position = CGPointMake(frame.size.width/2, ground.size.height + tree.size.height/3 - 10)
tree.zPosition = 10
tree.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(tree.size.width, tree.size.height - 10))
tree.physicsBody!.dynamic = false
addChild(tree)
// --> Physics <--
self.physicsWorld.gravity = CGVectorMake(0, -5.0)
self.physicsWorld.contactDelegate = self
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch:AnyObject in touches {
let location = touch.locationInNode(self)
if location.x < CGRectGetMidX(self.frame) {
hero.physicsBody!.velocity = CGVectorMake(0, 0)
hero.physicsBody!.applyImpulse(CGVectorMake(-5, 10))
}
else {
hero.physicsBody!.velocity = CGVectorMake(0, 0)
hero.physicsBody!.applyImpulse(CGVectorMake(5, 10))
}
}
}
}

Im not quite sure what you are trying to achieve. One way to make your character 'run' to the left (or right) is by moving a background (and other game objects) to the left when the character is running right (and vice versa). This way your character will always be in the center of the screen, because he is not really moving, you create an 'illusion' that he is moving in your gameworld. Giving your character a run animation will make it look even better...
There is a tutorial on the website of Ray Wenderlich which also covers 'parallax scrolling':
http://www.raywenderlich.com/49625/sprite-kit-tutorial-space-shooter
Hope this helps..

If you're trying to center the camera on the player, Apple Docs provide you a nice example for how to do so over here (I would add code, but the best way to learn is to get your hands dirty). You might have to translate the methods to swift, but it is the general implementation for what you wish to do. The problem with the answer above is that if you would move the background, you would have to calculate the forces and the impulses directly and apply the inverse vectors to the background. This might require more processing than with simply moving the camera. Hope this helps!!

Related

Swift SKNode position help - sprite position always 0,0 as it moves

Problem: I’m working in swift playground on an iPad Pro.
I’d like to get the current position coordinates of an SKSpriteNode with attached physicsBody as they move around the screen. No matter what it try, the sprite position returns 0,0, despite seeing it move across the screen.
I’ve attempted various implementations of convert(_:to:) and convert(_:from:) but the sprite position always ends up 0,0. My understanding is that this isn’t really necessary in the specific case here since the sprite is the direct child of the scene so the sprite’s position property should already be what it is in the scene coordinate system.
I’ve spent a lot of googling time attempting to figure this out without much success which makes me think I’m asking the wrong question. If so, please help me figure out what the right question to ask is!
The touch position print returns the correct position relative to the initial screen size. That’s what I’d like to get for the sprite.
Disclaimer - i don’t do this professionally so please forgive what I’m sure is ugly coding!
import SwiftUI
import SpriteKit
import PlaygroundSupport
var screenW = CGFloat(2732)
var screenH = CGFloat(2048)
struct ContentView: View {
var scene: SKScene {
let scene = GameScene()
scene.size = CGSize(width: 2732, height: 2048)
scene.scaleMode = .aspectFit
scene.backgroundColor = #colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
return scene
}
class GameScene: SKScene {
var ship1 = SKSpriteNode()
public override func didMove(to view: SKView) {
// gravity edge around screen for convenience
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
//turn off default 1g gravity
physicsWorld.gravity = .zero
//Add a ship sprite
let ship1 = SKSpriteNode(imageNamed: "ship.PNG")
ship1.setScale(0.2)
ship1.position = CGPoint(x: screenW * 0.2, y: screenH * 0.2)
ship1.physicsBody = SKPhysicsBody(rectangleOf: ship1.size)
ship1.physicsBody?.velocity = CGVector(dx: 0, dy: 100)
addChild(ship1)
}
//touch handling begin
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
//
let location = touch.location(in: self)
print(location)
print(ship1.position)
}
}
var body: some View {
SpriteView(scene: scene)
.ignoresSafeArea()
}
}
//star the view
let currentView = ContentView()
//Playground view setup
PlaygroundPage.current.setLiveView(currentView)
PlaygroundPage.current.wantsFullScreenLiveView = true
PlaygroundPage.current.needsIndefiniteExecution = true

Swift + Physics: Wrong angle calculation on collision

I have a simple SpriteKit App with walls and a ball. Both setup with a SKPhysicsBody.
When I apply a force in one direction, I expect the ball to reflect at the wall with the same angle when it collide but in opposite direction.
But sometimes I see the angle is weird. I played a lot with all the physicsBody properties, but was not able to fix it. Sometimes the first reflections look good, but then the third or sixth and sometimes the first reflection is at wrong angle.
I read from different posts, people are kinda self-calculating the "correct direction". But I can't imagine SpriteKits physic engine is not capable to do so.
Check my attached image to understand what I mean. Can anybody help on this? I don't want to start playing around with Box2d for Swift, since this looks like it will be too hard for me to integrate into Swift.
This is the physicsWorld init on my GameScene.swift file where all my elements are added to:
self.physicsWorld.gravity = CGVectorMake(0, 0)
Adding all my code would be way too much, so I add the important pieces. Hope its enough for analyze. All elements like the ball, walls are SKSpriteNode's
This is the physicsBody code for the ball:
self.physicsBody = SKPhysicsBody(circleOfRadius: Constants.Config.playersize+self.node.lineWidth)
self.physicsBody?.restitution = 1
self.physicsBody?.friction = 0
self.physicsBody?.linearDamping = 1
self.physicsBody?.allowsRotation = false
self.physicsBody?.categoryBitMask = Constants.Config.PhysicsCategory.Player
self.physicsBody?.collisionBitMask = Constants.Config.PhysicsCategory.Wall | Constants.Config.PhysicsCategory.Enemy
self.physicsBody?.contactTestBitMask = Constants.Config.PhysicsCategory.Wall | Constants.Config.PhysicsCategory.Enemy
This is the physicsBody for the walls:
el = SKSpriteNode(color: UIColor.blueColor(), size: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
el.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
el.physicsBody?.dynamic = false
el.physicsBody?.categoryBitMask = Constants.Config.PhysicsCategory.Wall
el.physicsBody?.collisionBitMask = Constants.Config.PhysicsCategory.Player
el.physicsBody?.contactTestBitMask = Constants.Config.PhysicsCategory.Player
At the end I just call the applyImpulse function on the balls physicsBody to make it moving in the physics simulation.
Also check my attached second image/gif. It shows the edge collision problem with a simple skphysics app without any special parameterization. just a rectangle with a ball in it and one vector applied as impulse.
Heres my full non-working code using the solution from appzYourLife.
class GameScene: SKScene {
private var ball:SKShapeNode!
override func didMoveToView(view: SKView) {
let screen = UIScreen.mainScreen().bounds
let p = UIBezierPath()
p.moveToPoint(CGPointMake(0,0))
p.addLineToPoint(CGPointMake(screen.width,0))
p.addLineToPoint(CGPointMake(screen.width, screen.height))
p.addLineToPoint(CGPointMake(0,screen.height))
p.closePath()
let shape = SKShapeNode(path: p.CGPath)
shape.physicsBody = SKPhysicsBody(edgeLoopFromPath: p.CGPath)
shape.physicsBody?.affectedByGravity = false
shape.physicsBody?.dynamic = false
shape.strokeColor = UIColor.blackColor()
self.addChild(shape)
ball = SKShapeNode(circleOfRadius: 17)
ball.name = "player"
ball.position = CGPoint(x: 20, y: 20)
ball.fillColor = UIColor.yellowColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: 17)
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.friction = 0
ball.physicsBody?.allowsRotation = false
self.addChild(ball)
self.ball.physicsBody?.applyImpulse(CGVector(dx: 2.4, dy: 9.7))
}
I just realized the mass and density of my ball sprite is nil. Why that? Even setting it keeps in nil.
Since i cannot imagine this is some unknown bug in SpriteKits physics engine,
i very hope someone can help me here as this is a full stopper for me.
It is a (known) bug in SpriteKits physic engine. But sometimes just playing with the values will make this bug disappear - for example, try changing the ball's speed (even by small value) every time it's hitting the wall.
Another solutions are listed here:
SpriteKit physics in Swift - Ball slides against wall instead of reflecting
(The same problem as yours, already from 2014, still not fixed..)
SpriteKit is working fine
I created a simple project and it is working fine.
The problem
As #Sulthan already suggested in a comment, I think the problem in your code is this line
el.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
Here instead of creating the physical border for your scene, you are creating a rectangular physics body which overlap the physics body of the Ball producing unrealistic behaviours.
To create the physics border of my scene I simply used this
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
Full code
By the way, this is the full code for my project
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let ball = Ball()
ball.position = CGPoint(x:frame.midX, y:frame.midY)
addChild(ball)
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
ball.physicsBody!.applyImpulse(CGVector(dx:50, dy:70))
physicsWorld.gravity = CGVector(dx:0, dy:0)
}
}
class Ball: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "ball")
super.init(texture: texture, color: .clearColor(), size: texture.size())
let physicsBody = SKPhysicsBody(circleOfRadius: texture.size().width/2)
physicsBody.restitution = 1
physicsBody.friction = 0
physicsBody.linearDamping = 0
physicsBody.allowsRotation = false
self.physicsBody = physicsBody
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I also used scene.scaleMode = .ResizeFill inside GameViewController.swift.

How to move sprite just left or right using CoreMotion?

For this game I am creating, I want the Sprite "Character" to either move left or right by me tilting the device either left or right.
I have looked into it and changed the code, but it seems like that it does not work.
When I run it, the "Character" moves in only one direction and still goes either up or down.
I would just like the "Character" to move only left or right, not up or down.
Any help on this?
Here is a look at the GameScene.swift file:
import SpriteKit
import CoreMotion
class GameScene: SKScene {
let motionManager = CMMotionManager()
var Ground = SKSpriteNode()
var Character = SKSpriteNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
Character = SKSpriteNode(imageNamed: "NinjaGhost")
Character.physicsBody = SKPhysicsBody(circleOfRadius: Character.size.height)
Character.size = CGSize(width: 200, height: 200)
Character.physicsBody?.allowsRotation = false
Character.zPosition = 2
Character.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
self.addChild(Character)
Ground = SKSpriteNode(imageNamed: "Dirt Background")
Ground.size = CGSize(width: 1920, height: 1080)
Ground.zPosition = 1
Ground.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
self.addChild(Ground)
motionManager.startAccelerometerUpdates()
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue() ) {
(data, error) in
self.physicsWorld.gravity = CGVectorMake(CGFloat((data?.acceleration.x)!) * 1, 0)
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
You should follow the swift guidelines, your properties should not start with capital letters, only classes, structs, enums and protocols should. It makes code harder to read on stack overflow (code is marked blue but shouldn't) and in general its not good practice.
Change your code to this because there was a few errors.
character = SKSpriteNode(imageNamed: "NinjaGhost")
character.size = CGSize(width: 200, height: 200) // Better to call this before positioning
character.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2) // Give pos before physics body
character.physicsBody = SKPhysicsBody(circleOfRadius: character.size.width / 2) // You have to divide by 2 when using circleOfRadius to get proper size in relation to the sprite
character.physicsBody?.allowsRotation = false
character.physicsBody?.affectedByGravity = false // Stop falling
character.zPosition = 2
self.addChild(character)
In your motion queue code you are manipulating the scene itself, so that will not work.
Try this code instead which uses the physics body to move the sprite. This is my preferred way of doing it because it gives the sprite more natural movement, especially when changing direction, compared to directly manipulating the sprites position property (If you want to manipulate the sprite position property directly than read this article. http://www.ioscreator.com/tutorials/move-sprites-accelerometer-spritekit-swift)
if let error = error { // Might as well handle the optional error as well
print(error.localizedDescription)
return
}
guard let data = motionManager.accelerometerData else { return }
if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft {
character.physicsBody?.applyForce(CGVectorMake(-100 * CGFloat(data.acceleration.y), 0))
} else {
character.physicsBody?.applyForce(CGVectorMake(100 * CGFloat(data.acceleration.y), 0))
}
This will make the sprite move on the x axis. I am using data.acceleration.y because in this example the game is in landscape so you have to use the Y data to move it on the x axis.
I am also checking in which landscape orientation the device is so that the motion works in both orientations.
Adjust the 100 here to get the desired force depending on the size of the sprite (higher is more force). Big sprites properly need a few 1000s to move.

Locking scene to a sprite node swift xcode

Basically, I want to move the character (player) and have the “camera” or scene follow them in the world. The problem is Apple/xcode does not have a built in camera node and if I try to move the scene, I get an error saying something to the effect of “SKScene cannot be moved.” I don’t want to just move the background around and lock the player in place because I want to be able to apply forces to the player node. I have gone to Apple’s website to the Advanced Scene Processing page and followed the example but it doesn’t work. I just need a nudge in the right direction. Thanks!
Code:
(I have deleted pieces of code that are irrelevant)
class PlayScene: SKScene, SKPhysicsContactDelegate {
let world = SKNode()
let player = SKShapeNode(circleOfRadius: 25)
var cannonRotation = 0.0
var fired = false
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.addChild(world)
//Adds the background to the world
//Adds the elements to the scene
player.fillColor = UIColor.whiteColor()
player.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMinY(self.frame)+65+25)
player.zPosition = 2
player.physicsBody = SKPhysicsBody(circleOfRadius: 25)
player.physicsBody?.dynamic = true
player.physicsBody?.restitution = 0.9
player.physicsBody?.categoryBitMask = BodyType.player.rawValue
//Adds the physics properties to GameScene
let physicsGround = SKPhysicsBody(edgeFromPoint: CGPoint(x: CGRectGetMinX(self.frame), y: CGRectGetMinY(self.frame)+65), toPoint: CGPoint(x: CGRectGetMaxX(self.frame), y: CGRectGetMinY(self.frame)+65))
self.physicsBody = physicsGround
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
powerLevel()
totalPower = powerReached * playCannon.powerMultiplier
self.addChild(player)
player.physicsBody?.applyImpulse(CGVector(dx: CGFloat(Float(totalPower)*cosf(Float(cannonRotation))), dy: CGFloat(Float(totalPower)*sinf(Float(cannonRotation)))))
fired = true
}
Override didsimulatePhysics, And in that function call this
self.centerOnNode(player);
What that does, It calles a function we will later create and it sendes a parameter of an SKNode and in your case it is the player node. Next create a method called centerOnNede and add the following parameters
func centerOnNode(node: SKNode) {
}
In the centerOnNode function add this following line of code.
var cameraPosition: CGPoint = node.scene?.convertPoint(node.position, fromNode: node.parent!)
cameraPosition.x = 0
let posX = node.parent?.position.x
let posY = node.parent?.position.y
node.parent?.position = CGPointMake(posX! - cameraPosition.x, posY! - cameraPosition.y)
That will center the camera on to the node and update it's position. Code converted from here. How to make camera follow SKNode in Sprite Kit?
There is a detailed example of how to do this in the Apple Adventure reference game. It has both Swift and Objective-C examples.
In summary what they recommend is that you
Put all nodes on a background node (SKNode) container.
implement didSimulatePhysics and move the world in the opposite direction of the player. That will keep the player in the middle of the screen. Special logic will be needed if you want the camera to bump into the edge of your world.
A camera node would be very helpful, but has not been implemented into SpriteKit.
As of iOS 9 there is now an SKCameraNode that does all the heavy lifting for you.
Simply add these three lines of code in your didMoveToView function:
let cameraNode = SKCameraNode()
self.addChild(cameraNode)
self.camera = cameraNode
Then in your didFinishUpdate function just set the cameraNode's position to whatever SKSpriteNode you want it to follow.

SKPhysics with Swift and SpriteKit

I am trying to create a iOS game using Swift Programming Language.
I want to use swipe gestures on the screen to make the character (player) move up, down, left and right
So far so good. But I want some physics. I want the player to collide with the edges of the screen. (and later I want it to collide with other stuff, but I need to know the code first ;)
QUESTION: How do I identify a collision between 2 objects (player and edge of screen)
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var player:SKSpriteNode = SKSpriteNode()
let moveUp = SKAction.moveByX(0, y:2000, duration:10.0) //it is set to 2000 because I want to see if it collides with the edge of the screen
let moveDown = SKAction.moveByX(0, y: -200, duration: 2.0)
// Define the bitmasks identifying various physics objects
let worldCategory:UInt32 = 0x1 << 0
let playerCategory:UInt32 = 0x1 << 1
override func didMoveToView(view: SKView) {
}
override init(size:CGSize) {
super.init(size: size)
self.backgroundColor = SKColor.whiteColor()
player = SKSpriteNode(imageNamed: "Kundvagn1")
player.position = CGPointMake(self.frame.size.width/2, player.size.height/2 + 20)
self.addChild(player)
player.runAction(moveUp)
self.physicsWorld.gravity = CGVectorMake(0, 0)
self.physicsWorld.contactDelegate = self
player.physicsBody = SKPhysicsBody(rectangleOfSize: player.size)
player.physicsBody?.dynamic = true //I dont think the question marks should be there, but Xcode doesn´t accept the code if there is no question marks there
player.physicsBody?.categoryBitMask = playerCategory
player.physicsBody?.contactTestBitMask = worldCategory
player.physicsBody?.collisionBitMask = 0
player.physicsBody?.usesPreciseCollisionDetection = true
}
func didBeginContact(contact: SKPhysicsContact!) {
println("Collision")
}
Assuming that you want the left and right edges to block, simply add two SKShapeNode rectangular objects to your scene, set their heights to the screen height, and place them just beyond the left and right edges of the screen. Set up their physics bodies and you should be good to go.
If you want all the edges of the screen to be boundaries, you need to setup a line-based physics body for your scene, as detailed here: Add boundaries to an SKScene
I added this in case you need a code example to jonogilmour's instructions . Turn on the ShowsPhysics = true in your viewController to see physics. Both sides should be blue. If you want a contact notification of course you would add to this code with your bit mask stuff. Hope this helps!
var leftEdge = SKNode()
leftEdge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPointZero, toPoint: CGPointMake(0.0, self.size.height))
leftEdge.position = CGPointZero;
self.addChild(leftEdge)
var rightEdge = SKNode()
rightEdge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPointZero, toPoint: CGPointMake(0.0, self.size.height))
rightEdge.position = CGPointMake(self.size.width, 0.0);
self.addChild(rightEdge)

Resources