基于Pygame的贪吃蛇python小游戏实现

基于Pygame的贪吃蛇python小游戏实现

0.前言

许多程序员入门的第一个项目都会选择贪吃蛇。用贪吃蛇来练手比较合适。因为贪吃蛇的算法并不难,但绝对够有启发作用,也比较经典。相对来说它很容易就能实现。对于新手来说,拿贪吃蛇来作为练习是很有必要的。

各个语言都会有许多共性,但实现的算法基本都差不多。这里就用Python为例,分析一下算法实现,并附上相应代码。不足之处还请指正。

1.分类

python是一门面向对象的程序语言,我们在编写一个项目时要首先根据需要对其进行分类,模块化处理。

分析得到,需要定义Snake类、Food类、Game类,分别代表蛇、食物、游戏规则。然后对类定义成员方法,比如Snake类需要进行拐弯、增加、减少等操作。

Food类
//Author:xiaobei

import random

class Food(object):
    def __init__(self,r_pos,color,state):
        self.r_pos = r_pos
        self.color = color
        self.state = state
    def update(self,snake,sound_effect):
        if self.r_pos in snake.body:
            self.state = 1
        else:
            self.state = 0
        if self.state == 1:
            self.r_pos = [random.randrange(30,720,30),random.randrange(30,540,30)]
            self.color = random.choice([(255,255,0),(255,0,255),(0,255,255)])
            snake.length+=1
            sound_effect.play()
Snake类
//Author:xiaobei

Snake(object):
    def __init__(self,length,body,direction,direction_change,head_pos,end):
        self.length = length
        self.body = body
        self.direction = "RIGHT"
        self.direction_change = "RIGHT"
        self.head_pos = head_pos
        self.end = False

#控制蛇的爬行
    def move(self):
        if self.length > len(self.body):
            self.body.append([0,0])
        for i in range(self.length-1,0,-1):
            self.body[i][0] = self.body[i-1][0]     #这里多重列表互相赋值不能整体,因为最外层代表首地址,即首元素,因此要分开赋值
            self.body[i][1] = self.body[i-1][1]
        if self.direction == "LEFT" :
            self.body[0][0]-=30
        elif self.direction == "RIGHT":
            self.body[0][0]+=30
        elif self.direction == "UP":
            self.body[0][1]-=30
        else:
            if self.direction == "DOWN":
                self.body[0][1]+=30

#控制不让贪吃蛇溢出
        if self.head_pos[0]>850:

            self.end = True
        elif self.head_pos[1] > 650:

            self.end = True
        elif self.head_pos[0] < -30:

            self.end = True
        else:
            if self.head_pos[1]<-30:
                self.end = True


    def turn(self):
        if self.direction_change == "LEFT" and self.direction != "RIGHT":
            self.direction = self.direction_change
        elif self.direction_change == "RIGHT" and self.direction != "LEFT":
            self.direction = self.direction_change
        elif self.direction_change == "UP" and self.direction != "DOWN":
            self.direction = self.direction_change
        else :
            if self.direction_change == "DOWN" and self.direction != "UP":
                self.direction = self.direction_change

这里偷了一个懒,没有定义Game类,我把游戏规则掺进了主程序main()中。

2.主要算法

(1)控制贪吃蛇拐弯

用pygame产生一个界面,我们把这个界面当做一个坐标网格,蛇身是一个坐标列表。用蛇头坐标作为蛇的行进方向,控制蛇身整体的方向。难点在于如何使蛇身在转弯的时候,使蛇身每一部分逐个而不是整体转向呢?

for i in range(self.length-1,0,-1):
            self.body[i][0] = self.body[i-1][0]     #这里多重列表互相赋值不能整体,因为最外层代表首地址,即首元素,因此要分开赋值
            self.body[i][1] = self.body[i-1][1]

这里我们采用一个循环,从尾部开始,只有蛇头在变动,后面的所有部分都在被蛇头牵连着动。假设有n个结点,nn-1 = nn,……,n2 = n1 .依次移动,形成一个拐弯效果。

(2)食物的刷新

食物刷新位置是在,蛇吃到食物之后才激发。所以重点在于如何判断是否吃到食物。这里有一个易错点:容易使食物坐标和蛇头的坐标永不吻合的的情况。这是因为蛇的坐标间隔以及食物刷新间隔位置,不是以同一递增/递减公差q进行的。

def update(self,snake,sound_effect):
    if self.r_pos in snake.body:
        self.state = 1
    else:
        self.state = 0
    if self.state == 1:
        self.r_pos = [random.randrange(30,720,30),random.randrange(30,540,30)]
        self.color = random.choice([(255,255,0),(255,0,255),(0,255,255)])
        snake.length+=1
        sound_effect.play()

