python 简单小游戏之贪吃蛇

使用python自带的库完成小游戏:

        游戏拥有元素:贪吃蛇基础功能,得分统计,最高得分统计,障碍墙。

        关键:使用二维表去保存地图数据,通过存储不同的数字去判断地图信息,随机生成的食物和吃掉食物后🐍的变化。

   参考文章:(33条消息) 用python自带的tkinter做游戏(一)—— 贪吃蛇 篇_python tkinter游戏_Juni Zhu的博客-CSDN博客

图片

 

使用库:

import tkinter as tk
from tkinter.messagebox import showinfo
import random

1. 初始化数据:在这里初始化所有的游戏参数包括(窗口大小 颜色,分数,运动速度等…)【可自己修改】:

    def __init__(self):

        self.window = None          # 实例化的窗体
        self.canvas = None          # 实例化的画布
        self.loop = 0               # 暂停标记,1为开启,0为暂停
        self.loop_id = None         # 实例化loop,用来取消循环
        self.game_map = []          # 整个游戏的地图
        self.snake_body = []        # 蛇身的坐标集
        self.food_xy = []           # 食物的坐标
        self.head_x = 0             # 蛇头的X坐标
        self.head_y = 0             # 蛇头的Y坐标
        self.dd = [0]               # 记录按键方向
        self.score = 0              # 记录得分
        self.score_max = 0          # 历史最高得分
        self.len = 3                # 蛇身初始长度(最小设定值为1,不包括蛇头)
        self.body_len = self.len    # 蛇身当前长度
        self.FPS = 100              # 蛇的移动速度(单位毫秒)
        self.row_cells = 27         # 一行多少个单元格(含边框)
        self.col_cells = 27         # 一共多少行单元格(含边框)
        self.canvas_bg = 'white'    # 游戏背景色
        self.cell_size = 20         # 方格单元格大小
        self.cell_gap = 0           # 方格间距
        self.frame_x = 15           # 左右边距
        self.frame_y = 15           # 上下边距
        self.win_w_plus = 120       # 窗口右边额外多出的宽度
        self.wall_list = list()     # 墙体障碍

        self.color_dict = {
            0: 'white',             # 0表示空白
            1: 'red',               # 1代表蛇头
            2: 'black',             # 2代表蛇身
            3: 'red',               # 3代表食物
            4: 'gray'               # 4代表墙
        }

        self.run_game()

2. 窗口的初始化:创建窗口 和 画布 以及 窗口显示文字

    def run_game(self):

        # 游戏窗口
        self.window = tk.Tk()           # 开一个窗口
        self.window.focus_force()       # 主窗口焦点
        self.window.title('贪吃蛇小游戏')

        # 窗体大小
        win_w_size = self.row_cells * self.cell_size + self.frame_x * 2 + self.win_w_plus
        win_h_size = self.col_cells * self.cell_size + self.frame_y * 2
        screenWidth = self.window.winfo_screenwidth()        # 获取显示区域的宽度
        screenHeight = self.window.winfo_screenheight()      # 获取显示区域的高度
        left = (screenWidth - win_w_size) // 2
        top = (screenHeight - win_h_size) // 2
        self.window.geometry("{}x{}+{}+{}".format(win_w_size, win_h_size, left, top))

        # 操作介绍
        txt_lable = tk.Label(self.window, text="操作方式:\n(W)向上\n(S)向下\n(A)向左\n(D)向又\n(空格)STOP", font=('Ya_hei', 15))
        txt_lable.place(x=self.cell_size * self.col_cells + self.cell_size * 2, y=self.cell_size * 10)

        # 创建画布
        canvas_h = win_h_size
        canvas_w = win_w_size - self.win_w_plus
        self.canvas = tk.Canvas(self.window, bg=self.canvas_bg, height=canvas_h, width=canvas_w, highlightthickness=0)
        self.canvas.place(x=0, y=0)
        self.game_start()

