使用Python游戏库Pygame开发贪吃蛇

写在前面:

学习Python有段时间了,最近看到网上有利用Pygame库开发小游戏的,感觉很有趣,所以打算自己也写一个玩玩。写了差不多一周,基本功能都已经实现,话不多说,直接来看看吧。

贪吃蛇

实现的功能:

1.创建屏幕、蛇、食物;

2.使用方向键控制蛇的移动;

3.蛇碰撞到食物视为“吃掉”,食物重新产生,蛇变大;

4.蛇出界或碰到自己,游戏结束;

5.附加:每次开局前需要按键Enter

main.py

# -*- coding:utf-8 -*-

import pygame, sys, time
import control
from food import *
from snake import *


def run_game():

    # 1.设置常量
    bg_color = (255, 163, 70)
    sk_color = (0, 0, 0)
    fd_color = (255, 0, 0)
    window_size = (750, 750)
    activate_game = False    # 检测游戏是否处于激活状态

    # 2.准备初始化Pygame
    pygame.init()
    screen = pygame.display.set_mode(window_size)
    pygame.display.set_caption("Snake")

    while True:
        control.start(screen, "press Enter to start!", activate_game)
        if not activate_game:
            food_pos, snake_list, move_direction, food_flag = control.init()    # 初始化游戏设置
        activate_game = control.game_stats(activate_game)

        time.sleep(0.1)  # 增加延时,降低cpu使用
        screen.fill(bg_color)

        if activate_game:
            snake = Snake(screen, sk_color, food_pos, snake_list)  # 创建蛇的实例
            last_snake = snake_list[-1]  # 将蛇尾标记
            move_direction = control.direction(move_direction)
            snake_list = control.move(move_direction, snake_list)
            food = Food(screen, fd_color, food_flag, food_pos)
            food_flag, food_pos = food.is_food_exist()
            snake_list, food_flag = control.eating(snake_list, food_pos, food_flag, last_snake)
            activate_game = control.game_over(activate_game, snake_list)  # 判断是否出界或吃到自己
            pygame.display.update()


run_game()

control.py

# -*- coding:utf -*-

import pygame, sys, copy


def init():
    food_pos = [0, 0]
    snake_list = [[240, 240], [230, 240]]
    move_direction = "right"
    food_flag = False
    return food_pos, snake_list, move_direction, food_flag


def start(screen, msg, activate_game):
    bt_color = (255, 255, 255)
    txt_color = (0, 0, 0)
    font = pygame.font.SysFont(None, 38)
    button = pygame.Rect(0, 0, 0, 0)
    screen_rect = screen.get_rect()
    button.center = screen_rect.center
    msg_image = font.render(msg, True, txt_color, bt_color)
    msg_image_rect = msg_image.get_rect()
    msg_image_rect.center = button.center
    if not activate_game:
        screen.fill(bt_color, button)
        screen.blit(msg_image, msg_image_rect)
        pygame.display.update()


def direction(move_direction):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if move_direction != "left" and event.key == pygame.K_RIGHT:
                move_direction = "right"
            elif move_direction != "right" and event.key == pygame.K_LEFT:
                move_direction = "left"
            elif move_direction != "down" and event.key == pygame.K_UP:
                move_direction = "up"
            elif move_direction != "up" and event.key == pygame.K_DOWN:
                move_direction = "down"
    return move_direction


def game_stats(activate_game):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN:
                activate_game = True
    return activate_game


def move(move_direction, snake_list):
    copy_snake_list = copy.deepcopy(snake_list)     # 此处注意使用deepcopy
    for i in range(len(snake_list)):  # 将蛇上一节的位置传给下一节
        if i > 0:
            snake_list[i] = copy.deepcopy(copy_snake_list[i - 1])
    if move_direction == "right":
        snake_list[0][0] += 10
    elif move_direction == "left":
        snake_list[0][0] -= 10
    elif move_direction == "up":
        snake_list[0][-1] -= 10
    elif move_direction == "down":
        snake_list[0][-1] += 10
    return snake_list


def eating(snake_list, food_pos, food_flag, last_snake):
    if (food_pos[0]-10 < snake_list[0][0] < food_pos[0]+10) and \
            (food_pos[-1]-10 < snake_list[0][-1] < food_pos[-1]+10):
        snake_list.append(last_snake)
        food_flag = False
    return snake_list, food_flag


def game_over(activate_game, snake_list):
    if snake_list[0][0] > 740 or snake_list[0][0] < 0:
        activate_game = False
    elif snake_list[0][-1] > 740 or snake_list[0][-1] < 0:
        activate_game = False
    body_list = snake_list[1:]
    for body in body_list:
        if body[0]-10 < snake_list[0][0] < body[0]+10 and body[-1]-10 < snake_list[0][-1] < body[-1]+10:
            activate_game = False
    return activate_game

food.py

# -*- coding:utf-8 -*-

import random, pygame


class Food(object):
    """食物类,判断食物是否存在并产生"""

    def __init__(self, screen, fd_color, food_flag, food_pos):
        self.screen = screen
        self.fd_color = fd_color
        self.food_flag = food_flag
        self.food_pos = food_pos

    def is_food_exist(self):
        if not self.food_flag:      # food不存在
            self.food_pos[0] = random.randint(0, 740)
            self.food_pos[-1] = random.randint(0, 740)
            pygame.draw.rect(self.screen, self.fd_color, (self.food_pos, (10, 10)))
            self.food_flag = True
        elif self.food_flag:
            pygame.draw.rect(self.screen, self.fd_color, (self.food_pos, (10, 10)))
        return self.food_flag, self.food_pos

snake.py

# -*- coding:utf-8 -*-

import pygame


class Snake(object):
        """创建蛇类"""

        def __init__(self, screen, sk_color, food_pos, snake_list):
            self.screen = screen
            self.sk_color = sk_color
            self.food_pos = food_pos
            self.snake_list = snake_list
            for snake in snake_list:
                pygame.draw.rect(self.screen, self.sk_color, (snake, (10, 10)))

总结:

一开始一直想使用面向对象的方法去写,但是写到一半的时候感觉已经有点偏离面向对象了,最后还是硬着头皮写完。虽然整体功能已经实现,但是对部分功能的实现还是存在一点粗糙的,列举一下:对蛇吃到食物后新增加的身体的处理,现在只是把吃之前的蛇最后一段身体标记,吃到之后直接把标记的作为最后一段身体(因为吃的时候,蛇尾已经移动一步)。有更好方法的朋友请赐教。说一下几个注意点:

1. pygame.draw.rect()参数很多,容易搞混,建议使用()区分各个参数;

2. 将每段蛇的x,y做为列表,然后将整条蛇做为一个二维列表,这样可以大大降低复杂度;

3. 使用time模块中的time.sleep()可以减少main中while循环次数,来降低电脑运行压力(老电脑福音);

4. 在传递每一段蛇身时,使用deepcopy,不然每段蛇身的内存地址都一样,改变其中一段,每段都会被改变; 

5.判断蛇头和食物碰撞、蛇头和蛇身碰撞时注意坐标的计算,很容易算错;

先就想到这么多了,以后再补充吧。。。

写在后面:

虽然是一个代码不过200多行的小游戏,但是过程中遇到了很多问题。的确,自己写代码比光看书要困难,在control.move中的深浅复制楞是卡了我一下午,最后在其他人的博客中才发现了问题,这也让我明白了分享的重要性。第一次写博客,写得很简单,以后会经常来记录和分享,就当给自己一个小目标吧。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值