gdmao002 发表于 2021-9-1 16:40:41

tkinter模块写的一个贪吃蛇游戏!附【详细源码】和【注释】回帖有奖前百位

本帖最后由 gdmao002 于 2021-9-13 12:14 编辑

问个问题 怎么设置回帖可见?
游戏名:贪吃蛇

游戏说明:
死亡条件:碰壁、吃自己!
状态:只有吃了食物才会随机生成其中一种状态,分别是:稳如老狗、幸运光滑、衰神附体之一
状态:稳如老狗:相对于上一次速度不变!
状态:幸运光滑:相对于上一次速度变慢!
状态:衰神附体:相对于上一次速度变快!

总Buff:总体速率对比刚开始是快还是慢,如果是正值代表慢,如果是负值代表快!
该说的就这么多了!
加油,快来开启你的欧皇人生吧~

游戏图片展示:
https://xxx.ilovefishc.com/album/202109/01/162242xjpo6z7r37x36oi0.jpg
https://xxx.ilovefishc.com/album/202109/01/162239g7zuqfjkqlama8ds.jpg
https://xxx.ilovefishc.com/album/202109/01/162237sb0epbfhb6jz8b85.jpg
https://xxx.ilovefishc.com/album/202109/01/162234qdyrlo5lln5zlvdl.jpg

代码部分:
import tkinter
import time
import random
from threading import Thread, Event
from tkinter import Canvas


# 游戏数值统计类
class MyScore(object):
    str_score = "Score:"
    str_buff = '状态:'
    str_random = '总Buff:'

    def __init__(self):
      self.game_window = Canvas(root, width=width, height=23, bg='Snow')# 'Snow'
      self.game_window.place(x=2, y=2)
      self.font_format = ('宋体', 12)
      self.score = -10
      self.buff = 0
      self.var_random = 0
      self.var_buff = '稳如老狗'
      self.sum = 0

    # 绘制矩形框
    def show_frame(self):
      self.game_window.create_rectangle(2, 2, 301, 24, tag=("frame",))
      self.game_window.create_rectangle(85, 2, 195, 24, tag=("frame",))

    # 增加积分提示
    def show_game_score(self):
      self.score += 10
      self.game_window.create_text(
            10, 13, text=MyScore.str_score + str(self.score), font=self.font_format, anchor=tkinter.W, tag=('score',))

    # 随机值显示
    def show_game_random(self):
      self.sum += self.buff
      self.game_window.create_text(
            200, 13, text=MyScore.str_random + '%.2f' % self.sum, font=self.font_format, anchor=tkinter.W,
            tag=('random',))

    # 增加状态显示
    def show_game_buff(self):
      if self.buff > 0:
            self.var_buff = '幸运光环'
      elif self.buff < 0:
            self.var_buff = '衰神附体'
      else:
            self.var_buff = '稳如老狗'
      self.game_window.create_text(
            141, 13, text=MyScore.str_buff + self.var_buff, font=self.font_format, tag=('buff',))


# 游戏界面类
class MyCanvas(object):
    msg_1 = "游戏说明"
    msg_2 = "1.开始\暂停\继续:Enter\Space"
    msg_3 = "2.移动方向:按wasd键"
    msg_end = "GAME OVER"
    msg_stop = "Press Enter\Space\nto continue the game"

    def __init__(self):
      self.game_window = Canvas(root, width=width, height=height, bg='Tan')# Tan
      self.game_window.place(x=2, y=27)
      self.font_format = ('宋体', 12)
      self.flag = True

    # 绘制矩形框
    def show_frame(self):
      self.game_window.create_rectangle(2, 2, width + 1, height + 1)

    # 绘制说明文本
    def game_description(self):
      self.game_window.create_rectangle(20, 70, 280, 200, fill='Snow', width=1, tag=('description',))
      self.game_window.create_text(150, 100, text=MyCanvas.msg_1, font=self.font_format, tag=('description',))
      self.game_window.create_text(
            32, 135, text=MyCanvas.msg_2, font=self.font_format, anchor=tkinter.W, tag=('description',))
      self.game_window.create_text(
            32, 170, text=MyCanvas.msg_3, font=self.font_format, anchor=tkinter.W, tag=('description',))

    # 游戏结束,说明文字!
    def show_game_over(self):
      self.game_window.create_text(150, 135, text=MyCanvas.msg_end, font=('Arial', 30), tag=("game_over",))

    # 游戏暂停,说明文字!
    def show_game_stop(self):
      self.game_window.create_text(
            150, 135, text=MyCanvas.msg_stop, font=('Arial', 22), fill='white', tag=("game_stop",))


