简体   繁体   中英

UICollisionBehavior and UIGravityBehavior not working as expected with DispatchQueue

I'm currently designing a UI for a framework I've been given that plays connect 4. This framework is encapsulated in a class called GameSession. I will not describe the ins and outs of it's API and how it operates. I don't believe it's important.

I believe I may be misunderstanding dispatch queues and I am using them incorrectly. But, I have searched endlessly and have not found anything that hints to a solution to my problem.

Here is aa short explanation on what is happening. The method that controls the moves is playGame(botStarts: Bool, atColumn: Int). In this method, a collision boundary is added at the correct row and column using a method called addBoundary(atRow: Int, atColumn: Int). Then, a disc is created using a method called dropDisc(atColumn: Int, color: UIColor). This method creates a custom UIView, adds it to the view on screen and adds collision and gravity behaviours. It falls until it reaches the previously added boundary.

In playGame() I am dropping the discs onto the screen using DispatchMain.Queue.async{dropDisc()}. But, every time I call playGame() for the second time and beyond, the custom discs are drawn at the top of the screen but they fail to fall. On the first iteration, they are drawn and fall as expected.

Below are the functions that I've referenced above.

private func playGame(botStarts: Bool, dropPieceAt: Int) {
        DispatchQueue.global(qos: .userInitiated).async {
            if botStarts {
                if let move = gameSession.move {
                    let column = move.action % self.gameSession.boardLayout.columns
                    let row = move.action / self.gameSession.boardLayout.columns
                    self.addBoundary(atRow: row, atColumn: column)
                    DispatchQueue.main.async {
                        self.dropDisc(atColumn: column, color: move.color)
                    }
                }
            } else {
                let column = dropPieceAt
                if self.gameSession.userPlay(at: column) {
                    if let move = self.gameSession.move {
                        print(move)
                        let column = move.action % self.gameSession.boardLayout.columns
                        let row = move.action / self.gameSession.boardLayout.columns
                        self.addBoundary(atRow: row, atColumn: column)
                        DispatchQueue.main.async {
                            self.dropDisc(atColumn: column, color: move.color)
                        }
                    }
                    if let move = self.gameSession.move {
                        let column = move.action % self.gameSession.boardLayout.columns
                        let row = move.action / self.gameSession.boardLayout.columns
                        self.addBoundary(atRow: row, atColumn: column)
                        DispatchQueue.main.async {
                            self.dropDisc(atColumn: column, color: move.color)
                        }
                    }
                }
                if self.gameSession.done {
                    if let outcome = self.gameSession.outcome {
                        DispatchQueue.main.async {
                            self.gameLabel.text = outcome.message + "\n Winning pieces \(outcome.winningPieces)"
                        }
                    }
                }
            }
        }
    }


 private func dropDisc(atColumn: Int, color: UIColor) {
        var frame = CGRect()
        frame.origin = CGPoint.zero
        frame.size = Constants.bubbleSize
        let x = CGFloat(39) + CGFloat(47 * atColumn)
        frame.origin.x = x
        let bubbleView = DiscView(frame: frame, color: color)
        gameView.addSubview(bubbleView)
        collider.addItem(bubbleView)
        gravity.addItem(bubbleView)
    }



    // Adds a boundary using the row and column obtained from game session.
    private func addBoundary(atRow: Int, atColumn: Int) {
        let fromCoordX = CGFloat(16 + (boardView.initialX-boardView.radius)) + CGFloat(47 * atColumn)
        let toCoordX = fromCoordX + CGFloat(24)
        let coordY =  CGFloat(198.5 + (boardView.initialY+boardView.radius)) + CGFloat(45 * atRow)
        let fromPoint = CGPoint(x: fromCoordX, y: coordY+1)
        let toPoint = CGPoint(x: toCoordX, y: coordY+1)
        self.collider.addBoundary(withIdentifier: "boundary" as NSCopying, from: fromPoint, to: toPoint)
        self.drawLineFromPoint(start: fromPoint, toPoint: toPoint, ofColor: UIColor.red, inView: self.gameView)

    }

Here is a screenshot of my screen: https://imgur.com/7M3fklo .

On the bottom row you can see the users disc (yellow) and the bots disc (red). These were added on the first call to playGame(). But, on the top, you can see the two discs that were added on the second call to playGame(). These do not fall.

No matter what I've tried

Any feedback is much appreciated!

Background threading is hard, and should be used only when absolutely necessary (because it is forced upon you, or because you have a time-consuming activity to perform and you don't want to freeze the interface, which is owned by the main thread). It doesn't seem necessary here. You are not doing anything time-consuming. And it looks like use of DispatchQueue.global might be confusing you; a background dispatch queue causes your code to run out of order, and apparently you weren't aware of this.

Solution: just get rid of all your DispatchQueue.global and DispatchQueue.main code. (In other words, eliminate those lines and matching right curly brace lines.) Everything will then just run on the main queue and there's no reason why it shouldn't.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM