python中,用tkinter模块写贪吃蛇

Posted 缘兮Fate

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python中,用tkinter模块写贪吃蛇相关的知识,希望对你有一定的参考价值。

如果转载标注出处,谢谢!

本作品属于作者原创作品!

下面是一些游戏画面:



游戏说明:
死亡条件:碰壁、吃自己!
状态:只有吃了食物才会随机生成其中一种状态,分别是:稳如老狗、幸运光滑、衰神附体之一
状态:稳如老狗:相对于上一次速度不变!
状态:幸运光滑:相对于上一次速度变慢!
状态:衰神附体:相对于上一次速度边快!
总Buff:总体速率对比刚开始是快还是慢,如果是正直代表慢,如果是负值代表快!
该说的就这么多了!
加油,快来开启你的欧皇人生吧~
本作品是用tkinter模块的canvas画图组件创作的!如果大家有什么好的提议或者说不解之处都可以发帖回复,免费分享自己写的小游戏,也希望能够有相同兴趣爱好的朋友,一起互粉探讨和进步,如果你喜欢我的作品。那么请给我一个关注把,谢谢。
发帖不易,希望大家能给个关注和分享,如果不想发评论也可以发私信给我的哈,我也会时常查看的!关于作品种如果有不解的也欢迎加好友探讨,谢谢你的来访,下面是原创代码部分了,模块基本都是系统自带的。所以不用担心的了!

下面是我的投稿视频链接:

import tkinter
import time
import random
from threading import Thread, Event
from tkinter import Canvas
from copy import deepcopy


