Create Wall / Barrier Using SKSpriteNode physicsBody - swift

I have a separate "Floor" class in my SKSpriteKit application. When I first created this class, I had a barrier around the whole frame using
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
but that was actually stopping SKSpriteNodes from entering from the top of the screen so now I'm trying to figure out how I can create a left, right and bottom of the frame wall but not the top. This is so I can avoid one of my SKSpriteNodes at the bottom of the screen (still viewable by users) that moves left and right from leaving the display. I'm trying below which is building as if it's going to work but then when I start up the game I don't see the the walls? I'm not too sure what I'm doing wrong, I have
skView.showsPhysics = true
import Foundation
import SpriteKit
class Floor: SKNode {
override init() {
super.init()
let leftWall = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 100, height: frame.height))
leftWall.position = CGPoint(x: 100, y: 100)
leftWall.physicsBody = SKPhysicsBody(rectangleOf: leftWall.size)
leftWall.physicsBody!.isDynamic = false
self.addChild(leftWall)
let rightWall = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 100, height: frame.height))
rightWall.position = CGPoint(x: 200, y: 200)
rightWall.physicsBody = SKPhysicsBody(rectangleOf: rightWall.size)
rightWall.physicsBody!.isDynamic = false
self.addChild(rightWall)
// Set the bit mask properties
self.physicsBody?.categoryBitMask = balloonCategory
self.physicsBody?.contactTestBitMask = nailDropCategory
//self.physicsBody?.collisionBitMask = balloonCategory
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemted")
}
}

Gravity is affecting your walls, so they are forever falling into oblivion.
When you created your edge loop, you did not need to worry about this because edge loops are always static because it has no volumne
Now you are using volume based bodies, so you need to account for things like gravity and other forces affecting your bodies.
To have your body ignore these forces, simply set isDynamic = false
let leftWall = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 100, height: frame.height))
leftWall.position = CGPoint(x: 100, y: 100)
leftWall.physicsBody = SKPhysicsBody(rectangleOf: leftWall.size)
leftWall.physicsBody!.isDynamic = false
self.addChild(leftWall)
let rightWall = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 100, height: frame.height))
rightWall.position = CGPoint(x: 200, y: 200)
rightWall.physicsBody = SKPhysicsBody(rectangleOf: rightWall.size)
rightWall.physicsBody!.isDynamic = false
self.addChild(rightWall)

Related

SKSpriteNodes Colliding and sticking on top each other

My SKSpritekit nodes are stacking on top each other and sticking to each other. I have added SKSprite nodes via the sks file and programmatically.
let texture = SKTexture(image: #imageLiteral(resourceName: "cone"))
trCone = SKSpriteNode(texture: texture, normalMap: texture)
//trCone?.texture = SKTexture(image: #imageLiteral(resourceName: "cone"))
trCone?.size = CGSize(width: 60, height: 65)
trCone?.position = CGPoint(x: 153, y: 267)
trCone?.zPosition = 15
trCone?.name = trainingStuff.text
trCone?.physicsBody = SKPhysicsBody(texture: SKTexture(image: #imageLiteral(resourceName: "cone")), size: CGSize(width: 60, height: 65))
trCone?.physicsBody?.allowsRotation = false
trCone?.physicsBody?.isDynamic = false
trCone?.physicsBody?.categoryBitMask = .zero
trCone?.physicsBody?.collisionBitMask = .zero
myTrainigArr?.append(trCone!)
self.myScene!.addChild(trCone!)
This is how I add the nodes. I would like them to overlap each other and pass through each other without any collisions but this is the outcome. When I try to move the nodes after they collide one is now stuck on top of the other. Any suggestions will be appreciated.

Why does my SKPhysicsBody not adjust when I change my SKNode's anchor point?

I create gems from an SKTexture (below), then assign it a physics body. When the user touches and moves the gem, I change it's actor point so that the gem isn't hidden under the users finger:
.anchorPoint = CGPoint(x: 0.5, y: 0)
However when I have showsPhysics set to true, I can see that the physics body is still at the gem's original anchor point (see attached image). Is this a bug? Is there some additional method I need to call?
super.init(texture: skTexture, color: .clear, size: .zero)
self.isHidden = true
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.size = CGSize(width: myGV.gemSize.width, height: myGV.gemSize.height)
self.zPosition = theZ.gem
self.position = CGPoint(x: 0, y: 0 )
self.name = fileName
self.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: fileName), size: CGSize(width: myGV.gemSize.width, height: myGV.gemSize.height))

SkShapeNode physicsbody weird behaviour

I created a subclass of SKNode:
class SquareDrop: SKNode {
init(image: SKSpriteNode) {
super.init()
self.setScale(0.3
//Set the starting position of the node
self.position = CGPoint(x: 0.5, y: UIScreen.main.bounds.height/2)
//Apply a physics body to the node
self.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "square"), size: CGSize(width: image.size.width * 0.3, height: image.size.height * 0.3))
self.addChild(image)
}
I created an instance in GameScene class and I set up it:
func spawnRain()
squareDrop = SquareDrop(image: SKSpriteNode(imageNamed: "square"))
squareDrop?.physicsBody?.linearDamping = 5
squareDrop?.physicsBody?.restitution = 0
self.addChild(squareDrop!)
}
It is the result:
It works like aspected, but instead of using an image I wanted to draw a square:
func spawnRain() {
//Here I did not use the class that I had created previously
let shape = SKShapeNode()
shape.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 100, height: 100), cornerRadius: 5).cgPath
shape.position = CGPoint(x: frame.midX - shape.frame.width/2 , y: UIScreen.main.bounds.height/2)
shape.fillColor = UIColor.red
shape.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 100, height: 100))
addChild(shape)
}
It doesn't seem like I aspected (the squares that I created have a strange behaviour). Any hints?
I am sorry for the question, but I am a noob of SpriteKit.

