计算机软件实习项目二 —— 贪吃蛇游戏 (代码实现) 12-16

代码实现

  不得不说python真是太香了,我感觉写起来比C++快,而且代码量更少,还有非常多十分方便的方法可以使用。在pycharm里有非常多的快捷键十分的方便,相较项目使用的visual studio快捷键更多更灵活。

  这篇博文我基于上一篇的博文结构进行代码粘贴以及解释。

一、贪吃龙

1. 龙的定义

(1)head_x,head_y:龙头部的坐标可以用两个变量来定义,一个储存龙头在地图的第几行,另一个储存龙头在地图的第几列。

(2)coordinate[]:整条龙包括龙头的坐标可以放在一个列表里,列表里的每一个元素为字符串类型,方便用于查找判定龙头是否与自身发生碰撞。

(3)direction:龙头部的当前方向。

# 龙变量
    # 龙头的坐标
    head_x = 0  # 龙头横坐标
    head_y = 0  # 龙头纵坐标
    direction = 0  # 龙头方向
    coordinate = []  # 坐标数组,整条龙每一个结点的坐标(包括龙头)
2. 食物的定义

(1)food_coordinate:用于储存当前食物的坐标。
(2)food_type:食物种类。

    # 食物变量
    food_coordinate = ''  # 食物坐标
    food_type = 1  # 食物种类
3. 窗口变量 & 初始化窗口
    # 窗口变量
    w_width = 1200
    w_height = 900
    row = 0  # 行数
    column = 0  # 列数
    information_bar = w_width - w_height  # 游戏界面信息窗口宽度
    keyboard = 1  # 限制键盘录入
    welcome_flag = 1  # 判断是否在欢迎界面
    pygame.mixer.init()
    music = pygame.mixer.music

(1)__init__方法: 设置窗口大小,窗口图标,以及固定窗口大小。

    # 定义构造函数初始化窗口
    def __init__(self):
        QWidget.__init__(self)  # 调用父类的构造函数
        self.setWindowTitle('贪吃龙 v1.0')  # 设置窗口标题
        self.setWindowIcon(QIcon("Logo.jpg"))
        self.resize(self.w_width, self.w_height)  # 游戏窗口大小
        self.setFixedSize(self.width(), self.height())  # 让游戏窗口大小固定
        self.timer = QTimer()  # 设置计时器
        # 音乐模块
        self.welcome()
4. 初始化游戏

 init()方法:
(1)随机龙头位置、龙头方向、食物位置食物种类;

# 初始化游戏
    def init(self):
        self.setStyleSheet('background-color:#D9DADF')  # 设定游戏背景颜色
        self.row = 16  # 行数
        self.column = 16  # 列数
        # 初始化龙头坐标并避免龙头离地图边缘太近
        self.head_x = random.randint(0 + 5, self.column - 1 - 5)  # 随机选取列
        self.head_y = random.randint(0 + 5, self.row - 1 - 5)  # 随机选取行
        self.coordinate = [str(self.head_x) + ":" + str(self.head_y)]  # 把龙头放进龙数组
        self.direction = random.randint(0, 3)  # 随机龙头初始方向(randint函数是双闭的)

(2)初始化龙的速度、分数;

        # 初始化龙速度
        self.speed = self.speed2
        self.score = 0

(3)初始化键盘标志、暂停标志、结束标志;

     self.keyboard = 1
        self.stop_flag = 0
        self.game_over = 0

(4)初始化游戏音乐;

        self.music.stop()
        self.music = pygame.mixer.Sound(r"game_music.mp3")
        self.music.play(-1)
5. 欢迎界面

(1)初始化欢迎界面音乐;

(2)根据游戏标志选择进入主游戏界面;

# 欢迎界面
    def welcome(self):
        if self.welcome_flag == 0:
            self.music.stop()
            # 初始化
            self.init()
            # 进入主游戏界面
            self.timer.timeout.connect(self.game)  # 设置定时器超时后的槽函数
            self.timer.start(self.speed)  # 设定游戏速度
            return
        self.music = pygame.mixer.Sound(r"menu_music.mp3")
        self.music.play(-1)
6. 开始游戏

(1)snakeMove()方法:龙的自动移动。
(2)check()方法:判断龙是否吃到食物、是否吃到自己、是否撞墙。
(3)update()方法:重新绘制,刷新地图。

