Swift spritekit didbegincontact being called with delay - swift

This is my GameScene code.
class GameScene: SKScene, SKPhysicsContactDelegate {
let orcWidth = UIScreen.main.bounds.width / 5
var orcCategory:UInt32 = 0x1 << 0
var knightCategory:UInt32 = 0x1 << 1
private var orc = SKSpriteNode()
private var knight = SKSpriteNode()
private var orcWalkingFrames: [SKTexture] = []
private var knightIdleFrames: [SKTexture] = []
private var knightAttackFrame: [SKTexture] = []
var background = SKSpriteNode(imageNamed: "game_background1")
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
setupbackground()
startGame()
}
func setupbackground() {
background.zPosition = 0
background.size = self.frame.size
background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
addChild(background)
}
func startGame() {
buildRandomOrcs()
buildKnight()
}
func stopGame() {
}
func buildOrc(yposition: CGFloat) {
var orcWalkFrames: [SKTexture] = []
let orcAnimatedAtlas = SKTextureAtlas(named: "OrcWalking")
let numImages = orcAnimatedAtlas.textureNames.count
for i in 0...numImages - 1 {
let orcTextureName = "0_Orc_Walking_\(i)"
orcWalkFrames.append(orcAnimatedAtlas.textureNamed(orcTextureName))
}
self.orcWalkingFrames = orcWalkFrames
let firstFrameTexture = orcWalkingFrames[0]
orc = SKSpriteNode(texture: firstFrameTexture)
orc.name = "orc"
orc.position = CGPoint(x: frame.minX-orcWidth/2, y: yposition)
self.orc.zPosition = CGFloat(self.children.count)
orc.scale(to: CGSize(width: orcWidth, height: orcWidth))
orc.physicsBody = SKPhysicsBody(rectangleOf: orc.size, center: orc.position)
orc.physicsBody?.affectedByGravity = false
orc.physicsBody?.isDynamic = true
orc.physicsBody?.categoryBitMask = orcCategory
orc.physicsBody?.contactTestBitMask = knightCategory
orc.physicsBody?.collisionBitMask = knightCategory
addChild(orc)
walkOrc()
moveOrcForward()
}
func buildKnight() {
var knightIdleFrames: [SKTexture] = []
let knightIdleAtlas = SKTextureAtlas(named: "KnightIdle")
let numImages = knightIdleAtlas.textureNames.count
for i in 0...numImages - 1 {
let orcTextureName = "_IDLE_00\(i)"
knightIdleFrames.append(knightIdleAtlas.textureNamed(orcTextureName))
}
self.knightIdleFrames = knightIdleFrames
let firstFrameTexture = knightIdleFrames[0]
knight = SKSpriteNode(texture: firstFrameTexture)
knight.name = "knight"
knight.position = CGPoint(x: frame.maxX-orcWidth/2, y: frame.midY)
self.knight.zPosition = 1
knight.scale(to: CGSize(width: -orcWidth, height: orcWidth))
knight.physicsBody = SKPhysicsBody(rectangleOf: knight.size, center: knight.position)
knight.physicsBody?.affectedByGravity = false
knight.physicsBody?.isDynamic = false
knight.physicsBody?.categoryBitMask = knightCategory
knight.physicsBody?.contactTestBitMask = orcCategory
knight.physicsBody?.collisionBitMask = orcCategory
addChild(knight)
idleKnight()
}
func idleKnight() {
knight.run(SKAction.repeatForever(SKAction.animate(with: knightIdleFrames, timePerFrame: 0.1)))
}
func walkOrc() {
orc.run(SKAction.repeatForever(SKAction.animate(with: orcWalkingFrames,timePerFrame: 0.025)))
}
func moveOrcForward() {
orc.run(SKAction.repeatForever(SKAction.moveBy(x: 55, y: 0, duration: 0.25)))
}
func buildRandomOrcs () {
let wait = SKAction.wait(forDuration: TimeInterval(makeRandomNumberBetween(min: 0, max: 0)))
let spawn = SKAction.run {
self.buildOrc(yposition: self.makeRandomCGFloatNumber())
}
let spawning = SKAction.sequence([spawn,wait])
self.run(SKAction.repeat(spawning, count: 10))
}
func makeRandomCGFloatNumber() -> CGFloat {
let randomNumber = arc4random_uniform(UInt32((frame.maxY-orcWidth/2) - (frame.minY+orcWidth/2))) + UInt32(frame.minY+orcWidth/2)
return CGFloat(randomNumber)
}
func makeRandomNumberBetween (min: Int, max: Int) -> Int{
let randomNumber = arc4random_uniform(UInt32(max - min)) + UInt32(min)
return Int(randomNumber)
}
func didBegin(_ contact: SKPhysicsContact) {
let collision:UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == orcCategory | knightCategory {
self.scene?.view?.isPaused = true
print("COLLIDED")
}
}
}
The problem is that the scene pauses almost 2-3 seconds after the collision.
I changed the position of knight and delay time changed.
For example, if I set position to frame.minX+orcWidth/2 there is no delay.
What is wrong with my code?

