7217浏览
查看: 7217|回复: 0

[教程] Mind+Python turtle 课程——09 弹球小游戏

[复制链接]
Mind+Python turtle 课程——09 弹球小游戏

课程目录:


功能介绍

上节课我们通过控制键盘的左右方向来控制小拦板,从而实现弹球小游戏的功能。这节课我们将继续使用键盘来制作贪吃蛇小游戏。首先我们先在屏幕内实现移动的小蛇,之后增加食物角色,当小蛇吃到食物时会增长相应的尺寸,最后增加游戏胜负的判断功能,从而实现贪吃蛇游戏效果。课程会分为三个任务的形式进行,下表为课程任务及涉及到的知识点。

实现功能
知识点
任务一:移动的蛇
巩固学习turtle库函数、列表索引以及学会freegame库函数的使用
任务二:实现吃食物功能
巩固学习随机函数、逻辑判断语句、for循环语句和作用域的认识
任务二:完成贪吃蛇游戏
巩固学习键盘控制函数、lambda函数的使用方法

项目实践
任务一:移动的蛇
1、任务分析

贪吃蛇的身体是由list(列表)构成的,list中每一个元组代表贪吃蛇在棋盘上的坐标,我们只需在每个坐标上都画上图案,就能制作出一条贪吃蛇来。关于列表的内容可以查看第五节课“模拟3D星空”,只不过这里是坐标点。运动如下图。

Mind+Python turtle 课程——09 弹球小游戏图1
没有删除最后一个坐标时贪吃蛇的运动过程

那我们如何让贪吃蛇活蹦乱跳,我们就要写一个move函数。怎么让蛇动起来呢?

其实很简单,我们只需删除list中最后一个坐标,再在蛇头部分插入新的位置。运动如下图(只能看到蛇头在运动)。


Mind+Python turtle 课程——09 弹球小游戏图2

                                                                                   移动前                       移动后


如何确定新的位置呢,我们就要设定贪吃蛇移动的方向(x,y),将原蛇头位置的坐标在移动方向上进行加减操作。如下图所示。

Mind+Python turtle 课程——09 弹球小游戏图3

如果设置初始的坐标为(0-10),其中x代表贪吃蛇左右移动,y的值代表上下移动。那么当触发向上移动时,假设向上触发得条件为x坐标不变,y坐标加10(具体得数值都可以自己设定)。两个坐标向量进行加法操作,那么会得到(00)。这样贪吃蛇就从初始坐标(0-10)向坐标为(00)的点移动了

2、编写程序

注意在编写程序时,需先安装freegames库,这里我们通过pip模式安装。

Mind+Python turtle 课程——09 弹球小游戏图4Mind+Python turtle 课程——09 弹球小游戏图5


import turtle# 导入绘图海龟模块
from freegames import vector,square#导入freegames的向量函数与绘图函数,用square正方形表示food、snake,用vector这个二维向量表示snake
snake = [vector(10, 0)]# 初始化snake,而且snake是列表,其元素是向量
position = vector(0, -10) # 用来控制贪吃蛇的移动方向和距离
def change(x, y):# 改变贪吃蛇移动方向部分,通过onkey函数和lambda表达式调用change函数,获得下一个移动坐标(x,y)
    position.x = x# 将x的值赋给position.x改变贪吃蛇目标的横坐标
    position.y = y# 将y的值赋给position.y改变贪吃蛇目标的纵坐标
def move():# 可以让蛇移动的方法
    head = snake[-1].copy()# 确定蛇头的位置,表示复制snake列表的最后一个元素,这里用列表的最后一个元素表示蛇头
    head.move(position)# 移动一步
    turtle.clear()# 清空蛇走过的位置
    snake.append(head)# 更新蛇的身体
    snake.pop(0)# 移除蛇走过的坐标
    for body in snake:# 循环遍历蛇的坐标
       square(body.x, body.y, 9, 'black') # 绘制黑色蛇
    turtle.update()
    turtle.ontimer(move, 100)# 定时执行move函数
