這個(gè)對(duì)角棋我將使用swift+SpriteKit實(shí)現(xiàn)岂津。本篇介紹基本的邏輯處理誊爹。
初步考慮贞远,有以下過程是我必須實(shí)現(xiàn)的:
1.繪制棋盤,加載棋子;
2.移動(dòng)棋子的邏輯笨忌;
3.判定勝負(fù)的邏輯蓝仲。
在實(shí)際做的過程中,還有更多零碎的邏輯等著我實(shí)現(xiàn)官疲。
1.定義各種記錄數(shù)據(jù)的變量袱结。
//棋盤的變量
var chessMap:ChessMap?
//記錄你此時(shí)是否已經(jīng)點(diǎn)擊了某個(gè)棋子
var clickedPiece:Bool = false
//所有棋子
var allPieceArray:NSMutableArray = []
//記錄已點(diǎn)擊的棋子位置:從0到8
var clickedPointIndex = -1
//記錄白棋和黑棋的位置
var allPosition:[Int] = [0,3,6,2,5,8]
//分別記錄黑棋和白棋的位置
var whitePieces:[Int] = []
var blackPieces:[Int] = []
2.創(chuàng)建棋盤和棋子,棋子在游戲過程中是不必消除的途凫。
override func didMove(to view: SKView) {
if !contentCreated
{
createSceneContens()
contentCreated = true
}
}
//創(chuàng)建內(nèi)容
private func createSceneContens()
{
self.backgroundColor = SKColor.yellow
self.scaleMode = .aspectFit
//增加背景棋盤:如何移到線條下方
let chessBackImage = SKSpriteNode.init(imageNamed: "chessBack")
chessBackImage.position = CGPoint(x:SCREEN_WIDTH/2,y:200)
self.addChild(chessBackImage)
chessMap = ChessMap.init(origin: CGPoint(x:SCREEN_WIDTH/2-80 ,y:120))
self.addChild(chessMap!)
createAllPieces()
}
//創(chuàng)建6枚棋子
private func createAllPieces()
{
//寫到這里
for chessIndex in 0...8
{
if chessIndex%3 == 0
{
let singlePiece = SKSpriteNode.init(imageNamed: "blackChess")
singlePiece.position = (chessMap?.positions[chessIndex])!
singlePiece.name = "blackChess"
self.addChild(singlePiece)
allPieceArray.add(singlePiece)
}
else if chessIndex%3 == 2
{
let singlePiece = SKSpriteNode.init(imageNamed: "whiteChess")
singlePiece.position = (chessMap?.positions[chessIndex])!
singlePiece.name = "whiteChess"
self.addChild(singlePiece)
allPieceArray.add(singlePiece)
}
}
}
3.移動(dòng)棋子的邏輯
這一步驟考慮的比較多垢夹,比如兩個(gè)玩家怎么輪流出手,棋子從一個(gè)位置是否能夠移動(dòng)到那里维费,移動(dòng)之后是否分出勝負(fù)了果元,所以自然會(huì)調(diào)用別的邏輯。
//移動(dòng)棋子的邏輯犀盟,并更新數(shù)據(jù),單個(gè)函數(shù)絕不超過50行
private func moveSinglePiece(positionIndex:Int)
{
let _position:CGPoint = (chessMap?.positions[positionIndex])!
for i in 0...allPieceArray.count-1
{
let whitePiece0:SKSpriteNode = allPieceArray[i] as! SKSpriteNode
if whitePiece0.position == chessMap?.positions[clickedPointIndex]
{
whitePiece0.position = _position
//同時(shí)更新所有棋子位置的數(shù)組
for k in 0...allPosition.count-1 {
if allPosition[k] == clickedPointIndex
{
allPosition.remove(at: k)
break
}
}
allPosition.append(positionIndex)
}
if whitePiece0.name == "whiteChess"
{
for index in 0...(chessMap?.positions.count)!-1
{
if whitePiece0.position == chessMap?.positions[index]
{
whitePieces.append(index)
break
}
}
}
else if whitePiece0.name == "blackChess"
{
for index in 0...(chessMap?.positions.count)!-1
{
if whitePiece0.position == chessMap?.positions[index]
{
blackPieces.append(index)
break
}
}
}
}
//判定是否一方勝利:必須統(tǒng)計(jì)移動(dòng)過后的棋子,并給出場(chǎng)景的提示
if hasWinTheGame(positions: whitePieces){
print("勝負(fù)已分")
showWinTips(blackWin: false)
}
else if hasWinTheGame(positions: blackPieces)
{
showWinTips(blackWin: true)
}
whitePieces = []
blackPieces = []
}
4.單個(gè)小邏輯的實(shí)現(xiàn)
//通過數(shù)組判斷勝利邏輯
private func hasWinTheGame(positions:[Int])->Bool
{
if (positions.contains(0)&&positions.contains(4)&&positions.contains(8)) || (positions.contains(2)&&positions.contains(4)&&positions.contains(6)){
return true
}
else
{
return false
}
}
//游戲結(jié)束而晒,加載勝利的動(dòng)畫
private func showWinTips(blackWin:Bool){
let winNode = addWinNode(blackWin: blackWin)
self.addChild(winNode)
let moveUp = SKAction.moveBy(x: 0, y: 100, duration: 0.5)
let zoom = SKAction.scale(to: 2, duration: 0.25)
let pause = SKAction.wait(forDuration: 1)
let fadeaway = SKAction.fadeOut(withDuration: 0.25)
let remove = SKAction.removeFromParent()
//設(shè)置多個(gè)action的集合
let moveSequence = SKAction.sequence([moveUp,zoom,pause,fadeaway,remove])
winNode.run(moveSequence, completion: {
//移除原有的SKNode
winNode.removeAllActions()
winNode.removeFromParent()
})
}
//加載勝利的場(chǎng)景:最好能有音樂
private func addWinNode(blackWin:Bool)->SKLabelNode
{
let winLabel = SKLabelNode.init(fontNamed: "Chalkduster")
if blackWin {
winLabel.text = "黑棋獲得勝利"
}
else
{
winLabel.text = "白棋獲得勝利"
}
winLabel.fontSize = 24
winLabel.position = CGPoint(x:self.size.width/2,y:self.size.height/2)
winLabel.fontColor = SKColor.red
winLabel.name = "winChessNode"
return winLabel
}
//判斷是否能夠從一個(gè)點(diǎn)移動(dòng)到另一個(gè)點(diǎn)
private func canMovePiece(position1:Int,position2:Int)->Bool
{
let canMoveHorizotal = abs(position1-position2)==3 //橫向移動(dòng)
let canMoveVertical = abs(position1-position2)==1 && (position1/3)==(position2/3)//縱向移動(dòng)
let canMoveSlant = (abs(position1-position2)==4 && position1%4==0) || (abs(position1-position2)==2 && (position1*position2==8 || position1*position2==24))//斜向移動(dòng)
return canMoveHorizotal || canMoveVertical || canMoveSlant
}
5.重載touches:方法,將所有邏輯集合阅畴。這里需要判斷是否點(diǎn)擊在棋子的位置倡怎。
//主要還是依賴于觸摸方法:必須簡(jiǎn)化該方法
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchPosition = touch?.location(in: self)
var isTouchPositionForPiece = false
//判斷是否在9個(gè)點(diǎn)上->判斷是否在當(dāng)前位置上
for positionIndex in 0...8
{
let _position:CGPoint = (chessMap?.positions[positionIndex])!
if distanceFrom(point1: _position, point2: touchPosition!) <= 20
{
isTouchPositionForPiece = true
if allPosition.contains(positionIndex)//必須點(diǎn)擊了已有棋子的位置
{
clickedPiece = true
clickedPointIndex = positionIndex
}
else//否則將上次點(diǎn)擊的棋子移動(dòng)該處
{
if clickedPiece && clickedPointIndex>=0
{
//先判斷是否能夠移動(dòng),如果不能后面不在操作,同時(shí)取消選中狀態(tài)
if !canMovePiece(position1: clickedPointIndex, position2: positionIndex)
{
clickedPiece = false
//點(diǎn)擊完畢之后恢復(fù)
clickedPointIndex = -1
return
}
moveSinglePiece(positionIndex: positionIndex)
}
clickedPiece = false
//點(diǎn)擊完畢之后恢復(fù)
clickedPointIndex = -1
}
break
}
}
}
以上就是實(shí)現(xiàn)基本功能的過程了监署。當(dāng)然還有開場(chǎng)動(dòng)畫颤专,音樂等效果都屏蔽了,不是核心邏輯钠乏。
因?yàn)橛螒虮容^簡(jiǎn)單栖秕,沒有實(shí)現(xiàn)業(yè)務(wù)實(shí)現(xiàn)和業(yè)務(wù)邏輯的分離。另一個(gè)重要的問題是雖然節(jié)點(diǎn)是可以移除和釋放的缓熟,但是一旦在iOS應(yīng)用中加載了SpriteKit頁(yè)面內(nèi)存總會(huì)增加幾十M累魔,現(xiàn)在還沒找到辦法能回收這部分內(nèi)存。
寫完又發(fā)現(xiàn)自己寫的有點(diǎn)弱够滑,繼續(xù)努力吧垦写!