Larisa
Larisa

Reputation: 791

Collision between two objects not detected after performing a move action on one of them

I seem to have a poor understanding of something related to collision detections, actions or positions in SpriteKit.

I have a player and bricks and when a player lands on any brick, he jumps of it and then all bricks move down for a certain distance. I have the player added as a child of the scene and the bricks as children of a node (SKNode) containing them. When I want to change their y-position, I just use a moveBy action on that node and the bricks do indeed move how I want them to.

The problem is, when they are moving or even when they stop, the player can't land on them anymore (collision between them and the player is not detected anymore), so the player just falls through them.

This is my code in GameScene.swift:

func moveBricksDownFor(movedForY: CGFloat) {
    let moveAction = SKAction.moveBy(CGVectorMake(0, -movedForY), duration: 1.0)
    brickNode.runAction(moveAction)
    for _ in 0...4 {addBrick(0.7)}
}

func playerCollidedWithBrick(brick: Brick) {
    if player.position.y > brick.position.y {
        player.physicsBody?.velocity.dy = player.maxVelocityY
    }
    if player.yChangedFor() > 70 {moveBricksDownFor(player.yChangedFor())}
}

Is it possible that physics body isn't attached to visual representations of the bricks or something like this?

Here is also the Player class (the parts used in the above code):

let maxVelocityY: CGFloat = 900
var startPositionY: CGFloat!

init(position: CGPoint) {
    let texture = SKTexture(imageNamed: "doodler.png")
    super.init(texture: texture, color: UIColor.blackColor(), size: CGSizeMake(60, 60))
    self.zPosition = Layer.player.rawValue
    self.position = position
    self.startPositionY = position.y
    self.physicsBody = SKPhysicsBody(rectangleOfSize: self.size)
    self.physicsBody?.affectedByGravity = true
    self.physicsBody?.allowsRotation = false
    self.physicsBody?.dynamic = true
    self.physicsBody?.categoryBitMask = PhysicsCategory.Player
    self.physicsBody?.contactTestBitMask = PhysicsCategory.Brick
    self.physicsBody?.collisionBitMask = PhysicsCategory.None
    self.physicsBody?.usesPreciseCollisionDetection = true
}

func yChangedFor() -> CGFloat {
    return (self.position.y - startPositionY)
}

I did google similar topics but didn't find any similar problems so I guess I am missing something fundamental here or am making a stupid mistake.

UPDATE (added the Brick class and initialization of worldNode (SKNode)):

class Brick: SKSpriteNode { 
    init(position:CGPoint) {
        let texture = SKTexture(imageNamed: "brick.png")
        super.init(texture: texture, color: UIColor.clearColor(), size: CGSizeMake(70, 10))
        self.position = position
        self.zPosition = Layer.brick.rawValue
        self.physicsBody = SKPhysicsBody(rectangleOfSize: self.size)
        self.physicsBody?.affectedByGravity = false
        self.physicsBody?.allowsRotation = true
        self.physicsBody?.dynamic = false
        self.physicsBody?.categoryBitMask = PhysicsCategory.Brick
        self.physicsBody?.contactTestBitMask = PhysicsCategory.Player
        self.physicsBody?.collisionBitMask = PhysicsCategory.None
        self.physicsBody?.usesPreciseCollisionDetection = true
    }
}

Setting the brickNode and adding bricks to it as its children:

func setupBrickNode() {
    brickNode.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
    brickNode.physicsBody?.affectedByGravity = false
}

func addBrick(minXPercantage: CGFloat) {
    let randomX = random(min: size.width*0.25, max: size.width - 100)
    let randomY = random(min: size.height*minXPercantage, max: size.height)
    let brick = Brick(position: CGPointMake(randomX, randomY))
    brickNode.addChild(brick)
}

func setupBricks() {
    for _ in 0...10 {addBrick(0.25)}
}

Updated the part where I move the brickNode (now with setting its velocity):

func moveBricks() {
    brickNode.physicsBody?.velocity = CGVectorMake(0, -player.maxVelocityY/4)
    let waitAction = SKAction.waitForDuration(1.0)
    let setBrickNodeVelocityToZero = SKAction.runBlock({self.brickNode.physicsBody?.velocity = CGVectorMake(0, 0)})
    runAction(SKAction.sequence([waitAction, setBrickNodeVelocityToZero]))
}

func playerCollidedWithBrick(brick: Brick) {
    if player.position.y > brick.position.y {
        player.physicsBody?.velocity.dy = player.maxVelocityY
        moveBricks()
    }
}

Upvotes: 1

Views: 312

Answers (3)

mikem
mikem

Reputation: 112

Example code

import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {
    var world: SKNode!
    var player: SKSpriteNode!

    override func didMoveToView(view: SKView) {
        physicsWorld.contactDelegate = self
        physicsWorld.gravity = CGVector(dx: 0, dy: -10)

        // World
        world = SKNode()
        addChild(world)

        // Player
        player = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 50, height: 50))
        player.position.x = size.width / 2

        player.physicsBody = SKPhysicsBody(rectangleOfSize: player.size)
        player.physicsBody!.categoryBitMask = 2
        player.physicsBody!.contactTestBitMask = 4
        player.physicsBody!.collisionBitMask = 0
        player.physicsBody!.mass = 1
        player.physicsBody!.velocity = CGVector(dx: 0, dy: 2000)
        player.physicsBody!.usesPreciseCollisionDetection = true

        world.addChild(player)

        // Bricks
        for index in 0...50 {
            // Player
            let brick = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 100, height: 25))
            brick.position = CGPoint(x: size.width / 2, y: 300 + CGFloat(index) * 200)
            brick.zPosition = -1

            brick.physicsBody = SKPhysicsBody(rectangleOfSize: brick.size)
            brick.physicsBody!.categoryBitMask = 4
            brick.physicsBody!.contactTestBitMask = 2
            brick.physicsBody!.collisionBitMask = 0
            brick.physicsBody!.dynamic = false

            world.addChild(brick)
        }
    }

    func didBeginContact(contact: SKPhysicsContact) {
        // Is the player falling?
        guard player.physicsBody!.velocity.dy <= 0 else {
            return
        }

        player.physicsBody!.velocity = CGVector(dx: 0, dy: 1000)
    }

    override func didFinishUpdate() {
        guard player.position.y > 200 else {
            return
        }

        world.position.y = -player.position.y + 200
    }
}

Upvotes: 1

mikem
mikem

Reputation: 112

Because particular brick position is always the same. You are moving it's parent not the brick itself.

let realBrickPosition = convertPoint(brick.position, toNode: self)  

Upvotes: 0

Alessandro Ornano
Alessandro Ornano

Reputation: 35392

If you want to land to a dynamic involved object probably you must set this.

self.physicsBody!.restitution = 0

Also, it's not the best attitude using SKAction's moveBy instead of correct motion by velocity or force vectors, this can be change your physics object values and you lost their directions.

Upvotes: 1

Related Questions