# 食物类
class Food(object):
    def __init__(self):
      self.food = []
      self.x = None
      self.y = None
      self.bg = 'Bisque'

    # 生产食物对象
    def produce_food(self):
      # 判定食物的坐标不能在蛇身体
      while True:
            self.x = (random.randint(0, 299) // 10) * 10
            self.y = (random.randint(0, 399) // 10) * 10
            for snake_x, snake_y in snake.node:
                if self.x == snake_x and self.y == snake_y:
                  break
            else:
                self.food.append((self.x, self.y))
                game_window.create_rectangle(self.x, self.y, self.x + 10, self.y + 10, fill=self.bg, tag=("food",))
                break

    # 判断食物是否为空
    def is_empty(self):
      if not len(self.food):
            return True

    # 删除食物对象 清列表
    def del_food(self):
      if not self.is_empty():
            game_window.delete("food")
            self.food.clear()


# 蛇类
class Snake(object):
    def __init__(self):
      # 存放节点数据
      self.node = []
      # 控制事件开关,也即是控制游戏开始\暂停\继续
      self.e = Event()
      # 定义蛇头的坐标 游戏窗口的正中间 每个蛇块占10像素的等宽高
      self.head_x = ((width - 10) // 10 // 2) * 10
      self.head_y = ((height - 10) // 10 // 2) * 10
      # 蛇的朝向
      self.direction = 'up'
      # 蛇的间隔时长
      self.t_snake = 0.15
      # 结束游戏的标志 真为判定游戏结束
      self.is_game_over = False
      # 尾结点
      self.end_x = None
      self.end_y = None

    # 删除所有对象id,node
    def del_all_id_node(self):
      game_window.delete('snake')
      self.node.clear()

    # 判断节点是否为空
    def is_empty(self):
      if not self.count:
            return True

    # 未吃到食物的情况:绘制头节node,删除尾节点node,移动id
    def headnode(self):
      # 删除尾部节点
      self.node.pop()
      # 更新头节点
      self.node.insert(0, (self.head_x, self.head_y))
      # 获得尾部对象 find_enclosed 判断在矩形内的对象,如果重叠是无法获得,返回的是元组类型的id对象
      end_id = game_window.find_enclosed(self.end_x - 1, self.end_y - 1, self.end_x + 10 + 1, self.end_y + 10 + 1)
      # 画布方法 coords() 可以将id对象改变位置与大小,目前我们用到改变位置 变到头部位置
      game_window.coords(end_id, self.head_x, self.head_y, self.head_x + 10, self.head_y + 10)
      # 变色处理
      game_window.itemconfig(end_id, fill=bg_snake)

    # 增加头部对象同步至列表node,绘制新id
    def add_headnode(self):
      game_window.create_rectangle(
            self.head_x, self.head_y, self.head_x + 10, self.head_y + 10, fill=bg_snake, tag=("snake",))
      # 头部插入新节点
      self.node.insert(0, (self.head_x, self.head_y))

    # 判断吃到食物后[删除食物对象,并生成新食物]
    def is_eat_food(self):
      if self.head_x == food.x and self.head_y == food.y:
            # 删除食物对象及id
            food.del_food()
            # 生成食物
            food.produce_food()
            return True

    # 更新游戏相关参数显示
    def update_parameters(self):
      # 吃到食物
      # 删除原有积分显示
      my_score.game_window.delete('score')
      # 更新积分显示
      my_score.show_game_score()
      # 删除原有游戏状态显示
      my_score.buff = random.uniform(-0.1, 0.1)
      my_score.game_window.delete('buff')
      # 更新现有游戏状态显示
      my_score.show_game_buff()
      # 删除原有数值显示
      my_score.game_window.delete('random')
      # 更新随机值显示
      my_score.show_game_random()

    # 吃到食物后更新速度显示
    def update_t_snake(self):
      # 当buff跟原先速度相加 如果小于0 则降速至肉眼能看到的死亡
      if self.t_snake + my_score.buff <= 0:
            self.t_snake = 0.01
      else:
            self.t_snake += my_score.buff

    # 游戏结束
    def game_over(self):
      # 显示结束文字说明
      my_canvas.show_game_over()
      # 要清空蛇对象id,node,清空食物对象
      self.del_all_id_node()
      food.del_food()

    # 移动一步的封装
    def one_step(self):
      # 判断是否吃到自己
      if self.is_eat_self():
            self.is_game_over = True
      # 判断吃到食物,不删除尾结点
      if self.is_eat_food():
            # 绘制添加头节点
            self.add_headnode()
            # 更新游戏相关参数显示
            self.update_parameters()
            # 更新游戏下次启动速度
            self.update_t_snake()
      else:
            # 未吃到食物的情况
            # 绘制头节点node,删除尾节点node,移动id,
            self.headnode()
      # 节点总计达到2个及以上要开始做第二位节点变色处理
      if self.count >= 2:
            self.change_colour()

    # 节点总计达到2个及以上要开始做第个蛇身位节点变色处理
    def change_colour(self):
      # 获取第二个位置的对象
      second_id = game_window.find_enclosed(
            self.node - 1, self.node - 1, self.node + 10 + 1, self.node + 10 + 1)
      # 画布方法 itemconfig() 能够处理尾部id对象的颜色更替
      game_window.itemconfig(second_id, fill=bg_snake_end)

    # 出场蛇头增加node
    def make_snake_head(self):
      game_window.create_rectangle(
            self.head_x, self.head_y, self.head_x + 10, self.head_y + 10, fill=bg_snake, tag=('snake',))
      self.node.append((self.head_x, self.head_y))

    # 获取节点个数
    @property
    def count(self):
      return len(self.node)

    # 判定是否吃到自己
    def is_eat_self(self):
      if self.count >= 2:
            for snake_x, snake_y in self.node:
                if self.head_x == snake_x and self.head_y == snake_y:
                  return True

    # 游戏开始的时候 加载相关显示菜单
    def load_show(self):
      # 增加积分显示
      my_score.show_game_score()
      # 增加状态显示
      my_score.show_game_buff()
      # 随机值显示
      my_score.show_game_random()
      # 游戏说明显示
      my_canvas.game_description()
      # 描边游戏框
      my_canvas.show_frame()
      my_score.show_frame()

    # 游戏开始入口
    def start(self):
      while True:
            self.load_show()
            self.run()
            break

    def run(self):
      while True:
            # 先等待游戏开始
            self.e.wait()
            # 头节点
            self.head_x, self.head_y = self.node, self.node
            # 尾节点
            self.end_x, self.end_y = self.node[-1], self.node[-1]
            # 判断方向
            if self.direction == 'up':
                # 越界判定顶部
                if self.head_y - 10 in range(0, height):
                  self.head_y -= 10
                  self.one_step()
                else:
                  self.is_game_over = True
            elif self.direction == 'down':
                # 越界判定底部
                if self.head_y + 10 in range(0, height):
                  self.head_y += 10
                  self.one_step()
                else:
                  self.is_game_over = True
            elif self.direction == 'left':
                # 越界判定左边
                if self.head_x - 10 in range(0, width):
                  self.head_x -= 10
                  self.one_step()
                else:
                  self.is_game_over = True
            else:
                # 越界判定右边
                if self.head_x + 10 in range(0, width):
                  self.head_x += 10
                  self.one_step()
                else:
                  self.is_game_over = True
            # 走完判定是否结束游戏
            if self.is_game_over:
                self.game_over()
                break
            time.sleep(self.t_snake)

    # 按键触发的各类事件 判定移动方向的
    def change_direction(self, event):
      if event.char.upper() == 'W':
            self.direction = 'up'
      elif event.char.upper() == 'S':
            self.direction = 'down'
      elif event.char.upper() == 'A':
            self.direction = 'left'
      elif event.char.upper() == 'D':
            self.direction = 'right'
      # 开始游戏\继续\暂停 Enter\Spaca键
      elif event.char.upper() == '\r' or event.char.upper() == ' ':
            # 刚开始游戏 只会运行一次
            if my_canvas.flag:
                # 进入这里代表已经开始游戏了,所以删除显示的游戏说明
                game_window.delete("description")
                # 出场生成食物
                food.produce_food()
                # 出场蛇头绘制
                self.make_snake_head()
                my_canvas.flag = False
            # 控制游戏开始\继续\暂停开关
            if self.e.is_set():
                # 显示游戏暂停信息
                my_canvas.show_game_stop()
                self.e.clear()
            else:
                # 删除游戏暂停信息
                game_window.delete("game_stop")
                self.e.set()

      else:
            pass


if __name__ == '__main__':
    # 定义根(游戏)窗口的基本信息 居中显示
    root = tkinter.Tk()
    root.title('My Snake')
    width_root, height_root = 306, 431# 300 425
    width_max, height_max = root.maxsize()
    size_center = '%dx%d+%d+%d' % (
      width_root, height_root, (width_max - width_root) / 2, (height_max - height_root) / 2)
    root.geometry(size_center)
    root.resizable(width=False, height=False)

    # 画布尺寸 还有游戏的相关尺寸
    width, height = 300, 400

    # # 设置蛇颜色参数
    bg_snake = 'Indigo'
    bg_snake_end = 'MediumPurple'

    # 初始化画布对象
    my_canvas = MyCanvas()

    # 获得画布对象
    game_window = my_canvas.game_window

    # 初始化数值对象
    my_score = MyScore()

    # 生成蛇类
    snake = Snake()

    # 生成食物类
    food = Food()

    # 绑定主窗口事件 主要是为了记录键盘的操作
    root.bind('<KeyPress>', snake.change_direction)

    # 开始游戏
    t1 = Thread(target=snake.start)
    t1.setDaemon(True)
    t1.start()

    # 主窗口循环
    root.mainloop()

niuniuniu666 发表于 2021-9-1 16:43:48

沙发?玩一下试试{:10_333:}

gdmao002 发表于 2021-9-1 16:52:57

niuniuniu666 发表于 2021-9-1 16:43
沙发?玩一下试试

好的加油哈~

小伤口 发表于 2021-9-1 17:24:26

厉害呀,我也要玩{:10_254:}

gdmao002 发表于 2021-9-1 18:31:36

小伤口 发表于 2021-9-1 17:24
厉害呀,我也要玩

感谢支持{:5_92:}

noah-py 发表于 2021-9-1 19:29:32

{:10_275:}

hrpzcf 发表于 2021-9-1 19:37:35

论坛大神越来越多了

gdmao002 发表于 2021-9-1 21:42:38

hrpzcf 发表于 2021-9-1 19:37
论坛大神越来越多了

谢谢支持哈,我还不是大神,论坛大神大有人在的{:9_229:}

王跌宕 发表于 2021-9-2 01:16:08

大佬{:10_301:}嘿嘿{:10_301:}

某一天 发表于 2021-9-2 02:27:51

楼主千金散尽还复来

trolwy 发表于 2021-9-2 06:55:59

挣鱼币做作业

不大不小甲鱼 发表于 2021-9-2 08:44:09

{:10_266:}

夏季在下季 发表于 2021-9-2 09:07:54

向大神学习{:5_109:}

遇见Tom 发表于 2021-9-2 14:31:31

一个小小的贪吃蛇要这么多代码{:7_122:}

番杰 发表于 2021-9-2 15:34:56

{:5_90:}

gdmao002 发表于 2021-9-2 16:57:05

番杰 发表于 2021-9-2 15:34


{:9_241:} 怎么了

gdmao002 发表于 2021-9-2 16:58:15

遇见Tom 发表于 2021-9-2 14:31
一个小小的贪吃蛇要这么多代码

如果只是简单的没有额外的东西是可以少一些代码,而且你看我代码基本都有注释,所以整体看很多,其实也没多少的{:9_223:}

番杰 发表于 2021-9-2 17:29:02

gdmao002 发表于 2021-9-2 16:57
怎么了

没有,我只是为了水鱼币,这个表情是表情里面的第一个,我都没看是啥,直接就点啦,没想到是个生气的表情,尴尬啦!

gdmao002 发表于 2021-9-2 19:15:01

番杰 发表于 2021-9-2 17:29
没有,我只是为了水鱼币,这个表情是表情里面的第一个,我都没看是啥,直接就点啦,没想到是个生气的表情 ...

{:9_217:} 哈哈了解了解

hornwong 发表于 2021-9-2 21:53:46

{:5_95:}
页: [1] 2 3 4 5 6
查看完整版本: tkinter模块写的一个贪吃蛇游戏!附【详细源码】和【注释】回帖有奖前百位