Sprites not contacting properly - ios

There's a ball and a paddle, the ball should be on the paddle and touching it but the ball never touches the paddle and stops before reaching the paddle, can anyone help me out?
import SpriteKit
class GameScene: SKScene ,SKPhysicsContactDelegate {
let ballCategory:UInt32 = 0x1 << 0
let bottomCategory:UInt32 = 0x1 << 1
let paddleCategory:UInt32 = 0x1 << 2
override init(size: CGSize){
super.init(size: size)
physicsWorld.contactDelegate = self
let ball = SKSpriteNode(imageNamed: "ball")
ball.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
addChild(ball)
ball.physicsBody = SKPhysicsBody(circleOfRadius: self.frame.size.width / 2)
ball.physicsBody?.dynamic = true
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.friction = 0
// ball.physicsBody?.applyImpulse(CGVectorMake(2,-2))
ball.physicsBody?.categoryBitMask = ballCategory
ball.physicsBody?.contactTestBitMask = paddleCategory
ball.physicsBody?.collisionBitMask = paddleCategory
ball.physicsBody?.affectedByGravity = true
let paddle = SKSpriteNode(imageNamed: "paddle")
paddle.position = CGPointMake(CGRectGetMidX(self.frame), paddle.frame.size.height * 2)
addChild(paddle)
paddle.physicsBody = SKPhysicsBody(rectangleOfSize: paddle.frame.size)
paddle.physicsBody?.affectedByGravity = false
paddle.physicsBody?.dynamic = false
paddle.physicsBody?.categoryBitMask = paddleCategory
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
func didBeginContact(contact: SKPhysicsContact) {
print("touched")
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}

The best way to determine where your nodes are making contact is to turn on Physics.
Open GameViewController.swift and enter the following code:
skView.showsPhysics = true

You are experiencing this issue because of this line:
ball.physicsBody = SKPhysicsBody(circleOfRadius: self.frame.size.width / 2)
self is a scene here.
You need this:
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.size.width / 2)
Turn on physics visual representation in your GameViewController like this:
skView.showsPhysics = true
and you will see what is going on.

Related

Collision between two objects

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

Gravity property not working in SpriteKit Scene

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)

Sprite Kit more than 1 time collision

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.

How do I make a simple character controller in SpriteKit?

I'm making a game in sprite kit and in order to move my character I am applying an impulse but that is a problem because that moves him every 50 pixels or so. My question is, could someone please show me how to make a simple character controller so my character can move continuously until the user lets go from holding down the screen?
My GameScene.swift file:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let character = SKSpriteNode(texture: SKTexture(imageNamed: "character"))
var move = false
override func didMoveToView(view: SKView) {
/* Setup your scene here */
//world
self.physicsWorld.contactDelegate = self
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.2)
character.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
character.physicsBody?.dynamic = true
character.physicsBody?.allowsRotation = false
self.addChild(character)
character.physicsBody?.affectedByGravity = true
//platform 1
var platform = SKSpriteNode(texture: SKTexture(imageNamed: "platform"))
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)
platform.physicsBody?.friction = 1
platform.physicsBody?.restitution = 0
platform.physicsBody?.linearDamping = 0
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)
platform2.physicsBody?.friction = 1
platform2.physicsBody?.restitution = 0
platform2.physicsBody?.linearDamping = 0
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)
platform3.physicsBody?.friction = 1
platform3.physicsBody?.restitution = 0
platform3.physicsBody?.linearDamping = 0
self.addChild(platform3)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if location.x < CGRectGetMidX(self.frame){
character.physicsBody?.applyImpulse(CGVector(dx: -50, dy: 0))
} else if location.x > CGRectGetMidX(self.frame){
character.physicsBody?.applyImpulse(CGVector(dx: 50, dy: 0))
}
}
func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
character.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
}
func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
};
}
One way to accomplish this is to maintain a variable to keep track of the state of the touch events (left, right, or none). Here are the basic steps:
In touchesBegan, set touch state variable to left or right
In update, apply impulse continuously while user is touching the screen
In touchesEnded, reset the touch state variable
Here's an example of how to implement this...
Above GameScene class definition, add the following
enum TouchState {
case Left
case Right
case None
}
In GameScene, add the following variable
var touchLocation:TouchState = .None
In GameScene, add or replace the following methods
override func update(currentTime: CFTimeInterval) {
// Apply impulse to physics body while users is touching the screen
switch (touchState) {
case .Left:
character.physicsBody?.applyImpulse(CGVector(dx: -1, dy: 0))
case .Right:
character.physicsBody?.applyImpulse(CGVector(dx: 1, dy: 0))
case .None:
break
}
}
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) {
touchState = .Left
} else if location.x > CGRectGetMidX(self.frame) {
touchState = .Right
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
// Update touch status
touchState = .None
}
Here's the GameScene.swift...
import SpriteKit
enum TouchLocation {
case Left
case Right
case None
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var character = SKSpriteNode(imageNamed: "character")
var touchLocation:TouchLocation = .None
override func didMoveToView(view: SKView) {
/* Setup your scene here */
scaleMode = .ResizeFill
//character
character.position = CGPointMake(view.frame.size.width * 0.5, view.frame.size.height * 0.5)
character.setScale(0.1)
character.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
character.physicsBody?.dynamic = true
character.physicsBody?.allowsRotation = false
self.addChild(character)
character.physicsBody?.affectedByGravity = true
//platform 1
var platform = SKSpriteNode(texture: SKTexture(imageNamed: "platform"))
platform.position = CGPointMake(view.frame.size.width * 0.6, CGRectGetMidY(view.frame))
platform.physicsBody = SKPhysicsBody(rectangleOfSize: platform.size)
platform.physicsBody?.dynamic = false
platform.setScale(0.25)
platform.physicsBody?.friction = 1
platform.physicsBody?.restitution = 0
platform.physicsBody?.linearDamping = 0
self.addChild(platform)
//platform 2
var platformTexture2 = SKTexture(imageNamed: "platform")
var platform2 = SKSpriteNode(texture: platformTexture2)
platform2.position = CGPointMake(view.frame.size.width * 0.4, view.frame.size.height * 0.3)
platform2.physicsBody = SKPhysicsBody(rectangleOfSize: platform2.size)
platform2.physicsBody?.dynamic = false
platform2.setScale(0.25)
platform2.physicsBody?.friction = 1
platform2.physicsBody?.restitution = 0
platform2.physicsBody?.linearDamping = 0
self.addChild(platform2)
//platform main
var platformTexture3 = SKTexture(imageNamed: "platform")
var platform3 = SKSpriteNode(texture: platformTexture2)
platform3.position = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMinY(view.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)
platform3.physicsBody?.friction = 1
platform3.physicsBody?.restitution = 0
platform3.physicsBody?.linearDamping = 0
self.addChild(platform3)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
switch (touchLocation) {
case .Left:
character.physicsBody?.applyImpulse(CGVector(dx: -1, dy: 0))
case .Right:
character.physicsBody?.applyImpulse(CGVector(dx: 1, dy: 0))
case .None:
break
}
}
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) {
touchLocation = .Left
} else if location.x > CGRectGetMidX(self.frame) {
touchLocation = .Right
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touchLocation = .None
}
}

