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 a a 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!