贪吃蛇游戏想必大家都不陌生,这一次就来介绍开发一个简单的贪吃蛇游戏。
Snake游戏
创建精灵
首先需要在MySprite的基础上创建新的精灵类以满足我们的需求
蛇
代码如下:
class SnakeSegment(MySprite):
def __init__(self, color=(20, 200, 20)):
MySprite.__init__(self)
image = pygame.Surface((32, 32)).convert_alpha()
image.fill((255, 255, 255, 0))
pygame.draw.circle(image, color, (16, 16), 16, 0)
self.set_image(image)
MySprite.update(self, 0)
class Snake():
def __init__(self):
self.velocity = Point(-1, 0)
self.old_time = 0
head = SnakeSegment((50, 250, 50))
head.X = 12 * 32
head.Y = 9 * 32
self.segments = list()
self.segments.append(head)
self.add_segment()
self.add_segment()
def update(self, ticks):
global step_time
if ticks > self.old_time + step_time:
self.old_time = ticks
for n in range(len(self.segments) - 1, 0, -1):
self.segments[n].X = self.segments[n - 1].X
self.segments[n].Y = self.segments[n - 1].Y
self.segments[0].X += self.velocity.x * 32
self.segments[0].Y += self.velocity.y * 32
def draw(self, surface):
for segment in self.segments:
surface.blit(segment.image, (segment.X, segment.Y))
def add_segment(self):
last = len(self.segments) - 1
segment = SnakeSegment()
start = Point(0, 0)
if self.velocity.x < 0:
start.x = 32
elif self.velocity.x > 0:
start.x = -32
if self.velocity.y < 0:
start.y = 32
elif self.velocity.y > 0:
start.y = -32
segment.X = self.segments[last].X + start.x
segment.Y = self.segments[last].Y + start.y
self.segments.append(segment)
我们构建了两个精灵类用于组成蛇,其中SnakeSegment类其实就是一个圆,用于表示蛇的长度以及表示蛇的身体的一部分,然后Snake类则是包含了多个SnakeSegment类构成一条蛇,其中的update()方法:
def update(self, ticks):
global step_time
if ticks > self.old_time + step_time:
self.old_time = ticks
for n in range(len(self.segments) - 1, 0, -1):
self.segments[n].X = self.segments[n - 1].X
self.segments[n].Y = self.segments[n - 1].Y
self.segments[0].X += self.velocity.x * 32
self.segments[0].Y += self.velocity.y * 32
由于是由多个SnakeSegment类组成的,因此我们这里蛇的移动的处理是依次移动SnakeSegment类的每一个对象。
食物
代码如下:
class Food(MySprite):
def __init__(self):
MySprite.__init__(self)
image = pygame.Surface((32, 32)).convert_alpha()
image.fill((255, 255, 255, 0))
pygame.draw.circle(image, (250, 250, 50), (16, 16), 16, 0)
self.set_image(image)
MySprite.update(self, 0)
self.X = random.randint(0, 23) * 32
self.Y = random.randint(0, 17) * 32
这其实也是一个圆,随机出现在图中某个位置上
游戏的一些初始化操作
代码如下:
def game_init():
global screen, backbuffer, font, timer, snake, food_group
pygame.init()
screen = pygame.display.set_mode((24 * 32, 18 * 32))
pygame.display.set_caption("Snake Game")
font = pygame.font.Font(None, 30)
timer = pygame.time.Clock()
backbuffer = pygame.Surface((screen.get_rect().width, screen.get_rect().height))
snake = Snake()
image = pygame.Surface((60, 60)).convert_alpha()
image.fill((255, 255, 255, 0))
pygame.draw.circle(image, (80, 80, 220, 70), (30, 30), 30, 0)
pygame.draw.circle(image, (80, 80, 250, 255), (30, 30), 30, 4)
food_group = pygame.sprite.Group()
food = Food()
food_group.add(food)
注意这里:
screen = pygame.display.set_mode((24 * 32, 18 * 32))
因为无论是蛇类,还是食物类,都是32*32的大小,因此将平面的长宽设置成32的倍数则刚刚好
实现蛇吃食物变长
hit_list = pygame.sprite.groupcollide(snake.segments, food_group, False, True)
if len(hit_list) > 0:
food_group.add(Food())
snake.add_segment()
这里使用到了pygame.sprite.groupcollide()冲突检测,因为考虑到食物随机出现,可能刚好出现在蛇身体的某一部分上(这也是一个小的问题,毕竟食物出现的位置理论上应该要避开蛇的身体,但这里没有考虑这个情况),因此是直接将蛇的身体与食物进行了冲突检测,若发生碰撞,则删除食物,同时蛇的长度增加。
检测蛇是否吃到自己
for n in range(1, len(snake.segments)):
if pygame.sprite.collide_rect(snake.segments[0], snake.segments[n]):
game_over = True
因为蛇的移动是第一个部分控制的,因此判断是否会知道自己,只需要用第一部分与剩余部分进行冲突检测即可。
实现蛇自动移动
其实到这里,就已经实现了Snake游戏的逻辑,直接运行即可。当然,这里还可以再添加一些代码以实现蛇的自动移动,这样就不需要我们手动控制方向了。
要实现该功能,首先要有几个辅助函数:
def get_current_direction():
global head_x, head_y
first_segment_x = snake.segments[1].X // 32
first_segment_y = snake.segments[1].Y // 32
if head_x - 1 == first_segment_x:
return "right"
elif head_x + 1 == first_segment_x:
return "left"
elif head_y - 1 == first_segment_y:
return "down"
elif head_y + 1 == first_segment_y:
return "up"
def get_food_direction():
global head_x, head_y
food = Point(0, 0)
for obj in food_group:
food = Point(obj.X // 32, obj.Y // 32)
if head_x < food.x:
return "right"
elif head_x > food.x:
return "left"
elif head_x == food.x:
if head_y < food.y:
return "down"
elif head_y > food.y:
return "up"
这两个函数的功能就是用来确定当前蛇的移动方向和食物的相对位置(相对snake.segments[0]而言)。之后就可以实现自动移动了:
def auto_move():
direction = get_current_direction()
food_dir = get_food_direction()
if food_dir == "left":
if direction != "right":
direction = "left"
elif food_dir == "right":
if direction != "left":
direction = "right"
elif food_dir == "up":
if direction != "down":
direction = "up"
elif food_dir == "down":
if direction != "up":
direction = "down"
if direction == "up":
snake.velocity = Point(0, -1)
elif direction == "down":
snake.velocity = Point(0, 1)
elif direction == "right":
snake.velocity = Point(1, 0)
elif direction == "left":
snake.velocity = Point(-1, 0)
实现自动移动需要用于按下空格键,只需要在检测键盘输入的时候,对空格键做相应的处理即可。
完整源代码
源代码如下:
import random
import sys
import pygame
from pygame.locals import *
from MyLibrary import *
class SnakeSegment(MySprite):
def __init__(self, color=(20, 200, 20)):
MySprite.__init__(self)
image = pygame.Surface((32, 32)).convert_alpha()
image.fill((255, 255, 255, 0))
pygame.draw.circle(image, color, (16, 16), 16, 0)
self.set_image(image)
MySprite.update(self, 0)
class Snake():
def __init__(self):
self.velocity = Point(-1, 0)
self.old_time = 0
head = SnakeSegment((50, 250, 50))
head.X = 12 * 32
head.Y = 9 * 32
self.segments = list()
self.segments.append(head)
self.add_segment()
self.add_segment()
def update(self, ticks):
global step_time
if ticks > self.old_time + step_time:
self.old_time = ticks
for n in range(len(self.segments) - 1, 0, -1):
self.segments[n].X = self.segments[n - 1].X
self.segments[n].Y = self.segments[n - 1].Y
self.segments[0].X += self.velocity.x * 32
self.segments[0].Y += self.velocity.y * 32
def draw(self, surface):
for segment in self.segments:
surface.blit(segment.image, (segment.X, segment.Y))
def add_segment(self):
last = len(self.segments) - 1
segment = SnakeSegment()
start = Point(0, 0)
if self.velocity.x < 0:
start.x = 32
elif self.velocity.x > 0:
start.x = -32
if self.velocity.y < 0:
start.y = 32
elif self.velocity.y > 0:
start.y = -32
segment.X = self.segments[last].X + start.x
segment.Y = self.segments[last].Y + start.y
self.segments.append(segment)
class Food(MySprite):
def __init__(self):
MySprite.__init__(self)
image = pygame.Surface((32, 32)).convert_alpha()
image.fill((255, 255, 255, 0))
pygame.draw.circle(image, (250, 250, 50), (16, 16), 16, 0)
self.set_image(image)
MySprite.update(self, 0)
self.X = random.randint(0, 23) * 32
self.Y = random.randint(0, 17) * 32
def game_init():
global screen, backbuffer, font, timer, snake, food_group
pygame.init()
screen = pygame.display.set_mode((24 * 32, 18 * 32))
pygame.display.set_caption("Snake Game")
font = pygame.font.Font(None, 30)
timer = pygame.time.Clock()
backbuffer = pygame.Surface((screen.get_rect().width, screen.get_rect().height))
snake = Snake()
image = pygame.Surface((60, 60)).convert_alpha()
image.fill((255, 255, 255, 0))
pygame.draw.circle(image, (80, 80, 220, 70), (30, 30), 30, 0)
pygame.draw.circle(image, (80, 80, 250, 255), (30, 30), 30, 4)
food_group = pygame.sprite.Group()
food = Food()
food_group.add(food)
def auto_move():
direction = get_current_direction()
food_dir = get_food_direction()
if food_dir == "left":
if direction != "right":
direction = "left"
elif food_dir == "right":
if direction != "left":
direction = "right"
elif food_dir == "up":
if direction != "down":
direction = "up"
elif food_dir == "down":
if direction != "up":
direction = "down"
if direction == "up":
snake.velocity = Point(0, -1)
elif direction == "down":
snake.velocity = Point(0, 1)
elif direction == "right":
snake.velocity = Point(1, 0)
elif direction == "left":
snake.velocity = Point(-1, 0)
def get_current_direction():
global head_x, head_y
first_segment_x = snake.segments[1].X // 32
first_segment_y = snake.segments[1].Y // 32
if head_x - 1 == first_segment_x:
return "right"
elif head_x + 1 == first_segment_x:
return "left"
elif head_y - 1 == first_segment_y:
return "down"
elif head_y + 1 == first_segment_y:
return "up"
def get_food_direction():
global head_x, head_y
food = Point(0, 0)
for obj in food_group:
food = Point(obj.X // 32, obj.Y // 32)
if head_x < food.x:
return "right"
elif head_x > food.x:
return "left"
elif head_x == food.x:
if head_y < food.y:
return "down"
elif head_y > food.y:
return "up"
if __name__ == "__main__":
game_init()
game_over = False
last_time = 0
auto_play = False
step_time = 400
while True:
timer.tick(50)
ticks = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
keys = pygame.key.get_pressed()
if keys[K_ESCAPE]:
sys.exit()
elif keys[K_UP] or keys[K_w]:
snake.velocity = Point(0, -1)
elif keys[K_DOWN] or keys[K_s]:
snake.velocity = Point(0, 1)
elif keys[K_LEFT] or keys[K_a]:
snake.velocity = Point(-1, 0)
elif keys[K_RIGHT] or keys[K_d]:
snake.velocity = Point(1, 0)
elif keys[K_SPACE]:
auto_play = True
if not game_over:
snake.update(ticks)
food_group.update(ticks)
hit_list = pygame.sprite.groupcollide(snake.segments, food_group, False, True)
if len(hit_list) > 0:
food_group.add(Food())
snake.add_segment()
for n in range(1, len(snake.segments)):
if pygame.sprite.collide_rect(snake.segments[0], snake.segments[n]):
game_over = True
head_x = snake.segments[0].X // 32
head_y = snake.segments[0].Y // 32
if head_x < 0 or head_x > 24 or head_y < 0 or head_y > 17:
game_over = True
backbuffer.fill((20, 50, 20))
snake.draw(backbuffer)
food_group.draw(backbuffer)
screen.blit(backbuffer, (0, 0))
if not game_over:
if auto_play:
auto_move()
print_text(font, 0, 0, "Length:" + str(len(snake.segments)))
print_text(font, 0, 20, "Position:" + str(snake.segments[0].X // 32) +
", " + str(snake.segments[0].Y // 32))
else:
print_text(font, 0, 0, "GAME OVER")
pygame.display.update()
运行结果如下:
|