Getting contact between nodes from different hierarchy - swift

How do you get contact between nodes from different hierarchy in the node tree, say a node in the scene (ball) and a child node (wall) of another node.
I have tried to build a plate with 4 walls which are child node of the plate. then I added a ball to the plate node (I also tried to let the ball be in the scene) and let it move. after defining the physics body, still I can't get a contact.
Here is my code:
class GameScene: SKScene {
let plate = SKSpriteNode ()
let wall1 = SKSpriteNode ()
let wall2 = SKSpriteNode ()
let wall3 = SKSpriteNode ()
let wall4 = SKSpriteNode ()
var ball = SKShapeNode ()
override func didMove(to view: SKView) {
self.backgroundColor = .white
plate.size = CGSize (width: self.size.width * 0.6 , height: self.size.width * 0.6)
plate.position = CGPoint (x: self.size.width / 2 , y: self.size.height / 2 )
plate.color = .orange
plate.zPosition = 0
self.addChild(plate)
plate.physicsBody = SKPhysicsBody (edgeLoopFrom: plate.frame)
plate.physicsBody?.isDynamic = false
plate.physicsBody?.friction = 0
plate.physicsBody?.restitution = 1
plate.physicsBody?.affectedByGravity = false
plate.physicsBody?.categoryBitMask = 1
plate.physicsBody?.contactTestBitMask = 0
plate.physicsBody?.collisionBitMask = 0
wall1.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall1.position = CGPoint (x: plate.size.width / 2, y: 0 )
wall1.color = .gray
wall1.zPosition = 2
plate.addChild(wall1)
wall1.physicsBody = SKPhysicsBody (edgeLoopFrom: wall1.frame)
wall2.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall2.position = CGPoint (x: 0, y: plate.size.height / 2 )
wall2.zRotation = plate.zRotation + .pi/2
wall2.color = .yellow
wall2.zPosition = 2
plate.addChild(wall2)
wall2.physicsBody = SKPhysicsBody (edgeLoopFrom: wall2.frame)
wall3.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall3.position = CGPoint (x: -plate.size.width / 2, y: 0 )
wall3.color = .cyan
wall3.zPosition = 2
plate.addChild(wall3)
wall3.physicsBody = SKPhysicsBody (edgeLoopFrom: wall3.frame)
wall4.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall4.position = CGPoint (x: 0, y: -plate.size.height / 2 )
wall4.zRotation = plate.zRotation + .pi/2
wall4.color = .magenta
wall4.zPosition = 2
plate.addChild(wall4)
wall4.physicsBody = SKPhysicsBody (edgeLoopFrom: wall4.frame)
wall4.physicsBody?.isDynamic = false
wall4.physicsBody?.friction = 0
wall4.physicsBody?.restitution = 1
wall4.physicsBody?.affectedByGravity = false
wall4.physicsBody?.categoryBitMask = 1
wall4.physicsBody?.contactTestBitMask = 0
wall4.physicsBody?.collisionBitMask = 0
ball = SKShapeNode (circleOfRadius: 10)
ball.strokeColor = .black
ball.lineWidth = 2
ball.fillColor = .blue
ball.position = CGPoint (x: self.size.width/2, y: self.size.height/2)
ball.zPosition = 2
plate.addChild(ball)
ball.physicsBody = SKPhysicsBody (circleOfRadius: 10)
ball.physicsBody?.isDynamic = true
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.categoryBitMask = 0
ball.physicsBody?.contactTestBitMask = 1
ball.physicsBody?.collisionBitMask = 1
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
plate.zRotation -= .pi/36
}}
I can't even have contact between my ball and the borders of the plate itself.
Thanks

Related

Collision detection inconsistent

