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

编写游戏无疑是理解面向对象最有效的方法,在学习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判断就可以实现。蛇体要采用三幅图像载入,让蛇头、蛇身、蛇尾更美观逼真。加入游戏声音之后,可玩性更强。在标题栏下方增加一个类似扫雷游戏的记分机制,使贪吃蛇能够记录该游戏的得分最高值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值