1202 2019-07-29 2020-06-25

前言:富教于乐,自己用python写一个贪吃蛇。

一、源代码

"""
贪吃蛇小游戏,只依赖于pygame库
按照自己的逻辑完全重写,简化了部分操作,后期有待完善
源地址 https://github.com/guliang21/pygame
Created by HK on 2019-07-26
"""

import random
import sys
import time
import pygame
from collections import deque
from pygame.locals import *

Color = pygame.color.Color

# 方格大小
SIZE = 20
# 网格线宽度
LINE_WIDTH = 1
LINE_COLOR = Color("white")
# 格子倍数
W_MULTIPLE = 30
H_MULTIPLE = W_MULTIPLE // 6 * 5

# 屏幕宽度
SCREEN_WIDTH = SIZE * W_MULTIPLE + LINE_WIDTH * (W_MULTIPLE + 1)
# 屏幕高度
SCREEN_HEIGHT = SIZE * H_MULTIPLE + LINE_WIDTH * (H_MULTIPLE + 1)
# 背景色
BG_COLOR = Color("grey")

SNAKE_COLOR = Color("green")
# 蛇运动的速度,值越低速度越快
ORIGIN_SPEED = 0.2


# 初始游戏网格、背景
def paint_game_bg(screen):
    # 填充背景色
    screen.fill(BG_COLOR)
    i = 0
    # H_MULTIPLE + 1条横线
    while i <= H_MULTIPLE:
        pygame.draw.line(screen, LINE_COLOR, (0, i * SIZE + i * LINE_WIDTH), (SCREEN_WIDTH, i * SIZE + i * LINE_WIDTH),
                         LINE_WIDTH)
        i += 1
    # W_MULTIPLE + 1条横线
    i = 0
    while i <= W_MULTIPLE:
        pygame.draw.line(screen, LINE_COLOR, (i * SIZE + i * LINE_WIDTH, 0), (i * SIZE + i * LINE_WIDTH, SCREEN_HEIGHT),
                         LINE_WIDTH)
        i += 1


class FoodStyle:
    score = 0
    Color = None

    def __init__(self, score, color1):
        self.score = score
        self.Color = color1

    def __str__(self):
        return "(score=%d, Color=%s)" % (self.score, self.Color)


# 食物的分值及颜色
FOOT_STYLES = [FoodStyle(10, Color("red")), FoodStyle(20, Color("orange")), FoodStyle(30, Color("yellow"))]


class Food:
    x, y, style = 0, 0, 0

    def __init__(self, x, y, style):
        self.x, self.y, self.style = x, y, style

    def print_self(self, screen):
        x = self.x * SIZE + self.x * LINE_WIDTH + LINE_WIDTH
        y = SCREEN_HEIGHT - (self.y * SIZE + SIZE) - self.y * LINE_WIDTH - LINE_WIDTH
        pygame.draw.rect(screen, self.style.Color, (x, y, SIZE, SIZE), 0)

    @staticmethod
    def create_random_food(snake):
        # 新出的随机点必须在屏幕内
        food_x = random.randint(0, W_MULTIPLE - 1)
        food_y = random.randint(0, H_MULTIPLE - 1)
        # 如果食物出现在蛇身上,则重来
        while Point(food_x, food_y) in snake.snake:
            food_x = random.randint(0, W_MULTIPLE - 1)
            food_y = random.randint(0, H_MULTIPLE - 1)
        food_style = FOOT_STYLES[random.randint(0, FOOT_STYLES.__len__() - 1)]
        print('食物坐标为(%d, %d), 样式为%s' % (food_x, food_y, food_style))
        return Food(food_x, food_y, food_style)


class Point:
    x, y = 0, 0

    def __init__(self, x, y):
        self.x, self.y = x, y

    def __str__(self):
        return "(x=%d, y=%d)" % (self.x, self.y)

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y


class Direction:
    UP, DOWN, LEFT, RIGHT = Point(0, 1), Point(0, -1), Point(-1, 0), Point(1, 0)


