spritekit collision detection inconsistency (failing to get position of both nodes) - swift

Within didBeginContact function I'm successfully capturing the collision between a horizontal grid line and a circle.
At the collision I can successfully get the position of the circle(head).
if let headBody = contact.bodyB.node as? SnakeBodyUnit {
print("head position ", headBody.position)
}
This successfully prints out:
head position (87.536979675293, 267.116882324219)
Now if I try to examine the horizontal line:
if let tripRow = contact.bodyA.node as? HeadTripper {
print("row position ", tripRow.position)
}
I get the following error:
po tripRow
error: :1:1: error: use of unresolved identifier 'tripRow'
tripRow
Here is the condition where this detection fires:
if (contact.bodyA.categoryBitMask == ColliderType.TripRow && contact.bodyB.categoryBitMask == ColliderType.Head) {
//contact successfully fires
}
The HeadTripper class and the SnakeBodyUnit class are both SKNode classes.
Here is the SnakeBodyUnit class:
import SpriteKit
class SnakeBodyUnit : SKNode {
var bodyDir: Direction?
var id = -1
var staticIDref = -1
var partX:CGFloat = -1
var partY:CGFloat = -1
var bodyT = -1
var staticTurn: TurnCrumb?
var turnRequested = 0
var requestedTurn: Direction?
var unitHolder: SKSpriteNode?
init(size: CGSize, bodyType: Int) {
super.init()
bodyT = bodyType
let reducedSize = size.width
unitHolder = SKSpriteNode()
unitHolder!.size = CGSize(width: (size.width), height: (size.height))
if (bodyT == 0) {
//head
unitHolder!.color = UIColor.clearColor()
//unitHolder!.position = CGPoint(x:xPos, y:yPos);
let shape = SKShapeNode(circleOfRadius: reducedSize/2)
shape.fillColor = UIColor.whiteColor()
shape.strokeColor = UIColor.whiteColor()
shape.position = CGPoint(x: reducedSize/2, y: reducedSize/2)
unitHolder!.addChild(shape)
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize, reducedSize), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Head
self.physicsBody?.dynamic = false
self.physicsBody?.contactTestBitMask = ColliderType.TripRow | ColliderType.TripColumn | ColliderType.Food | ColliderType.Body | ColliderType.WallLeft | ColliderType.WallRight | ColliderType.WallBottom | ColliderType.WallTop | ColliderType.Canvas
} else if (bodyT == 1) {
//body part
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize, reducedSize), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Body
self.physicsBody!.collisionBitMask = 0
self.physicsBody?.dynamic = true
self.physicsBody?.contactTestBitMask = ColliderType.WallLeft | ColliderType.WallRight | ColliderType.WallBottom | ColliderType.WallTop | ColliderType.BodyStatic
} else if (bodyT == 2) {
//static corner
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize/2, reducedSize/2), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.BodyStatic
self.physicsBody!.collisionBitMask = 0
self.physicsBody?.dynamic = true
}
unitHolder!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(unitHolder!)
}
func updateColor(color: UIColor) {
unitHolder!.color = color
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//fatalError("init coder not implemented")
}
}
Here is the HeadTripper class:
import SpriteKit
class HeadTripper : SKNode {
var id = -1
var rowTripLine: SKSpriteNode?
var colTripLine: SKSpriteNode?
init(size: CGSize, rowOrCol: Int) {
//init(size: CGSize, xPos: Double, yPos: Double, rowOrCol: Int) {
super.init()
//row 0
//col 1
if (rowOrCol == 0) {
//row
rowTripLine = SKSpriteNode()
rowTripLine!.color = UIColor.redColor()
//rowTripLine!.position = CGPoint(x: 0, y: yPos);
rowTripLine!.size = CGSize(width: Int(size.width), height: 1)
//borderLeft!.physicsBody = SKPhysicsBody(rectangleOfSize: size, center: CGPointMake(reducedSize/2, reducedSize/2))
rowTripLine!.physicsBody = SKPhysicsBody(rectangleOfSize: rowTripLine!.size, center: CGPointMake(rowTripLine!.size.width/2, rowTripLine!.size.height/2))
rowTripLine!.physicsBody?.affectedByGravity = false
rowTripLine!.physicsBody?.categoryBitMask = ColliderType.TripRow
rowTripLine!.physicsBody!.collisionBitMask = 0
rowTripLine!.physicsBody?.dynamic = true
rowTripLine!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(rowTripLine!)
} else {
//col
colTripLine = SKSpriteNode()
colTripLine!.color = UIColor.redColor()
//colTripLine!.position = CGPoint(x: xPos, y: 0);
colTripLine!.size = CGSize(width: 1, height: Int(size.height))
//borderLeft!.physicsBody = SKPhysicsBody(rectangleOfSize: size, center: CGPointMake(reducedSize/2, reducedSize/2))
colTripLine!.physicsBody = SKPhysicsBody(rectangleOfSize: colTripLine!.size, center: CGPointMake(colTripLine!.size.width/2, colTripLine!.size.height/2))
colTripLine!.physicsBody?.affectedByGravity = false
colTripLine!.physicsBody?.categoryBitMask = ColliderType.TripColumn
colTripLine!.physicsBody!.collisionBitMask = 0
colTripLine!.physicsBody?.dynamic = true
colTripLine!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(colTripLine!)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init coder not implemented")
}
}
I generate the HeadTripper lines like this in the main scene:
for var i = 0; i < rowCount; i++ {
let rowTrip = HeadTripper(size: size, rowOrCol: 0)
rowTrip.id = i
rowTrip.position.x = 0
rowTrip.position.y = CGFloat(yVal)
self.addChild(rowTrip)
rowLines.append(CGFloat(rowTrip.position.y))
CGPathMoveToPoint(linePath, nil, 0, CGFloat(yVal))
CGPathAddLineToPoint(linePath, nil, CGFloat(theGrid.screenWidth), CGFloat(yVal))
yVal += (theGrid.snakeHigh + yValPad)
}
Here is how the collider types are setup:
struct ColliderType {
static let Head: UInt32 = 0
static let Food: UInt32 = 0b1
static let Body: UInt32 = 0b10
static let BodyStatic: UInt32 = 0b100
static let WallLeft: UInt32 = 0b1000
static let WallBottom: UInt32 = 0b10000
static let WallRight: UInt32 = 0b100000
static let WallTop: UInt32 = 0b1000000
static let None: UInt32 = 0b10000000
static let Canvas: UInt32 = 0b100000000
static let TripColumn: UInt32 = 0b1000000000
static let TripRow: UInt32 = 0b10000000000
}
The tripRow let setup happens on line 310. On line 309 I can see the contact.bodyA:
0x000000012758db20
{
NSObject = {
isa = 0x000000012758db20
}
_representedObject = 0x00000001275765a0
_field = 0x0000000000000000
_dynamicType = 2
_world = 0x0000000127761b90
_joints = 0x0000000127576270 "0 values"
_inUse = true
_shapeType = 2
_radius = 0
_edgeRadius = 0.0010000000474974513
_mask = 0x0000000000000000
_isPinned = false
_allowsRotation = true
_postStepBlock = 0x000000019e666c70
}
I can also see the contact.bodyA.node:
0x00000001275765a0
{
UIKit.UIResponder = {...}
}
Line 311 never gets called because its in the if condition that isn't passing.
At line 313 I attempt to look at tripRow and get this:
(lldb) po tripRow
error: <EXPR>:1:1: error: use of unresolved identifier 'tripRow'
tripRow
It sounds like issue is related to the children within HeadTripper. I have named the children "row" and "col" respectively.
print("body A name ", contact.bodyA.node?.name)
let tripRow = contact.bodyA.node as? SKSpriteNode
This gives a name of "row". Also tripRow now prints out as:
(lldb) po tripRow
0x0000000155e737c0
{
SpriteKit.SKNode = {...}
}
I think this is very close. The problem still exists that the position is not accurate. The y value should be a number like 54 and it is zero:
(lldb) po tripRow?.position
(x = 0, y = 0)

This was resolved by having just 1 SKSpriteNode referenced in the HeadTripper SKNode class instead of 2.

Related

SpriteKit - Collision Off after HIT

I am writing a spritekit game and I have a problem, I would like the framework not to detect collisions after it is detected. For example, the player is dealt damage and becomes insensitive to attacks, e.g. for 20 seconds. Now when a collision is detected, it is detected all the time (e.g. 50 times)
Is there a way to temporarily disable collisions ??
Player code :
func createPlayer() {
var walkFrames: [SKTexture] = []
var walkFrames2: [SKTexture] = []
var walkFrames3: [SKTexture] = []
var walkFrames4: [SKTexture] = []
// let spaceShipTexture = SKTexture(imageNamed: "p2.png")
let playerAnimatedAtlas = SKTextureAtlas(named: "walk")
let playerAnimatedAtlas_idle = SKTextureAtlas(named: "idle")
let playerAnimatedAtlas_jump = SKTextureAtlas(named: "jump")
let playerAnimatedAtlas_attack = SKTextureAtlas(named: "attack")
var _: [SKTexture] = []
for i in 0...6 {
let playerTextureName = "1_entity_000_WALK_00\(i).png"
walkFrames.append(playerAnimatedAtlas.textureNamed(playerTextureName))
}
for z in 0...6 {
let playerTextureName_idle = "1_entity_000_IDLE_00\(z).png"
walkFrames2.append(playerAnimatedAtlas_idle.textureNamed(playerTextureName_idle))
}
for p in 0...6 {
let playerTextureName_jump = "1_entity_000_JUMP_00\(p).png"
walkFrames3.append(playerAnimatedAtlas_jump.textureNamed(playerTextureName_jump))
}
for k in 0...6 {
let playerTextureName_attack = "1_entity_000_ATTACK_00\(k).png"
walkFrames4.append(playerAnimatedAtlas_attack.textureNamed(playerTextureName_attack))
}
playerWalkingFrames = walkFrames
let firstFrameTexture = playerWalkingFrames[0]
player.position = CGPoint(x: -1050, y: -90 )
//player.setScale(0.090)
player.size = CGSize(width: 170, height: 120)
// player.physicsBody = SKPhysicsBody(rectangleOf:CGSize(width: 35, height: 100))
let spaceShipTexture = SKTexture(imageNamed: "p2.png")
let texturedSpaceShip = SKSpriteNode(texture: spaceShipTexture)
player.physicsBody = SKPhysicsBody(texture: spaceShipTexture , size: CGSize(width: player.size.width,
height: player.size.height));
player.physicsBody?.usesPreciseCollisionDetection = true
//player.physicsBody = SKPhysicsBody(texture: play, size: player.size)
player.physicsBody?.allowsRotation = false
player.physicsBody?.categoryBitMask = playerCategory
player.physicsBody?.collisionBitMask = enemy1Category
//player.physicsBody?.contactTestBitMask = enemy1Category
playerWalkingFrames_idle = walkFrames2
playerWalkingFrames_jump = walkFrames3
playerWalkingFrames_attack = walkFrames4
self.addChild(player)
}
collision code :
func didBegin(_ contact: SKPhysicsContact) {
let collison: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collison == playerCategory | enemy1Category{
print("COLLISION") }
else {
//print("Brak Kolizji" )
}
if collison == playermieczCategory | enemy1Category {
print("miecz")
}
if collison == playermieczCategory | piratkaCategory {
print("miecz_Piratka")
// health -= 0.2
// piratka.isHidden = true
}
if collison == playerCategory | piratkaCategory{
// if collison == nil {return}
player.physicsBody?.contactTestBitMask = 0
// czarny.physicsBody?.applyImpulse(CGVector(dx: 10, dy: 0))
// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {
// self.playerhealth -= 0.01
// print("nico")
print("HIT3")
// }
}

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
}
}
}
}}