My collision detection is working most of the time, but sometimes one physics body will go inside the other, when they should collide. I have a car with physics and road sides with physics. The car will blow through the road sides occasionally. I'm moving the car forward by position, however I have also tried moving forward by force and the same thing happens. Does anyone have any idea what would be causing this?
I've defined my physics categorys like so;
struct PhysicsCategory: OptionSet {
let rawValue: UInt32
init(rawValue: UInt32) { self.rawValue = rawValue }
static let CarCategory = PhysicsCategory(rawValue: 0b00001)
static let RoadSidesCategory = PhysicsCategory(rawValue: 0b00010)
static let NoCollisionsCategory = PhysicsCategory(rawValue: 0b00000)
}
Giving the car physics like so;
func giveCarPhysics(physics: Bool) {
//give physicsBody to the car and set to collide with the background
if physics {
self.size = CGSize(width: self.size.width, height: self.size.height)
self.physicsBody = SKPhysicsBody(rectangleOf: CGSize (width: self.size.width * 0.6, height: self.size.height * 0.9), center: CGPoint(x: self.anchorPoint.x, y: self.anchorPoint.y + self.size.height / 2))
self.physicsBody?.mass = vehicleMass
self.physicsBody?.usesPreciseCollisionDetection = true
self.physicsBody?.friction = 0.5
self.physicsBody?.restitution = 0.2
self.physicsBody?.affectedByGravity = false
self.physicsBody?.isDynamic = true
self.physicsBody?.linearDamping = airResistance
self.physicsBody?.categoryBitMask = PhysicsCategory.CarCategory.rawValue
self.physicsBody?.collisionBitMask = PhysicsCategory.RoadSidesCategory.rawValue
self.physicsBody?.contactTestBitMask = PhysicsCategory.RoadSidesCategory.rawValue
}
}
And then giving the road sides physics;
func createPhysicsRight() {
for (index, backgroundImageRight) in self.physicsRight.enumerated() {
let roadSpriteR = SKSpriteNode()
roadSpriteR.anchorPoint = CGPoint(x: 0.5, y: 0.5)
roadSpriteR.position = CGPoint(x: 0, y: CGFloat(self.physicsCounterR) * self.frame.size.height)
roadSpriteR.zPosition = 0
roadSpriteR.name = "RoadR \(index)"
print(roadSpriteR.name as Any)
roadSpriteR.physicsBody = SKPhysicsBody(polygonFrom: backgroundImageRight)
roadSpriteR.physicsBody?.mass = backgroundMass
roadSpriteR.physicsBody?.affectedByGravity = false
roadSpriteR.physicsBody?.isDynamic = false
roadSpriteR.physicsBody?.categoryBitMask = PhysicsCategory.RoadSidesCategory.rawValue
roadSpriteR.physicsBody?.collisionBitMask = PhysicsCategory.CarCategory.rawValue
roadSpriteR.physicsBody?.contactTestBitMask = PhysicsCategory.CarCategory.rawValue
roadSpriteRArray.append(roadSpriteR)
}
}
func createPhysicsLeft() {
for (index, backgroundImageLeft) in self.physicsLeft.enumerated() {
let roadSpriteL = SKSpriteNode()
roadSpriteL.anchorPoint = CGPoint(x: 0.5, y: 0.5)
roadSpriteL.position = CGPoint(x: 0, y: CGFloat(self.physicsCounterL) * self.frame.size.height)
roadSpriteL.zPosition = 0
roadSpriteL.name = "RoadL \(index)"
roadSpriteL.physicsBody = SKPhysicsBody(polygonFrom: backgroundImageLeft)
roadSpriteL.physicsBody?.mass = backgroundMass
roadSpriteL.physicsBody?.affectedByGravity = false
roadSpriteL.physicsBody?.isDynamic = false
roadSpriteL.physicsBody?.categoryBitMask = PhysicsCategory.RoadSidesCategory.rawValue
roadSpriteL.physicsBody?.collisionBitMask = PhysicsCategory.CarCategory.rawValue
roadSpriteL.physicsBody?.contactTestBitMask = PhysicsCategory.CarCategory.rawValue
roadSpriteLArray.append(roadSpriteL)
}
}

Swift Sprite Kit Physics Body: Ball slows down after applying an impulse

I am quite new to Swift and the Sprite Kit!
I tried to build a Pong game. Everything works well but the ball on which I am applying an impulse slows down really quickly!
I already turned off gravity, friction and damping and set the restitution of the ball and the scene to 1.
Here is my Code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
    
let player = SKShapeNode(rectOf: CGSize(width: 200, height: 30))
let enemy = SKShapeNode(rectOf: CGSize(width: 200, height: 30))
let ball = SKShapeNode(circleOfRadius: 10)
override func didMove(to view: SKView) {
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.restitution = 1
border.friction = 0
border.angularDamping = 0
border.linearDamping = 0
self.physicsBody = border
scene?.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
player.position = CGPoint(x:  0, y: -(scene?.size.height)! * 0.35)
player.fillColor = UIColor.white
player.strokeColor = UIColor.clear
self.addChild(player)
player.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 200, height: 30))
player.physicsBody?.friction = 0
player.physicsBody?.isDynamic = false
player.physicsBody?.angularDamping = 0
player.physicsBody?.linearDamping = 0
player.physicsBody?.restitution = 1
player.physicsBody?.affectedByGravity = false
enemy.position = CGPoint(x: 0, y: (scene?.size.height)! * 0.35)
enemy.fillColor = UIColor.white
enemy.strokeColor = UIColor.clear
self.addChild(enemy)
enemy.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 200, height: 30))
enemy.physicsBody?.friction = 0
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.linearDamping = 0
enemy.physicsBody?.angularDamping = 0
enemy.physicsBody?.isDynamic = false
ball.position = CGPoint(x: 0, y: 0)
ball.fillColor = UIColor.white
ball.strokeColor = UIColor.clear
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.affectedByGravity = true
ball.physicsBody = SKPhysicsBody(circleOfRadius: 10)
ball.physicsBody?.isDynamic = true
self.addChild(ball)
ball.physicsBody?.applyImpulse(CGVector(dx: -30, dy: -30))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let pos = touch.location(in: self)
player.position.x = pos.x
}
}
override func update(_ currentTime: TimeInterval) {
if ball.position.y >  0{
enemy.position.x = ball.position.x
}
}
}
Thank you already!
Be sure to always create your physics body before you change its properties
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.affectedByGravity = true
ball.physicsBody = SKPhysicsBody(circleOfRadius: 10) <----Needs to be first
ball.physicsBody?.isDynamic = true
self.addChild(ball)
ball.physicsBody?.applyImpulse(CGVector(dx: -30, dy: -30))

