这节课我们要开发一个小游戏:Tick Tack Toe 也叫 Noughts & Crosses,翻译过来应该是井字游戏。
我百度了一下,有人这么回答的:
是一种俩人制的棋类游戏,又名为圈圈叉叉,两名游戏者在圈圈(O)和叉叉(X)两种符号中,各选一种,然后在一张3x3格(也就是我们所说九宫格)棋盘上轮流下,一般叉叉先走,当一种符号在棋盘中的空格内,或竖向,或横向,或斜向成3连的情形时,该符号的游戏者胜出,此游戏由于不复杂,一般适用于小朋友们。
课程笔记文集地址:Udemy课程:The Complete iOS 9 Developer Course - Build 18 Apps
也就是从这节视频中,我意识到,编程的思维的重要性。我可以照着作者的方法迅速敲击出来代码,然后实现这个效果,但是,如果规则变了呢?或者规则没变,我没看过这节视频,我该如何实现这样的规则呢?为什么这一步要用这个方法呢?为什么要遍历结果呢?还有没有其他的方法可以实现同样的规则?单单是这些问题,让我在这节视频上困了一个周,继续往下看的进度严重被延后了。
庆幸的是,我在这个周里,接触到了李笑来的书《把时间当朋友》,里面说,卡住了,先继续往前走,只要把这个卡住的记下来就好了,说不定等你往前走到一定程度,回头看,发现这个问题就能轻易解决了呢。
所以,我决定把这节视频先搁置起来,继续往前走,继续看剩下的视频,记得有天要回来。
视频看到了 27:00。
一、布局Storeboard
1.首先布局棋盘背景,使用UIIamgeView控件,设置AutoLayout约束。
2.放置UIButton控件,去掉Button的文字使用图片,放置到正确的位置然后,设置AutoLayout约束。然后依次放置设置剩下的Button控件,一共有9个UIButton控件。实际效果图如下:
3.给Button控件建立Action和Outlet连接,不用给9个Button都建立连接,只给第一个建立连接就可以了。(剩下的8个Button的Action连接后面说怎么做)
@IBOutlet weak var button: UIButton!
@IBAction func buttonPressed(sender: AnyObject) {
}
二、写代码:初步布局
1. setImage
方法
Button控件上显示哪个图片可以用setImage
方法,如下:
@IBAction func buttonPressed(sender: AnyObject) {
var image = UIImage(named: "cross.png")
button.setImage(image, forState: UIControlState.Normal)
}
这样点击一下按钮,按钮外表就变成了叉号。
2.给剩下的8个Button控件创建Action连接
将剩下的8个Button控件用Ctrl拖拽法连接到Action的代码上,如下图:
拖拽8次。这样点击任何一个按钮,第一个按钮外表就变成了叉号。这不是我们想要的效果啊。
3. 参数 sender 的作用
我们想要的效果是,点击哪个按钮,哪个按钮变成叉号。这怎么办?建立八个Outlet连接?太麻烦了!这时候,我们就用到了@IBAction func buttonPressed():
方法中参数 sender
的功能:
@IBAction func buttonPressed(sender: AnyObject) {
var image = UIImage(named: "cross.png")
sender.setImage(image, forState: UIControlState.Normal)
}
用了sender,我们可以把一开始创建的Outlet连接删掉了。
这效果对了。
4. Tag 属性的作用
Button控件有个属性叫 tag,如下图:
每个Button控件的Tag是一个单独的数字,这样就给每个Button控件一个唯一的标识符。Tag的作用像是标识符(identifier),不过tag只能是Int类型的数值且正数。
第一个Button控件的tag是0,第二个是2,以此类推,第九个是8。
这样,当我们点击集中某个按钮时,sender.tag
就是我们设置的对应的tag值。比如我们给最后一个Button设置的tag值是8,点击最后一个Button时,sender.tag
实际上就是数字8。
5.实现圆圈叉号交替出现的效果
在Storyborad中去掉Button的图片,按钮是叉号还是圆圈全部用代码来控制。
// 1 是圆圈 2 是叉号
var activePlayer = 1
@IBAction func buttonPressed(sender: AnyObject) {
var image = UIImage()
if activePlayer == 1 {
image = UIImage(named: "nought.png")!
activePlayer = 2
} else {
image = UIImage(named: "cross.png")!
activePlayer = 1
}
sender.setImage(image, forState: UIControlState.Normal)
}
运行,点击不同的框,先出现圆圈然后叉号,不错,几乎是我们的效果了。不过,点击出现圆圈,再点击一下,变成叉号了。需要的效果是,在某个框中画了圈或者叉号,就不能再改了。
三、写代码:实现游戏规则
1.数组的力量
现在我们想办法让被点击过一次的按钮再次点击时不会更换图片。这里我们用到数组。
var gameState = [0,0,0,0,0,0,0,0,0]
@IBAction func buttonPressed(sender: AnyObject) {
if gameState[sender.tag] == 0 {
var image = UIImage()
gameState[sender.tag] = activePlayer
if activePlayer == 1 {
image = UIImage(named: "nought.png")!
activePlayer = 2
} else {
image = UIImage(named: "cross.png")!
activePlayer = 1
}
sender.setImage(image, forState: UIControlState.Normal)
}
}
妙用了数组的index 和 sender.tag
。
关键代码:
gameState[sender.tag]
sender.tag
可能的值是数字0-8,数组gameState
的index也是0-8
2.什么条件就赢了?
这才是一个游戏的关键,涉及到算法,很多游戏的算法才是关键,界面什么的都好弄。
先列出作者的代码,烧脑:
var winningCombinations = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
@IBAction func buttonPressed(sender: AnyObject) {
...
for combination in winningCombinations {
if gameState[combination[0]] != 0 && gameState[combination[0]] == gameState[combination[1]] && gameState[combination[1]] == gameState[combination[2]] {
print("你赢啦!!")
}
}
}
想要实现游戏规则,首先我们要判断出什么情况下就算是赢了。比如横着3个连成行,竖着3个连成排,斜着3个连成一条线。
横着3个连成行:[0,1,2],[3,4,5],[6,7,8]
竖着3个连成排[0,3,6],[11,4,7],[2,5,8]
斜着3个连成一条线[0,4,8],[2,4,6]
看代码:
关键代码是:
gameState[combination[0]] != 0 && gameState[combination[0]] == gameState[combination[1]] && gameState[combination[1]] == gameState[combination[2]]
可以拆分成3个条件:
1)首先不等于0
2)第一个数等于第二个
3)第二个数等于第三个数字
只需要遍历所有的情况,然后只要符合这个情况,就算赢啦
3.有人赢了之后就停止游戏
gameActive = false
在你赢了之后,赋值
@IBAction func buttonPressed(sender: AnyObject) {
//这里加入 gameActive 状态的判断
if (gameState[sender.tag] == 0 && gameActive == true) {
4.判断是谁赢了,弹出显示文案
if gameState[combination[0]] == 1 {
gameOverLabel.text = "Noughts have won!"
} else {
gameOverLabel.text = "Crosses have won!"
}
endGame()
endGame的方法如下:
func endGame() {
gameOverLabel.hidden = false
playAgainButton.hidden = false
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.gameOverLabel.center = CGPointMake(self.gameOverLabel.center.x + 500, self.gameOverLabel.center.y)
self.playAgainButton.center = CGPointMake(self.playAgainButton.center.x + 500, self.playAgainButton.center.y)
})
}
四、写代码:
课程笔记记录到了27:00