Your problem isn't things are being delayed, your problem is your bounding box is not where you think it is
use view.showPhysics = true to determine where your boxes are
once you realize they are in the wrong spots, go to this line
knight.physicsBody = SKPhysicsBody(rectangleOf: knight.size, center: knight.position)
and fix it
knight.physicsBody = SKPhysicsBody(rectangleOf: knight.size)
Do so for the rest of your bodies

My guess is that manipulations of SKView properties must happen on main thread, i.e.
DispatchQueue.main.async { [unowned self] in
self.scene?.view?.isPaused = true
print("COLLIDED")
}

Related

How to determine the most accurate collision of two circles?

I am creating a new game application in code!
I created some bubbles using spriteKit. I used the texture for this.
They randomly fly around the playing field. When they intersect with each other, they must collide and change their path.
The first problem: of course, the image of a bubble in the shape of a circle. But in a collision, the bubbles touch each other’s faces not as a circle, but as a square
The second problem: they cling to each other, I think this is the problem of the first problem!
If you known how to fix this is problem please help my, because I can't find solve that problem.
I think this problem can be solved with a Mask, but I don’t know how. Thanks a lot)
this is my bubbleClass:
class SKButton: SKSpriteNode{
private var buttonTexture:SKTexture
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
self.buttonTexture = texture!
super.init(texture:buttonTexture , color: .black, size: buttonTexture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}}
This is ViewController:
class GameViewController: UIViewController {
#IBOutlet weak var mySKView: SKView!
override func viewDidLoad() {
super.viewDidLoad()
if let view = mySKView {
let scene = GameScene(size: CGSize(width: 2048, height: 1536))
scene.scaleMode = .aspectFill
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}}
this is GameScene:
class GameScene: SKScene {
private var arrayBubbles = [SKButton?]()
private var arrayVelosity = [CGPoint]()
private var bubbleMovePointsPerSecX = [CGFloat]()
private var bubbleMovePointsPerSecY = [CGFloat]()
private var currentVelosity:CGPoint!
private var lastUpdateTime: TimeInterval = 0
private var dt: TimeInterval = 0
private let min = 0
private let max = 3
private let bubbleTexture = SKTexture(imageNamed: "bubble")
private let playableRect: CGRect!
lazy var background: SKSpriteNode = {
let back = SKSpriteNode(imageNamed: "back")
back.position = CGPoint(x: self.size.width/2, y: size.height/2)
back.anchorPoint = CGPoint(x: 0.5, y: 0.5)
back.zPosition = -1
return back
}()
lazy var bubble1: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/2, y: size.height - (size.height/3))
button.setScale(0)
button.zPosition = 1
button.name = "bubble1"
return button
}()
lazy var bubble2: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/3, y: size.height/2)
button.setScale(0)
button.zPosition = 1
button.name = "bubble2"
return button
}()
lazy var bubble3: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width - (size.width/3), y: size.height/2)
button.setScale(0)
button.zPosition = 1
button.name = "bubble3"
return button
}()
override init(size: CGSize) {
let playableHeight = size.width/(16.0/9.0)
let playableMargin = (size.height - playableHeight)/2
playableRect = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight)
super.init(size:size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(to view: SKView) {
addChilds()
appendBubblesToArray()
setRandomBubbleSpead()
appendVelosityToArray()
showApearBubble()
}
private func addChilds(){
addChild(background)
addChild(bubble1)
addChild(bubble2)
addChild(bubble3)
}
private func appendBubblesToArray(){
arrayBubbles.append(bubble1)
arrayBubbles.append(bubble2)
arrayBubbles.append(bubble3)
}
private func setRandomBubbleSpead(){
for _ in min..<arrayBubbles.count{
let randomSpeadX = CGFloat.random(in: -200..<200)/CGFloat(Double.pi)
bubbleMovePointsPerSecX.append(randomSpeadX)
let randomSpeadY = CGFloat.random(in: -200..<200)/CGFloat(Double.pi)
bubbleMovePointsPerSecY.append(randomSpeadY)
}
}
private func appendVelosityToArray(){
for index in min..<arrayBubbles.count{
currentVelosity = CGPoint(x: bubbleMovePointsPerSecX[index], y: bubbleMovePointsPerSecY[index])
arrayVelosity.append(currentVelosity)
}
}
private func showApearBubble(){
let apearBubble = SKAction.scale(to: 1, duration: 0.5)
let action = [apearBubble]
bubble1.run(SKAction.sequence(action))
bubble2.run(SKAction.sequence(action))
bubble3.run(SKAction.sequence(action))
}
override func didEvaluateActions() {
checkColisions()
}
private func checkColisions(){
enumerateChildNodes(withName: "//*") { [weak self] node, _ in
guard let self = self else { return }
switch node.name{
case "bubble1":
self.checkBubbleIntersection(self.bubble1)
break
case "bubble2":
self.checkBubbleIntersection(self.bubble2)
break
case "bubble3":
self.checkBubbleIntersection(self.bubble3)
break
default:
return
}
}
}
private func hitBubble(_ index: Int){
arrayVelosity[index].y = -arrayVelosity[index].y
arrayVelosity[index].x = -arrayVelosity[index].x
}
private func checkBubbleIntersection(_ firstBubble: SKButton){
for bubble in arrayBubbles {
if firstBubble.name != bubble!.name{
if firstBubble.frame.insetBy(dx: 80,dy: 80).intersects(bubble!.frame){
guard let indexBubble = self.arrayBubbles.firstIndex(of: bubble) else { return }
self.hitBubble(indexBubble)
}
}
}
}
override func update(_ currentTime: TimeInterval) {
if lastUpdateTime > 0{
dt = currentTime - lastUpdateTime
} else{
dt = 0
}
lastUpdateTime = currentTime
for bubble in arrayBubbles{
if bubble != nil {
guard let index = arrayBubbles.firstIndex(of: bubble) else { return }
moveSprite(sprite: arrayBubbles[index]!, velosity: arrayVelosity[index])
boundsCheckBubbles(sprite: arrayBubbles[index]!, index: index)
}
}
}
private func moveSprite(sprite: SKButton, velosity: CGPoint){
let amountToMove = CGPoint(x: velosity.x * CGFloat(dt), y: velosity.y * CGFloat(dt))
sprite.position = CGPoint(x: sprite.position.x + amountToMove.x,
y: sprite.position.y + amountToMove.y)
}
private func boundsCheckBubbles(sprite: SKButton, index: Int){
let bottomLeft = CGPoint(x: 80, y: playableRect.minY)
let topRight = CGPoint(x: size.width-80, y: playableRect.maxY)
if sprite.position.x <= bottomLeft.x{
sprite.position.x = bottomLeft.x
arrayVelosity[index].x = -arrayVelosity[index].x
}
if sprite.position.x >= topRight.x{
sprite.position.x = topRight.x
arrayVelosity[index].x = -arrayVelosity[index].x
}
if sprite.position.y <= bottomLeft.y{
sprite.position.y = bottomLeft.y
arrayVelosity[index].y = -arrayVelosity[index].y
}
if sprite.position.y >= topRight.y{
sprite.position.y = topRight.y
arrayVelosity[index].y = -arrayVelosity[index].y
}
}}
I myself was able to solve this problem.
But I found another way to solve this problem. I use a bitmask and collision!
class GameScene: SKScene, SKPhysicsContactDelegate {
private var arrayBubbles = [SKButton?]()
private var bubbleTexture = SKTexture(image: #imageLiteral(resourceName: "Oval"))
private let playableRect: CGRect!
let bubble1Category:UInt32 = 0x1 << 0;
let bubble2Category:UInt32 = 0x1 << 1;
let bubble3Category:UInt32 = 0x1 << 2;
let borderCategory:UInt32 = 0x1 << 10
var borderBody: SKPhysicsBody!
lazy var bubble1: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/2, y: size.height - (size.height/3))
button.zPosition = 1
button.name = "bubble1"
button.physicsBody = SKPhysicsBody(circleOfRadius: bubbleTexture.size().width/2)
button.physicsBody?.categoryBitMask = bubble1Category
button.physicsBody?.contactTestBitMask = bubble2Category | bubble3Category | borderCategory
button.physicsBody!.collisionBitMask = bubble2Category | bubble3Category | borderCategory
button.physicsBody?.affectedByGravity = false
button.physicsBody?.linearDamping = 0.0
button.physicsBody?.restitution = 0.0
return button
}()
lazy var bubble2: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/3, y: size.height/2)
button.zPosition = 1
button.name = "bubble2"
button.physicsBody = SKPhysicsBody(circleOfRadius: bubbleTexture.size().width/2)
button.physicsBody?.categoryBitMask = bubble2Category
button.physicsBody?.contactTestBitMask = bubble1Category | bubble3Category | borderCategory
button.physicsBody!.collisionBitMask = bubble1Category | bubble3Category | borderCategory
button.physicsBody?.affectedByGravity = false
button.physicsBody?.linearDamping = 0.0
button.physicsBody?.restitution = 0.0
return button
}()
lazy var bubble3: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear , size: bubbleTexture.size())
button.position = CGPoint(x: size.width - (size.width/3), y: size.height/2)
button.zPosition = 1
button.name = "bubble3"
button.physicsBody = SKPhysicsBody(circleOfRadius: bubbleTexture.size().width/2)
button.physicsBody?.categoryBitMask = bubble3Category
button.physicsBody?.contactTestBitMask = bubble1Category | bubble2Category | borderCategory
button.physicsBody!.collisionBitMask = bubble1Category | bubble2Category | borderCategory
button.physicsBody?.affectedByGravity = false
button.physicsBody?.linearDamping = 0.0
button.physicsBody?.restitution = 0.0
return button
}()
override init(size: CGSize) {
let playableHeight = size.width/(16.0/9.0)
let playableMargin = (size.height - playableHeight)/2
playableRect = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight)
super.init(size:size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
// Set border property
setBorderBody()
addChilds()
appendBubblesToArray()
showApearBubble()
setFirstBubbleVelosity()
}
func setFirstBubbleVelosity(){
for bubble in arrayBubbles{
bubble!.physicsBody?.velocity = calculateRandomBubbleVelosity()
}
}
func setBorderBody(){
borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.categoryBitMask = borderCategory
borderBody.friction = 0.0
self.physicsBody = borderBody
}
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case bubble1Category | bubble2Category:
bubble1.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble1Category | bubble3Category:
bubble1.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble1Category | borderCategory:
bubble1.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble2Category | bubble1Category:
bubble2.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble2Category | bubble3Category:
bubble2.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble2Category | borderCategory:
bubble2.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble3Category | bubble1Category:
bubble3.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble3Category | bubble2Category:
bubble3.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble3Category | borderCategory:
bubble3.physicsBody?.velocity = calculateRandomBubbleVelosity()
default: print("Unknown contact detected")
}
}
private func addChilds(){
addChild(bubble1)
addChild(bubble2)
addChild(bubble3)
}
private func appendBubblesToArray(){
arrayBubbles.append(bubble1)
arrayBubbles.append(bubble2)
arrayBubbles.append(bubble3)
}
private func calculateRandomBubbleVelosity() -> CGVector{
let randomVelosityByX = CGFloat.random(in: -200..<200)
let randomVelosityByY = CGFloat.random(in: -200..<200)
let randomVelosity = CGVector(dx: randomVelosityByX * cos(100.0), dy: randomVelosityByY * sin(100.0))
return randomVelosity
}
private func showApearBubble(){
bubble1.setScale(1.5)
bubble2.setScale(1.5)
bubble3.setScale(1.5)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
enumerateChildNodes(withName: "bubble1") { node, _ in
let bubble = node as! SKButton
if bubble.contains(touch.location(in: self)){
//Some your logic
}
}
}
}}

