写在前面:
学习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中的深浅复制楞是卡了我一下午,最后在其他人的博客中才发现了问题,这也让我明白了分享的重要性。第一次写博客,写得很简单,以后会经常来记录和分享,就当给自己一个小目标吧。