# 游戏开始
    def game(self):
        # 限制一次刷新只能改变一次键盘
        self.keyboard = 1
        # 自动移动龙
        self.snakeMove()
        # 判断是否吃到食物
        self.check()
        # print("dir:" + str(self.direction) + "    head: " + str(self.head_x) + " , " + str(
        #     self.head_y) + "    body: " + str(self.coordinate) + "    food:" + self.food_coordinate)
        # 更新
        self.update()
7. 子方法
(1)snakeMove()方法:

  根据当前龙的方位,更新龙头坐标。

# 龙的移动
    def snakeMove(self):
        if self.direction == 0:
            self.head_x -= 1
        elif self.direction == 1:
            self.head_x += 1
        elif self.direction == 2:
            self.head_y -= 1
        elif self.direction == 3:
            self.head_y += 1
(2)food()方法:

  随机产生食物坐标,且坐标不与整条龙的任意一个结点坐标重合。

# 生成食物的坐标
    def food(self):
        while True:
            food_x = random.randint(0 + 2, self.row - 1 - 2)
            food_y = random.randint(0 + 2, self.column - 1 - 2)
            temp = str(food_x) + ":" + str(food_y)
            # 新生成食物坐标不能与龙重合
            if temp not in self.coordinate:
                self.food_coordinate = temp
                break
(3)bodyMove()方法:

  更新龙身和龙头的坐标位置。

 # 龙的位置更新
    def bodyMove(self):
        temp = str(self.head_x) + ':' + str(self.head_y)
        # 更新龙身位置
        for n in range(len(self.coordinate) - 1, 0, -1):
            self.coordinate[n] = self.coordinate[n - 1]
        # 更新龙头位置
        self.coordinate[0] = temp
(4)check()方法:

  ①判断龙是否吃到食物,如果吃到,更新速度、分数、各种标记。

# 检查龙是否吃到食物
    def check(self):
        temp = str(self.head_x) + ':' + str(self.head_y)
        # 判断龙是否吃到食物
        if self.food_coordinate == temp:
            self.coordinate.insert(0, temp)
            pygame.mixer.music.load(r"get_point.mp3")
            pygame.mixer.music.play(start=0.5)
            if self.food_type == 1:
                self.score += 30
            elif self.food_type == 2:
                self.score += 50
            elif self.food_type == 3:
                self.score += 70
            self.speed -= 5
            self.timer.start(self.speed)  # 更新游戏速度
            self.food()
            self.food_type = random.randint(1, 3)
            if self.score >= self.goal_score:
                self.gameOver(False)
                return

  ②判断龙是否触碰到边界,如果碰到就执行结束游戏方法。

        else:
            # 判断边界
            if (self.head_x < 0 or self.head_x >= self.column) or (self.head_y < 0 or self.head_y >= self.row):
                self.gameOver(True)
                return

  ③判断龙头是否触碰到自身,如果碰到就执行结束游戏方法。

# 判断龙头是否触碰到龙身
            if temp in self.coordinate[1:]:  # 龙头碰到龙身
                self.gameOver(True)
                return
            self.bodyMove()
(5)gameOver

  ①更新最高分。

# 游戏结束,弹出提示框 flag == True(游戏失败);bWin == False(恭喜过关)
    def gameOver(self, flag):
        self.timer.stop()
        if self.score > self.top_score:
            self.top_score = self.score  # 保存最高分

  ②根据游戏结束标记判断游戏失败或游戏胜利。

        if flag:
            self.game_over = 1  # 游戏失败
            pygame.mixer.music.load(r"lose_music.mp3")
            pygame.mixer.music.play()

  ③画出胜利标志,播放对应音效。

 else:
            self.game_over = 2  # 游戏胜利
            pygame.mixer.music.load(r"win_music.mp3")
            pygame.mixer.music.play()
        self.update()
8. 事件
(1) keyPressEvent()方法:

  获取键盘按下的按键,通过区分不同按键来做出对应操作。

# 键盘事件
    def keyPressEvent(self, event):
        QWidget.keyPressEvent(self, event)
        key = event.key()

  ①限制每次刷新按键次数,避免出现bug

        # 如果此轮刷新已经按下过按钮则不允许再按下
        if self.keyboard == 0:
            return

  ②↑↓←→ 键:改变龙头方向

