IOS 入门(基于 IOS Apprentice 的真正面向零基础的完整解决的方案)

010 计算得分

2017-10-14  本文已影响19人  ErlichLiu

上一节

上一节我们的做的游戏基本已经可以玩了,后面几乎都是对游戏的优化过程,这同样重要且有意思。这一节我们将花很长的篇幅来讲述如何计算得分的相关问题。

计算得分


其实计算得分是一个很简单的思路,就是来计算currentValuetargetValue之间的差值嘛。

但是会出现一个问题,如果比较的情况像下面一样,就会出现负值,所以记下来一点,我们要解决负数的问题。

算法


你即将做的就是使用算法来做运算,算法就是在一段时间内,让机器逐地解决计算问题。而你要设计的算法很简单。

目前有很多著名的算法,比如说快速排序算法,它可以快速的通过二进制搜索并排学出一个表单。人们已经创造出很多算法,你可以直接把它们用在你的程序中,可以节省很多你思考的时间。

然而你还是要在程序里写一点你自己的算法,比如简单的排序,太难的算法可能会让你直接陷入绝望的境地,对于初学者就不推荐直接挑战很难的算法。但这个过程依旧很有意思。

在计算机科学领域的主要任务就是学习并寻找到更好的算法来解决问题。

你通常可以简单的描述出算法,算法通常是一系列的计算过程,你也可以在头脑或者纸上演绎一遍算法过程,但是对于一些太复杂的算法就很头疼了,你需要将这些算法转换成计算机代码才能解决。

如果你不知道如何将算法中计算的步骤用代码呈现出来,那你就找张纸,然后把你要计算的东西,一步一步的写在纸上,然后再想每一步应该怎样转换成代码。

一旦你知道这个过程是怎么回事了,那后面的都是小菜一碟了。

在算法中,是可以使用不同的方法解决问题的,我将给你展示两个可以相互代替的算法:

var difference: Int 
if currentValue > targetValue {
  difference = currentValue - targetValue 
} else if targetValue > currentValue {
  difference = targetValue - currentValue 
} else {
  difference = 0 
}

if 结构是我们新介入的一个结构,它可以让你的代码做出决策,运作的方式是这样的:

if something is true { 
  then do this 
} else if something else is true {
  then do that instead 
} else {
  do something when neither of the above are true 
}

在 if 之后的语句称之为逻辑条件,如果这个条件是真的,比如currentValue>targetValue,那么紧接着这个逻辑条件语句花括号的代码就会被执行。

如果这个条件语句是假的,计算机就会继续寻找下一个 else if 后面跟着的条件语句并验证,如果条件被验证为真,那么执行这个语句后面花括号的内容,如果是假,那继续向下寻找 else 或者 else if 语句,如果只剩下 else 并且没有条件语句的时候,那么就会执行这个 else 后花括号内的内容。

在实现当前计算分数的算法前,我们先要有一个本地的变量来就三差值,我们将变量的名称定为difference吧。

var difference:Int = 0

然后我们开始计算currentValuetargetValue的差值,但这之前,我们要先判断谁大。

if currentValue > targetValue {

如果currentValue真的大于targetValue,那我们这样计算差值

difference = currentValue - targetValue

你看到我选择当前值和目标值的英文作为变量,采用差值的英文也作为变量,你当然也可以把这句话写为a = c - b,这样会使代码表述的含义不够明确。

如果当前值并不大于目标值,我们就要执行下面的语句了

} else if targetValue > currentValue {

这时候,我们在判断,是否目标值大于当前值呢?如果大于的话,那我们执行下面的代码。

difference = targetValue - currentValue

如果当前值既不大于也不小于目标值,那就只有一种结果了,所以我们会直接写一个 else ,就不用再写条件语句了

} else { 
difference = 0 
}

让我们把这个算法放入到动作方法中,把它添加到showAlert()方法中。

@IBAction func showAlert() {
var difference: Int 
if currentValue > targetValue { 
difference = currentValue - targetValue
} else if targetValue > currentValue { 
difference = targetValue - currentValue 
} else {
difference = 0 
}
let message = "The value of the slider is: \(currentValue)" + "\nThe target value is: \(targetValue)" + "\nThe difference is: \(difference)" 
. . .
}

现在运行程序,你就能看到放上差值计算后的效果啦。

计算差值的替代方法


在这之前你可能就已经注意到了除了对比当前值和目标值大小,然后计算出一个正数的差值的方法了。上面的算法需要八行代码,我们还有更简洁的办法。

直接用目标值减去当前值,如果得出的结构是负数,那就乘以 -1 让它成为正数。

那我们的算法写出来大概是这样

var difference = currentValue - targetValue 
if difference < 0 { 
difference = difference * -1 
}

相较于之前的算法,这个已经简化了不少了,而且还有一个变化,你创建了一个变量,并且给变量赋值,这些都是发生在一行代码上,所以你不一定需要这样写写代码:

var difference: Int difference = currentValue - targetValue

其中第二句的difference = difference * -1还可以继续简化为difference *= -1

当然你也可以这样写

var difference = currentValue - targetValue 
if difference < 0 { 
difference = -difference 
}

使用新的算法代码来代替以前的算法吧

@IBAction func showAlert() {
var difference = currentValue - targetValue 
if difference < 0 { 
difference = difference * -1 
}
let message = . . .
}

