I have this object that is a SKSpriteNode created from an image. The whole scene area itself is the boundary from where this object should not escape. Top and bottom boundaries are recognised but not the sides? Any help appreciated. Thanks
import SpriteKit
var ship = SKSpriteNode()
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blackColor()
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsWorld.gravity = CGVectorMake(0.0, -9.8)
ship = SKSpriteNode(imageNamed: "spark")
ship.position = CGPointMake(200, 400)
ship.physicsBody.linearDamping = 0
ship.physicsBody.restitution = 0.95
ship.physicsBody.friction = 0
self.addChild(ship)
var myVector = CGVectorMake(20, 20)
ship.physicsBody.applyImpulse(myVector)
let emitter = SKEmitterNode(fileNamed: "myExhaust")
emitter.position = CGPointMake(0.0, -ship.size.height / 2.0)
ship.addChild(emitter)
emitter.targetNode = self
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
var touch = touches.anyObject() as UITouch
// ship.position = [touch locationInNode:self];
ship.position = touch.locationInNode(self)
}
}
Try using the SKScene's size to create a CGRect and don't use SKScene.frame directly. SKScene.size determines the size of the visible portion of the SKScene and may be different from SKScene.frame.size.
override func didMoveToView(view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFromRect: CGRectMake(0, 0, size.width, size.height))
// ...
}
(Side Note: with Swift you don't need to use self there)
Related
I have created a simple project but I have a problem in the collisions.
It's simple (ball moving and vertical line) but didn't figure out how to stop the ball if it is touched the line.
import SpriteKit
class GameScene: SKScene,SKPhysicsContactDelegate {
var rPipe = SKSpriteNode() // Left Pipe
var ball1 = SKSpriteNode() // Ball
enum ColliderType:UInt32 {
case Ball1 = 1
case Pipe = 2
}
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
// Pipe
let rPipeTexture = SKTexture(imageNamed: "pipe_r.png")
rPipe = SKSpriteNode(texture: rPipeTexture)
rPipe.position = CGPoint(x: CGRectGetMaxX(self.frame)-50, y: CGRectGetMidY(self.frame)-30)
rPipe.physicsBody = SKPhysicsBody(rectangleOfSize: rPipeTexture.size())
rPipe.physicsBody?.dynamic = false
rPipe.physicsBody?.categoryBitMask = ColliderType.Pipe.rawValue
rPipe.physicsBody?.contactTestBitMask = ColliderType.Pipe.rawValue
rPipe.physicsBody?.collisionBitMask = ColliderType.Pipe.rawValue
self.addChild(rPipe)
// Ball
let ballTexture = SKTexture(imageNamed: "gBall.png")
ball1 = SKSpriteNode(texture: ballTexture)
ball1.position = CGPoint(x: CGRectGetMinX(self.frame)+675, y: CGRectGetMaxY(self.frame)-220)
ball1.physicsBody = SKPhysicsBody(circleOfRadius: ballTexture.size().height/2)
ball1.physicsBody?.dynamic = false
ball1.physicsBody?.categoryBitMask = ColliderType.Ball1.rawValue
ball1.physicsBody?.contactTestBitMask = ColliderType.Pipe.rawValue
ball1.physicsBody?.collisionBitMask = ColliderType.Pipe.rawValue
self.addChild(ball1)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if ball1.containsPoint(location) {
ball1.position.x = location.x
}
}
}
func didBeginContact(contact: SKPhysicsContact) {
print("Contact")
}
One of your collided objects's dynamic property should be set to true. Otherwise the collision will be ignored. After setting dynamic, you also need to set affectedByGravity to false because the ball should not be affected by the gravity.
ball1.physicsBody?.dynamic = true
ball1.physicsBody?.affectedByGravity = false
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Beach Ball-100")
self.addChild(sprite)
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
sprite.color = UIColor.redColor()
sprite.physicsBody = SKPhysicsBody(edgeLoopFromRect:self.frame)
sprite.physicsBody?.affectedByGravity = true
var action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
}
}
}
The code above is present in my scene class. When I run it, the ball object is not falling as expected since the gravity property is set to true. Please explain what I am doing wrong.
The Physicsbody is wrong. You need the frame of the sprite and not of the world:
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.frame.size)
Add SKPhysicsContactDelegate:
class GameScene: SKScene, SKPhysicsContactDelegate
Define your physicsWorld in didMoveToView or in touchesBegan(like you did in example)
physicsWorld.gravity = CGVectorMake(0, -1.0)
physicsWorld.contactDelegate = self
Give your node proper SKPhysicsBody size
sprite.physicsBody = SKPhysicsBody(circleOfRadius: frame.size.width / 2)
my game is not this but like this:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var sprite = SKSpriteNode()
override func didMoveToView(view: SKView)
{
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody?.categoryBitMask = 1
self.physicsBody?.contactTestBitMask = 1
self.physicsWorld.gravity = CGVectorMake(0, -10)
self.physicsWorld.contactDelegate = self
}
func didBeginContact(contact: SKPhysicsContact) {
print("contact")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let spriteTexture = SKTexture(imageNamed: "Spaceship")
sprite = SKSpriteNode(texture: spriteTexture)
sprite.position = CGPoint(x: CGRectGetMidX(self.frame) , y: CGRectGetMidY(self.frame))
sprite.size = CGSizeMake(80, 80)
sprite.physicsBody = SKPhysicsBody(texture: spriteTexture, size: CGSizeMake(80, 80))
sprite.physicsBody?.categoryBitMask = 1
sprite.physicsBody?.collisionBitMask = 1
sprite.physicsBody?.contactTestBitMask = 1
sprite.physicsBody?.linearDamping = 0;
self.addChild(sprite)
}
}
if you paste this code and run you see a lot of "contact" string. I want only 1 contact this:
so i want when contact only 1 time collision
I edited my question can anyone help?
1.Create ‘Base Game’ from Xcode template based on SpriteKit
2.Paste code below to GameScene class
class GameScene: SKScene, SKPhysicsContactDelegate {
var sprite = SKSpriteNode()
override func didMoveToView(view: SKView)
{
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody?.categoryBitMask = 1
self.physicsBody?.collisionBitMask = 1
self.physicsWorld.gravity = CGVectorMake(0, -10)
self.physicsWorld.contactDelegate = self
self.physicsBody?.restitution = 0.0
}
func didEndContact(contact: SKPhysicsContact) {
print("End contact")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let spriteTexture = SKTexture(imageNamed: "Spaceship")
sprite = SKSpriteNode(texture: spriteTexture)
sprite.physicsBody = SKPhysicsBody(texture: spriteTexture, size: CGSizeMake(80, 80))
sprite.size = CGSizeMake(80, 80)
sprite.position = CGPoint(x: CGRectGetMidX(self.frame) , y: CGRectGetMidY(self.frame))
sprite.physicsBody?.categoryBitMask = 1
sprite.physicsBody?.collisionBitMask = 1
sprite.physicsBody?.contactTestBitMask = 1
sprite.physicsBody?.fieldBitMask = 1
sprite.physicsBody?.restitution = 0
self.addChild(sprite)
}
}
IMPORTANT NOTE Try to use didEndContact delegate method instead of didBeginContact. In this case you'll get only one invoke while didBeginContact invokes several times.
I am making a simple side scrolling game using apple's swift programming language and I want to make my character move left when the left half of the screen is touched and right when the other half is touched. I have done this using this code:
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if location.x < CGRectGetMidX(self.frame){
character.position.x -= 30
} else if location.x > CGRectGetMidX(self.frame){
character.position.x += 30
} else {
println("jump")
}
}
but he stops moving immediately after he moves 30 px over. my question is, can someone explain how to make him keep moving until the user lifts their finger?
This is my GameScene.swift file:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let character = SKSpriteNode(texture: SKTexture(imageNamed: "character"))
override func didMoveToView(view: SKView) {
/* Setup your scene here */
//world
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
//background
var background = SKSpriteNode(imageNamed: "background")
background.size.height = self.frame.size.height
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(background)
//character
character.position = CGPointMake(self.frame.size.width * 0.6, self.frame.size.height * 0.6)
character.setScale(0.015)
character.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(character.size.width / 2))
var charPos = character.position
character.physicsBody?.dynamic = true
self.addChild(character)
//platform 1
var platformTexture = SKTexture(imageNamed: "platform")
var platform = SKSpriteNode(texture: platformTexture)
platform.position = CGPointMake(self.frame.size.width * 0.6, CGRectGetMidY(self.frame))
platform.physicsBody = SKPhysicsBody(rectangleOfSize: platform.size)
platform.physicsBody?.dynamic = false
platform.setScale(0.25)
self.addChild(platform)
//platform 2
var platformTexture2 = SKTexture(imageNamed: "platform")
var platform2 = SKSpriteNode(texture: platformTexture2)
platform2.position = CGPointMake(self.frame.size.width * 0.4, self.frame.size.height * 0.3)
platform2.physicsBody = SKPhysicsBody(rectangleOfSize: platform2.size)
platform2.physicsBody?.dynamic = false
platform2.setScale(0.25)
self.addChild(platform2)
//platform main
var platformTexture3 = SKTexture(imageNamed: "platform")
var platform3 = SKSpriteNode(texture: platformTexture2)
platform3.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame) + platform3.size.height / 3)
platform3.physicsBody = SKPhysicsBody(rectangleOfSize: platform3.size)
platform3.physicsBody?.dynamic = false
platform3.setScale(1)
platform3.size.width = platform3.size.width * CGFloat(2.0)
self.addChild(platform3)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
}
You have to apply force constantly to character in every update: call in order to move it without stopping. You can also apply impulses in order to move character, or make it jump. Here is an example based on you code, to give you a basic idea how you can move characters using physics (or by changing velocity vector of a character manually). Look through the comments to find what is important.
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let character = SKSpriteNode(texture: SKTexture(imageNamed: "pauseButton"))
//Boolean variable to store information about move signal (updated in touchesBegan and touchesEnded method)
var move = false
override func didMoveToView(view: SKView) {
/* Setup your scene here */
//world
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
self.physicsWorld.contactDelegate = self; //don't forget to set contact delegate if you want to use contact detection and methods like didBeginContact and didEndContact
//background
//just a physical border so that character can't escape from us :)
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
//character
character.position = CGPointMake(self.frame.size.width * 0.6, self.frame.size.height * 0.6)
character.setScale(0.415)
character.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(character.size.width / 2))
var charPos = character.position
character.physicsBody?.dynamic = true
self.addChild(character)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
//Hold finger at upper area to move character constantly to the right.
if location.y > 400{
//moving allowed, force is applied in update method. read the docs about applyImpulse and applyForce methods and the differences between those two.
move = true
}else{
if location.x < CGRectGetMidX(self.frame){
character.physicsBody?.applyImpulse(CGVector(dx: -30, dy: 0))
//tap somewhere above this to make character jump
if(location.y > 250) {
character.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 50))
}
} else if location.x > CGRectGetMidX(self.frame){
character.physicsBody?.applyImpulse(CGVector(dx: 30, dy: 0))
//tap somewhere above this to make character jump
if(location.y > 250) {
character.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 50))
}
}
}
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
move = false
//character.physicsBody?.velocity = CGVector(dx: 0, dy: 0) //you can make character to stop by manually setting its velocity vector to (0,0)
}
override func update(currentTime: CFTimeInterval) {
if(move){character.physicsBody?.applyForce(CGVector(dx: 30, dy: 0))}
//if(move){character.position = CGPoint(x: character.position.x+1, y:character.position.y)} //not recommended if you need physics simulation (eg. collisions)
}
}
Note that you can change node's position in every update call to achieve what you want, like this :
if(move){character.position = CGPoint(x: character.position.x+1, y:character.position.y)}
But this way you will pull the node out of physics simulation and you can experience unexpected results. Search SO about this topic, there are some good posts about all this.
Hope this helps a bit.
every time I add the following code:
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
the physics body for my "ball" item stops moving. It worked before adding the previous line.
here is all my code:
class GameScene: SKScene {
// creaTE nODE/Sprite
let ball = SKSpriteNode(imageNamed: "ball.png")
override func didMoveToView(view: SKView) {
// set up scene here
self.backgroundColor = SKColor.whiteColor()
//add physics to scene
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
//create cg piont for sprite
ball.position = CGPointMake(size.width / 2, size.height / 2)
//give it some real life physics!
ball.physicsBody = SKPhysicsBody(circleOfRadius: size.height / 2)
addChild(ball)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
This line is causing the problem:
ball.physicsBody = SKPhysicsBody(circleOfRadius: size.height / 2)
You need to use
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height / 2)
The physicsBody being created in the previous line was the size of the scene, hence it did not have space to move!
You can check the physicsBody being created by setting the showsPhysics property to YES in the SKView. Check here for more details.