Swift spritekit didbegincontact being called with delay

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")
}

How to reference SKSpriteNode without full instantiation in Swift3?

Previously, I had been using var progressBar = ProgressBar?() right above init within one of my SKSpriteNode subclasses, to allow various methods to reference progressBar throughout the class like so:
class Ship:SKSpriteNode{
var progressBar = ProgressBar?()
static var shipState = "norm"
var powerupOn = false
var moveSpeed:CGFloat
var lives:Int
var canShoot = false
let las = SKSpriteNode(texture:laserTexture)
var gun:GenericGun = GenericGun()
let scaleFactor:CGFloat = 0.07
init(startPosition startPos:CGPoint, controllerVector:CGVector){
self.lives = 3
self.moveSpeed = 160
super.init(texture: ship0, color: UIColor.clear, size: ship0.size())
//Set position
self.position = startPos
attachGun(gun)
self.setScale(scaleFactor)
self.physicsBody = SKPhysicsBody(texture: self.texture!, size: self.size)
self.physicsBody?.isDynamic = true
self.physicsBody?.categoryBitMask = PhysicsCategory.Ship
self.physicsBody?.collisionBitMask = 0
self.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
self.physicsBody?.allowsRotation = false
self.physicsBody?.angularVelocity = CGFloat(0)
self.physicsBody?.usesPreciseCollisionDetection = true
self.physicsBody?.affectedByGravity = false //TBD
self.physicsBody?.velocity.dx = controllerVector.dx * moveSpeed
self.physicsBody?.velocity.dy = controllerVector.dy * moveSpeed
self.name = "ship"
//Call animations
self.animateShip1()
}
func updateVelocity(_ v:CGVector){
self.physicsBody?.velocity.dx = v.dx * moveSpeed
self.physicsBody?.velocity.dy = v.dy * moveSpeed
}
func attachGun(_ gun:GenericGun){
gun.position = CGPoint(x: 0, y: 0)
self.addChild(gun)
}
func animateShip1() {
let animate = SKAction.animate(with: shipFrames, timePerFrame: 0.1)
let forever = SKAction.repeatForever(animate)
self.run(forever)
}
func updateShipProperties(shipVelocity v:CGVector,laserStartPos laserStart:CGPoint){
updateVelocity(v)
progressBar?.position = CGPoint(x: self.position.x - (self.size.width/2 + self.size.width/7.0), y: self.position.y)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In Swift3 I'm getting an error that I need to provide full arguments, is there a way to achieve this same functionality in Swift3?

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