if __name__ == '__main__':# 主程序
    turtle.setup(420, 420, 370, 0)# 创建窗体大小
    turtle.hideturtle()# 隐藏箭头显示
    turtle.tracer(False)# 关闭绘画效果
    move()# 调用让蛇移动的方法
    turtle.listen()# 事件***
    turtle.onkey(lambda: change(10, 0), 'Right')# 按键盘右键,蛇向右走
    turtle.onkey(lambda: change(-10, 0), 'Left')# 按键盘左键,蛇向左走
    turtle.onkey(lambda: change(0, 10), 'Up')# 按键盘上键,蛇向上走
    turtle.onkey(lambda: change(0, -10), 'Down')# 按键盘下键,蛇向下走
    turtle.done()# 停止画笔绘制,但绘图窗体不关闭
3、运行效果

运行此段程序,可以看到在弹出的窗口中,小蛇在上面移动,我们通过键盘可以控制其移动的方向。

Mind+Python turtle 课程——09 弹球小游戏图6
4、代码释义

4.1代码中freegames库函数的使用

from freegames import vector,square#导入freegames的向量函数与绘图函数,用square正方形表示food、snake,用vector这个二维向量表示snake

4.1.1代码解释
导入freegames的向量函数与绘图函数,square正方形表示foodsnake,用vector这个二维向量表示snake

4.1.1freegames库
freegamesApache2授权的免费Python游戏集合,用于教育和娱乐。这些游戏是用简单的Python代码编写的,是为实验和改变而设计的,代码少且易读并且有丰富的库函数方便调用。

4.2代码中turtle.tracer()函数
    turtle.tracer(False)# 关闭绘画效果

4.2.1代码解释
启用/禁用海龟动画并设置刷新图形的延迟时间。如果没有关闭时会有snake的绘画过程(程序中的体现可以看到箭头在绘制原点),游戏实现效果会变差。因此这里我们选择关闭绘画过程。

4.2.2使用方法
turtle.tracer(n=None,delay=None),其中n -- 非负整型数;delay -- 非负整型数。如turtle.tracer(False)或者duturtle.tracer(0)表示为图形将一次性画好;turtle.tracer(1)为图形按照正常速度进行,可有可无;turtle.tracer(4)为在循环中,图形将一次画出4次循环的图。

4.3代码中列表索引
    head = snake[-1].copy()# 确定蛇头的位置,表示复制snake列表的最后一个元素,这里用列表的最后一个元素表示蛇头

4.3.1代码解释
确定蛇头的位置,表示复制snake列表的最后一个元素,这里用列表的最后一个元素表示蛇头。

4.3.2索引
序列中的每个元素都有一个编号,也称为索引。这个索引是从0开始递增的,即下标为0表示第一个元素,下标为1表示第二个元素,依次类推,如下图所示。
元素1
元素2
元素3
元素4
元素...
元素n
索引下标:0                        1                        2                                4                        ...                        n-1
序列的正数索引
Python比较神奇,它的索引可以是负数。这个索引从右向左计数,也就是从最后的一个元素开始计数,即最后一个元素的索引值是-1,倒数第二个元素的索引值是-2,依次类推,如下图所示。
元素1
元素2
元素3
元素4
元素...
元素n
索引下标:-(n-1)                -(n-2)        -(n-3)                        ...                        -2                        -1
序列的负数索引

任务二:实现吃食物功能
1、任务分析
任务一中我们没有吃到食物是需要删除贪吃蛇尾部的元素,而这里当我们吃到食物时不需要删除贪吃蛇尾部的元素。既当贪吃蛇蛇头的坐标与食物的坐标重合的话,贪吃蛇就吃到了食物。如果贪吃蛇吃到了食物,就在棋盘上随机更新食物。如果随机生成的食物的坐标,恰好与贪吃蛇的位置重合的话,就继续随机产生坐标,直到确保与贪吃蛇的坐标不同的时候。