3. 创建地图数据,使用二维表的形式存储数据(0为空白, 4为墙壁)

    def create_map(self):
        # 创建地图列表 通过列表存放
        self.game_map = []
        for i in range(0, self.col_cells):
            self.game_map.append([])
        for i in range(0, self.col_cells):
            for j in range(0, self.row_cells):
                self.game_map[i].append(j)
                self.game_map[i][j] = 0
        print("--------------------     地图数据      ---------------------")
        print(self.game_map)
        # 绘制障碍物 可以自己设置
        for i in range(0, self.row_cells - 1):
            self.game_map[0][i] = 4
            self.game_map[self.col_cells - 1][i] = 4

        for i in range(0, self.col_cells - 1):
            self.game_map[i][0] = 4
            self.game_map[i][self.row_cells - 1] = 4
        self.game_map[-1][-1] = 4

        u = 5
        d = 20
        for i in range(5, 10):
            self.wall_list.append([u, i])
            self.game_map[u][i] = 4
            self.wall_list.append([d, i])
            self.game_map[d][i] = 4

        for i in range(17, 22):
            self.wall_list.append([u, i])
            self.game_map[u][i] = 4
            self.wall_list.append([d, i])
            self.game_map[d][i] = 4
        print(self.wall_list)

4. 创建像素格:

    def create_cells(self):
        # 创建单元格 像素点
        for y in range(0, self.col_cells):
            for x in range(0, self.row_cells):
                a = self.frame_x + self.cell_size * x
                b = self.frame_y + self.cell_size * y
                c = self.frame_x + self.cell_size * (x + 1)
                d = self.frame_y + self.cell_size * (y + 1)
                e = self.canvas_bg
                f = self.cell_gap
                g = self.color_dict[self.game_map[y][x]]
                self.canvas.itemconfig(self.canvas.create_rectangle(a, b, c, d, outline=e, width=f, fill=g), fill=g)

