这个对角棋我将使用swift+SpriteKit实现。本篇介绍基本的逻辑处理。
初步考虑,有以下过程是我必须实现的:
1.绘制棋盘,加载棋子;
2.移动棋子的逻辑;
3.判定胜负的逻辑。
在实际做的过程中,还有更多零碎的逻辑等着我实现。
1.定义各种记录数据的变量。
//棋盘的变量
var chessMap:ChessMap?
//记录你此时是否已经点击了某个棋子
var clickedPiece:Bool = false
//所有棋子
var allPieceArray:NSMutableArray = []
//记录已点击的棋子位置:从0到8
var clickedPointIndex = -1
//记录白棋和黑棋的位置
var allPosition:[Int] = [0,3,6,2,5,8]
//分别记录黑棋和白棋的位置
var whitePieces:[Int] = []
var blackPieces:[Int] = []
2.创建棋盘和棋子,棋子在游戏过程中是不必消除的。
override func didMove(to view: SKView) {
if !contentCreated
{
createSceneContens()
contentCreated = true
}
}
//创建内容
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()
}
//创建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.移动棋子的逻辑
这一步骤考虑的比较多,比如两个玩家怎么轮流出手,棋子从一个位置是否能够移动到那里,移动之后是否分出胜负了,所以自然会调用别的逻辑。
//移动棋子的逻辑,并更新数据,单个函数绝不超过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
//同时更新所有棋子位置的数组
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
}
}
}
}
//判定是否一方胜利:必须统计移动过后的棋子,并给出场景的提示
if hasWinTheGame(positions: whitePieces){
print("胜负已分")
showWinTips(blackWin: false)
}
else if hasWinTheGame(positions: blackPieces)
{
showWinTips(blackWin: true)
}
whitePieces = []
blackPieces = []
}
4.单个小逻辑的实现
//通过数组判断胜利逻辑
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
}
}
//游戏结束,加载胜利的动画
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()
//设置多个action的集合
let moveSequence = SKAction.sequence([moveUp,zoom,pause,fadeaway,remove])
winNode.run(moveSequence, completion: {
//移除原有的SKNode
winNode.removeAllActions()
winNode.removeFromParent()
})
}
//加载胜利的场景:最好能有音乐
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
}
//判断是否能够从一个点移动到另一个点
private func canMovePiece(position1:Int,position2:Int)->Bool
{
let canMoveHorizotal = abs(position1-position2)==3 //横向移动
let canMoveVertical = abs(position1-position2)==1 && (position1/3)==(position2/3)//纵向移动
let canMoveSlant = (abs(position1-position2)==4 && position1%4==0) || (abs(position1-position2)==2 && (position1*position2==8 || position1*position2==24))//斜向移动
return canMoveHorizotal || canMoveVertical || canMoveSlant
}
5.重载touches:方法,将所有逻辑集合。这里需要判断是否点击在棋子的位置。
//主要还是依赖于触摸方法:必须简化该方法
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let touch = touches.first
let touchPosition = touch?.location(in: self)
var isTouchPositionForPiece = false
//判断是否在9个点上->判断是否在当前位置上
for positionIndex in 0...8
{
let _position:CGPoint = (chessMap?.positions[positionIndex])!
if distanceFrom(point1: _position, point2: touchPosition!) <= 20
{
isTouchPositionForPiece = true
if allPosition.contains(positionIndex)//必须点击了已有棋子的位置
{
clickedPiece = true
clickedPointIndex = positionIndex
}
else//否则将上次点击的棋子移动该处
{
if clickedPiece && clickedPointIndex>=0
{
//先判断是否能够移动,如果不能后面不在操作,同时取消选中状态
if !canMovePiece(position1: clickedPointIndex, position2: positionIndex)
{
clickedPiece = false
//点击完毕之后恢复
clickedPointIndex = -1
return
}
moveSinglePiece(positionIndex: positionIndex)
}
clickedPiece = false
//点击完毕之后恢复
clickedPointIndex = -1
}
break
}
}
}
以上就是实现基本功能的过程了。当然还有开场动画,音乐等效果都屏蔽了,不是核心逻辑。
因为游戏比较简单,没有实现业务实现和业务逻辑的分离。另一个重要的问题是虽然节点是可以移除和释放的,但是一旦在iOS应用中加载了SpriteKit页面内存总会增加几十M,现在还没找到办法能回收这部分内存。
写完又发现自己写的有点弱,继续努力吧!