2、编写程序
import turtle# 导入绘图海龟模块
from freegames import vector,square#导入freegames的向量函数与绘图函数,square正方形表示foodsnake,用vector这个二维向量表示snake
from random import randrange#导入随机函数的randrange工具,使food随机出现
snake = [vector(10, 0)]# 初始化snake,而且snake是列表,其元素是向量
position = vector(0, -10) # 用来控制贪吃蛇的移动方向和距离
food = vector(0,0)#  初始化food变量,用来表示第一个食物在坐标为(0,0)
def change(x, y):# 改变贪吃蛇移动方向部分,通过onkey函数和lambda表达式调用change函数,获得下一个移动坐标(x,y
    position.x = x# x的值赋给position.x改变贪吃蛇目标的横坐标
    position.y = y# y的值赋给position.y改变贪吃蛇目标的纵坐标
def move():# 可以让蛇移动的方法
    head = snake[-1].copy()# 确定蛇头的位置,表示复制snake列表的最后一个元素,这里用列表的最后一个元素表示蛇头
    head.move(position)# 移动一步
    turtle.clear()# 清空蛇走过的位置
    snake.append(head)# 更新蛇的身体
    if head == food:# 如果蛇吃到食物
        print('Snake:', len(snake))# 根据蛇的长度进行加分
        food.x = randrange(-15, 15) * 10  # 随机生成食物x坐标
        food.y = randrange(-15, 15) * 10  # 随机生成食物y坐标
    else:
        snake.pop(0)# 移除蛇走过的坐标
    turtle.clear()# 清空蛇走过的位置
    for body in snake:# 循环遍历蛇的坐标
       square(body.x, body.y, 9, 'black') # 绘制黑色蛇
    square(food.x, food.y, 9, 'green')# 绘制绿色食物
    turtle.update()
    turtle.ontimer(move, 100)# 定时执行move函数
if __name__ == '__main__':# 主程序
    turtle.setup(420, 420, 370, 0)# 创建窗体大小
    turtle.hideturtle()# 隐藏箭头显示
    turtle.tracer(False)# 关闭绘画效果
    move()# 调用让蛇移动的方法
    turtle.listen()# 事件***
    turtle.onkey(lambda: change(10, 0), 'Right')# 按键盘右键,蛇向右走
    turtle.onkey(lambda: change(-10, 0), 'Left')# 按键盘左键,蛇向左走
    turtle.onkey(lambda: change(0, 10), 'Up')# 按键盘上键,蛇向上走
    turtle.onkey(lambda: change(0, -10), 'Down')# 按键盘下键,蛇向下走
    turtle.done()# 停止画笔绘制,但绘图窗体不关闭
3、运行效果
运行此段程序,运行此段程序,可以看到在弹出的窗口中,小蛇在上面移动,我们通过键盘可以控制其移动的方向。此时如果吃到食物时,贪吃蛇的长度会加长,并且在屏幕任意位置又会重新出现新的食物。

Mind+Python turtle 课程——09 弹球小游戏图7
4、代码释义
4.1代码中if __name__ == '__main__'的使用
if __name__ == '__main__':# 主程序

4.1.1代码解释
主程序函数,模块可以通过检查自己的 __name__ 来得知是否运行在 main 作用域中,这使得模块可以在作为脚本运行时条件性地执行一些代码,而在被 import 时不会执行。

4.1.2作用域
通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。

任务三:完成贪吃蛇游戏

1、任务分析
首先我们的贪吃蛇不能朝着当前移动方向的反方向移动,体现在代码中,就是当前方向与改变方向的乘积不能为负值。其次是如果贪吃蛇蛇头的坐标与边框的坐标重合的话,蛇卒。如果贪吃蛇各个部分的坐标有重合的话,就说明贪吃蛇咬到了自己,游戏结束。

2、编写程序
from random import randrange#导入随机函数的randrange工具,使food随机出现
from freegames import vector,square#导入freegames的向量函数与绘图函数,square正方形表示foodsnake,用vector这个二维向量表示snake
import turtle# 导入绘图海龟模块
food = vector(0,0)#  初始化food变量,用来表示第一个食物在坐标为(0,0)
snake = [vector(10, 0)]# 初始化snake,而且snake是列表,其元素是向量
position = vector(0, -10) # 用来控制贪吃蛇的移动方向和距离
def change(x, y):# 改变贪吃蛇移动方向部分,通过onkey函数和lambda表达式调用change函数,获得下一个移动坐标(x,y
    position.x = x# x的值赋给position.x改变贪吃蛇目标的横坐标
    position.y = y# y的值赋给position.y改变贪吃蛇目标的纵坐标