SKPhysicsContact Not Detecting categoryBitMask Collision

So I have my "Floor.swift" class below which is basically a bunch of walls. I have objects coming from the top of the screen and once the Floor and SKSpriteNodes collide, I'd like the SKSpriteNode to be removed. Below is my Floor class.
import Foundation
import SpriteKit
class Floor: SKNode {
override init() {
super.init()
let leftWall = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 5, height: 50))
leftWall.position = CGPoint(x: 0, y: 50)
leftWall.physicsBody = SKPhysicsBody(rectangleOf: leftWall.size)
leftWall.physicsBody!.isDynamic = false
self.addChild(leftWall)
let rightWall = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 5, height: 50))
rightWall.position = CGPoint(x: 375, y: 50)
rightWall.physicsBody = SKPhysicsBody(rectangleOf: rightWall.size)
rightWall.physicsBody!.isDynamic = false
self.addChild(rightWall)
let bottomWall = SKSpriteNode(color: UIColor.brown, size: CGSize(width: 500, height: 10))
bottomWall.position = CGPoint(x: 150, y: -5)
bottomWall.physicsBody = SKPhysicsBody(rectangleOf: bottomWall.size)
bottomWall.physicsBody!.isDynamic = false
self.addChild(bottomWall)
// Set the bit mask properties
self.physicsBody?.categoryBitMask = floorCategory
self.physicsBody?.contactTestBitMask = objectCategory | pointCategory | lifeCategory
self.physicsBody?.collisionBitMask = dodgeCategory
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemted")
}
}
Then in my GameScene class under "func didBegin(_ contact: SKPhysicsContact)" I wrote:
if (contact.bodyA.categoryBitMask == floorCategory) && (contact.bodyB.contactTestBitMask == objectCategory | pointCategory | lifeCategory) {
contact.bodyB.node!.removeFromParent()
print("COLLISION")
}
But for some reason I'm not getting any detection what's so ever. I made my "Floor" class be a "contactTestBitMask" on each class of my objectCategory, pointCategory, lifeCategory. What am I doing wrong!? My other collisions are detected but not this one.
Your leftWall, rightWall and bottomWall have the default contactTest and collision bit masks, so will collide with everything and generate contacts with nothing. Except they all have their isDynamic property set to false, so they can’t collide with or contact anything (although other things can collide and contact them if those other things have isDynamic set to true).
But this is probably what you want (Floors and walls are usually not dynamic). I suspect the real problem is that objects of Floor class won’t have a physics body, unless you initialise it somewhere else. You have these lines:
self.physicsBody?.categoryBitMask = ...
self.physicsBody?.contactTestBitMask = ...
self.physicsBody?.collisionBitMask = ...
but you haven't actually created self.physicsBody. Your code would crash except you’ve used optional chaining (self,phyicsBody?) and so Swift gets to the ‘?’ and says “Oh - it’s optional and doesn’t exist. I’ll just stop here then”
At the very least, try creating a physics body for your Floor object using the physics bodies of the 3 wall objects:
Self.physicsBody = SKPhysicsBody(Bodies: [leftWall.physicsBody, rightWall.PhysicsBoidy, bottomWall.physicsBody])
(SKPhysicsBody has an initialiser that takes an array of physics bodies to create a new physics body).

Does a parent have its children's physicsBodies?

