I have a gameboard consisting of a two dimensional SKShapeNode
tile map. It actually works great, but I find that the logic in touchDown(atPoint pos: CGPoint)
is a complete mess, filled with conditionals:
if viewModel.playerIsUsingSkill {
if player.characterSpriteNode.contains(pos) || self.gameBoardArray[self.player.xPosition][self.player.yPosition].contains(pos) {
useSkill()
viewModel.deSelecting(completion: { [weak self] in
self?.playerSkillCards.color = UIColor(named: "portrait_background") ?? UIColor.blue
self?.viewModel.playerIsUsingSkill = false
})
} else {
viewModel.deSelecting(completion: { [weak self] in
self?.playerSkillCards.color = UIColor(named: "portrait_background") ?? UIColor.blue
self?.viewModel.playerIsUsingSkill = false
})
}
} else {
if player.characterActionPoints > 0 {
updateGUI()
self.isUserInteractionEnabled = false
self.setupAttackButton()
var breakOuterLoop = false
// MARK: - Combat Button is Pressed
if(attackButton.contains(pos) && !attackButton.isHidden) {
viewModel.attackButtonIsClicked(attacker: player, defender: enemy) { result in
self.player.attackAnimation(animationAtlas: self.player.characterAttackTextureAtlas, completion: { [weak self] in
guard let self = self else { return }
self.player.idleAnimation(animationAtlas: self.player.characterIdleTextureAtlas)
})
self.player.turnCharacterInCombat(defender: self.enemy)
switch result {
case .hit:
print("HIT")
self.enemy.characterHitAnimation(animationAtlas: self.enemy.characterHitTextureAtlas, completion: { [weak self] in
guard let self = self else { return }
self.enemy.idleAnimation(animationAtlas: self.enemy.characterIdleTextureAtlas)
})
self.addChild(self.viewModel.showHitOrMiss(text: "HIT", position: self.enemy.characterSpriteNode.position, character: self.enemy))
case .miss:
self.addChild(self.viewModel.showHitOrMiss(text: "MISS", position: self.enemy.characterSpriteNode.position, character: self.enemy))
case .destroyed:
self.enemy.characterSpriteNode.removeFromParent()
self.enemy.characterSpriteNode.isAccessibilityElement = true
self.attackButton.isHidden = true
}
}
checkCharacterActionPoints()
}
else {
// MARK: - Loop Whole GameBoard, Find Tile Location Based on Coordinates
for x in 0...gameBoardArray.count - 1 where !breakOuterLoop{
for y in 0...gameBoardArray.count - 1 {
if gameBoardArray[x][y].contains(pos) {
viewModel.currentSelectedTile = gameBoardArray[x][y]
// MARK: - Deselecting
if viewModel.currentSelectedTile == viewModel.previousSelectedTile && (viewModel.characterIsSelected || viewModel.tileClickedOnce) {
viewModel.deSelecting(completion: {
self.removeGUI()
})
}
// MARK: - Selecting Character
else if viewModel.currentSelectedTile == player.characterTilePosition && !viewModel.characterIsSelected {
viewModel.deselectEnemy()
viewModel.selectingCharacter(i: x, j: y, completion: {
self.addGUI()
self.player.proximityCheck(opponent: self.enemy, completion: { [weak self] enemyIsInProximity in
if enemyIsInProximity {
self?.attackButton.isHidden = false
self?.attackChance.isHidden = false
}
})
})
}
// MARK: - Selecting Other
else if !viewModel.characterIsSelected && viewModel.currentSelectedTile != player.characterTilePosition {
viewModel.selectingOther(i: x, j: y, enemyVM: enemy)
}
// MARK: - Moving Character
else if viewModel.characterIsSelected && viewModel.currentSelectedTile != player.characterTilePosition {
viewModel.characterIsMoving(i: x, j: y, completion: { moveWasWithinLimit, shouldDeSelect in
// Making sure that the player position tile is not acessible for enemy movement
self.viewModel.currentSelectedTile.isAccessibilityElement = false
self.viewModel.previousSelectedTile.isAccessibilityElement = true
// MARK: - Selecting Enemy, DeSelecting Character
if(shouldDeSelect) {
self.viewModel.deSelecting(completion: {
self.removeGUI()
})
self.viewModel.shouldDeSelect = false
}else {
// MARK: - Move Character Within Move Limit
if(moveWasWithinLimit) {
self.player.previousX = self.player.xPosition
self.player.previousY = self.player.yPosition
self.player.xPosition = x
self.player.yPosition = y
self.viewModel.turnCharacter(previousX: self.player.previousX, previousY: self.player.previousY, playerOrEnemy: &self.player)
self.player.characterTilePosition = self.gameBoardArray[x][y]
self.attackButton.isHidden = true
self.attackChance.isHidden = true
self.checkCharacterActionPoints()
self.player.proximityCheck(opponent: self.enemy, completion: { [weak self] enemyIsInProximity in
if enemyIsInProximity {
self?.attackButton.isHidden = false
self?.attackChance.isHidden = false
}
})
} else {
// MARK: - DeSelecting
self.viewModel.deSelecting(completion: {
self.removeGUI()
})
}
}
})
}
else {
print("Error")
}
viewModel.previousSelectedTile = viewModel.currentSelectedTile
breakOuterLoop = true
break
}
}
}
if player.characterActionPoints > 0 { isUserInteractionEnabled = true }
}//else
}//if
}
}
It's basically: If you press this part, x will happen; if you press a player y will happen; if you press an enemy z will happen. Then it's filled with conditionals for different scenarios within all those conditionals. As I said, it works quite well and without any bugs, but I find it immensely hard to work with and fragile to new code. Is there another way I can go about the code? Not sure where to start, so though I should check with some experts on spriteKit
and gameplayKit
on how to handle it. Any framework I should look at for example...