SKSpriteNode will not appear when its physics body is dynamic

The SKSprite will only appear on the scene if .isDynamic = false otherwise it will not show up. Although it does not show up, it seems to be contacting with the other nodes in the scene.
I have tried playing around with the x and y values of the "lemming" node but this still results in it not appearing.
The Question:
What do I need to add or remove in order to make the "lemming" node be affected
by gravity, appear in the scene, and contact correctly with the "cliff" node?
In the GameScene.swift file:
//MARK: - Nodes
var background = SKSpriteNode()
var cliff = SKTexture()
var lemming = SKSpriteNode()
//MARK: -Catergories
let lemmingCatergory : UInt32 = 0x1 << 1
let cliffCatergory : UInt32 = 0x1 << 2
//MARK: - Start of game functions
override func didMove(to view: SKView) {
backgroundColor = UIColor.green
scaleMode = .aspectFit
size = UIScreen.main.bounds.size
physicsWorld.gravity = CGVector(dx: 0, dy: -9.81)
physicsWorld.contactDelegate = self
addBackground()
addCliff()
addLemming()
view.showsPhysics = true
}
//MARK: -Node customization
private func addBackground() {
background = SKSpriteNode(imageNamed: "background")
background.name = "Background"
background.position = CGPoint(x: frame.midX, y: frame.midY)
background.size = UIScreen.main.bounds.size
background.physicsBody = SKPhysicsBody(rectangleOf: background.frame.size)
background.physicsBody?.isDynamic = false
background.physicsBody?.affectedByGravity = false
addChild(background)
}
private func addCliff() {
var cliffNode : SKSpriteNode!
cliff = SKTexture(imageNamed: "cliff")
cliffNode = SKSpriteNode(texture: cliff)
cliffNode.name = "Cliff"
cliffNode.position = CGPoint(x: frame.midX, y: frame.midY)
cliffNode.size = UIScreen.main.bounds.size
cliffNode.physicsBody = SKPhysicsBody(texture: cliff, size: cliff.size())
cliffNode.physicsBody!.categoryBitMask = cliffCatergory
cliffNode.physicsBody!.collisionBitMask = lemmingCatergory
cliffNode.physicsBody!.contactTestBitMask = lemmingCatergory
cliffNode.physicsBody?.isDynamic = true
cliffNode.physicsBody?.affectedByGravity = false
cliffNode.physicsBody?.pinned = true
cliffNode.physicsBody?.allowsRotation = false
addChild(cliffNode)
}
private func addLemming() {
lemming = SKSpriteNode(imageNamed: "lemming")
lemming.size = CGSize(width: lemming.size.width / 15, height: lemming.size.height / 15)
lemming.position = CGPoint(x: 0.5, y: 0)
lemming.physicsBody = SKPhysicsBody(rectangleOf: lemming.frame.size)
lemming.physicsBody!.categoryBitMask = lemmingCatergory
lemming.physicsBody!.collisionBitMask = cliffCatergory
lemming.physicsBody!.contactTestBitMask = cliffCatergory
lemming.physicsBody?.isDynamic = true
lemming.physicsBody?.affectedByGravity = true
addChild(lemming)
}
func didBegin(_ contact: SKPhysicsContact) {
print("Body A: \(String(describing: contact.bodyA.node!.name))")
print("Body B: \(String(describing: contact.bodyB.node!.name))")
}

