Collision problem from texture swift SKphysicsbody - swift

I am trying to create collision for a 2d game house, such as walls, furniture, etc. But when I try to create a physics body from the texture, I only get collision on one wall.
Ive tried to set collisions with the editor and in code.
let colhouse = self.childNode(withName: "Umatilla")!
let texture = SKTexture(imageNamed: "warehouse-collisions")
colhouse.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
I want it to have collisions on the entire texture, the background is transparent, so some bodies are not touching or connected:
(source: i.ibb.co)

Trying to create a single physics body from a texture with non continuous bodies is not possible with the SKPhysicsBody(texture: texture, size: texture.size()) function.
To get around this, create nodes in the editor, place them over your collision zones(only for reference). That alone will solve the issue, however if you begin to create a big map. This causes a lag problem with xcode. To get around this I then took the Width and Hight aswell as position. Then created the boundaries in code as such:
//okay so this is the first thing we have to run, setup the collisions
func setupCollisions(){
//first we need the data
let data = [
SKPhysicsBody(rectangleOf: CGSize(width: 7.096, height: 180.423), center: CGPoint(x: 215.266, y: -5.504)),
SKPhysicsBody(rectangleOf: CGSize(width: 429.879, height: 6.631), center: CGPoint(x: -3.222, y: -92.34)),
SKPhysicsBody(rectangleOf: CGSize(width: 7.096, height: 173.735), center: CGPoint(x: -214.614, y: -2.161)),
SKPhysicsBody(rectangleOf: CGSize(width: 43.492, height: 144.075), center: CGPoint(x: -189.318, y: -16.986)),
SKPhysicsBody(rectangleOf: CGSize(width: 133.314, height: 45.883), center: CGPoint(x: -100.913, y: -66.08)),
SKPhysicsBody(rectangleOf: CGSize(width: 58.041, height: 6.642), center: CGPoint(x: -182.044, y: 81.377)),
SKPhysicsBody(rectangleOf: CGSize(width: 301.388, height: 6.662), center: CGPoint(x: 61.022, y: 81.386)),
SKPhysicsBody(rectangleOf: CGSize(width: 89.409, height: 93.006), center: CGPoint(x: 93.072, y: -12.631))
]
//now lets create a single physicsbody from all the data
let physicsbody1 = SKPhysicsBody(bodies: data)
physicsbody1.isDynamic = false
self.physicsBody = physicsbody1
}
once you do it this way, remove the collisions nodes( walls, sofas, misc, etc) from the editor as we create them in code now. Just make sure to run your custom setupCollisions() function as soon as possible or needed.

Related

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

Create Wall / Barrier Using SKSpriteNode physicsBody

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)

Why do texture based vs. rectangle based physics bodies intersect the edge loop?

In my SKScene class I have several texture based physics bodies in my game, e.g.:
object1.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "object_name1"), size: CGSize(width: 113, height: 107))
object2.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "object_name2"), size: CGSize(width: 113, height: 65))
etc.
I also have rectangle based physics bodies, e.g.:
object3.physicsBody = SKPhysicsBody(rectangleOf: object3.size)
object4.physicsBody = SKPhysicsBody(rectangleOf: object4.size)
etc.
I have defined the game boundary as follows:
self.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.width, height: frame.height))
I have set up all the appropriate physics properties for each object (SKSpriteNode).
The problem I am having is that the texture based physics bodies go beyond the game boundary as I move them, and can potentially fall out of the scene, but the rectangle based ones remain constrained by the boundary (as expected).
Any idea(s) why?

How to contain SKEmitterNode particles in parent Node?

I would like to add a SKEmitterNode to SKNode but have its particles stay inside the parent node's frame. Kinda like clipsToBounds property on UIView.
Example: the particles from the emitter should not leave the black square SKSpriteNode:
You could do it with SKCropNode. Like this:
if let particles = SKEmitterNode(fileNamed: "rain.sks") {
let cropNode = SKCropNode()
cropNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
cropNode.zPosition = 3
cropNode.maskNode = SKSpriteNode(color: SKColor.blackColor(), size: CGSize(width: 150, height: 150))
cropNode.addChild(particles)
addChild(cropNode)
}
Unfortunately this works only on iOS8... When you try to add an emitter into crop node in iOS9, you will probably run into some issues, eg. nothing will be rendered and fps drop may occur. And this is already known issue.
Like it's said in that link, instead of particles being rendered, nothing actually happens. Personally, I haven't experienced fps issues , but particles are definitely not rendered.
A workaround would be to add a node which will hold an emitter, and then mask that container node. So, let's add an SKSpriteNode to make that black background like from your example. Let's call it background:
if let particles = SKEmitterNode(fileNamed: "rain.sks") {
let cropNode = SKCropNode()
cropNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
cropNode.zPosition = 3
let blackNode = SKSpriteNode(color: .blackColor(), size: CGSize(width: 200, height: 200))
blackNode.addChild(particles)
cropNode.maskNode = SKSpriteNode(color: SKColor.blackColor(), size: CGSize(width: 200, height: 200))
cropNode.addChild(blackNode)
addChild(cropNode)
}

Add SKShapeNode in another one

I want to draw a square in another square; but I can't position the second one in the right way.
What can I do for positioning based on first square coordinates?
var tileBackground = SKShapeNode(rectOfSize: CGSize(width: screenWidth, height: screenWidth))
tileBackground.name = "tileBackground"
tileBackground.fillColor = SKColor.whiteColor()
tileBackground.position = CGPoint(x: frame.midX, y: frame.midY);
self.addChild(tileBackground)
var tile = SKShapeNode(rectOfSize: CGSize(width: tileSize, height: tileSize))
tile.name = "tile1"
tile.fillColor = SKColor.redColor()
tile.position = CGPointMake(100,100)
tileBackground.addChild(tile)
You should use SKSpriteNode instead. That way you can add it easily and without setting the position manually. Because in an SKSpriteNode, the center is always the center of the node:
var tileBackground = SKSpriteNode(color: UIColor.whiteColor(), size: CGSizeMake(width: screenWidth, height: screenWidth))
tileBackground.name = "tileBackground"
tileBackground.position = CGPoint(x: frame.midX, y: frame.midY);
self.addChild(tileBackground)
var tile = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: tileSize, height: tileSize))
tile.name = "tile1"
tileBackground.addChild(tile)
A node's position is relative to its parent position.
If you want the tile node to be in the center of its parent then set its position as:
tile.position = CGPoint(x: tileBackground.size.width / 2.0, y: tileBackground.size.height / 2.0)
which will place the tile in center of the tileBackground node.
also note that you do not need to update the tile position to "follow" the tileBackground node. Updating the tileBackground position will automatically keep the tile in its center