Python百日百行代码挑战-day5,day6,python小游戏,贪吃蛇游戏实战记录
写在前面
本来想着昨天就发这个东西的,但是实际上手之后发现并不是那么简单,因此花了两天时间来做记录,我觉得这个工作量完全对得起这个天数,估计弄完之后得休息一下,把人写麻了都。
本来我想放完整版的代码,但是不太合适,一方面我的代码很大部分来源于某教程,虽然我自己改了很多一部分,但是总归不太合适,容易侵犯版权。第二方面我的变量名写的太恶臭了,我怕回头给我封号了。因此本次记录中,仅记录一些核心编程要点,一些类的设置及逻辑思考等等。如果想要我的完整版代码的同学,可以私聊我找我要,但是希望你拿去之后仅供学习参考使用。
游戏本身还是有很多bug,但是我懒得改了,毕竟我只是为了练习一下python,过多较真感觉没啥用,也就留着了。
需要用到的工具包
- thinkter
- random
- time
游戏本体设定
首先在写游戏的时候,需要设定一个游戏本体的大类,这个类当中应该包含游戏的基本逻辑,基本内容,运行规律等等。接下来的介绍,以贪吃蛇游戏为例。
键盘交互
在本游戏的类设定中,需要对键盘输入进行理解交互,代码如下
self.canvas.bind_all('<KeyPress-Left>',self.turn_left) # 键盘的左键
self.canvas.bind_all('<KeyPress-Right>',self.turn_right) # 键盘的右键
self.canvas.bind_all('<KeyPress-Up>',self.turn_up) # 键盘的上键
self.canvas.bind_all('<KeyPress-Down>',self.turn_down) # 键盘的下键
def turn_left(self,evt): # 向左的时候x轴值会减少,下面的同理
self.x=-1
self.y=0
def turn_right(self,evt):
self.x=1
self.y=0
def turn_up(self,evt):
self.y=-1
self.x=0
def turn_down(self,evt):
self.y=1
self.x=0
canvas.bind_all 的作用即是键值交互,例如,按一下键盘左键,用户希望得到的是贪吃蛇向左移动,所以这里会触发turn_left函数,注释写了,就是为了减少x的值,后面运行的时候,让x乘20就行了,就能移动明白了。
游戏界面设定
游戏界面设定只有一行代码,相当简单,这里我把整个屏幕(600×600)都用来当做游戏界面了。
本游戏中,蛇身体的块和食物方块均用20×20的小方格。
self.canvas = Canvas(self.tk, width = 600, height = 600)
初始蛇位置设定
self.body = [(8,8),(8,9),(8,10)]
判断是否吃到食物
吃食物这里我的判断原则是,贪吃蛇的头(元组中第一个元素)是否与食物块的坐标相重合。如果重合即为吃到食物,如果不重合就没有吃到。因此本函数的判断语句就是对比一下两个方块的左上和右下两个坐标点是否一致,一致则返回1,函数里面那个参数d的含义,事实上是一个类,在下面会介绍,是食物这个类。这个函数在下面一个函数中会用到。
def eatfood(self,d): # d是一个类,本例中是食物类
if self.body[0][0] * 20 == d.x11 and self.body[0][1] * 20 == d.y11: # 判断两个格子顶点是否一致
return 1
else:
return 0
吃食物设定
贪吃蛇当中,吃食物最直观的就是,吃完身体会长一节,完了在生成一个新的食物,是否吃到食物的判定用的是上面那个函数,如果没吃到,就是无任何变化,但是事实上python需要删除蛇身体的最后一节,添加一个新的身体节来表示已经移动,所以else下面用了一个del语句。
def eatornot(self,d): # d同样代表了食物这个类
if self.eatfood(d):
self.newfood = 1 # 判断是否需要生成新的食物
self.numberofeatingfood +=1 # 身体总的节数+1
else:
del self.body[self.numberofeatingfood] # 删除身体最后一节
撞墙判定
撞墙会导致游戏结束,是否撞墙我的逻辑是判断头部的左上角顶点是否和画布边界的值一致。但是现在想想看这个想法是错误的,写这个博客的时候发现的,我重新想了一下,应该是四个原则
- 头部左上角顶点是否碰到右侧墙面
- 头部右上角顶点是否碰到左侧墙面
- 头部左下角顶点是否碰到上侧墙面
- 头部右上角顶点是否碰到下侧墙面
其中上下不用太在意,左侧墙面就用右侧的点就行,等等。碰到了的话游戏就结束
def impactwallornot(self):
if self.body[0][0] <= 0 or self.body[0][0] >= 30 or self.body[0][1] <= 0 or self.body[0][1] >=30:
self.duangwall = 1
贪吃蛇属性设定
贪吃蛇最主要的部分还是画出蛇的位置,无需初始化任何参数,我这里就这么写的
def __init__(self):
pass
贪食蛇展示函数
这一部分看起来很复杂,但是实际很简单,仅以第一部分为例,初始状态不用做任何动作,所以就pass了。接下来判断的是v.x=1和v.y=0的情况,根据上面的键值交互函数,可以知道这里是判断右移的情况,后面那个direction是为了不让贪食蛇向左移动(比如现在方向向左,我就会让这个参数等于2)。如果判断条件都满足,首先使得横坐标+1,因为是右移嘛。完了在索引为0的位置插入一个新的方格参数,最后判断一下是不是吃到食物,下面3个和这个大同小异,就不一一介绍了。最后的for循环是为了画出蛇的身体。
def snakelocation(self,v):
if v.x==0 and v.y==0:
pass
elif v.x==1 and v.y==0 and v.direction!=2:
a = v.body[0][0]+1
v.body.insert(0,(a,v.body[0][1]))
v.direction = 1
v.eatornot(d)
elif v.x==-1 and v.y==0 and v.direction!=1:
a = v.body[0][0]-1
v.body.insert(0,(a,v.body[0][1]))
v.direction = 2
v.eatornot(d)
elif v.x==0 and v.y==1 and v.direction!=4:
a = v.body[0][1]+1
v.body.insert(0,(v.body[0][0],a))
v.direction = 3
v.eatornot(d)
elif v.x==0 and v.y==-1 and v.direction!=3:
a = v.body[0][1]-1
v.body.insert(0,(v.body[0][0],a))
v.direction = 4
v.eatornot(d)
for i in v.body:
x1 = i[0] * 20
y1 = i[1] * 20
v.canvas.create_rectangle(x1,y1,x1+20,y1+20,fill='blue')
蛇身碰撞判定函数
蛇不能自己吃自己,所以蛇头在移动过程中不能碰到身体的其他地方,这里我采用的算法是:将蛇头的参数保留下来,和后面的参数一一对比,如果一样的话,返回一个1值,用来表示发生碰撞,但是写博客的时候想了想,应该直接break出去,能省点时间毕竟。
def snakeimpactself(self):
self.test = 0 # 未发生碰撞的值
for i,himself in enumerate(v.body): # 遍历索引和值
if i == 0:
self.judgement = himself # 保存头的值
else:
if self.judgement == himself: # 判断和其他的部位是否碰撞
self.test = 1
食物属性设定
食物生成
这个是对参数的初始化,x11左上角,x21右上角,y11左下角,y21右下角。while循环是为了判断食物是不是长在了蛇身上,那个函数后面介绍。
def __init__(self):
self.a = random.randint(1,29)
self.b = random.randint(1,29)
self.x11 = self.a * 20
self.y11 = self.b * 20
self.x21 = self.x11 + 20
self.y21 = self.y11 + 20
while 1:
if self.snakeimpact() == 1:
self.a = random.randint(1, 29)
self.b = random.randint(1, 29)
self.x11 = self.a * 20
self.y11 = self.b * 20
self.x21 = self.x11 + 20
self.y21 = self.y11 + 20
continue
else:
break
画出食物
def foodlocation(self,v):
v.canvas.create_rectangle(self.x11,self.y11,self.x21,self.y21,fill='black')
判断食物是不是长在了蛇身上
说来惭愧,我在测试的时候好像发现这个函数吧,好像没啥用,该长人家接着长,我也没找到问题在哪,但是还是记录一下吧。这里我的逻辑是,遍历蛇身体,如果发现食物的左上角和右下角坐标重合,就重新生成一个食物(就是上面写的那个判断语句,就是干这事的)。
def snakeimpact(self):
for i in v.body:
if self.x11 == i[0] and self.y11 == i[1]:
return 1
其他常用函数介绍及学习
本次写游戏的过程中,发现了很多新的不认识的函数,简单对其中一部分介绍一下,以后混个眼熟吧。
- canvas.create_rectangle(x1,y1,x2,y2,fill=‘’) 后面4个参数,分别是左上坐标和右下角坐标,还可以加一个参数就是颜色
- canvas.create_text(x1,x2,text,fill,font) 创建文本用的,前两个位置参数,text是你写的内容,fill是颜色,font是尺寸
- tk.title() 画布标题
总结
改bug真不容易,以后就把bug说成是游戏特色算了
防火防盗防诈骗