5. 初始化 🐍的信息和位置

    def create_snake(self):
        # 蛇头 在边框中间
        self.snake_body = [[self.col_cells // 2, self.row_cells // 2]]
        # 蛇身 蛇头上色,颜色为定义的1
        self.game_map[self.snake_body[0][0]][self.snake_body[0][1]] = 1

6. 食物的随机生成

    def create_food(self):

        # 食物 通过 random 随机生成
        self.food_xy = [0, 0]
        self.food_xy[1] = random.randint(1, self.row_cells - 2)
        self.food_xy[0] = random.randint(1, self.col_cells - 2)
        while self.game_map[self.food_xy[0]][self.food_xy[1]] != 0:
            self.food_xy[0] = random.randint(1, self.row_cells - 2)
            self.food_xy[1] = random.randint(1, self.col_cells - 2)

        self.game_map[self.food_xy[0]][self.food_xy[1]] = 3

7. 读取🐍在地图上的信息

    def snake_xy(self):
        # 读取蛇
        xy = []
        for i in range(0, self.col_cells):
            try:  # 查找数值为1的坐标,没有就返回0。为防止在0列,先加上1,最后再减去。
                x = self.game_map[i].index(1) + 1
            except:
                x = 0
            xy.append(x)
        self.head_x = max(xy)
        self.head_y = xy.index(self.head_x)
        self.head_x = self.head_x - 1

8. 控制🐍的移动

    def move_snake(self, event):

        # 记录按键的方向,a上 b下 c左 d右
        def move_key(a, b, c, d):
            direction = event.keysym

            if self.head_x != self.snake_body[-1][1]:
                if direction == a:
                    self.dd[0] = 1
                if direction == b:
                    self.dd[0] = 2
            else:
                if direction == c:
                    self.dd[0] = 3
                if direction == d:
                    self.dd[0] = 4

            if self.head_y != self.snake_body[-1][0]:
                if direction == c:
                    self.dd[0] = 3
                if direction == d:
                    self.dd[0] = 4
            else:
                if direction == a:
                    self.dd[0] = 1
                if direction == b:
                    self.dd[0] = 2

        def pause_key(key):
            """ 暂停键 """
            direction = event.keysym
            if direction == key:
                self.loop = 0
                showinfo('暂停', '按确定键继续')
                self.loop = 1
                self.window.after(self.FPS, self.game_loop)

        move_key('w', 's', 'a', 'd')
        move_key('W', 'S', 'A', 'D')
        move_key('Up', 'Down', 'Left', 'Right')
        pause_key('space')

9. 判断游戏是否结束

    def game_over(self):

        def over():
            showinfo('游戏结束', '本次得分: {}\n\n再来一局'.format(self.score))
            # 判断分数
            if self.score >= self.score_max:
                self.score_max = self.score
            self.score = 0
            self.body_len = self.len
            self.game_start()

        if [self.head_y, self.head_x] in self.snake_body[0:-2]: over()
        if self.head_x == self.row_cells - 1 or self.head_x == 0: over()
        if self.head_y == self.col_cells - 1 or self.head_y == 0: over()
        if [self.head_y, self.head_x] in self.wall_list: over()

10.  记录蛇头运行轨迹,生成蛇身

    def snake_record(self):

        # 记录蛇头运行轨迹,生成蛇身
        temp = []
        temp.append(self.head_y)
        temp.append(self.head_x)
        print(self.head_y, self.head_x, self.game_map[self.head_y][self.head_x])
        self.snake_body.append(temp)

        if self.snake_body[-1] == self.snake_body[-2]:
            del self.snake_body[-1]

        # 碰到食物身体加长,并再随机生成一个食物
        if [self.head_y, self.head_x] == self.food_xy:
            self.score += 1
            self.body_len += 1
            self.create_food()
        # 限制蛇身长度,不超过设定值
        elif len(self.snake_body) > self.body_len:
            self.game_map[self.snake_body[0][0]][self.snake_body[0][1]] = 0
            del self.snake_body[0]

        # 在方向 自动前进
        def move(d, x, y):
            if self.dd[0] == d:  # 根据方向值来决定走向
                self.game_map[self.head_y + x][self.head_x + y] = 1
                self.game_map[self.head_y + 0][self.head_x + 0] = 2

        move(1, -1, 0)
        move(2, 1, 0)
        move(3, 0, -1)
        move(4, 0, 1)

11.  每次吃到食物后🐍会变长,分数会增加 所以吃到一次,画面会刷新一次。

    def game_loop(self):
        # 吃到food 循环 刷新
        self.snake_record()
        self.snake_xy()
        self.canvas.delete('all')  # 清除canvas
        self.create_cells()
        self.game_over()
        if self.loop == 1:
            txt_s = tk.Label(self.window, text="当前得分:\n({})\n\n最高得分:\n({})".format(self.score, self.score_max), font=('Ya_hei', 15))
            txt_s.place(x=self.cell_size * self.col_cells + self.cell_size * 2, y=self.cell_size * 2)
            self.loop_id = self.window.after(self.FPS, self.game_loop)

12.  游戏的开始 按顺序 初始化函数

    def game_start(self):

        # 游戏开始
        self.loop = 1       # 暂停标记,1为开启,0为暂停
        self.dd = [0]       # 记录按键方向
        self.create_map()
        self.create_snake()
        self.create_food()
        self.window.bind('<Key>', self.move_snake)
        self.snake_xy()
        self.game_loop()

13. 游戏结束关闭窗口

        def close_w():
            self.loop = 0
            self.window.after_cancel(self.loop_id)
            self.window.destroy()

        self.window.protocol('WM_DELETE_WINDOW', close_w)
        self.window.mainloop()

14. 启动main

if __name__ == '__main__':
    Snake()

全部代码:

import tkinter as tk
from tkinter.messagebox import showinfo
import random


class Snake:

    def __init__(self):

        self.window = None          # 实例化的窗体
        self.canvas = None          # 实例化的画布
        self.loop = 0               # 暂停标记,1为开启,0为暂停
        self.loop_id = None         # 实例化loop,用来取消循环
        self.game_map = []          # 整个游戏的地图
        self.snake_body = []        # 蛇身的坐标集
        self.food_xy = []           # 食物的坐标
        self.head_x = 0             # 蛇头的X坐标
        self.head_y = 0             # 蛇头的Y坐标
        self.dd = [0]               # 记录按键方向
        self.score = 0              # 记录得分
        self.score_max = 0          # 历史最高得分
        self.len = 3                # 蛇身初始长度(最小设定值为1,不包括蛇头)
        self.body_len = self.len    # 蛇身当前长度
        self.FPS = 100              # 蛇的移动速度(单位毫秒)
        self.row_cells = 27         # 一行多少个单元格(含边框)
        self.col_cells = 27         # 一共多少行单元格(含边框)
        self.canvas_bg = 'white'    # 游戏背景色
        self.cell_size = 20         # 方格单元格大小
        self.cell_gap = 0           # 方格间距
        self.frame_x = 15           # 左右边距
        self.frame_y = 15           # 上下边距
        self.win_w_plus = 120       # 窗口右边额外多出的宽度
        self.wall_list = list()     # 墙体障碍

        self.color_dict = {
            0: 'white',             # 0表示空白
            1: 'red',               # 1代表蛇头
            2: 'black',             # 2代表蛇身
            3: 'red',               # 3代表食物
            4: 'gray'               # 4代表墙
        }

        self.run_game()

    def run_game(self):

        # 游戏窗口
        self.window = tk.Tk()           # 开一个窗口
        self.window.focus_force()       # 主窗口焦点
        self.window.title('贪吃蛇小游戏')

        # 窗体大小
        win_w_size = self.row_cells * self.cell_size + self.frame_x * 2 + self.win_w_plus
        win_h_size = self.col_cells * self.cell_size + self.frame_y * 2
        screenWidth = self.window.winfo_screenwidth()        # 获取显示区域的宽度
        screenHeight = self.window.winfo_screenheight()      # 获取显示区域的高度
        left = (screenWidth - win_w_size) // 2
        top = (screenHeight - win_h_size) // 2
        self.window.geometry("{}x{}+{}+{}".format(win_w_size, win_h_size, left, top))

        # 操作介绍
        txt_lable = tk.Label(self.window, text="操作方式:\n(W)向上\n(S)向下\n(A)向左\n(D)向又\n(空格)STOP", font=('Ya_hei', 15))
        txt_lable.place(x=self.cell_size * self.col_cells + self.cell_size * 2, y=self.cell_size * 10)

        # 创建画布
        canvas_h = win_h_size
        canvas_w = win_w_size - self.win_w_plus
        self.canvas = tk.Canvas(self.window, bg=self.canvas_bg, height=canvas_h, width=canvas_w, highlightthickness=0)
        self.canvas.place(x=0, y=0)
        self.game_start()


    def create_map(self):
        # 创建地图列表 通过列表存放
        self.game_map = []
        for i in range(0, self.col_cells):
            self.game_map.append([])
        for i in range(0, self.col_cells):
            for j in range(0, self.row_cells):
                self.game_map[i].append(j)
                self.game_map[i][j] = 0
        print("--------------------     地图数据      ---------------------")
        print(self.game_map)
        # 绘制障碍物 可以自己设置
        for i in range(0, self.row_cells - 1):
            self.game_map[0][i] = 4
            self.game_map[self.col_cells - 1][i] = 4

        for i in range(0, self.col_cells - 1):
            self.game_map[i][0] = 4
            self.game_map[i][self.row_cells - 1] = 4
        self.game_map[-1][-1] = 4

        u = 5
        d = 20
        for i in range(5, 10):
            self.wall_list.append([u, i])
            self.game_map[u][i] = 4
            self.wall_list.append([d, i])
            self.game_map[d][i] = 4

        for i in range(17, 22):
            self.wall_list.append([u, i])
            self.game_map[u][i] = 4
            self.wall_list.append([d, i])
            self.game_map[d][i] = 4
        print(self.wall_list)

    def create_cells(self):
        # 创建单元格 像素点
        for y in range(0, self.col_cells):
            for x in range(0, self.row_cells):
                a = self.frame_x + self.cell_size * x
                b = self.frame_y + self.cell_size * y
                c = self.frame_x + self.cell_size * (x + 1)
                d = self.frame_y + self.cell_size * (y + 1)
                e = self.canvas_bg
                f = self.cell_gap
                g = self.color_dict[self.game_map[y][x]]
                self.canvas.itemconfig(self.canvas.create_rectangle(a, b, c, d, outline=e, width=f, fill=g), fill=g)

    def create_snake(self):
        # 蛇头 在边框中间
        self.snake_body = [[self.col_cells // 2, self.row_cells // 2]]
        # 蛇身 蛇头上色,颜色为定义的1
        self.game_map[self.snake_body[0][0]][self.snake_body[0][1]] = 1

    def create_food(self):

        # 食物 通过 random 随机生成
        self.food_xy = [0, 0]
        self.food_xy[1] = random.randint(1, self.row_cells - 2)
        self.food_xy[0] = random.randint(1, self.col_cells - 2)
        while self.game_map[self.food_xy[0]][self.food_xy[1]] != 0:
            self.food_xy[0] = random.randint(1, self.row_cells - 2)
            self.food_xy[1] = random.randint(1, self.col_cells - 2)

        self.game_map[self.food_xy[0]][self.food_xy[1]] = 3

    def snake_xy(self):
        # 读取蛇
        xy = []
        for i in range(0, self.col_cells):
            try:  # 查找数值为1的坐标,没有就返回0。为防止在0列,先加上1,最后再减去。
                x = self.game_map[i].index(1) + 1
            except:
                x = 0
            xy.append(x)
        self.head_x = max(xy)
        self.head_y = xy.index(self.head_x)
        self.head_x = self.head_x - 1

    def move_snake(self, event):

        # 记录按键的方向,a上 b下 c左 d右
        def move_key(a, b, c, d):
            direction = event.keysym

            if self.head_x != self.snake_body[-1][1]:
                if direction == a:
                    self.dd[0] = 1
                if direction == b:
                    self.dd[0] = 2
            else:
                if direction == c:
                    self.dd[0] = 3
                if direction == d:
                    self.dd[0] = 4

            if self.head_y != self.snake_body[-1][0]:
                if direction == c:
                    self.dd[0] = 3
                if direction == d:
                    self.dd[0] = 4
            else:
                if direction == a:
                    self.dd[0] = 1
                if direction == b:
                    self.dd[0] = 2

        def pause_key(key):
            """ 暂停键 """
            direction = event.keysym
            if direction == key:
                self.loop = 0
                showinfo('暂停', '按确定键继续')
                self.loop = 1
                self.window.after(self.FPS, self.game_loop)

        move_key('w', 's', 'a', 'd')
        move_key('W', 'S', 'A', 'D')
        move_key('Up', 'Down', 'Left', 'Right')
        pause_key('space')



    def game_over(self):

        def over():
            showinfo('游戏结束', '本次得分: {}\n\n再来一局'.format(self.score))
            # 判断分数
            if self.score >= self.score_max:
                self.score_max = self.score
            self.score = 0
            self.body_len = self.len
            self.game_start()

        if [self.head_y, self.head_x] in self.snake_body[0:-2]: over()
        if self.head_x == self.row_cells - 1 or self.head_x == 0: over()
        if self.head_y == self.col_cells - 1 or self.head_y == 0: over()
        if [self.head_y, self.head_x] in self.wall_list: over()

    def snake_record(self):

        # 记录蛇头运行轨迹,生成蛇身
        temp = []
        temp.append(self.head_y)
        temp.append(self.head_x)
        print(self.head_y, self.head_x, self.game_map[self.head_y][self.head_x])
        self.snake_body.append(temp)

        if self.snake_body[-1] == self.snake_body[-2]:
            del self.snake_body[-1]

        # 碰到食物身体加长,并再随机生成一个食物
        if [self.head_y, self.head_x] == self.food_xy:
            self.score += 1
            self.body_len += 1
            self.create_food()
        # 限制蛇身长度,不超过设定值
        elif len(self.snake_body) > self.body_len:
            self.game_map[self.snake_body[0][0]][self.snake_body[0][1]] = 0
            del self.snake_body[0]

        # 在方向 自动前进
        def move(d, x, y):
            if self.dd[0] == d:  # 根据方向值来决定走向
                self.game_map[self.head_y + x][self.head_x + y] = 1
                self.game_map[self.head_y + 0][self.head_x + 0] = 2

        move(1, -1, 0)
        move(2, 1, 0)
        move(3, 0, -1)
        move(4, 0, 1)

    def game_loop(self):
        # 吃到food 循环 刷新
        self.snake_record()
        self.snake_xy()
        self.canvas.delete('all')  # 清除canvas
        self.create_cells()
        self.game_over()
        if self.loop == 1:
            txt_s = tk.Label(self.window, text="当前得分:\n({})\n\n最高得分:\n({})".format(self.score, self.score_max), font=('Ya_hei', 15))
            txt_s.place(x=self.cell_size * self.col_cells + self.cell_size * 2, y=self.cell_size * 2)
            self.loop_id = self.window.after(self.FPS, self.game_loop)


    def game_start(self):

        # 游戏开始
        self.loop = 1       # 暂停标记,1为开启,0为暂停
        self.dd = [0]       # 记录按键方向
        self.create_map()
        self.create_snake()
        self.create_food()
        self.window.bind('<Key>', self.move_snake)
        self.snake_xy()
        self.game_loop()

        def close_w():
            self.loop = 0
            self.window.after_cancel(self.loop_id)
            self.window.destroy()

        self.window.protocol('WM_DELETE_WINDOW', close_w)
        self.window.mainloop()


if __name__ == '__main__':
    Snake()

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
心中带点小风骚的头像心中带点小风骚普通用户
上一篇 2023年11月9日
下一篇 2023年11月9日

相关推荐