Child nodes inside SKNode disappears with fast shaking device with CMMotionManager applied to move balls

I've create a SKNode with physicsBody edgeFromLoop circular inside which i've added 8 small circular nodes. and 8 circular nodes are moving with CMMotionManager inside Parent circular node. with fast motion shake some balls disappears from screen. SKScene Class given below
Balls only disappears when anyone shake mobile hard randomly.
There are 8 balls initially but after some hard shake reduced.
class GameScene: SKScene {
let motionManager = CMMotionManager()
var Circle = SKShapeNode(circleOfRadius: 108)
var sound = SKAction.playSoundFileNamed(getRandomSound(), waitForCompletion: false)
var collisionBitmasks: [UInt32] = [UInt32]()
override func didMove(to view: SKView) {
Circle.fillTexture = SKTexture.init(image: UIImage.init(named: "img_Ball") ?? UIImage())
Circle.position = CGPoint.init(x: 0, y: 0)
Circle.name = "defaultCircle"
Circle.lineWidth = 0.0
Circle.fillColor = SKColor.lightGray.withAlphaComponent(0.8)
Circle.physicsBody = SKPhysicsBody.init(edgeLoopFrom: UIBezierPath.init(ovalIn: CGRect.init(x: Circle.frame.minX + 12,
y: Circle.frame.minY + 12,
width: Circle.frame.width - 24,
height: Circle.frame.height - 24)).cgPath)
Circle.physicsBody?.isDynamic = true
Circle.physicsBody?.affectedByGravity = false
Circle.physicsBody?.allowsRotation = false
addChild(Circle)
self.physicsWorld.contactDelegate = self
startAcceleroMeter()
for index in 1...8 {
addBalls(index)
}
}
func stop() {
motionManager.stopAccelerometerUpdates()
Circle.children.forEach { (ball) in
ball.physicsBody?.isDynamic = false
ball.physicsBody?.affectedByGravity = false
}
}
func startAcceleroMeter() {
Circle.children.forEach { (ball) in
ball.physicsBody?.isDynamic = true
ball.physicsBody?.affectedByGravity = true
}
motionManager.startAccelerometerUpdates()
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdates(to: .main) { (motionData, error) in
let gravity = CGVector.init(dx: (motionData?.acceleration.x ?? 0.0) * 20, dy: (motionData?.acceleration.y ?? 0.0) * 20)
print(gravity)
self.physicsWorld.gravity = gravity
}
}
func stopPlaying() -> [String] {
var dictionary = [String]()
for (_, ball) in (Circle.children).enumerated() {
dictionary.append("\(ball.position.x), \(ball.position.y), 0.0")
}
return dictionary
}
func addBalls(_ ballNo: Int) {
let ball = SKShapeNode.init(circleOfRadius: 12)
ball.name = "ball\(ballNo)"
ball.fillTexture = SKTexture.init(linearGradientWithAngle: CGFloat.pi, colors: [BallColors(rawValue: ballNo)?.toUIColor(false) ?? UIColor(), BallColors(rawValue: ballNo)?.toUIColor(true) ?? UIColor()], locations: [0, 1], size: ball.frame.size)
ball.fillColor = UIColor.white
ball.physicsBody = SKPhysicsBody.init(circleOfRadius: 12)
ball.physicsBody?.isDynamic = true
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.allowsRotation = true
ball.physicsBody?.friction = 0.2
ball.physicsBody?.restitution = 0.9
ball.physicsBody?.linearDamping = 0.1
ball.physicsBody?.angularDamping = 0.1
ball.physicsBody?.mass = 0.349065870046616
ball.physicsBody?.usesPreciseCollisionDetection = true
ball.position = CGPoint(x: Circle.frame.midX - CGFloat(ballNo), y: Circle.frame.midY - CGFloat(ballNo))
ball.physicsBody?.fieldBitMask = 1
ball.physicsBody?.categoryBitMask = UInt32.init(ballNo + 100)
let collisionBitMask = UInt32.init(ballNo + 20) | UInt32.init(ballNo + 100)
collisionBitmasks.append(collisionBitMask)
ball.physicsBody?.collisionBitMask = collisionBitMask //To be different for each
ball.physicsBody?.contactTestBitMask = UInt32.init(ballNo + 20)
Circle.addChild(ball)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
if collisionBitmasks.contains(contact.bodyA.collisionBitMask) && collisionBitmasks.contains(contact.bodyB.collisionBitMask) {
run(sound)
}
}
}
You need a hidden magic here. Each small ball adds a constraint that can prevent any accident from high speeds:
ball.constraints = [SKConstraint.distance(SKRange(upperLimit: 108 - 12), to: Circle)]

