Search code examples
swiftsprite-kitskphysicsbodyskphysicsjointskphysicscontact

How to obtain one solid SKPhysicsBody or SKNode object from SKNodes with contact?


I'm trying to create a House Stack-like game (https://apps.apple.com/gb/app/house-stack/id1458713825?l) but having problem to assign physics mechanism.

My main problem is to create a single object when Node drop and contact with others and move them down in a solid form, once they reach to specific height.

I've tried to create fixed-joints with contact between nodes and move them when last Node position y = 0 but fixed-joints are not working as i thought. Nodes are shaking like a snake and collapsing when they moved.

Also tried to create a new single object with contact and calculate its height but couldn't figure out how should i use init(bodies: [SKPhysicsBody]). Even if i assign physicsbody properties to new object, i can't measure or call its height and update body with another Node contact.

Any idea or example would be nice. Here is my codes, thanks in advance.

SKNode(inside touchesBegan to create same node multiple times):

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let Node1 = SKSpriteNode(imageNamed: "Object1")
        Node1.position = MovingObject.position
        Node1.zPosition = 2
        Node1.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: Node1.size.width, height: Node1.size.height))
        Node1.physicsBody?.friction = 0.9
        Node1.physicsBody?.restitution = 0.1
        Node1.physicsBody?.allowsRotation = true
        Node1.physicsBody!.contactTestBitMask = Node1.physicsBody!.collisionBitMask
        Node1.physicsBody!.categoryBitMask = Node1Category
        addChild(Node1)
    }

Moving Object:

Inside GameScene;
var MovingObject = SKSpriteNode(imageNamed: "Object1")
let ObjectMove = SKAction.sequence([(SKAction.moveBy(x: 200, y: 0, duration: 0.5)),(SKAction.moveBy(x: -400, y: 0, duration: 1)),(SKAction.moveBy(x: 200, y: 0,  duration: 0.5))])

Inside override func didMove(to view: SKView);
        MovingObject.position = CGPoint(x: 0, y: frame.maxY * 0.55)
        MovingObject.zPosition = 1
        MovingObject.run(SKAction.repeatForever(ObjectMove))
        addChild(MovingObject)

Joints and moves:

func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.node?.name == "Node1" && contact.bodyB.node?.name == "Node1"
        {
            let joint2 = SKPhysicsJointFixed.joint(withBodyA: contact.bodyA , bodyB: contact.bodyB, anchor: CGPoint(x: contact.contactPoint.x, y:contact.contactPoint.y))
            
            let pos2 = (contact.bodyA.node?.position.x)!-(contact.bodyB.node?.position.x)!

            if pos2 <= Node1.size.width * 0.5 && pos2 >= -Node1.size.width * 0.5{
                physicsWorld.add(joint2)
            }

            if (contact.bodyB.node?.position.y)! >= 0 {
                let movenodedown = SKAction.moveBy(x: 0, y: -Node1.size.height, duration: 0.5)
                contact.bodyB.node?.run(movenodedown)
                ground.run(movenodedown)     
            }   
        }

Solution

  • For those who need to be enlightened about similar problems;

    I found my solution by using enumerateChildNodes(withName:using:) and physicsBody.isDynamic = false together instead of fixed joints. Code is below.

    func didBegin(_ contact: SKPhysicsContact)
        {
    if contact.bodyA.node?.name == "Node1" && contact.bodyB.node?.name == "Node1"
            {
    let pos2 = (contact.bodyA.node?.position.x)!-(contact.bodyB.node?.position.x)!
    
                if pos2 <= Node1.size.width * 0.5 && pos2 >= -Node1.size.width * 0.5.
                {
                    contact.bodyB.node?.physicsBody?.isDynamic = false
                    contact.bodyA.node?.physicsBody?.collisionBitMask = 0
                    contact.bodyA.node?.physicsBody?.contactTestBitMask = 0
                    
                    if (contact.bodyB.node?.position.y)! >= 0
                    {
                        let movenodesdown = SKAction.moveBy(x: 0, y: Node1.size.height , duration: 0.5)
    
                        enumerateChildNodes(withName: "Node1") { (nodes, stop) in
                            let Node1 = nodes as! SKSpriteNode
                            Node1.run(movenodesdown)
                        }
                    }
                }
            }
        }