# 游戏数值统计类
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_buff = \'稳如老狗\'
        self.var_random = 0
        self.score_id = None
        self.lever_id = None
        self.buff_id = None
        self.random_id = None
        self.sum = 0

    # 绘制线框
    def show_frame(self):
        self.game_window.create_rectangle(2, 2, 301, 24)
        self.game_window.create_rectangle(85, 2, 195, 24)

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

    # 删除积分对象
    def del_show_game_score(self):
        if self.score_id is not None:
            self.game_window.delete(self.score_id)
            self.score_id = None

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

    # 删除随机值显示
    def del_show_game_random(self):
        if self.random_id is not None:
            self.game_window.delete(self.random_id)
            self.random_id = None

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

    # 删除状态显示
    def del_show_game_buff(self):
        if self.buff_id is not None:
            self.game_window.delete(self.buff_id)
            self.buff_id = None


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

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

    def show_frame(self):
        self.game_window.create_rectangle(2, 2, width + 1, height + 1)
        my_score.show_frame()

    # 绘制说明文本
    def game_description(self):
        if not len(self.showlist):
            id_bg = self.game_window.create_rectangle(20, 70, 280, 200, fill=\'Snow\', width=1)
            id_1 = self.game_window.create_text(150, 100, text=MyCanvas.msg_1, font=self.font_format)
            id_2 = self.game_window.create_text(26, 135, text=MyCanvas.msg_2, font=self.font_format, anchor=tkinter.W)
            id_3 = self.game_window.create_text(26, 170, text=MyCanvas.msg_3, font=self.font_format, anchor=tkinter.W)
            self.showlist.extend([id_bg, id_1, id_2, id_3])

    # 删除游戏结束,说明文字并清空id!
    def del_game_description(self):
        [self.game_window.delete(elt) for elt in self.showlist]
        self.showlist.clear()

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


# 食物类
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))
                self.food.append(game_window.create_rectangle(self.x, self.y, self.x + 10, self.y + 10, fill=self.bg))
                break

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

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


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

    # 删除所有对象id,node
    def del_all_id_node(self):
        [game_window.delete(elt) for elt in self.id]
        self.node.clear()

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

    # 增加头部对象同步至列表id,node 如果节点超过2个就要变色原先头节点
    def add_headnode(self):
        new_head_id = game_window.create_rectangle(
            self.head_x, self.head_y, self.head_x + 10, self.head_y + 10, fill=bg_snake)
        self.id.insert(0, new_head_id)
        self.node.insert(0, [self.head_x, self.head_y])

    # 增加尾部对象同步至列表id,node
    def add_endnode(self):
        end_id = game_window.create_rectangle(
            self.end_x, self.end_y, self.end_x + 10, self.end_y + 10, fill=bg_snake_end)
        self.id.append(end_id)
        self.node.append([self.end_x, self.end_y])

    # 删除尾部对象同步至列表id,node
    def del_endnode(self):
        end_id = self.id.pop()
        game_window.delete(end_id)
        self.node.pop()

    # 判断是否吃到
    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.del_show_game_score()
        my_score.show_game_score()
        # 游戏状态显示
        my_score.buff = random.uniform(-0.1, 0.1)
        my_score.del_show_game_buff()
        my_score.show_game_buff()
        # 更新随机值显示
        my_score.del_show_game_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):
        # 绘制头节点
        self.add_headnode()
        # 判断是否吃到自己
        if self.is_eat_self():
            self.is_game_over = True
        # 判断吃到食物,不删除尾结点
        if self.is_eat_food():
            # 更新游戏相关参数显示
            self.update_parameters()
            # 更新游戏下次启动速度
            self.update_t_snake()
            # 节点总计达到2个及以上要开始做第二位节点变色处理
            if len(self.node) >= 2:
                self.change_colour()
        else:
            # 未吃到食物且大于等于2节点,变色处理
            if len(self.node) >= 2:
                self.change_colour()
            # 未吃到食物且就一个节点,删除尾节点
            self.del_endnode()

    # 节点总计达到2个及以上要开始做第二位节点变色处理
    def change_colour(self):
        # 因为无法直接变色,只能绘制对象后在删除对象 也同步至ID
        second_id = game_window.create_rectangle(
            self.node[1][0], self.node[1][1], self.node[1][0] + 10, self.node[1][1] + 10, fill=bg_snake_end)
        # 删除原对应位置id
        game_window.delete(self.id[1])
        del self.id[1]
        self.id.insert(1, second_id)

    # 出场蛇头同步增加id,node
    def make_snake_head(self):
        if self.is_empty():
            head_id = game_window.create_rectangle(
                self.head_x, self.head_y, self.head_x + 10, self.head_y + 10, fill=bg_snake)
            self.id.append(head_id)
            self.node.append([self.head_x, self.head_y])

    # 判定是否吃到自己
    def is_eat_self(self):
        if len(self.node) >= 2:
            for snake_x, snake_y in self.node[1:]:
                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()

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

    def run(self):
        while True:
            # 先等待游戏开始
            self.e.wait()
            # 头节点 用深拷贝防止出现数值问题
            self.head_x, self.head_y = deepcopy(self.node[0][0]), deepcopy(self.node[0][1])
            # 尾节点
            self.end_x, self.end_y = deepcopy(self.node[-1][0]), deepcopy(self.node[-1][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 len(my_canvas.showlist) != 0:
                # 进入这里代表已经开始游戏了,所以删除显示的游戏说明
                my_canvas.del_game_description()
                # 出场生成食物
                food.produce_food()
                # 出场蛇头绘制
                self.make_snake_head()
            # 控制游戏开始\\继续\\暂停开关
            if self.e.is_set():
                self.e.clear()
            else:
                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()

如果转载标注出处,谢谢!

以上是关于python中,用tkinter模块写贪吃蛇的主要内容,如果未能解决你的问题,请参考以下文章

用Python写一个贪吃蛇

Python入门学习:一步步教你怎么用Python写贪吃蛇游戏

python写贪吃蛇小游戏

python游戏开发:打造最经典的贪吃蛇游戏

使用Python写一个贪吃蛇

手把手教你使用Python写贪吃蛇游戏(pygame)