def is_inside(head):# 判断蛇头是否在窗体内
    return -200 < head.x < 190 and -200 < head.y < 190
def move():# 可以让蛇移动的方法
    head = snake[-1].copy()# 确定蛇头的位置,表示复制snake列表的最后一个元素,这里用列表的最后一个元素表示蛇头
    head.move(position)# 移动一步
    if not is_inside(head) or head in snake:# 如果蛇的头部位于边界外或者蛇头在蛇的身体中
        square(head.x, head.y, 9, 'red')# 绘制红色蛇头,说明游戏结束
        turtle.update()# 更新
        return# 用来终止程序的运行,其后语句不会再执行
    snake.append(head)# 更新蛇的身体
    if head == food:# 如果蛇吃到食物
        print('Snake:', len(snake))# 根据蛇的长度进行加分
        food.x = randrange(-15, 15) * 10  # 随机生成食物x坐标
        food.y = randrange(-15, 15) * 10  # 随机生成食物y坐标
    else:
        snake.pop(0)# 移除蛇走过的坐标
    turtle.clear()# 清空蛇走过的位置
    for body in snake:# 循环遍历蛇的坐标
        square(body.x, body.y, 9, 'black') # 绘制黑色蛇
    square(food.x, food.y, 9, 'green')# 绘制绿色食物
    turtle.update()
    turtle.ontimer(move, 100)# 定时执行move函数
if __name__ == '__main__':# 主程序
    turtle.setup(420, 420, 370, 0)# 创建窗体大小
    turtle.hideturtle()# 隐藏箭头显示
    turtle.tracer(False)# 关闭绘画效果
    move()# 调用让蛇移动的方法
    turtle.listen()# 事件***
    turtle.onkey(lambda: change(10, 0), 'Right')# 按键盘右键,蛇向右走
    turtle.onkey(lambda: change(-10, 0), 'Left')# 按键盘左键,蛇向左走
    turtle.onkey(lambda: change(0, 10), 'Up')# 按键盘上键,蛇向上走
    turtle.onkey(lambda: change(0, -10), 'Down')# 按键盘下键,蛇向下走
    turtle.done()# 停止画笔绘制,但绘图窗体不关闭
3、运行效果
基于任务二的条件下,当小蛇碰到边框或者朝着当前移动方向的反方向移动时,绘制出红色蛇头,说明游戏结束。

Mind+Python turtle 课程——09 弹球小游戏图8Mind+Python turtle 课程——09 弹球小游戏图9

4、代码释义

4.1代码中lambda库函数的使用
    turtle.listen()# 事件***
    turtle.onkey(lambda: change(10, 0), 'Right')# 按键盘右键,蛇向右走
    turtle.onkey(lambda: change(-10, 0), 'Left')# 按键盘左键,蛇向左走
    turtle.onkey(lambda: change(0, 10), 'Up')# 按键盘上键,蛇向上走
    turtle.onkey(lambda: change(0, -10), 'Down')# 按键盘下键,蛇向下走

4.1.1代码解释
监听键盘事件,当键盘按下相应的按键时执行相应的功能,相对于前面小海龟抓鱼的课程程序,这是一种缩写方式,在这里我们省略掉了新建键盘控制函数的过程。

4.1.2lambda函数
lambda函数又称匿名函数,是指没有名字的函数,应用在需要一个函数但是又不想费神去命名这个函数的场合。通常情况下,这样的函数只使用一次。


项目小结
通过本节课程的学习,我们巩固了urtle库函数、学会了列表索引以及freegame库函数的使用,制作了移动的蛇的任务。之后利用随机函数、逻辑判断语句、for循环语句和作用域实现了小蛇吃食物的功能。最后通过键盘控制函数和lambda函数实现了贪吃蛇游戏的功能制作。

