差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
mp_resp_game [2021/10/24 23:05] gongyusu [2. 两人游戏] |
mp_resp_game [2022/02/24 22:19] (当前版本) gongyusu [1. 单人游戏] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | ## 反应时间测试 | + | ## 编写一个反应时间测试的游戏 |
- | 使用一个LED和按钮创建一个简单的反应计时游戏,可以供一个人玩,也可以两个人一起玩。 | + | [[mcu|微控制器]]不仅出现在工业设备中,还广泛用在家庭中的许多电子产品中,包括玩具和游戏。 |
- | 微控制器不仅出现在工业设备中,还为家庭中的许多电子产品提供动力,包括玩具和游戏。在本章中,我们将设计一款简单的反应时间测试游戏,看看你的朋友中谁会在灯熄灭的一瞬间能最快点击按键。 | + | 在本节中,我们将设计一款简单的反应时间测试游戏,看看我们的朋友中谁会在灯熄灭的一瞬间能最快点击按键。 |
- | 对反应时间的研究被称为心理计时法,虽然这是一门硬科学,它也是许多基于技能的游戏的基础,包括你即将创建的游戏。你的反应时间 - 你的大脑来响应并判断、发送信号去执行的过程,以毫秒计,人类的平均反应时间大约是200-250毫秒, 但有些人喜欢得更快的反应时间, 给他们一个真正的优势在游戏中! | + | 对反应时间的研究被称为心理计时法,虽然这是一门硬科学,它也是许多基于技能的游戏的基础,包括我们即将创建的游戏。我们的反应时间 - 我们的大脑来响应并判断、发送信号去执行的过程是以毫秒计的,人类的平均反应时间大约在200-250毫秒范围, 但有些人可以达到更快的反应时间, 通过这个游戏就给他们一个机会来展示他们的优势! |
- | 对于这个项目,你需要用到你的Pico核心板、学习板上任何颜色的LED,一个或两个按键开关。 | + | 这个游戏是使用一个LED和按钮创建一个简单的反应计时游戏,可以一个人测试,也可以两个人一起玩,我们需要用到[[rpi_pico|Pico]]核心板、学习板上任何颜色的[[LED]],1个或2个按键开关。 |
### 1. 单人游戏 | ### 1. 单人游戏 | ||
- | 在这个游戏的程序中,使用学习板上的一颗LED作为输出设备,取代了你通常在游戏机上使用的电视;使用学习板上的一个按键进行控制,而你的Pico是游戏主机,尽管比你通常看到的要小得多! | + | 在这个游戏的程序中,使用学习板上的一颗LED作为输出设备,取代了我们在游戏机上通常使用的电视;使用学习板上的一个按键进行控制,而我们的[[rpi_pico|Pico]]是游戏主机,尽管比我们通常看到的要小得多! |
- | 现在你需要真正地编写游戏。像往常一样,将你的Pico连接到你的树莓派或其他电脑,并加载Thonny、创建一个新程序、并通过导入machine库来启动它,这样你就可以控制你的Pico的GPIO引脚: | + | 现在我们需要真正地编写游戏。像往常一样,将我们的Pico连接到树莓派或其它电脑上,并加载[[Thonny_ide|Thonny]]、创建一个新程序、并通过导入machine库来启动它,这样我们就可以控制Pico的GPIO引脚: |
<code python> | <code python> | ||
行 17: | 行 17: | ||
</code> | </code> | ||
- | 你还需要utime库: | + | 我们还需要导入utime库: |
<code python> | <code python> | ||
import utime | import utime | ||
</code> | </code> | ||
- | 此外,你将需要一个新的库:urandom,它是一个创建随机数的库,它在这个游戏中是一个关键的部分,使得游戏更有趣,并使用在这个游戏中,以防止玩家谁已经玩它之前简单地倒数固定的秒点击运行按钮。 | + | 此外,我们还需要一个新的库 - urandom,它是一个创建随机数的库,在这个游戏中是一个关键的部分,能使得游戏更有趣,并可以防止已经玩过这个游戏的玩家通过简单地倒数固定的秒点击运行按钮,从而影响了测试的真实效果。 |
- | 接下来,设置一个按下的变量为False(稍后详细介绍),并设置您正在使用的两个引脚: | + | 接下来,设置一个按下的变量为False(稍后详细介绍),并设置我们正在使用的两个引脚: |
* 用于LED的GP16(黄色的LED) | * 用于LED的GP16(黄色的LED) | ||
* 用于按钮开关的GP12 | * 用于按钮开关的GP12 | ||
行 38: | 行 38: | ||
MicroPython中断请求以完全相同的方式工作,它允许某些东西中断主程序,在本例中是按下一个按钮开关。在某些方面,它类似于一个线程,因为有一段代码位于主程序之外。不过,与线程不同的是代码不会持续运行,它只在触发中断时运行。 | MicroPython中断请求以完全相同的方式工作,它允许某些东西中断主程序,在本例中是按下一个按钮开关。在某些方面,它类似于一个线程,因为有一段代码位于主程序之外。不过,与线程不同的是代码不会持续运行,它只在触发中断时运行。 | ||
- | 首先为中断定义一个处理程序,这被称为回调函数,是中断触发时运行的代码。与任何类型的嵌套代码一样,处理程序的代码——第一行之后的所有代码——每一层都需要缩进四个空格,Thonny会自动为你做这件事。 | + | 首先为中断定义一个处理程序,这被称为回调函数,是中断触发时运行的代码。与任何类型的嵌套代码一样,处理程序的代码——第一行之后的所有代码——每一层都需要缩进四个空格,Thonny会自动为我们做这件事。 |
<code python> | <code python> | ||
def button_handler(pin): | def button_handler(pin): | ||
行 47: | 行 47: | ||
</code> | </code> | ||
- | 这个处理程序首先检查被按下的变量的状态,然后将其设置为True以忽略进一步的按键按压(从而结束游戏)。然后输出有关触发中断的引脚的信息。这不是太重要的时刻,你只有一个引脚配置为一个输入,GP12,所以中断将总是来自那个引脚,但让你测试你的中断很容易。 | + | 这个处理程序首先检查被按下的变量的状态,然后将其设置为True以忽略进一步的按键按压(从而结束游戏)。然后输出有关触发中断的引脚的信息。这不是太重要的时刻,我们目前只有一个引脚(GP12)配置为一个输入, 所以中断将总是来自那个引脚,让我们测试中断比较容易。 |
继续下面的程序,记住删除Thonny自动创建的缩进,以下代码不是处理程序的一部分: | 继续下面的程序,记住删除Thonny自动创建的缩进,以下代码不是处理程序的一部分: | ||
行 56: | 行 56: | ||
</code> | </code> | ||
- | 这段代码对你来说马上就很熟悉了: | + | 这段代码对我们来说马上就很熟悉了: |
- 第一行将连接到GP16引脚的LED打开; | - 第一行将连接到GP16引脚的LED打开; | ||
- 下一行暂停了程序; | - 下一行暂停了程序; | ||
- | - 最后一行再次关闭LED,这是给玩家的信号,该按按键了。 | + | - 最后一行再次关闭LED,这是给玩家的信号,该摁下按键了。 |
- | 不是使用固定的延迟,它利用urandom库暂停程序5到10秒之间-“均匀”部分指的是这两个数字之间的均匀分布。 | + | 我们使用的延迟时间不是固定的,而是通过调用urandom库生成的随机时间 - 暂停程序5到10秒之间-“均匀”部分指的是这两个数字之间的均匀分布。 |
- | 不过,目前还没有什么东西等着按下按钮。你需要设置中断,通过在你的程序底部输入以下行: | + | 不过,目前还没有什么东西等着按下按钮。我们需要设置中断,通过在我们的程序底部输入以下行: |
<code python> | <code python> | ||
行 78: | 行 78: | ||
**中断申请的上升沿和下降沿** | **中断申请的上升沿和下降沿** | ||
- | 如果你需要编写一个触发中断的程序,无论管脚信号是上升还是下降都可以触发,你可以使用管道或竖条符号( | )来组合这两个触发信号: | + | 如果我们需要编写一个触发中断的程序,无论管脚信号是上升还是下降都可以触发,可以使用管道或竖条符号( | )来组合这两个触发信号: |
<code python> | <code python> | ||
button.irq(trigger= machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING,handler = button_handler) | button.irq(trigger= machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING,handler = button_handler) | ||
行 85: | 行 85: | ||
</WRAP> | </WRAP> | ||
- | 你的程序应该是这样的: | + | 我们的程序应该是这样的: |
<code python> | <code python> | ||
import machine | import machine | ||
行 106: | 行 106: | ||
</code> | </code> | ||
- | 单击“运行”按钮并将程序作为Reaction_Game.py 保存到您的Pico。 您会看到LED亮起:这是您的手指放在按钮上准备就绪的信号。 | + | 单击“运行”按钮并将程序作为Reaction_Game.py 保存到Pico。 我们会看到LED亮起:这是让我们把手指放在按钮上准备就绪的信号。 |
当LED熄灭时,尽快按下按钮。 | 当LED熄灭时,尽快按下按钮。 | ||
- | 当您按下按钮时,它会触发您之前编写的处理程序代码。 查看Shell区域:您会看到Pico打印了一条消息,确认中断是由引脚GP16触发的。您还将看到另一个详细信息:mode=IN告诉您该引脚已配置为输入。 不过,这条信息对游戏的意义不大:为此,您需要一种方法来计时玩家的反应速度。首先从按钮处理程序中删除行 print(pin) - 您不再需要它了。 | + | 当我们按下按钮时,它会触发我们之前编写的处理程序代码。查看Shell区域我们会看到Pico打印了一条消息,确认中断是由引脚GP16触发的。我们还将看到另一个详细信息:mode=IN告诉我们该引脚已配置为输入。 不过,这条信息对游戏的意义不大:为此,我们需要一种方法来计时玩家的反应速度。首先从按钮处理程序中删除行 print(pin) - 我们不再需要它了。 |
- | 转到程序底部并添加一个新行,就在设置中断的位置上方: | + | 转到程序底部,就在设置中断的位置上方添加一个新行: |
<code python> | <code python> | ||
timer_start = utime.ticks_ms() | timer_start = utime.ticks_ms() | ||
</code> | </code> | ||
- | 这将创建一个名为timer_start的新变量, | + | 这将创建一个名为timer_start的新变量,并用utime.ticks_ms()函数的输出填充它,该函数计算自utime库开始计数以来经过的毫秒数。这提供了一个参考点:刚好在LED熄灭之后和就在中断触发器准备好读取按钮按下之前的时间。 |
- | + | ||
- | 并用utime.ticks_ms()函数的输出填充它,该函数计算自utime库开始计数以来经过的毫秒数。这提供了一个参考点:刚好在LED熄灭之后和就在中断触发器准备好读取按钮按下之前的时间。 | + | |
接下来,返回按键处理程序并添加以下两行,记住它们需要缩进四个空格,以便MicroPython知道它们构成嵌套代码的一部分: | 接下来,返回按键处理程序并添加以下两行,记住它们需要缩进四个空格,以便MicroPython知道它们构成嵌套代码的一部分: | ||
行 126: | 行 124: | ||
print("Your reaction time was " + str(timer_reaction) + " milliseconds!") | print("Your reaction time was " + str(timer_reaction) + " milliseconds!") | ||
</code> | </code> | ||
- | 第一行创建了另一个变量,这次是实际触发中断的时间——换句话说,当你按下按键时。不过,它不是像以前那样简单地从utime.ticks_ms()读取数据,而是使用 utime.ticks_diff()一个函数它提供了触发这行代码的时间与变量timer_start中保存的参考点之间的差异。 | ||
- | 第二行打印结果,但使用连接来很好地格式化它。文本或字符串的第一位告诉用户后面的数字是什么意思; + 表示接下来的任何内容都应该与该字符串一起打印。在这种情况下,接下来是内容timer_reaction 变量 - 以毫秒为单位获取计时器参考点与按下按钮并触发中断之间的差异。 | + | 第一行创建了另一个变量,这次是实际触发中断的时间,换句话说,当我们按下按键时。不过,它不是像以前那样简单地从utime.ticks_ms()读取数据,而是使用 utime.ticks_diff()一个函数它提供了触发这行代码的时间与变量timer_start中保存的参考点之间的差异。 |
+ | |||
+ | 第二行打印结果,使用了格式化的打印方式,文本或字符串的第一位告诉用户后面的数字是什么意思; + 表示接下来的任何内容都应该与该字符串一起打印。在这种情况下,接下来是内容timer_reaction变量 - 以毫秒为单位获取计时器参考点与按下按钮并触发中断之间的差异。 | ||
- | 最后,最后一行再连接一个字符串,这样用户就知道这个数字是以毫秒为单位测量的,而不是其他一些单位,如秒或微秒。注意间距:你会看到'was'之后和第一个字符串的结束引号之前有一个尾随空格,第二个字符串的开引号之后和'milliseconds'这个词之前有一个前导空格。如果没有这些,连接的字符串将打印类似“您的反应时间为323毫秒”的内容。 | + | 最后,最后一行再连接一个字符串,这样用户就知道这个数字是以毫秒为单位测量的,而不是其它一些单位,如秒或微秒。注意间距:我们会看到'was'之后和第一个字符串的结束引号之前有一个尾随空格,第二个字符串的开引号之后和'milliseconds'这个词之前有一个前导空格。如果没有这些,连接的字符串将打印类似“Your reaction time was323ms”的内容。 |
- | 你的程序现在应该是这样的: | + | 我们的程序现在应该是这样的: |
<code python> | <code python> | ||
行 161: | 行 160: | ||
</WRAP> | </WRAP> | ||
- | 再次单击运行按钮,等待LED熄灭,然后按下按钮。这一次,您将看到一条信息,告诉您按下按钮的速度,而不是有关触发中断的引脚的报告 - 测量您的反应时间。再次单击“运行”按钮,看看这次您是否可以更快地按下按钮 - 在这个游戏中,你要争取尽可能低的分数! | + | 再次单击运行按钮,等待LED熄灭,然后按下按钮。这一次,我们将看到一条信息,告诉我们按下按钮的速度,而不是有关触发中断的引脚的报告 - 测量我们的反应时间。再次单击“运行”按钮,看看这次我们是否可以更快地按下按钮 - 在这个游戏中,我们要争取尽可能低的分数! |
### 2. 两人游戏 | ### 2. 两人游戏 | ||
- | 单人游戏很有趣,但让您的朋友参与进来就更好了。您可以先邀请他们玩您的游戏,然后比较您的高分(或者说低分),看看谁的反应时间最快。然后,您可以修改您的游戏,让您进行正面交锋! | + | 单人游戏很有趣,但也能让其他的朋友参与进来就更好了。我们可以先邀请他们玩我们编写好的游戏,然后比较我们的高分(或者说低分),看看谁的反应时间最快。然后,我们可以修改游戏,让多个人可以进行正面交锋! |
- | 我们需要另外的一个按钮,返回到您在Thonny中的程序,找到您设置第一个按钮的位置。在这一行的正下方,添加: | + | 如果要设计两个人能玩的游戏,我们需要另外的一个按钮,返回到Thonny中的程序,找到设置第一个按钮的位置,在这一行的正下方,添加: |
<code python> | <code python> | ||
行 178: | 行 177: | ||
</code> | </code> | ||
- | 您也需要在程序的其它地方进行相同的更改。滚动到底部,我们的代码并将设置中断触发器的行更改为: | + | 我们也需要在程序的其它地方进行相同的更改。滚动到代码的底部,将设置中断触发器的行更改为: |
<code python> | <code python> | ||
行 189: | 行 188: | ||
</code> | </code> | ||
- | 你的程序现在应该是这样的: | + | 我们的程序现在应该是这样的: |
<code python> | <code python> | ||
import machine | import machine | ||
行 217: | 行 216: | ||
**中断和处理程序** | **中断和处理程序** | ||
- | 您创建的每个中断都需要一个处理程序,但一个处理程序可以处理任意数量的中断。 在这个程序的情况下,你有两个中断都进入同一个处理程序——这意味着无论哪个中断触发,它们都会运行相同的代码。 一个不同的程序可能有两个处理程序,让每个中断运行不同的代码——这完全取决于你需要你的程序做什么。 | + | 我们创建的每个中断都需要一个处理程序,但一个处理程序可以处理任意数量的中断。 在这个程序的情况下,我们有两个中断都进入同一个处理程序, 这意味着无论哪个中断触发,它们都会运行相同的代码。一个不同的程序可能有两个处理程序,让每个中断运行不同的代码,这完全取决于我们需要程序来做什么。 |
</WRAP> | </WRAP> | ||
- | 单击“运行”图标,等待LED熄灭,然后按下左侧的按钮开关:您将看到游戏与以前相同,将您的反应时间打印到Shell区域。 再次单击“运行”图标,但这次当LED熄灭时,按右侧按钮:游戏将正常运行,正常打印您的反应时间。 | + | 单击“运行”图标,等待LED熄灭,然后按下左侧的按钮开关:我们将看到游戏与以前相同,将我们的反应时间打印到Shell区域。 再次单击“运行”图标,但这次当LED熄灭时,按右侧按钮:游戏将正常运行,正常打印我们的反应时间。 |
- | 为了让游戏更刺激一点,你可以让它报告两个玩家中的哪一个是第一个按下按钮的。 返回程序顶部,就在您设置LED和两个按钮的下方,并添加以下内容: | + | 为了让游戏更刺激一点,我们可以让它报告两个玩家中的哪一个是第一个按下按钮的。 返回程序顶部,就在我们设置LED和两个按钮的下方,添加以下内容: |
<code python> | <code python> | ||
fastest_button = None | fastest_button = None | ||
行 234: | 行 233: | ||
</code> | </code> | ||
- | 请记住,这些行需要缩进四个空格,以便MicroPython知道它们是函数的一部分。这两行允许你的函数改变,而不是仅仅读取,fastest_button变量,并将它设置为包含触发中断的引脚的详细信息,你的游戏在本章前面打印到Shell区域的相同细节,包括触发引脚的编号。 | + | 请记住,这些行需要缩进四个空格,以便[[MicroPython]]知道它们是函数的一部分。这两行允许我们的函数改变,而不是仅仅读取,fastest_button变量,并将它设置为包含触发中断的引脚的详细信息,我们的游戏在本章前面打印到Shell区域的相同细节,包括触发引脚的编号。 |
现在转到程序的底部,并添加以下两行: | 现在转到程序的底部,并添加以下两行: | ||
+ | |||
<code python> | <code python> | ||
while fastest_button is None: | while fastest_button is None: | ||
行 241: | 行 241: | ||
</code> | </code> | ||
- | 这会创建一个循环,但它不是一个无限循环:在这里,您已经告诉MicroPython 只有当fast_button变量仍然为零时才在循环中运行代码 - 这是在程序开始时初始化的值。 实际上,这会暂停程序的主线程,直到中断处理程序更改变量的值。 如果两个玩家都没有按下按钮,程序将简单地暂停。 | + | 这会创建一个循环,但它不是一个无限循环:在这里,我们已经告诉[[MicroPython]]只有当fast_button变量仍然为零时才在循环中运行代码 - 这是在程序开始时初始化的值。 实际上,这会暂停程序的主线程,直到中断处理程序更改变量的值。如果两个玩家都没有按下按钮,程序将简单地暂停。 |
- | 最后,您需要一种方法来确定哪位玩家获胜并祝贺他们。 在程序底部键入以下内容,确保删除Thonny在第一行为您创建的四个空格缩进——这些行不构成循环的一部分: | + | |
+ | 最后,我们需要一种方法来确定哪位玩家获胜并祝贺他们。 在程序底部键入以下内容,确保删除Thonny在第一行为我们创建的四个空格缩进, 这些行不构成循环的一部分: | ||
<code python> | <code python> | ||
行 253: | 行 254: | ||
第一行设置了一个“if”条件,它查看fastest_button变量是否is left_button - 表示IRQ是由左侧按钮触发的。 如果是这样,它将打印一条消息——下面的行缩进四个空格,以便MicroPython知道只有在条件为真时才应该运行它——祝贺左手玩家,其按钮连接到GP12。 | 第一行设置了一个“if”条件,它查看fastest_button变量是否is left_button - 表示IRQ是由左侧按钮触发的。 如果是这样,它将打印一条消息——下面的行缩进四个空格,以便MicroPython知道只有在条件为真时才应该运行它——祝贺左手玩家,其按钮连接到GP12。 | ||
- | 下一行不应缩进,将条件扩展为“elif”——“else if”的缩写,表示“如果第一个条件不为真,请检查下一个条件”。 这次它查看fastest_button变量是否为right_button——如果是,则打印一条消息祝贺右手玩家,其按钮连接到GP13。 | + | 下一行不应缩进,将条件扩展为“elif”——“else if”的缩写,表示“如果第一个条件不为真,请检查下一个条件”。 这次它查看fastest_button变量是否为right_button ——如果是,则打印一条消息祝贺右手玩家,其按钮连接到GP13。 |
- | 您完成的程序应如下所示: | + | 我们完成的程序应如下所示: |
<code python> | <code python> | ||
行 290: | 行 291: | ||
</code> | </code> | ||
- | 按下运行按钮并等待LED熄灭,但现在不要按下任何一个按钮开关。 您会看到Shell区域保持空白,并且没有带回»›提示符;那是因为主线程仍在运行,位于您创建的循环中。 | + | 按下运行按钮并等待LED熄灭,但现在不要按下任何一个按钮开关。 我们会看到Shell区域保持空白,并且没有带回»›提示符;那是因为主线程仍在运行,位于我们创建的循环中。 |
- | 现在按下连接到引脚GP12的左侧按钮。 您会看到一条祝贺您打印到Shell的消息——您的左手是赢家! 再次单击运行并尝试推送 | + | 现在按下连接到引脚GP12的左侧按钮, 我们会看到一条祝贺我们打印到Shell的消息——您的左手是赢家! 再次单击运行并尝试推送 |
- | LED熄灭后右侧的按钮:您会看到另一条消息打印出来,这次是祝贺您的右手。 再次单击“运行”,这一次将一根手指放在每个按钮上:同时按下它们,看看你的右手还是左手更快! | + | LED熄灭后右侧的按钮:我们会看到另一条消息打印出来,这次是祝贺您的右手。 再次单击“运行”,这一次将一根手指放在每个按钮上:同时按下它们,看看你的右手还是左手更快! |
- | 现在您已经创建了一个两人游戏,您可以邀请您的朋友一起玩,看看你们中谁的反应时间最快! | + | 现在我们已经创建了一个两人游戏,我们可以邀请朋友们一起玩了,看看他们中谁的反应时间最快! |
<WRAP center round tip 60%> | <WRAP center round tip 60%> |