sprite kit collision not working

This is a game I have been working on. In summary there is a moving block named enemy and I want it to collide with an invisible static block called invisibleGround2. I have it printing hit when they supposedly collide but they are not colliding. Ive read every swift collision documentation by apply and others out there and I dont know whats wrong. Any help would be much appreciated!
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var orb = SKSpriteNode(imageNamed: "orb")
var scrollingG:scrollingGround?
var invisibleGround = SKSpriteNode(imageNamed: "invisible")
var invisibleGround2 = SKSpriteNode(imageNamed: "invisible")
let enemies = [SKSpriteNode(imageNamed: "blueE.png"), SKSpriteNode(imageNamed: "redE.png")]
let enemyCategory : UInt32 = 1
let jumperCategory : UInt32 = 1
let rotateDuration = 2
let enemyMoveSpeed = 5
let groundScrollingSpeed = 5
func createOrb(){
let orbConst = frame.size.width/2
let xConstraint = SKConstraint.positionX(SKRange(constantValue: orbConst))
orb.position = CGPoint(x: frame.size.width/2, y: 480)
orb.physicsBody = SKPhysicsBody(texture: orb.texture!, size: orb.texture!.size())
orb.constraints = [xConstraint]
self.addChild(orb)
}
func createScrollingGround () {
scrollingG = scrollingGround.scrollingNodeWithImage(imageName: "ground", containerWidth: self.size.width)
scrollingG?.scrollingSpeed = CGFloat(groundScrollingSpeed)
scrollingG?.anchorPoint = .zero
self.addChild(scrollingG!)
}
func createGround(){
invisibleGround2.size.width = 1
invisibleGround2.size.height = 1
invisibleGround2.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:1, height: 100))
invisibleGround2.physicsBody?.isDynamic = false
invisibleGround2.position = CGPoint(x: 530, y: 191)
invisibleGround2.physicsBody?.categoryBitMask = jumperCategory
invisibleGround2.physicsBody?.collisionBitMask = enemyCategory
invisibleGround2.physicsBody?.contactTestBitMask = enemyCategory
invisibleGround2.name = "jumper"
invisibleGround.position = CGPoint(x: 0, y: 190)
invisibleGround.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.size.width * 3, height: 10))
invisibleGround.physicsBody?.isDynamic = false
self.addChild(invisibleGround2)
self.addChild(invisibleGround)
}
func getRandomEnemy(fromArray array:[SKSpriteNode])->SKSpriteNode{
return array[Int(arc4random_uniform(UInt32(array.count)))]
}
func spawnEnemy() {
if let enemy = getRandomEnemy(fromArray: enemies).copy() as? SKSpriteNode {
enemy.position = CGPoint(x: frame.size.width + frame.size.width/3, y: 440)
enemy.physicsBody = SKPhysicsBody(texture: enemy.texture!, size: enemy.texture!.size())
if enemy.size.width < 95 {
enemy.physicsBody?.categoryBitMask = enemyCategory
enemy.physicsBody?.collisionBitMask = jumperCategory
enemy.physicsBody?.contactTestBitMask = jumperCategory
}
enemy.name = "enemy"
self.addChild(enemy)
let moveLeft = SKAction.moveBy(x: -1500, y: 0, duration: TimeInterval(enemyMoveSpeed))
enemy.run(moveLeft)
}
}
func addEnemies () {
self.run(SKAction.repeatForever(SKAction.sequence([SKAction.run {
self.spawnEnemy()
}, SKAction.wait(forDuration: 4)])))
}
func jump() {
orb.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 335))
}
func rotate() {
let rotate = SKAction.rotate(byAngle: CGFloat(M_PI * -2.55), duration: TimeInterval(rotateDuration))
let repeatAction = SKAction.repeatForever(rotate)
orb.run(repeatAction)
}
override func didMove(to view: SKView) {
createScrollingGround()
createOrb()
createGround()
rotate()
addEnemies()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
jump()
}
override func update(_ currentTime: TimeInterval) {
if self.scrollingG != nil {
scrollingG?.update(currentTime: currentTime)
func didBegin(_ contact: SKPhysicsContact) {
let bodyA = contact.bodyA.categoryBitMask
let bodyB = contact.bodyB.categoryBitMask
if bodyA == jumperCategory && bodyB == enemyCategory {
print("hit")
} else if bodyA == enemyCategory && bodyB == jumperCategory {
print("hit 2")
}
}
}
}
}
Not an swer, but some things to check:
Have you set the scene’s physicsworld delegate property set to
self?
Move didBegin outside of update
Is the 'enemy' sprite's width < 95?
Add a print("Contact detected") as the first line of your relocated didBegin so you at least know that some contact has been detected.
See my answer here https://stackoverflow.com/a/43605825/1430420 for a simple SK collision demo which might help - I think it needs updates to Swift 4 which I'll try and do.