class Snake:
    snake = None

    # 初始化蛇,依赖一个双端队列,每次蛇进食都是在蛇头部,即队列右边append一个point
    # (0,0) (1,0) (2.0) 坐标轴为自然坐标轴
    def __init__(self):
        self.snake = deque()
        self.snake.append(Point(0, 0))
        self.snake.append(Point(1, 0))
        self.snake.append(Point(2, 0))

    def __str__(self):
        info = '遍历蛇节点,从尾到头依次输出如下\n'
        it = iter(self.snake)
        for x in it:
            info += x.__str__() + ' '
        return info

    def get_head(self):
        return self.snake[self.snake.__len__() - 1]

    def move(self, pos):
        head = self.get_head()
        new_head = Point(head.x + pos.x, head.y + pos.y)
        if (0 <= new_head.x <= W_MULTIPLE - 1 and 0 <= new_head.y <= H_MULTIPLE - 1
                and new_head not in self.snake):
            self.snake.append(new_head)
            self.snake.popleft()
            return True
        return False

    def eat_food(self, pos, food):
        head = self.get_head()
        new_head = Point(head.x + pos.x, head.y + pos.y)
        if food == new_head:
            self.snake.append(new_head)
            print('吃到食物啦:%s' % new_head)
            return True
        return False

    # 画蛇,注意这里坐标选定及边框宽度的问题
    def print_self(self, screen):
        paint_game_bg(screen)
        for point in self.snake:
            x = point.x * SIZE + point.x * LINE_WIDTH + LINE_WIDTH
            y = SCREEN_HEIGHT - (point.y * SIZE + SIZE) - point.y * LINE_WIDTH - LINE_WIDTH
            w, h = SIZE, SIZE
            # rect参数为((x, y, width, height))
            pygame.draw.rect(screen, SNAKE_COLOR, (x, y, w, h), 0)


def paint(screen, snake, food):
    paint_game_bg(screen)
    snake.print_self(screen)
    food.print_self(screen)


def main():
    # 初始化pygame
    pygame.init()
    # 初始化屏幕画布
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    # 初始化标题
    pygame.display.set_caption('贪吃蛇')
    # 初始化游戏背景
    paint_game_bg(screen)
    # 初始化蛇
    snake = Snake()
    snake.print_self(screen)
    # 初始化食物
    food = Food.create_random_food(snake)
    food.print_self(screen)
    # 初始化方向
    pos = Direction.RIGHT
    # 输入锁, 当值为true是才接收输入,有待后续完善
    # lock = False

    # 系统参数
    game_over = False
    pause = False
    last_move_time = time.time()

    while True:
        for event in pygame.event.get():
            # 当点击关闭窗口时触发
            if event.type == QUIT:
                sys.exit()
            # 键盘事件
            elif event.type == KEYDOWN:
                # 回车键
                if event.key == K_RETURN:
                    # 如果游戏结束,则初始化数据,从新开始
                    if game_over:
                        print('再来一盘')
                        game_over = False
                        snake = Snake()
                        food = Food.create_random_food(snake)
                        pos = Direction.RIGHT
                        last_move_time = time.time()
                # 空格键暂停游戏
                elif event.key == K_SPACE:
                    if not game_over:
                        print('暂停/恢复游戏')
                        pause = not pause
                # 断绝后面的输键值入
                if pause:
                    continue
                # w键或W键或上方向键
                if event.key in (K_w, K_UP):
                    # 如果不是y轴向下,则方向为y轴向上,下同
                    if pos != Direction.DOWN:
                        pos = Direction.UP
                elif event.key in (K_s, K_DOWN):
                    if pos != Direction.UP:
                        pos = Direction.DOWN
                elif event.key in (K_a, K_LEFT):
                    if pos != Direction.RIGHT:
                        pos = Direction.LEFT
                elif event.key in (K_d, K_RIGHT):
                    if pos != Direction.LEFT:
                        pos = Direction.RIGHT
                # 使按键效果明显
                if snake.eat_food(pos, food):
                    food = Food.create_random_food(snake)
                elif snake.move(pos):
                    pass
                elif not game_over:
                    game_over = True
                    print('Game Over')
        # 就不使用线程了,依靠时间差来判定移动,这一步可能会存在误判
        now_time = time.time()
        if not game_over and not pause:
            if now_time - last_move_time > ORIGIN_SPEED:
                if snake.eat_food(pos, food):
                    food = Food.create_random_food(snake)
                elif snake.move(pos):
                    pass
                elif not game_over:
                    game_over = True
                    print('Game Over')
                last_move_time = now_time
        paint(screen, snake, food)
        pygame.display.update()


if __name__ == '__main__':
    main()

总访问次数: 379次, 一般般帅 创建于 2019-07-29, 最后更新于 2020-06-25

进大厂! 欢迎关注微信公众号,第一时间掌握最新动态!