(edgeLoopFrom) doesn't work for a moving object inside a frame

I'm looking for a way to move a ball inside a frame. I set up a square spriteNode and make its physicsBody property to be of edge type. I then put the ball inside it.
I can't get the contact between the two and the ball runs off the frame.
class GameScene: SKScene {
let square = SKSpriteNode ()
var ball = SKShapeNode ()
override func didMove(to view: SKView) {
self.backgroundColor = .white
//building the frame
square.size = CGSize (width: self.size.width * 0.8 , height: self.size.width * 0.8)
square.position = CGPoint (x: self.size.width / 2 , y: self.size.height / 2 )
square.color = .orange
square.zPosition = 2
self.addChild(square)
square.physicsBody = SKPhysicsBody (edgeLoopFrom: square.frame)
square.physicsBody?.isDynamic = false
square.physicsBody?.allowsRotation = true
square.physicsBody?.friction = 0
square.physicsBody?.restitution = 1
square.physicsBody?.affectedByGravity = false
square.physicsBody?.categoryBitMask = 1
square.physicsBody?.contactTestBitMask = 2
square.physicsBody?.collisionBitMask = 2
//making the moving ball
ball = SKShapeNode (circleOfRadius: 10)
ball.strokeColor = .black
ball.lineWidth = 2
ball.fillColor = .blue
ball.position = CGPoint (x: self.size.width / 2 , y: self.size.height/2 )
ball.zPosition = 2
self.addChild(ball)
ball.physicsBody = SKPhysicsBody (circleOfRadius: 10)
ball.physicsBody?.isDynamic = true
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.categoryBitMask = 2
ball.physicsBody?.contactTestBitMask = 1
ball.physicsBody?.collisionBitMask = 1
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
square.zRotation -= .pi/36
}
}
I even tried to add ball to the square but nothing happened.

How to make an sprite bounce within an object?

I am trying to make a ball bounce within a circle so it cant get out of the circumference of the circle. Now the ball bounce in self.frame. How can I make the ball bounce within the circle?
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sceneBody.friction = 0
self.physicsBody = sceneBody
var ball = SKShapeNode(circleOfRadius: 9)
ball.fillColor = SKColor.whiteColor()
ball.position = view.center
ball.physicsBody = SKPhysicsBody(circleOfRadius: 9)
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.restitution = 1
ball.physicsBody?.linearDamping = 0
ball.zPosition = 3
self.addChild(ball)
circulo = SKSpriteNode(imageNamed: "circuloFondo2")
//Circle that I want the ball bounce within
circulo.size = CGSize(width:view.frame.size.width - padding , height: view.frame.size.width - padding)
circulo.color = UIColor(red: 0.15, green: 0.15, blue: 0.15, alpha: 1)
circulo.colorBlendFactor = 1
circulo.alpha = 0.35
circulo.position = view.center
self.addChild(circulo)
circulo.zPosition = 1
I've prepared a simple test project to explain how to do it:
import UIKit
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
enum CollisionTypes: UInt32 {
case Circle = 1
case Ball = 2
//case Enemy1 = 4
//case Enemy2 = 8
//case Enemy3 = 16
//case EnemyBoss = 32
}
var circulo: SKSpriteNode!
var padding: CGFloat = 40.0
var center: CGPoint!
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, -6)
self.physicsWorld.contactDelegate = self
self.center = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
// Prepare circle
circulo = SKSpriteNode(imageNamed: "circuloFondo2.png")
//Circle that I want the ball bounce within
circulo.size = CGSize(width:view.frame.size.width - padding , height: view.frame.size.width - padding)
let circlePath = UIBezierPath(arcCenter: self.center, radius: (view.frame.size.width - padding*2)/2, startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
circulo.color = UIColor(red: 0.15, green: 0.15, blue: 0.15, alpha: 1)
circulo.colorBlendFactor = 1
circulo.alpha = 1
circulo.position = self.center
circulo.zPosition = 1
// Set physics
let circuloBody = SKPhysicsBody.init(edgeLoopFromPath: circlePath.CGPath)
self.physicsBody = circuloBody
self.physicsBody!.affectedByGravity = false
self.physicsBody!.usesPreciseCollisionDetection = true
self.physicsBody!.dynamic = true
self.physicsBody!.mass = 0
self.physicsBody!.friction = 0
self.physicsBody!.linearDamping = 0
self.physicsBody!.angularDamping = 0
self.physicsBody!.restitution = 1
self.physicsBody!.categoryBitMask = CollisionTypes.Circle.rawValue
self.physicsBody!.contactTestBitMask = CollisionTypes.Ball.rawValue
//self.circulo.physicsBody!.collisionBitMask = 0
self.addChild(circulo)
//Prepare ball
let ball = SKShapeNode(circleOfRadius: 9)
ball.fillColor = SKColor.whiteColor()
ball.position = self.center
ball.zPosition = 1
// Set physics
ball.physicsBody = SKPhysicsBody(circleOfRadius: 9)
ball.physicsBody!.affectedByGravity = true
ball.physicsBody!.restitution = 0.8
ball.physicsBody!.linearDamping = 0
ball.physicsBody!.friction = 0.3
ball.physicsBody?.dynamic = true
ball.physicsBody!.mass = 0.5
ball.physicsBody!.allowsRotation = true
ball.physicsBody!.categoryBitMask = CollisionTypes.Ball.rawValue
ball.physicsBody!.contactTestBitMask = CollisionTypes.Circle.rawValue
//ball.physicsBody!.collisionBitMask = 0
self.addChild(ball)
}
func didBeginContact(contact: SKPhysicsContact) {
// elements
if (contact.bodyA.categoryBitMask == CollisionTypes.Circle.rawValue &&
contact.bodyB.categoryBitMask == CollisionTypes.Ball.rawValue) {
print("contact between circle and ball")
}
}
The circuloFondo2 image:
A demo about the project:
In this answer you can see all the code to do it, but if you want you can download the project here