项目扩展
尝试增加游戏开始和难度选择功能,可以查看之前课程的程序进行设计。

参考程序
from random import randrange#导入随机函数的randrange工具,使food随机出现
from freegames import vector,square#导入freegames的向量函数与绘图函数,用square正方形表示food、snake,用vector这个二维向量表示snake
import turtle# 导入绘图海龟模块
from time import sleep#导入时间库sleep函数
screen = turtle.Screen()#赋值screen对象
#产生一个输入难度的对话框
difficulty=screen.numinput("难度","请输入游戏的难度(1~3)",minval=1,maxval=3)
food = vector(0,0)#  初始化food变量,用来表示第一个食物在坐标为(0,0)处
snake = [vector(10, 0)]# 初始化snake,而且snake是列表,其元素是向量
position = vector(0, -10*difficulty) # 用来控制贪吃蛇的移动方向和距离
def change(x, y):# 改变贪吃蛇移动方向部分,通过onkey函数和lambda表达式调用change函数,获得下一个移动坐标(x,y)
    position.x = x# 将x的值赋给position.x改变贪吃蛇目标的横坐标
    position.y = y# 将y的值赋给position.y改变贪吃蛇目标的纵坐标
def is_inside(head):# 判断蛇头是否在窗体内
    return -200 < head.x < 190 and -200 < head.y < 190
def move():# 可以让蛇移动的方法
    head = snake[-1].copy()# 确定蛇头的位置,表示复制snake列表的最后一个元素,这里用列表的最后一个元素表示蛇头
    head.move(position)# 移动一步
    if not is_inside(head) or head in snake:# 如果蛇的头部位于边界外或者蛇头在蛇的身体中
        square(head.x, head.y, 9, 'red')# 绘制红色蛇头,说明游戏结束
        turtle.write("game over",font=("Arial",40,"normal"))#显示失败
        turtle.update()# 更新
        return# 用来终止程序的运行,其后语句不会再执行
    snake.append(head)# 更新蛇的身体
    if head == food:# 如果蛇吃到食物
        count=len(snake)
        count=count+1#计次
        print('Snake:', count)# 根据蛇的长度进行加分
        if count == 10*difficulty:#到达规定的分数值
            turtle.write("victory!",font=("Arial",40,"normal"))#显示失败
            turtle.exitonclick()#点击退出
        food.x = randrange(-15, 15) * 10  # 随机生成食物x坐标
        food.y = randrange(-15, 15) * 10  # 随机生成食物y坐标
        sleep(0.01)#推迟0.01秒再执行
    else:
        snake.pop(0)# 移除蛇走过的坐标
    turtle.clear()# 清空蛇走过的位置
   
    for body in snake:# 循环遍历蛇的坐标
        square(body.x, body.y, 9, 'black') # 绘制黑色蛇
    square(food.x, food.y, 9, 'green')# 绘制绿色食物
    turtle.update()
    turtle.ontimer(move, 100)# 定时执行move函数
if __name__ == '__main__':# 主程序
    turtle.setup(420, 420, 370, 0)# 创建窗体大小
    turtle.hideturtle()# 隐藏箭头显示
    turtle.tracer(False)# 关闭绘画效果
    move()# 调用让蛇移动的方法
    turtle.listen()# 事件***
    turtle.onkey(lambda: change(10, 0), 'Right')# 按键盘右键,蛇向右走
    turtle.onkey(lambda: change(-10, 0), 'Left')# 按键盘左键,蛇向左走
    turtle.onkey(lambda: change(0, 10), 'Up')# 按键盘上键,蛇向上走
    turtle.onkey(lambda: change(0, -10), 'Down')# 按键盘下键,蛇向下走
    turtle.done()# 停止画笔绘制,但绘图窗体不关闭



参考效果
Mind+Python turtle 课程——09 弹球小游戏图10Mind+Python turtle 课程——09 弹球小游戏图11
附件:代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail