强化学习Agent系列(一)——PyGame游戏编程,Python 贪吃蛇制作实战教学

强化学习Agent系列(一)——PyGame游戏编程,Python 贪吃蛇制作实战教学

文章目录

  • 强化学习Agent系列(一)——PyGame游戏编程,Python 贪吃蛇制作实战教学
  • 一、前言
    • 1、pygame介绍
    • 2、 安装Pygame
    • 3. Pygame常用模块
    • 二、pygame 入门
      • 1、窗口初始化与事件初认识
      • 2、创建绿色方块并键盘移动
      • 3、控制绿色方块吃掉红色果子
      • 4、控制绿色方块吃掉红色果子,身体长度加一节
      • 三、pygame初级

        一、前言

        大家好,未来的开发者们请上座

        随着人工智能的发展,强化学习基本会再次来到人们眼前,遂想制作一下相关的教程。强化学习第一步基本离不开虚拟环境的搭建,下面用大家耳熟能详的贪吃蛇游戏为基础,制作一个Agent,完成对这个游戏的绝杀。

        万里长城第一步:用python开发贪吃蛇游戏

        1、pygame介绍

        用Python进行游戏开发的首选模块就是PyGame。

        pygame 是一个流行的跨平台Python模块,专为电子游戏设计,包含图像、声音等,创建在SDL(Simple DirectMedia Layer)基础上,允许实时电子游戏研发而不会被低级语言,如C语言或是更低级的汇编语言束缚。基于这样一个设想,所有需要的游戏功能和理念(主要是图像方面)都完全简化为游戏逻辑本身,所有的资源结构都可以由高级语言(如Python)提供。它提供了一套丰富的功能,使得游戏开发者可以轻松地创建图形、动画、音效和游戏逻辑。

        以下是 pygame 的一些关键特性:

        注:不需要特地去记,一开始只需要知道pygame有哪些关键特性,哪些功能他可以实现即可。

        1. 显示

          pygame 提供了一种简单的方式来创建和管理游戏窗口以及渲染图形。

          它支持多种图像格式,包括 BMP、JPG、PNG、GIF 等。

        2. 事件

          pygame 有一个事件系统,可以处理键盘、鼠标、游戏手柄等输入设备的事件。

          开发者可以轻松地查询和响应用户的交互。

        3. 图形

          pygame 提供了基本的2D图形绘制功能,包括线条、形状、图像渲染等。

          它也支持图像的变换,如缩放、旋转和翻转。

        4. 音效和音乐

          pygame 可以播放音效和音乐文件,支持多种音频格式。

          它提供了音量控制、播放控制等基本的音频操作。

        5. 精灵

          pygame 的精灵(Sprite)系统可以帮助管理游戏中的对象。

          精灵类可以用来表示游戏中的每一个元素,如角色、物品等。

        6. 时钟

          pygame 的时钟(Clock)功能可以帮助控制游戏的帧率,确保游戏以均匀的速度运行。

        7. 碰撞检测

          pygame 提供了简单的碰撞检测功能,可以检测两个元素是否相互接触或重叠。

        8. 字体和文本

          pygame 支持字体渲染,可以在游戏中显示文本。

        9. 跨平台

          pygame 可以运行在多种操作系统上,包括 Windows、Mac OS X 和 Linux。

          安装 pygame 通常很简单,只需使用 pip 命令即可:

        2、 安装Pygame

        pip install pygame
        

        一旦安装了 pygame,你就可以开始创建游戏窗口、加载图像、处理用户输入和创建游戏逻辑了。pygame 的社区活跃,有大量的教程和文档可供学习,这使得即使是编程新手也能够开始他们的游戏开发之旅。

        3. Pygame常用模块

        Pygame做游戏开发的优势在于不需要过多地考虑底层相关的内容,可以把工作中心放在游戏逻辑上。例如,PyGame中集成了很多和底层相关的模块,如访问显示设备、管理事件、使用字体等。

        Pygame常用模块如下:

        模块名功能
        pygame.display访问显示设备。
        pygame.draw绘制形状、线和点。
        pygame.event管理事件。
        pygame.font使用字体。
        pygame.image加载和存储图片。
        pygame.key读取键盘按键。
        pygame.mixer声音。
        pygame.mouse鼠标。
        pygame.movie播放视频。
        pygame.music播放音乐。
        pygame.rect管理矩形区域。
        pygame.sndarray操作声音数据。
        pygame.sprite操作移动图像。
        pygame.surface管理图像和屏幕。
        pygame.surfarray管理点阵图像数据。
        pygame.time管理时间和帧信息。
        pygame.transform缩放和移动图像。

        二、pygame 入门

        1、窗口初始化与事件初认识

        使用Pygame的display模块和event模块创建一个PyGame窗口,代码结构主要分三部分:

        1. 模块导入
        2. pygame窗口初始化
        3. 游戏主体

        初始化涉及到的pygame与sys函数:

        模块名功能
        pygame.init()初始化所有的pygame模块,使用其他模块之前基本一定要调用init方法
        pygame.display.set_mode()初始化窗口的宽高,并返回游戏屏幕(后续对象都需要画到游戏屏幕上)
        pygame.display.set_caption()初始化窗口的名字
        pygame.event.get()键盘、鼠标、手柄 事件响应
        pygame.QUIT点击窗口右上角红X的事件
        sys.exit()程序退出
        pygame.quit()程序终止前调用,卸载所有pygame模块
        # -*- coding: utf-8 -*-
        # 一、模块导入
        import sys         # 导入sys模块
        import pygame       # 导入pygame模块
        # 二、pygame窗口初始化
        pygame.init()                           # 初始化pygame
        size = width, height = 320, 240         # 设置窗口尺寸
        screen = pygame.display.set_mode(size)  # 显示窗口
        # 设置标题
        pygame.display.set_caption('窗口初始化测试')
        # 三、游戏主体
        # 执行死循环,确保窗口一直显示,同时监听关闭按钮事件
        while True:
            # 检查事件
            for event in pygame.event.get():    # 遍历所有事件
                if event.type == pygame.QUIT:     # 如果单击关闭按钮
                    pygame.quit()       # 卸载所有pygame模块
                    sys.exit()          # 终止当前程序
        

        运行结果如下:

        这样子你就创建了一个窗口,点击关闭按钮,程序捕捉到pygame.QUIT事件,pygame.quit()卸载完pygame模块后,运行 sys.exit() 退出程序。

        这样子就开始了基于pygame制作游戏的第一步。窗口初始化

        2、创建绿色方块并键盘移动

        涉及到游戏基本就离不开控制角色移动,遂就将其当做第二课

        运行结果如下:

        该课程涉及到的pygame函数(不重复上一课学习到的了):

        模块名功能
        pygame.time.Clock()创建时钟对象,用于控制屏幕绘制速度 – 刷新频率
        clock.tick(60)时钟对象(一定要放在整个游戏循环最后,不然会起不到稳定控制游戏刷新速度的效果),设置频率为60。如果你的游戏逻辑和渲染非常快,tick 方法将会使程序暂停,以保持 60 FPS 的速度,如果速度慢于60则不会等待
        pygame.key.get_pressed()获取当前全部键盘key的状态,没按下的按钮值为False,按下的按钮值为True
        screen.fill()快捷填充背景色
        pygame.draw.rect()绘制矩阵(游戏屏幕,颜色,矩阵(x,y,width,height),width(矩阵粗细,没有则填充整个矩阵))
        pygame.display.update()更新屏幕画面

        注:额外知识点补充游戏中的坐标系:

        具体的逻辑代码如下

        使用Pygame的创建绿色方块并键盘移动,代码结构主要分三部分:

        1. 模块导入
        2. pygame窗口初始化
        3. 游戏主体

          3. 1 游戏内对象初始化

          3. 2 监听按键、鼠标事件并设置事件内容,修改方块位置

          3. 3 依据方块的位置宽高,重新渲染画面和方块

        # -*- coding: utf-8 -*-
        # 一、模块导入
        import sys         # 导入sys模块
        import pygame       # 导入pygame模块
        # 二、pygame窗口初始化
        pygame.init()                           # 初始化pygame
        size = width, height = 320, 240         # 设置窗口尺寸
        screen = pygame.display.set_mode(size)  # 显示窗口
        # 设置标题
        pygame.display.set_caption('创建绿色方块并键盘移动')
        # 三、游戏主体
        # 执行死循环,确保窗口一直显示,同时监听关闭按钮事件
        ## 3.1 游戏内对象初始化
        # 设置矩阵的属性
        rect_color = (0, 255, 0)  # 绿色
        rect_len = 20  # 矩阵的边长
        rect_pos = [width // 2, height // 2]  # 矩阵的初始位置
        rect_speed = [2, 2]  # 矩阵的速度
        # 定义时钟对象
        clock = pygame.time.Clock()
        while True:
            # 3.2 监听按键、鼠标事件并设置事件内容,修改方块的值
            # 检查事件
            for event in pygame.event.get():    # 遍历所有事件
                if event.type == pygame.QUIT:     # 如果单击关闭按钮
                    pygame.quit()       # 卸载所有pygame模块
                    sys.exit()          # 终止当前程序
            # 检查按键,并对上下左右每一个按键,绑定事件
            keys = pygame.key.get_pressed()
            if keys[pygame.K_LEFT]:
                rect_pos[0] -= rect_speed[0]
            if keys[pygame.K_RIGHT]:
                rect_pos[0] += rect_speed[0]
            if keys[pygame.K_UP]:
                rect_pos[1] -= rect_speed[1]
            if keys[pygame.K_DOWN]:
                rect_pos[1] += rect_speed[1]
            # 3.3 依据方块的位置宽高,重新渲染画面和方块
            #确保方块不会移出屏幕
            rect_pos[0]=max(0,min(width-rect_len,rect_pos[0]))
            rect_pos[1]=max(0,min(height-rect_len,rect_pos[1]))
            # 填充背景色(每次用黑色的背景覆盖掉旧有的画面)
            screen.fill((0,0,0))
            #画矩阵
            pygame.draw.rect(screen,rect_color,(*rect_pos,rect_len,rect_len))
            # 更新屏幕
            pygame.display.update()
            # 控制游戏刷新速度
            clock.tick(60)
        

        上述代码中,添加了键盘事件检测。pygame.key.get_pressed()能够获取键盘状态,查询目标按钮的状态并添加相应的事件,如:

         keys = pygame.key.get_pressed()
            if keys[pygame.K_LEFT]:
                rect_pos[0] -= rect_speed[0]
            if keys[pygame.K_RIGHT]:
                rect_pos[0] += rect_speed[0]
            if keys[pygame.K_UP]:
                rect_pos[1] -= rect_speed[1]
            if keys[pygame.K_DOWN]:
                rect_pos[1] += rect_speed[1]
        

        如果按下left,则rect的x值减少,按下right,则rect的x值增加。如果按下up,则y值减少,按下down,y值增加。具体对应关系可参考上面的pygame中的游戏坐标系进行理解。

        3、控制绿色方块吃掉红色果子

        游戏基本一定会涉及到游戏内物体的交互,一个游戏的玩法核心就是交互,在设计游戏玩法的时候,各位帅哥美女们可以参考下面,设计一下交互规则。

        创建交互规则如下:

        • 1、玩家控制绿色方块移动
        • 2、如果地图中没有红色果子,在地图任意位置,随机生成一个果子
        • 3、绿色方块的位置与红色果子位置一致时,红色果子刷新位置

          具体的逻辑代码如下

          初始化随机生成一个果子的位置,并渲染出来

          当二者左上角的位置距离<8 px的时候,果子刷新位置

          该课程涉及到的pygame函数(不重复上一课学习到的了):

          模块名功能
          random.randint(0, 30)随机从0到30这31个数抽一个数
          np.linalg.norm(3,4)= 5求一个向量的长度,与sqrt(pow(x, 2) + pow(y, 2))等价
          *迭代对象(比如程序中的:*foodPosition)列表解包,比如:将foodPosition拆成零散的变量,而不是之前的元组

          额外补充知识:

          具体代码如下:

          # -*- coding: utf-8 -*-
          # 一、模块导入
          import sys  # 导入sys模块
          import pygame  # 导入pygame模块
          import random  # 导入random模块
          import numpy as np
          # 二、pygame窗口初始化
          pygame.init()  # 初始化pygame
          size = width, height = 320, 240  # 设置窗口尺寸
          screen = pygame.display.set_mode(size)  # 显示窗口
          # 设置标题
          pygame.display.set_caption('3、控制绿色方块吃掉红色果子')
          # 三、游戏主体
          # 执行死循环,确保窗口一直显示,同时监听关闭按钮事件
          ## 3.1 游戏内对象初始化
          # 设置矩阵的属性
          rect_color = (0, 255, 0)  # 绿色
          rect_len = 20  # 矩阵的边长
          rect_pos = [width // 2, height // 2]  # 矩阵的初始位置
          rect_speed = [2, 2]  # 矩阵的速度
          # 设置果子的初始属性
          food_color = (255,0,0 )  # 果子颜色:红色
          food_len = 15           # 果子大小:15像素
          foodPosition = (random.randint(0, width - 30) , random.randint(0, height - 30) ) # 果子的初始位置
          # 定义时钟对象
          clock = pygame.time.Clock()
          while True:
              # 3.2 监听按键、鼠标事件并设置事件内容,修改方块的值
              # 检查事件
              for event in pygame.event.get():  # 遍历所有事件
                  if event.type == pygame.QUIT:  # 如果单击关闭按钮
                      pygame.quit()  # 卸载所有pygame模块
                      sys.exit()  # 终止当前程序
              # 检查按键,并对上下左右每一个按键,绑定事件
              keys = pygame.key.get_pressed()
              if keys[pygame.K_LEFT]:
                  rect_pos[0] -= rect_speed[0]
              if keys[pygame.K_RIGHT]:
                  rect_pos[0] += rect_speed[0]
              if keys[pygame.K_UP]:
                  rect_pos[1] -= rect_speed[1]
              if keys[pygame.K_DOWN]:
                  rect_pos[1] += rect_speed[1]
              if np.linalg.norm((rect_pos[0]-foodPosition[0],rect_pos[1]-foodPosition[1]))<8:
                  foodPosition = (random.randint(0, width - 30) , random.randint(0, height - 30) ) # 果子的初始位置
              # 3.3 依据方块的位置宽高,重新渲染画面和方块
              # 确保方块不会移出屏幕
              rect_pos[0] = max(0, min(width - rect_len, rect_pos[0]))
              rect_pos[1] = max(0, min(height - rect_len, rect_pos[1]))
              # 填充背景色(每次用黑色的背景覆盖掉旧有的画面)
              screen.fill((0, 0, 0))
              # 画果子
              pygame.draw.rect(screen, food_color, (*foodPosition, food_len, food_len))
              # 画矩阵
              pygame.draw.rect(screen, rect_color, (*rect_pos, rect_len, rect_len))
              # 更新屏幕
              pygame.display.update()
              # 控制游戏刷新速度
              clock.tick(60)
          

          4、控制绿色方块吃掉红色果子,身体长度加一节

          定义游戏名字:贪吃蛇

          创建交互规则如下:

          • 1、玩家控制绿色蛇移动
          • 2、如果地图中没有红色果子,在地图任意位置,随机生成一个果子
          • 3、绿色方块的位置与红色果子位置一致时,蛇身长度长一节
          • 4、绿色方块的位置与红色果子位置一致时,红色果子刷新位置

            翻译成代码逻辑:

            初始化随机生成一个果子的位置,并渲染出来

            蛇吃果子代码逻辑:当二者重叠超过百分之七十时候,果子刷新位置

            蛇移动代码原理:每次在监听键盘,如果有移动操作导致头部发生改变,那么在新的位置绘制一个矩形,并插入蛇的身体列表开头做为新的头部。

            如果这一步没有吃到果子则弹出尾巴,有就不弹出。

            该课程涉及到的pygame函数(不重复上一课学习到的了):

            模块名功能
            pygame.Rect(x,y,width,height)生成pygame中的Rect对象(后面碰撞检测会用到)
            rect1.clip(rect2)求两个Rect对象的重叠矩阵

            代码逻辑如下:

            # -*- coding: utf-8 -*-
            # 一、模块导入
            import sys  # 导入sys模块
            import pygame  # 导入pygame模块
            import random  # 导入random模块
            import numpy as np
            # 二、pygame窗口初始化
            pygame.init()  # 初始化pygame
            size = width, height = 320, 240  # 设置窗口尺寸
            screen = pygame.display.set_mode(size)  # 显示窗口
            # 设置标题
            pygame.display.set_caption('3、控制绿色方块吃掉红色果子')
            # 三、游戏主体
            # 执行死循环,确保窗口一直显示,同时监听关闭按钮事件
            ## 3.1 游戏内对象初始化
            # 设置矩阵的属性
            rect_color = (0, 255, 0)  # 绿色
            rect_len = 20  # 矩阵的边长
            snake_len = 3
            body0 = pygame.Rect(width // 2, height // 2, rect_len, rect_len)  # 矩形
            rect_snake_body = [body0]  # 矩阵的初始位置
            rect_speed = [2, 2]  # 矩阵的速度
            # 设置果子的初始属性
            food_color = (255, 0, 0)  # 果子颜色:红色
            food_len = 15  # 果子大小:15像素
            foodPosition = (random.randint(0, width - 30), random.randint(0, height - 30))  # 果子的初始位置
            food_body = pygame.Rect(*foodPosition, food_len, food_len)
            # 定义时钟对象
            clock = pygame.time.Clock()
            while True:
                # 3.2 监听按键、鼠标事件并设置事件内容,修改方块的值
                # 检查事件
                for event in pygame.event.get():  # 遍历所有事件
                    if event.type == pygame.QUIT:  # 如果单击关闭按钮
                        pygame.quit()  # 卸载所有pygame模块
                        sys.exit()  # 终止当前程序
                rect_pos = [rect_snake_body[0].x, rect_snake_body[0].y]
                # 检查按键,并对上下左右每一个按键,绑定事件
                keys = pygame.key.get_pressed()
                if keys[pygame.K_LEFT]:
                    rect_pos[0] -= rect_speed[0]
                if keys[pygame.K_RIGHT]:
                    rect_pos[0] += rect_speed[0]
                if keys[pygame.K_UP]:
                    rect_pos[1] -= rect_speed[1]
                if keys[pygame.K_DOWN]:
                    rect_pos[1] += rect_speed[1]
                # 3.3 根据上一步的数值变化进行,二次处理处理功能:1、不会移出屏幕。2、吃掉果子。3、蛇身移动
                # 1、确保蛇不会移出屏幕
                rect_pos[0] = max(0, min(width - rect_len, rect_pos[0]))
                rect_pos[1] = max(0, min(height - rect_len, rect_pos[1]))
                # 2、吃掉果子逻辑
                overRect = food_body.clip(rect_snake_body[0])
                if overRect.width * overRect.height >= food_body.width * food_body.height * 0.7:
                    foodPosition = food_body.x, food_body.y = random.randint(0, width - 30), random.randint(0,height - 30)  # 果子的随机位置
                    snake_len += 1  # 蛇的身长加1
                # 3、移动
                if rect_pos[0] != rect_snake_body[0].x or rect_pos[1] != rect_snake_body[0].y:
                    newHead = pygame.Rect(*rect_pos, rect_len, rect_len)
                    rect_snake_body.insert(0, newHead)
                    if len(rect_snake_body) >= snake_len:
                        rect_snake_body.pop()
                # 3.4 渲染
                # 填充背景色(每次用黑色的背景覆盖掉旧有的画面)
                screen.fill((0, 0, 0))
                # 画果子
                pygame.draw.rect(screen, food_color, food_body)
                # 倒序画蛇身
                for i in range(len(rect_snake_body)):
                    # 画矩阵
                    p=rect_snake_body[len(rect_snake_body)-i-1]
                    pygame.draw.rect(screen, rect_color, p)
                    pygame.draw.rect(screen, (200,200,200), p,1)
                # 更新屏幕
                pygame.display.update()
                # 控制游戏刷新速度
                clock.tick(60)
            

            三、pygame初级

            经过前面的学习,想必各位帅哥美女对怎么用pygame写交互逻辑有了一些初步的认知。现在让我们学习一下:1、图片导入。2、文本。3、游戏结束和重新开始。4、空格游戏暂停

            初级的博客在个人博客里可以搜到,后续看反响,如果还行整理成视频在b站发布。

            中级的话就是工程开发,将以上的全部,按照工程标准分多个文件开发。后面也会提到