# 按键模块
        if key == Qt.Key_Up and self.direction != 1:  # 上
            self.direction = 0
            self.keyboard = 0
        elif key == Qt.Key_Down and self.direction != 0:  # 下
            self.direction = 1
            self.keyboard = 0
        elif key == Qt.Key_Left and self.direction != 3:  # 左
            self.direction = 2
            self.keyboard = 0
        elif key == Qt.Key_Right and self.direction != 2:  # 右
            self.direction = 3
            self.keyboard = 0

  ③Space 键:开始游戏 / 暂停游戏 / 重新开始游戏

# 暂停模块
        if key == Qt.Key_Space and self.game_over == 0:
            pygame.mixer.music.load(r"key_music.mp3")
            pygame.mixer.music.play()
            self.stop_flag += 1
            if self.stop_flag % 2 == 1:
                self.timer.stop()
                self.update()
            else:
                self.timer.start(self.speed)

  ④结束游戏模块:游戏结束后进行相应的键盘处理

# 结束游戏模块
        if self.game_over != 0:
            # 重新开始游戏
            if key == Qt.Key_Space:
                pygame.mixer.music.load(r"key_music.mp3")
                pygame.mixer.music.play()
                self.init()
                self.timer.start(self.speed)
                self.game()

  ⑤欢迎界面:在欢迎界面下对按键进行处理

# 欢迎界面
        if self.welcome_flag == 1:
            if key == Qt.Key_Space:
                pygame.mixer.music.load(r"key_music.mp3")
                pygame.mixer.music.play()
                self.welcome_flag = 0
                self.welcome()

  ⑥Esc 键:退出游戏

# 退出游戏
        if key == Qt.Key_Escape:
            self.close()
(2)paintEvent()方法:

  每次执行update()方法的时候会调用paintEvent方法,通过在类内定义的各种标志来绘制不同的图形。

  ①创建一个画笔。

# 绘制
    def paintEvent(self, event):

        QWidget.paintEvent(self, event)
        painter = QPainter(self)  # 创建一个画笔

  ②根据欢迎界面标志判断是否要画欢迎界面,如果不是则画主游戏界面。

# 欢迎界面
        if self.welcome_flag == 1:
            painter.drawImage(QRectF(0, 0, self.width(), self.height() + 50),
                              QImage('welcome.jpeg'))
            painter.setFont(QFont('方正小篆体', 100))
            painter.drawText(self.width() - 900, 400, "贪吃龙")
            painter.setFont(QFont('汉仪尚巍手书W', 30))
            painter.drawText(self.width() - 850, 525, "空格 键:开始游戏")
            painter.drawText(self.width() - 825, 600, "Esc 键:退出游戏")
            return

  ③画主界面的背景图、信息栏图、分割线、分数、最高分、提示信息等。

        # 主游戏界面
        pen = QPen(QColor(200, 200, 200), 1, Qt.DashLine)  # 设定画笔的颜色、粗细以及线型
        # 反锯齿
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setPen(pen)  # 更新画笔参数

        painter.drawImage(QRectF(0, 0, self.width() - self.information_bar, self.height()),
                          QImage('game.jpg'))

        painter.drawImage(QRectF(self.width() - self.information_bar, 0, self.information_bar, self.height()),
                          QImage('information.jpg'))

        row_space = self.height() / self.row  # 行间距
        column_space = (self.width() - self.information_bar) / self.column  # 列间距

        # 绘制行线和列线
        for n in range(self.row + 1):
            painter.drawLine(QPointF(0, row_space * n), QPointF(self.width() - self.information_bar, row_space * n))
        for n in range(self.column + 1):
            painter.drawLine(QPointF(column_space * n, 0), QPointF(column_space * n, self.height()))

        pen = QPen(QColor(50, 50, 50), 5, Qt.SolidLine)  # 设定画笔的颜色、粗细以及线型
        painter.setPen(pen)  # 更新画笔参数
        painter.drawLine(QPointF(self.width(), 0), QPointF(self.width(), self.height()))
        painter.drawLine(QPointF(0, 0), QPointF(self.width(), 0))
        painter.drawLine(QPointF(0, 0), QPointF(0, self.height()))
        painter.drawLine(QPointF(0, self.height()), QPointF(self.width(), self.height()))
        painter.drawLine(QPointF(self.width() - self.information_bar, 0),
                         QPointF(self.width() - self.information_bar, self.height()))

  ④龙龙头、龙身、龙尾巴。