Use random number (arc4random_uniform) to display random shapes

The purpose for this code is to do random number to display random shapes but the simulator displays them in the same spot which is the bottom left corner of the screen, for some reason the random for the position which is a is not working.
Code:
import UIKit
import SpriteKit
import Darwin
class StartGame: SKScene {
var scoreLabel = SKLabelNode(fontNamed: "cholkDuster")
var square = SKSpriteNode(imageNamed: "square")
var circle = SKSpriteNode(imageNamed: "circle")
var rectangle = SKSpriteNode(imageNamed: "rectangle")
var triangle = SKSpriteNode(imageNamed: "triangle")
let bg = SKSpriteNode(imageNamed: "background.png")
var score = 0
override func didMoveToView(view: SKView) {
//random number for the shapes
var timecreatShapes = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("creatShapes"), userInfo: nil, repeats: true)
//background image
bg.position = CGPoint(x: CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
bg.size.width = self.frame.size.width
bg.size.height = self.frame.size.height
self.addChild(bg)
self.scoreLabel.text = "0"
self.scoreLabel.fontSize = 42
self.scoreLabel.position = CGPoint(x: CGRectGetMidX(self.frame) , y: CGRectGetMidY(self.frame))
self.addChild(scoreLabel)
scoreLabel.zPosition = 2
self.physicsWorld.gravity = CGVectorMake(0, -0.5)
//declaring a square image
square.size = CGSize(width: 150, height: 100)
square.color = SKColor.redColor()
square.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
square.physicsBody?.dynamic = true
square.physicsBody?.allowsRotation = false
//declaring a circle image
circle.size = CGSize(width: 150, height: 100)
circle.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
circle.physicsBody?.dynamic = true
circle.physicsBody?.allowsRotation = false
//declaring a triangle
triangle.size = CGSize(width: 150, height: 100)
triangle.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
triangle.physicsBody?.dynamic = true
triangle.physicsBody?.allowsRotation = false
//declaring rectangle
rectangle.size = CGSize(width: 150, height: 100)
rectangle.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
rectangle.physicsBody?.dynamic = true
rectangle.physicsBody?.allowsRotation = false
}
func creatShapes () {
var x = Int ( arc4random_uniform(4) + 1)
var a = CGFloat ( arc4random_uniform(1000) + 1)
switch(x){
case 1:
circle.position = CGPoint (x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"circle"))
case 2:
square.position = CGPoint(x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"square"))
case 3:
rectangle.position = CGPoint(x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"rectangle"))
case 4:
triangle.position = CGPoint(x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"triangle"))
default:
break
}
println(x)
println(a)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (self.nodeAtPoint(location) == self.square || self.nodeAtPoint(location) == self.triangle || self.nodeAtPoint(location) == self.circle || self.nodeAtPoint(location) == self.rectangle){
self.score++
}
self.scoreLabel.text = String(self.score)
}
}