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

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

Related

Collision problem from texture swift SKphysicsbody

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.

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.

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)

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

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