用pygame写个简单的贪吃蛇游戏

来源:互联网 发布:mac打印jpeg 编辑:程序博客网 时间:2024/04/27 15:12

编写游戏无疑是理解面向对象最有效的方法,在学习python面向对象部分的过程中,我尝试写了如下一个简单的贪吃蛇小游戏,作为理解面向对象、事件触发机制、pygame模块的一个途径。OK,废话少说,上代码。

首先介绍页面布局,为了美观,我们把每块蛇的身体定义为14x14像素的矩形,实物为直径14像素的圆。为什么采用14像素这个值呢?因为在身体大小合适的15像素附近,不想用一个奇数导致确定圆心的过程中会有浮点数产生,增加额外的内存消耗。当然您也可以使用16像素balablabla。游戏标题为“Greedy Snake”,窗口大小采用560x560像素,560/14=40,故横向纵向分别可以产生40个贪吃蛇的身体长度。

定义Snake类,包含初始化蛇的位置和蛇移动的初始方向。用一个嵌套列表来存储蛇的身体的位置,初始蛇向右移动,使用复制身体位置的方式移动蛇。

class Snake(object):    """This is the snake class, including the initializing, body, eat food, and direction to move."""    def __init__(self):        """Using the random module to create a initial snake. The snake has three parts:        a head, a body, and a tail."""        self.pos_list = [[42, 42]]        self.direction = 'R'        def posList(self):        "Return all the points of the snake."        return self.pos_list        def changeDirection(self, direction):        "Change the direction of the snake."        self.direction = direction        def moveDirection(self):        "Change the points to keep the snake moving in the given direction."        body_length = len(self.pos_list) - 1        while body_length > 0:            self.pos_list[body_length] = deepcopy(self.pos_list[body_length - 1])            body_length -= 1        if self.direction == 'R':            self.pos_list[0][0] += 14        elif self.direction == 'L':            self.pos_list[0][0] -= 14        elif self.direction == 'U':            self.pos_list[0][1] -= 14        elif self.direction == 'D':            self.pos_list[0][1] += 14        def eatFood(self, foodPoint):        "The snake eats a point and add the point to its body."        self.pos_list.insert(0, foodPoint)

然后,是食物这个类。类中包含对于食物坐标的初始值,和更新食物坐标的方法。

class Food(object):        """Initialize the random coordinate of the food and return it."""        def __init__(self):            x = randint(5, 35) * 14            y = randint(5, 35) * 14            self.food_position = [x, y]                def foodList(self):            "Return the coordinate of the food point."            return self.food_position                def updateFood(self):            "When the food is eaten, update the position of the food."            self.food_position[0] = randint(0, 39) * 14            self.food_position[1] = randint(0, 39) * 14

接下来,定义一个贪吃蛇的函数,在这个函数中编写贪吃蛇移动的逻辑,更新食物坐标,判断蛇是否吃到了食物、是否撞到了墙上。逻辑上,如前所述,窗口大小采用560x560,使用时钟控制每秒钟的画面不超过20帧从而控制游戏的画面感,屏幕底色为白色,食物和蛇均为红色,当贪吃蛇撞到墙或者撞到自身时,绘制一幅底色为褐色、字体为红色的“Game Over!”图案,并等待退出事件,关闭窗口。

def snakeMain():    "The logic to run the game."    pygame.init()    screen = pygame.display.set_mode((560, 560), 0, 32)    pygame.display.set_caption("Greedy Snake")        clock = pygame.time.Clock()    snake = Snake()    food = Food()    direction_flag = 'R'        while True:        screen.fill((255, 255, 255))        for event in pygame.event.get():            if event.type == QUIT:                exit()            if event.type == KEYDOWN:                if event.key == K_RIGHT:                    direction_flag = 'R'                elif event.key == K_LEFT:                    direction_flag = 'L'                elif event.key == K_UP:                    direction_flag = 'U'                elif event.key == K_DOWN:                    direction_flag = 'D'                        # Draw the food and the snake        food_position = deepcopy(food.foodList())        food_position[0] += 7        food_position[1] += 7        pygame.draw.circle(screen, (255, 0, 0), food_position, 10)        snake_position = snake.posList()        for pos in snake_position:            temp_rect = pygame.Rect(pos[0], pos[1], 14, 14)            pygame.draw.rect(screen, (255, 0, 0), temp_rect)        pygame.display.update()                # 20 frame of pictures per second        # and move the snake        frame_rate = clock.tick(20)        snake.changeDirection(direction_flag)        snake.moveDirection()                #Judge whether the snake eats a food or not        food_position = deepcopy(food.foodList())        snake_head = deepcopy(snake.posList()[0])        if direction_flag == 'R':            snake_head[0] += 14        elif direction_flag == 'L':            snake_head[0] -= 14        elif direction_flag == 'U':            snake_head[1] -= 14        elif direction_flag == 'D':            snake_head[1] += 14        if snake_head == food_position:            snake.eatFood(food_position)            food.updateFood()                # Judge whether the snake gets a collision or not        gameover_flag = False        snake_position = snake.posList()        snake_head = snake_position[0]        if direction_flag == 'R':            if snake_head[0] > 546:                gameover_flag = True        elif direction_flag == 'L':            if snake_head[0] < 0:                gameover_flag = True         elif direction_flag == 'U':            if snake_head[1] < 0:                gameover_flag = True        elif direction_flag == 'D':            if snake_head[1] > 546:                gameover_flag = True        for pos in range(1, len(snake_position) - 1):            if snake_head == snake_position[pos]:                gameover_flag = True                if gameover_flag:            pygame.font.init()            screen.fill((100, 0, 0))            font = pygame.font.SysFont("arial", 32)            text = font.render("Game Over!", True, (255, 0, 0))            screen.blit(text, (260, 260))            pygame.display.update()            while True:                again_event = pygame.event.poll()                if again_event.type == QUIT:                    exit()

最后,判断是否在当前命名空间中执行该主体函数,如果是,则执行贪吃蛇游戏。

if __name__ == '__main__':    snakeMain()            

总体来说,这是一个非常简单的贪吃蛇版本,逻辑上还需要继续完善,bug还需要修复。在接下来的修改过程中,我会将它转变成1个像素1个像素之间的移动,而不再将窗口分割成多个矩形,这样可以增加蛇移动的流畅度。另一方面,方向移动方面,向前移动不会再接受向后移动的事件,向左或向右类似,很简单,通过if判断就可以实现。蛇体要采用三幅图像载入,让蛇头、蛇身、蛇尾更美观逼真。加入游戏声音之后,可玩性更强。在标题栏下方增加一个类似扫雷游戏的记分机制,使贪吃蛇能够记录该游戏的得分最高值。

0 0