| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- from abc import ABCMeta, abstractmethod
- from enum import Enum, unique
- from random import randrange
- from threading import Thread
- import pygame
- class Color(object):
- """颜色"""
- GRAY = (242, 242, 242)
- BLACK = (0, 0, 0)
- GREEN = (0, 255, 0)
- PINK = (255, 20, 147)
- @unique
- class Direction(Enum):
- """方向"""
- UP = 0
- RIGHT = 1
- DOWN = 2
- LEFT = 3
- class GameObject(object, metaclass=ABCMeta):
- """游戏中的对象"""
- def __init__(self, x=0, y=0, color=Color.BLACK):
- """
- 初始化方法
- :param x: 横坐标
- :param y: 纵坐标
- :param color: 颜色
- """
- self._x = x
- self._y = y
- self._color = color
- @property
- def x(self):
- return self._x
- @property
- def y(self):
- return self._y
- @abstractmethod
- def draw(self, screen):
- """
- 绘制
- :param screen: 屏幕
- """
- pass
- class Wall(GameObject):
- """围墙"""
- def __init__(self, x, y, width, height, color=Color.BLACK):
- """
- 初始化方法
- :param x: 横坐标
- :param y: 纵坐标
- :param width: 宽度
- :param height: 高度
- :param color: 颜色
- """
- super().__init__(x, y, color)
- self._width = width
- self._height = height
- @property
- def width(self):
- return self._width
- @property
- def height(self):
- return self._height
- def draw(self, screen):
- pygame.draw.rect(screen, self._color,
- (self._x, self._y, self._width, self._height), 4)
- class Food(GameObject):
- """食物"""
- def __init__(self, x, y, size, color=Color.PINK):
- """
- 初始化方法
- :param x: 横坐标
- :param y: 纵坐标
- :param size: 大小
- :param color: 颜色
- """
- super().__init__(x, y, color)
- self._size = size
- self._hidden = False
- def draw(self, screen):
- if not self._hidden:
- pygame.draw.circle(screen, self._color,
- (self._x + self._size // 2, self._y + self._size // 2),
- self._size // 2, 0)
- self._hidden = not self._hidden
- class SnakeNode(GameObject):
- """蛇身上的节点"""
- def __init__(self, x, y, size, color=Color.GREEN):
- """
- 初始化方法
- :param x: 横坐标
- :param y: 纵坐标
- :param size: 大小
- :param color: 颜色
- """
- super().__init__(x, y, color)
- self._size = size
- @property
- def size(self):
- return self._size
- def draw(self, screen):
- pygame.draw.rect(screen, self._color,
- (self._x, self._y, self._size, self._size), 0)
- pygame.draw.rect(screen, Color.BLACK,
- (self._x, self._y, self._size, self._size), 1)
- class Snake(GameObject):
- """蛇"""
- def __init__(self, x, y, size=20, length=5):
- """
- 初始化方法
- :param x: 横坐标
- :param y: 纵坐标
- :param size: 大小
- :param length: 初始长度
- """
- super().__init__()
- self._dir = Direction.LEFT
- self._nodes = []
- self._alive = True
- self._new_dir = None
- for index in range(length):
- node = SnakeNode(x + index * size, y, size)
- self._nodes.append(node)
- @property
- def dir(self):
- return self._dir
- @property
- def alive(self):
- return self._alive
- @property
- def head(self):
- return self._nodes[0]
- def change_dir(self, new_dir):
- """
- 改变方向
- :param new_dir: 新方向
- """
- if new_dir != self._dir and \
- (self._dir.value + new_dir.value) % 2 != 0:
- self._new_dir = new_dir
- def move(self):
- """移动"""
- if self._new_dir:
- self._dir, self._new_dir = self._new_dir, None
- snake_dir = self._dir
- x, y, size = self.head.x, self.head.y, self.head.size
- if snake_dir == Direction.UP:
- y -= size
- elif snake_dir == Direction.RIGHT:
- x += size
- elif snake_dir == Direction.DOWN:
- y += size
- else:
- x -= size
- new_head = SnakeNode(x, y, size)
- self._nodes.insert(0, new_head)
- self._nodes.pop()
- def collide(self, wall):
- """
- 撞墙
- :param wall: 围墙
- """
- head = self.head
- if head.x < wall.x or head.x + head.size > wall.x + wall.width \
- or head.y < wall.y or head.y + head.size > wall.y + wall.height:
- self._alive = False
- def eat_food(self, food):
- """
- 吃食物
- :param food: 食物
- :return: 吃到食物返回True否则返回False
- """
- if self.head.x == food.x and self.head.y == food.y:
- tail = self._nodes[-1]
- self._nodes.append(tail)
- return True
- return False
- def eat_self(self):
- """咬自己"""
- for index in range(4, len(self._nodes)):
- node = self._nodes[index]
- if node.x == self.head.x and node.y == self.head.y:
- self._alive = False
- def draw(self, screen):
- for node in self._nodes:
- node.draw(screen)
- def main():
- def refresh():
- """刷新游戏窗口"""
- screen.fill(Color.GRAY)
- wall.draw(screen)
- food.draw(screen)
- snake.draw(screen)
- pygame.display.flip()
- def handle_key_event(key_event):
- """处理按键事件"""
- key = key_event.key
- if key == pygame.K_F2:
- reset_game()
- elif key in (pygame.K_a, pygame.K_w, pygame.K_d, pygame.K_s):
- if snake.alive:
- if key == pygame.K_w:
- new_dir = Direction.UP
- elif key == pygame.K_d:
- new_dir = Direction.RIGHT
- elif key == pygame.K_s:
- new_dir = Direction.DOWN
- else:
- new_dir = Direction.LEFT
- snake.change_dir(new_dir)
- def create_food():
- """创建食物"""
- unit_size = snake.head.size
- max_row = wall.height // unit_size
- max_col = wall.width // unit_size
- row = randrange(0, max_row)
- col = randrange(0, max_col)
- return Food(wall.x + unit_size * col, wall.y + unit_size * row, unit_size)
- def reset_game():
- """重置游戏"""
- nonlocal food, snake
- food = create_food()
- snake = Snake(250, 290)
- def background_task():
- nonlocal running, food
- while running:
- if snake.alive:
- refresh()
- clock.tick(10)
- if snake.alive:
- snake.move()
- snake.collide(wall)
- if snake.eat_food(food):
- food = create_food()
- snake.eat_self()
- """
- class BackgroundTask(Thread):
- def run(self):
- nonlocal running, food
- while running:
- if snake.alive:
- refresh()
- clock.tick(10)
- if snake.alive:
- snake.move()
- snake.collide(wall)
- if snake.eat_food(food):
- food = create_food()
- snake.eat_self()
- """
- wall = Wall(10, 10, 600, 600)
- snake = Snake(250, 290)
- food = create_food()
- pygame.init()
- screen = pygame.display.set_mode((620, 620))
- pygame.display.set_caption('贪吃蛇')
- # 创建控制游戏每秒帧数的时钟
- clock = pygame.time.Clock()
- running = True
- # 启动后台线程负责刷新窗口和让蛇移动
- # BackgroundTask().start()
- Thread(target=background_task).start()
- # 处理事件的消息循环
- while running:
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- running = False
- elif event.type == pygame.KEYDOWN:
- handle_key_event(event)
- pygame.quit()
- if __name__ == '__main__':
- main()
|