I was wondering what the physicsBody of a parent would be if all I do is this:
let combinedBodies = SKSpriteNode()
addChild(combinedBodies)
//Create its children like this as an example:
let child = SKSpriteNode(color: UIColor.red, size: CGSize(width: 10, height: 20))
child.physicsBody = SKPhysicsBody(rectangleOf: child.size)
child.physicsBody?.categoryBitMask = collisionType.child.rawValue
combinedBodies.addChild(child)
allBodies.append(child.physicsBody?)
Would the physicsBody of combinedBodies be the combination of all its children's physicsBodies, or would it have none and I would have to manually add it? Also, what would the categoryBitMask of the parent be? Would it be collisionType.child.rawValue if all of its children had that category, or would I manually have to give it its own category?
Edit: I have used this:
combinedBodies.physicsBody = SKPhysicsBody(bodies: )
I have a list where I store all the children's physicsBodies and then put that list into this command (I keep updating the combinedBodies.physicsBody when new and old children get deleted). However, now I have the problem that the physicsbodies of the parent aren't where the original physicsbodies of the children were (they are at different positions on the screen), they are in the middle of the screen. Is there a way to fix this?
I have also changed the physicsBody of the combinedBodies like this:
combinedBodies.physicsBody = SKPhysicsBody(bodies: allBodies)
combinedBodies.physicsBody?.isDynamic = true
combinedBodies.physicsBody?.categoryBitMask = collisionType.combinedBodies.rawValue
combinedBodies.physicsBody?.collisionBitMask = 0
combinedBodies.physicsBody?.contactTestBitMask = 0
combinedBodies.physicsBody?.affectedByGravity = false
combinedBodies.physicsBody?.friction = 0.0
Please note that all the code in this question is basically my code, just that the new children are created through an iteration with different positions, rotations, and colors.
EDIT WITH EXAMPLE CODE: I have been able to recreate my issue in a test program which I have added here (the allLine is the parent of ScreenLine and line in this code).
class GameScene: SKScene, SKPhysicsContactDelegate {
enum type: UInt32 {
case line = 1
case screenLine = 2
case allLine = 4
}
override func didMove(to view: SKView) {
let allLine = SKSpriteNode()
addChild(allLine)
var points = [CGPoint]()
var bodies = [SKPhysicsBody]()
points = [CGPoint(x: 0, y: 300), CGPoint(x: -200, y: 100)]
let width = 5.0
let height = {() -> Double in
let distX = abs(points[1].x - points[0].x)
let distY = abs(points[1].y - points[0].y)
let dist = sqrt((distX * distX) + (distY * distY))
return Double(dist)
}()
let ScreenLine = SKSpriteNode(color: UIColor.init(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0), size: CGSize(width: width, height: height))
ScreenLine.position = CGPoint(x: ((points[1].x + points[0].x)/2), y: ((points[1].y + points[0].y)/2))
let rotation = {() -> CGFloat in
let m = (points[1].y - points[0].y)/(points[1].x - points[0].x)
let angle = CGFloat(Double.pi/2) - atan(m)
return -angle
}()
ScreenLine.zRotation = rotation
ScreenLine.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: CGPoint(x: 0, y: 0))
ScreenLine.physicsBody?.contactTestBitMask = type.screenLine.rawValue
ScreenLine.physicsBody?.collisionBitMask = 0
ScreenLine.physicsBody?.contactTestBitMask = 0
ScreenLine.physicsBody?.isDynamic = true
ScreenLine.physicsBody?.affectedByGravity = false
allLine.addChild(ScreenLine)
bodies.append(ScreenLine.physicsBody!)
points = [CGPoint(x: -100, y: 300), CGPoint(x: -300, y: 100)]
let line = SKSpriteNode(color: UIColor.init(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0), size: CGSize(width: width, height: height))
line.position = CGPoint(x: ((points[1].x + points[0].x)/2), y: ((points[1].y + points[0].y)/2))
line.zRotation = rotation
line.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: CGPoint(x: 0, y: 0))
line.physicsBody?.contactTestBitMask = type.screenLine.rawValue
line.physicsBody?.collisionBitMask = 0
line.physicsBody?.contactTestBitMask = 0
line.physicsBody?.isDynamic = true
line.physicsBody?.affectedByGravity = false
allLine.addChild(line)
bodies.append(line.physicsBody!)
allLine.physicsBody = SKPhysicsBody(bodies: bodies)
allLine.physicsBody?.isDynamic = true
allLine.physicsBody?.categoryBitMask = type.allLine.rawValue
allLine.physicsBody?.collisionBitMask = 0
allLine.physicsBody?.contactTestBitMask = 0
allLine.physicsBody?.affectedByGravity = false
allLine.physicsBody?.friction = 0.0
}
}
UPDATE: Now by centering the physicsBody of the children to where the node is (not CGPoint.zero), but where I place the child on the screen, the physicsBody is in the right position when I add it to the list.
I set the line and screen line physicsBody positions like this:
ScreenLine.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: ScreenLine.position)
line.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: line.position)
PROBLEM THAT I'M CURRENTLY TRYING TO FIGURE OUT:
This makes the physicsBodies of allLine centred. However, they aren't rotated like they should be. They don't preserve their rotations. How do I fix this??
Thanks for any answers and help :D