swift: sprite kit collisions and bitmask not detected

I have a problem with the following sprite kit code. I'm trying to detect when a flying ball is colliding with a line. However nothing happens when the two collide. However when the ball hits the edge of the scene, the following is printed out:
contact 1
bitmask1: 4294967295
bitmask2: 4294967295
Problem 1: Why aren't the line and ball collisions being detected?
Problem 2: Why are both of the bitmask the same on edge collision? I can't work with the bodies if I don't know which is which.
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Ball : UInt32 = 0b1 // 1
static let Line : UInt32 = 0b10 // 2
static let Shape : UInt32 = 0b100 // 3 or 4?
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
physicsWorld.contactDelegate = self
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: view.frame);
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
let ball = SKShapeNode(circleOfRadius: 5)
ball.fillColor = UIColor.whiteColor()
ball.strokeColor = UIColor.whiteColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.size.width/2)
ball.physicsBody?.velocity = CGVector(dx: 200.0, dy: 200.0)
ball.position = CGPoint(x: view.bounds.width/2, y:view.bounds.height/2)
ball.physicsBody?.friction = 0.0;
ball.physicsBody?.restitution = 1.0;
ball.physicsBody?.linearDamping = 0.0;
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.applyImpulse(CGVector(dx:CGFloat(100), dy:CGFloat(100)));
ball.physicsBody?.categoryBitMask = PhysicsCategory.Ball
ball.physicsBody?.dynamic = true
ball.physicsBody?.contactTestBitMask = PhysicsCategory.Line
ball.physicsBody?.collisionBitMask = PhysicsCategory.Line
self.addChild(ball)
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
touch = touches.anyObject() as UITouch!
var linePhysicsBody = SKPhysicsBody()
linePhysicsBody.categoryBitMask = PhysicsCategory.Line
linePhysicsBody.contactTestBitMask = PhysicsCategory.Ball
linePhysicsBody.collisionBitMask = PhysicsCategory.Ball
linePhysicsBody.dynamic = false
linePhysicsBody.usesPreciseCollisionDetection = true
lineNode = SKShapeNode()
lineNode.physicsBody = linePhysicsBody
lineNode.name = "drawingLine"
lineNode.path = linePath
lineNode.lineWidth = 5.0
lineNode.strokeColor = UIColor.redColor()
lineNode.glowWidth = 1.0
self.addChild(lineNode)
}
func didBeginContact(contact: SKPhysicsContact) {
println("contact \(++tempCounter)")
println("bitmask1: \(contact.bodyA.categoryBitMask)")
println("bitmask2: \(contact.bodyA.categoryBitMask)")
}
For Problem 1:
your line physics body has a size of 0. You have to create at least a small rectangle which represents the line. Otherwize there is no collision.
For Problem 2:
func didBeginContact(contact: SKPhysicsContact) {
println("contact \(++tempCounter)")
println("bitmask1: \(contact.bodyA.categoryBitMask)")
println("bitmask2: \(contact.bodyA.categoryBitMask)")
}
There's a typo. You must use bodyB for bitmask2

Resources