在此基础上,当蛇头坐标与食物坐标吻合,便激发食物刷新位置。

(3)游戏结束条件

游戏结束条件无非两种:一是蛇头与自身重合,二十蛇头触到边界。这两种方法同样是通过坐标判断来实的。

if snake.head_pos in snake.body[1:] or snake.end:
    screen.blit(text2Image,(200,200))
    pygame.mixer.music.stop()
    sound_end.play()
    pygame.display.update()
    time.sleep(5)
    pygame.quit()
    sys.exit()

3.界面实现与主函数

界面实现依靠pygame第三方库来执行,pygame库的安装可以用pip命令行来实现,在控制台进行。

pip install -U pygame

参数-U是安装game为最新版。

pygame的使用请参考《Python 游戏开发—Pygame 快速入门》

主要代码如下:

//Author:xiaobei

import pygame
import os
import sys
import time
import random
from pygame.locals import *
from Snake import Snake
from Food import Food

pygame.init()

#混音器初始化
pygame.mixer.init()

#加载音效
sound_effect = pygame.mixer.Sound("C:\Windows\media\Windows Proximity Notification.wav")
sound_end = pygame.mixer.Sound("C:\Windows\media\Ring07.wav")
pygame.mixer.music.load("C:\Windows\media\flourish.mid")
pygame.mixer.music.set_volume(0.5)
sound_effect.set_volume(1)
pygame.mixer.music.play(-1) #-1控制无限循环

#设置游戏时钟
clock = pygame.time.Clock()

#设置主窗体相对位置、大小,标题、背景颜色
os.environ["SDL_VIDEO_WINDOW_POS"] = "%d,%d"%(50,50)
screen = pygame.display.set_mode((820,620))
screen.fill([255,235,100])
pygame.display.set_caption("贪吃蛇小游戏")

#设置文本
pygame.font.init
text1 = pygame.font.Font(None,35)
text1Image = text1.render("SCORE:",True,[100,125,255])
text2 = pygame.font.Font(None,120)
text2Image = text2.render("Gameover",True,[100,125,255])
text3 = pygame.font.Font(None,35)
text4 = pygame.font.Font(None,35)
screen.blit(text1Image,(5,5))



#创建贪吃蛇对象、食物等对象
snake = Snake(length = 3,body = [[120,90],[90,90],[60,90]],direction = "RIGHT",direction_change ="RIGHT", head_pos = [120,100],end = False)
food = Food(r_pos = [120,420],color = (100,255,100),state = 0)


score = 0

#进入循环
while True:
#设置帧率刷新速度
    clock.tick(5)

#设置窗口便于刷新
    screen = pygame.display.set_mode((820,620))
    screen.fill([255,235,100])

#绘制文本“分数”
    score = len(snake.body)-3
    screen.blit(text1Image,(5,5))
    text3Image = text3.render("{}".format(score),True,[100,125,255])
    screen.blit(text3Image,(120,5))

#绘制文本“时间”
    text4Image = text4.render("time: {} seconds".format(int(time.clock())),True,[100,125,255])
    screen.blit(text4Image,(200,5))

#绘制蛇身、食物
    food_circle = pygame.draw.rect(screen,food.color,(food.r_pos[0],food.r_pos[1],28,28),0)
    for i in range(snake.length):
        snake_rent = pygame.draw.rect(screen,(120,255,205),(snake.body[i][0],snake.body[i][1],28,28),0)



#设置退出条件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

#获取键盘事件
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                snake.direction_change = "LEFT"
            elif event.key == pygame.K_RIGHT:
                snake.direction_change = "RIGHT"
            elif event.key == pygame.K_UP:
                snake.direction_change = "UP"
            else:
                if event.key == pygame.K_DOWN:
                    snake.direction_change = "DOWN"


#控制蛇的转向
    snake.head_pos = snake.body[0]
    food.update(snake,sound_effect)
    snake.move()
    snake.turn()

#游戏结束
    if snake.head_pos in snake.body[1:] or snake.end:
        screen.blit(text2Image,(200,200))
        pygame.mixer.music.stop()
        sound_end.play()
        pygame.display.update()
        time.sleep(5)
        pygame.quit()
        sys.exit()
#刷新界面
    pygame.display.update()

4.说明

我把每个类模块独立为单个文件,把它们放在同一项目文件夹下,然后在主程序中调用它们,主程序也是一个文件,直接运行主文件即可运行程序。(不好意思,这里又偷了一个懒,嘿嘿!)

运行界面如下:运行效果界面
主程序中有背景音乐和音效的绝对路径引用,读者可自行忽略删改。恭喜,到这里一个贪吃蛇小程序大功告成!

不当之处,还望指正,谢谢。

原文地址:https://www.cnblogs.com/slz99/p/12527753.html