cnt = 0
        # 画整条龙
        for n in self.coordinate:
            row = int(n[:n.find(':')])
            column = int(n[n.find(':') + 1:])
            # 画龙头
            if cnt == 0:
                if self.direction == 0:  # 朝上
                    painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                                      QImage('head_up.png'))
                elif self.direction == 1:  # 朝下
                    painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                                      QImage('head_down.png'))
                elif self.direction == 2:  # 朝左
                    painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                                      QImage('head_left.png'))
                elif self.direction == 3:  # 朝右
                    painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                                      QImage('head_right.png'))
            elif cnt == self.coordinate.__len__() - 1:
                painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                                  QImage('tail.png'))
            else:  # 龙身
                painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                                  QImage('body.png'))
            cnt += 1

  ⑤根据食物种类标记,画不同的食物。

# 画食物
        # 获取方块位置
        row = int(self.food_coordinate[:self.food_coordinate.find(':')])
        column = int(self.food_coordinate[self.food_coordinate.find(':') + 1:])

        if self.food_type == 1:
            painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                              QImage('dragon_ball_1.png'))
        elif self.food_type == 2:
            painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                              QImage('dragon_ball_2.png'))
        elif self.food_type == 3:
            painter.drawImage(QRectF(column * column_space, row * row_space, column_space, row_space),
                              QImage('dragon_ball_3.png'))

  ⑥画信息栏,包括:分数、最高分、按键规则、游戏规则。

 # 画信息栏
        painter.setPen(QColor(0, 0, 0))
        painter.setFont(QFont('汉仪尚巍手书W', 30))
        painter.drawText(self.width() - 215, 100, "分 数")
        painter.drawText(self.width() - 190, 175, str(self.score))
        painter.drawText(self.width() - 215, 275, "最高分")
        painter.drawText(self.width() - 190, 350, str(self.top_score))
        painter.drawText(self.width() - 250, 450, "按键规则")
        painter.setFont(QFont('汉仪尚巍手书W', 20))
        painter.drawText(self.width() - 250, 525, "↑↓←→控制")
        painter.drawText(self.width() - 250, 575, " 空格 暂停")
        painter.setFont(QFont('汉仪尚巍手书W', 30))
        painter.drawText(self.width() - 250, 675, "游戏规则")
        painter.setFont(QFont('汉仪尚巍手书W', 20))
        painter.drawText(self.width() - 235, 750, "不能碰墙壁")
        painter.drawText(self.width() - 235, 800, "不能碰身体")
        painter.drawText(self.width() - 235, 850, "1000分过关")

  ⑦根据游戏暂停标记画游戏暂停提示。

# 画游戏暂停
        if self.stop_flag % 2 == 1:
            painter.setFont(QFont('汉仪尚巍手书W', 100))
            painter.drawText(self.width() - 1085, 425, "游戏暂停")
            painter.setFont(QFont('汉仪尚巍手书W', 30))
            painter.drawText(self.width() - 960, 550, "按 空格 键继续游戏")
            painter.drawText(self.width() - 950, 625, "按 Esc 键退出游戏")

  ⑧根据游戏结束标记绘制游戏胜利 / 游戏失败,显示得分以及提示信息。

# 画游戏结束
        painter.setPen(QColor(242, 15, 21))
        if self.game_over == 1:
            painter.setFont(QFont('汉仪尚巍手书W', 100))
            painter.drawText(self.width() - 1085, 400, "游戏失败")
        elif self.game_over == 2:
            painter.setFont(QFont('汉仪尚巍手书W', 100))
            painter.drawText(self.width() - 1085, 400, "恭喜过关")
        painter.setPen(QColor(0, 0, 0))

        if self.game_over != 0:
            painter.setFont(QFont('汉仪尚巍手书W', 50))
            painter.drawText(self.width() - 1050, 525, "你的得分为:")
            painter.setPen(QColor(245, 126, 13))
            painter.drawText(self.width() - 525, 525, str(self.score))
            painter.setPen(QColor(0, 0, 0))
            painter.setFont(QFont('汉仪尚巍手书W', 30))
            painter.drawText(self.width() - 1000, 625, "按 空格 键重新开始游戏")
            painter.drawText(self.width() - 950, 700, "按 Esc 键退出游戏")

  这一篇是贪吃龙游戏代码的实现,游戏的游玩见下一篇博客。

                    ———2020.12.16(罗涵)

                           THE END

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值