didBeginContact not working in Swift 2 + SpriteKit

I'm working on a game, and I'm using spritekit and Swift.
This is my code:
import SpriteKit
struct collision {
static let arrow:UInt32 = 0x1 << 1
static let runner:UInt32 = 0x1 << 2
static let target:UInt32 = 0x1 << 3
static let targetCenter:UInt32 = 0x1 << 4
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var person = SKSpriteNode()
var box = SKSpriteNode()
var screenSize:CGSize!
var gameScreenSize:CGSize!
var gameStarted:Bool = false
var moveAndRemove = SKAction()
var boxVelocity:NSTimeInterval = 5.5
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, -1.0)
self.physicsWorld.contactDelegate = self
screenSize = self.frame.size
gameScreenSize = view.frame.size
createPerson()
}
func createPerson() -> Void {
person.texture = SKTexture(imageNamed:"person")
person.setScale(1.0)
person.size = CGSize(width: 80, height: 80)
person.position = CGPoint(x: screenSize.width / 2, y: 150)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.dynamic = false
self.addChild(person)
}
func createTarget() -> Void {
box = SKSpriteNode()
box.size = CGSize(width: 70, height: 100)
box.setScale(1.0)
box.position = CGPoint(x: (screenSize.width / 3) * 2, y: screenSize.height + box.size.height)
box.texture = SKTexture(imageNamed: "box")
box.physicsBody? = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
box.physicsBody?.affectedByGravity = false
box.physicsBody?.dynamic = true
box.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(box)
let distance = CGFloat(self.frame.height - box.frame.height)
let moveTargets = SKAction.moveToY(-distance, duration: boxVelocity)
let removeTargets = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([moveTargets,removeTargets])
box.runAction(moveAndRemove)
}
func createBall() ->Void {
let ball = SKSpriteNode()
ball.size = CGSize(width: 20, height: 22)
ball.zPosition = 5
let moveToXY = CGPoint(x: self.size.width, y: self.size.height)
ball.texture = SKTexture(imageNamed: "ball")
ball.position = CGPointMake(person.position.x + ball.size.width, person.position.y + ball.size.height)
ball.physicsBody? = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.dynamic = true
ball.physicsBody?.usesPreciseCollisionDetection = true
let action = SKAction.moveTo(moveToXY, duration: 1.5)
let delay = SKAction.waitForDuration(1.5)
ball.runAction(SKAction.sequence([action,delay]))
self.addChild(ball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if gameStarted == false {
gameStarted = true
let spawn = SKAction.runBlock { () in
self.createTarget()
}
let delay = SKAction.waitForDuration(1.5)
let spawnDelay = SKAction.sequence([spawn, delay])
let spanDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spanDelayForever)
} else {
createBall()
boxVelocity -= 0.1
}
}
func didBeginContact(contact: SKPhysicsContact) {
print("Detect")
}
func didEndContact(contact: SKPhysicsContact) {
print("end detect")
}
override func update(currentTime: CFTimeInterval) {
}
}
But when I run the game, the collision between objects does not. I'm trying to solve a while, but found nothing. Can someone help me?
Project files.
Try with these to modifications:
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
and
ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.contactTestBitMask = collision.target
Note the absence of "?" while you init the physicsBody and the new contactTestBitMask