重新运行 App,你会得到跟之前别无二致的体验,除此之外,像之前你用过的arc4random_uniform()lround()函数一样,还有一个函数是专门求绝对值的abs()。整个过程可以极大地简化为一句话let difference = abs(targetValue - currentValue),有了这个方法,你完全不必关心到底是谁减去谁了,现在你可以将这个算法改成这样:

@IBAction func showAlert() {
let difference = abs(targetValue - currentValue)
let message = . . .
}

所以经过一系列的分析和简化,整个过程是不是已经被大大简化啦。

你是否还注意到其它的变化?我们在这一次声明变量用的的是 let 而不是 var了,这两个最大的区别就是,用 var 声明的变量,变量的值在以后是可以随意被赋值的;用 let 声明的常量意味着你只能给常量赋一次值。所以在称谓上你就看出差别了,常量就是一直都是一个值,而变量的值则是可以根据需求变化的。

在这里,我们之所以用常量是因为只计算一次差值,并不会再次产生变化,所以用常量会更好点。还有一些消息啊、提醒啊之类的都会采用常量的方式来声明。常量在 Swift 中很常见,通常用来在一小段时间来存储一个不会变的数值,如果存储时间很长,但值不会发生变化的时候也用常量而非变量来保存。

得分是怎么来的?


现在你已经可以轻松地获取到滑块的值,并计算差值。
现在来修改一下 showAlert()方法中的内容:

@IBAction func showAlert() {
let difference = abs(targetValue - currentValue) 
let points = 100 - difference
let message = "You scored \(points) points" 
. . .
}

如果你正好把滑块放在目标值上,那最大的分数就是 100 分,相反最小的分数就是 1 分了(因为滑块范围是 1 - 100 哦),所以即便是做的最差也有一分

记录玩家的总分数


想要记录玩家的总分数,就要累加玩家每个轮次的新分数,所以我们要添加一个新的变量来记录玩家的总分数这个值。

class ViewController: UIViewController {
var currentValue: Int = 0 
var targetValue: Int = 0 
var score = 0 //这是新添加的一行

发现什么新的问题了吗?这一次我们声明 score 变量的时候并没有指明这是一个 Int 类型的。这就涉及到 Swift 的一个特性,如果你不表明这是什么类型的变量,那么 Swift 会根据给这个变量赋的值来推断这个变量可能是什么类型的。所以,实际上你也不必指明其它变量到底是什么类型的。

所以之前用到的变量可以这样来声明:

var currentValue = 0 
var targetValue = 0

得益于 Swift 的智能推断,这样你就可以只是声明一个变量并赋一个初始值,大多数时候 Swift 都是可以正确的推断出你要声明的变量类型。

@IBAction func showAlert() { 
let difference = abs(targetValue - currentValue) 
let points = 100 - difference 
score += points //这一行是新添加的
let message = "You scored \(points) points" 
. . .
}

这里倒是没什么以外的,只是添加了这一行代码

score += points

这样就可以计算出总成绩了,当然你也可以这样来写这行代码:

score = score + points

我会比较倾向于使用 += 这样更加简短的写法。

把总分数显式在屏幕上


其实在这之前你已经做过了一次这个流程了,创建一个 outlet 然后将 Label 和 outlet 挂钩。如果你这一次不需要我的帮助,那么就证明你已经掌握了这个流程了。

@IBOutlet weak var scoreLabel: UILabel!
func updateLabels() { 
targetLabel.text = String(targetValue) 
scoreLabel.text = String(score) 
}

在这个方法中也没有什么陌生内容,把 score 变量从 Int 类型转换为 String 类型并赋值。这样当 score 分数发生变化,并执行到updateLabels()方法的时候,屏幕上的显式分数的 Label 就会被刷新显式。

记录轮次


既然要记录分数,那就要同步记录这是第几个轮次。

需要记录轮次信息,那就需要一个新的变量来存储,所以要声明一个新的变量:

var round = 0

如果你声明变量的类型也是 OK 的:

var round:Int = 0

还需要添加一个 outlet:

@IBOutlet weak var roundLabel: UILabel!

然后你需要将显示轮次信息的 Label 和 outlet 绑定上。

然后修改updateLabels()方法:

func updateLabels() { 
targetLabel.text = String(targetValue) 
scoreLabel.text = String(score) 
roundLabel.text = String(round) 
}

大概知道哪里放增加轮次的逻辑了吗?我觉得放在startNewRound()方法里是个不错的地方。毕竟每开始新的一个轮次都会执行这个方法,所以把计算轮次的逻辑放在这里是合理的。

修改startNewRound()方法:

func startNewRound() { 
round += 1 // 这一行是新添加的
targetValue = 1 + Int(arc4random_uniform(100)) 
currentValue = 50 
slider.value = Float(currentValue)
}

当你声明round变量的时候,你赋的初值是 0 ,当程序运行时,初始化的 round = 0,当你点击完 Hit Me 后执行了 startNewRound()方法,然后 round = 1。如果你遇到了点击一次 Hit Me 后轮次直接变成 2 的情况,请检查一下 showAlert()方法中updateLabel()是否在startNewRound()方法前面。

一个真正可玩的 App 我们就算是做完了,快去回顾一下这个小节的东西吧,下一个小节我们将开始逐步完善和美化这个 App。

下一节

上一篇下一篇

猜你喜欢

热点阅读