作者 | 李秋鍵
責編 | Carol
封圖 | CSDN 下載自視覺中國
近幾年來Python語言得到了快速發展,而Pygame作為Python開發應用和遊戲必備的庫更是展現了Python的優越性。
而今天我們就將藉助Pygame建立吃豆人遊戲。
吃豆人是電子遊戲歷史上的經典街機遊戲,由Namco公司的岩谷徹設計並由Midway Games在1980年發行。Pac-Man被認為是80年代最經典的街機遊戲之一,遊戲的主角小精靈的形象甚至被作為一種大眾文化符號,或是此產業的代表形象。
而Pygame模塊是跨平台Python模塊,專為電子遊戲設計,包含圖像、聲音。建立在SDL基礎上,允許實時電子遊戲研發而無需被低級語言(如機器語言和彙編語言)束縛。
最終遊戲效果如下可見:
一、實驗前的準備
首先我們使用的python版本是3.6.5所用到的模塊是pygame模塊,用來創建遊戲框架。Random模塊用來隨機生成方向。
素材準備
首先我們將圖片放到images目錄下,背景音樂放到sounds目錄下。
如下圖可見:
遊戲搭建
1、定義一些精靈:
整體的類變量定義包括牆類,通過pygame的圖片填充作為牆類的加載;同理還包括食物類和角色。而怪物的隨機運動使用random產生隨機運動方向。
其對應的代碼如下:
import random
import pygame
'''牆類'''
classWall( pygame. sprite. Sprite):
def__init__( self, x, y, width, height, color, **kwargs) :
pygame.sprite.Sprite.__init_ _( self)
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect
self.rect.left = x
self.rect.top = y
'''食物類'''
classFood( pygame. sprite. Sprite):
def__init__( self, x, y, width, height, color, bg_color, **kwargs) :
pygame.sprite.Sprite.__init_ _( self)
self.image = pygame.Surface([width, height])
self.image.fill(bg_color)
self.image.set_colorkey(bg_color)
pygame.draw.ellipse( self.image, color, [ 0, 0, width, height])
self.rect = self.image.get_rect
self.rect.left = x
self.rect.top = y
'''角色類'''
classPlayer( pygame. sprite. Sprite):
def__init__( self, x, y, role_image_path) :
pygame.sprite.Sprite.__init_ _( self)
self.role_name = role_image_path.split( '/')[- 1].split( '.')[ 0]
self.base_image = pygame.image.load(role_image_path).convert
self.image = self.base_image.copy
self.rect = self.image.get_rect
self.rect.left = x
self.rect.top = y
self.prev_x = x
self.prev_y = y
self.base_speed = [ 30, 30]
self.speed = [ 0, 0]
self.is_move = False
self.tracks = []
self.tracks_loc = [ 0, 0]
'''改變速度方向'''
defchangeSpeed( self, direction) :
ifdirection[ 0] < 0:
self.image = pygame.transform.flip( self.base_image, True, False)
elif direction[ 0] > 0:
self.image = self.base_image.copy
elif direction[ 1] < 0:
self.image = pygame.transform.rotate( self.base_image, 90)
elif direction[ 1] > 0:
self.image = pygame.transform.rotate( self.base_image, - 90)
self.speed = [direction[ 0] * self.base_speed[ 0], direction[ 1] * self.base_speed[ 1]]
returnself.speed
'''更新角色位置'''
defupdate( self, wall_sprites, gate_sprites) :
ifnotself. is_move:
returnFalse
x_prev = self.rect.left
y_prev = self.rect.top
self.rect.left += self.speed[ 0]
self.rect.top += self.speed[ 1]
is_collide = pygame.sprite.spritecollide( self, wall_sprites, False)
ifgate_sprites is notNone:
ifnotis_collide:
is_collide = pygame.sprite.spritecollide( self, gate_sprites, False)
ifis_collide:
self.rect.left = x_prev
self.rect.top = y_prev
returnFalse
returnTrue
'''生成隨機的方向'''
defrandomDirection( self) :
returnrandom.choice([[- 0. 5, 0], [ 0. 5, 0], [ 0, 0. 5], [ 0, - 0. 5]])
2、遊戲關卡定義:
在這裡設置好了關卡一。關卡的定義必須包括牆的位置,不同關卡牆的位置和怪物的位置不同。更多關卡可以參照設置
對應代碼如下:
importpygame
fromSprites import*
NUMLEVELS = 1
'''關卡一'''
classLevel1:
def__init__(self):
self.info = 'level1'
'''創建牆'''
defsetupWalls(self, wall_color):
self.wall_sprites = pygame.sprite.Group
wall_positions = [[ 0, 0, 6, 600],
[ 0, 0, 600, 6],
[ 0, 600, 606, 6],
[ 600, 0, 6, 606],
[ 300, 0, 6, 66],
[ 60, 60, 186, 6],
[ 360, 60, 186, 6],
[ 60, 120, 66, 6],
[ 60, 120, 6, 126],
[ 180, 120, 246, 6],
[ 300, 120, 6, 66],
[ 480, 120, 66, 6],
[ 540, 120, 6, 126],
[ 120, 180, 126, 6],
[ 120, 180, 6, 126],
[ 360, 180, 126, 6],
[ 480, 180, 6, 126],
[ 180, 240, 6, 126],
[ 180, 360, 246, 6],
[ 420, 240, 6, 126],
[ 240, 240, 42, 6],
[ 324, 240, 42, 6],
[ 240, 240, 6, 66],
[ 240, 300, 126, 6],
[ 360, 240, 6, 66],
[ 0, 300, 66, 6],
[ 540, 300, 66, 6],
[ 60, 360, 66, 6],
[ 60, 360, 6, 186],
[ 480, 360, 66, 6],
[ 540, 360, 6, 186],
[ 120, 420, 366, 6],
[ 120, 420, 6, 66],
[ 480, 420, 6, 66],
[ 180, 480, 246, 6],
[ 300, 480, 6, 66],
[ 120, 540, 126, 6],
[ 360, 540, 126, 6]]
forwall_position inwall_positions:
wall = Wall(*wall_position, wall_color)
self.wall_sprites.add(wall)
returnself.wall_sprites
'''創建門'''
defsetupGate(self, gate_color):
self.gate_sprites = pygame.sprite.Group
self.gate_sprites.add(Wall( 282, 242, 42, 2, gate_color))
returnself.gate_sprites
'''創建角色'''
defsetupPlayers(self, hero_image_path, ghost_images_path):
self.hero_sprites = pygame.sprite.Group
self.ghost_sprites = pygame.sprite.Group
self.hero_sprites.add(Player( 287, 439, hero_image_path))
foreach inghost_images_path:
role_name = each.split( '/')[ -1].split( '.')[ 0]
ifrole_name == 'Blinky':
player = Player( 287, 199, each)
player.is_move = True
player.tracks = [[ 0, -0.5, 4], [ 0.5, 0, 9], [ 0, 0.5, 11], [ 0.5, 0, 3], [ 0, 0.5, 7], [ -0.5, 0, 11], [ 0, 0.5, 3],
[ 0.5, 0, 15], [ 0, -0.5, 15], [ 0.5, 0, 3], [ 0, -0.5, 11], [ -0.5, 0, 3], [ 0, -0.5, 11], [ -0.5, 0, 3],
[ 0, -0.5, 3], [ -0.5, 0, 7], [ 0, -0.5, 3], [ 0.5, 0, 15], [ 0, 0.5, 15], [ -0.5, 0, 3], [ 0, 0.5, 3],
[ -0.5, 0, 3], [ 0, -0.5, 7], [ -0.5, 0, 3], [ 0, 0.5, 7], [ -0.5, 0, 11], [ 0, -0.5, 7], [ 0.5, 0, 5]]
self.ghost_sprites.add(player)
elifrole_name == 'Clyde':
player = Player( 319, 259, each)
player.is_move = True
player.tracks = [[ -1, 0, 2], [ 0, -0.5, 4], [ 0.5, 0, 5], [ 0, 0.5, 7], [ -0.5, 0, 11], [ 0, -0.5, 7],
[ -0.5, 0, 3], [ 0, 0.5, 7], [ -0.5, 0, 7], [ 0, 0.5, 15], [ 0.5, 0, 15], [ 0, -0.5, 3],
[ -0.5, 0, 11], [ 0, -0.5, 7], [ 0.5, 0, 3], [ 0, -0.5, 11], [ 0.5, 0, 9]]
self.ghost_sprites.add(player)
elifrole_name == 'Inky':
player = Player( 255, 259, each)
player.is_move = True
player.tracks = [[ 1, 0, 2], [ 0, -0.5, 4], [ 0.5, 0, 10], [ 0, 0.5, 7], [ 0.5, 0, 3], [ 0, -0.5, 3],
[ 0.5, 0, 3], [ 0, -0.5, 15], [ -0.5, 0, 15], [ 0, 0.5, 3], [ 0.5, 0, 15], [ 0, 0.5, 11],
[ -0.5, 0, 3], [ 0, -0.5, 7], [ -0.5, 0, 11], [ 0, 0.5, 3], [ -0.5, 0, 11], [ 0, 0.5, 7],
[ -0.5, 0, 3], [ 0, -0.5, 3], [ -0.5, 0, 3], [ 0, -0.5, 15], [ 0.5, 0, 15], [ 0, 0.5, 3],
[ -0.5, 0, 15], [ 0, 0.5, 11], [ 0.5, 0, 3], [ 0, -0.5, 11], [ 0.5, 0, 11], [ 0, 0.5, 3], [ 0.5, 0, 1]]
self.ghost_sprites.add(player)
elifrole_name == 'Pinky':
player = Player( 287, 259, each)
player.is_move = True
player.tracks = [[ 0, -1, 4], [ 0.5, 0, 9], [ 0, 0.5, 11], [ -0.5, 0, 23], [ 0, 0.5, 7], [ 0.5, 0, 3],
[ 0, -0.5, 3], [ 0.5, 0, 19], [ 0, 0.5, 3], [ 0.5, 0, 3], [ 0, 0.5, 3], [ 0.5, 0, 3],
[ 0, -0.5, 15], [ -0.5, 0, 7], [ 0, 0.5, 3], [ -0.5, 0, 19], [ 0, -0.5, 11], [ 0.5, 0, 9]]
self.ghost_sprites.add(player)
returnself.hero_sprites, self.ghost_sprites
'''創建食物'''
defsetupFood(self, food_color, bg_color):
self.food_sprites = pygame.sprite.Group
forrow inrange( 19):
forcol inrange( 19):
if(row == 7orrow == 8) and(col == 8orcol == 9orcol == 10):
continue
else:
food = Food( 30*col+ 32, 30*row+ 32, 4, 4, food_color, bg_color)
is_collide = pygame.sprite.spritecollide(food, self.wall_sprites, False)
ifis_collide:
continue
is_collide = pygame.sprite.spritecollide(food, self.hero_sprites, False)
ifis_collide:
continue
self.food_sprites.add(food)
returnself.food_sprites
3、遊戲創建:
在通過關卡定義牆等位置後以及精靈自身屬性怪物運動、食物定義等後,通過調用已經創建好的類達到搭建遊戲的目的。
具體如下可見:
importos
importsys
importpygame
importLevels
'''定義一些必要的參數'''
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = ( 0, 0, 255)
GREEN = ( 0, 255, 0)
RED = ( 255, 0, 0)
YELLOW = ( 255, 255, 0)
PURPLE = ( 255, 0, 255)
SKYBLUE = ( 0, 191, 255)
BGMPATH = os.path.join(os.getcwd, 'resources/sounds/bg.mp3')
ICONPATH = os.path.join(os.getcwd, 'resources/images/icon.png')
FONTPATH = os.path.join(os.getcwd, 'resources/font/ALGER.TTF')
HEROPATH = os.path.join(os.getcwd, 'resources/images/pacman.png')
BlinkyPATH = os.path.join(os.getcwd, 'resources/images/Blinky.png')
ClydePATH = os.path.join(os.getcwd, 'resources/images/Clyde.png')
InkyPATH = os.path.join(os.getcwd, 'resources/images/Inky.png')
PinkyPATH = os.path.join(os.getcwd, 'resources/images/Pinky.png')
'''開始某一關遊戲'''
defstartLevelGame(level, screen, font):
clock = pygame.time.Clock
SCORE = 0
wall_sprites = level.setupWalls(SKYBLUE)
gate_sprites = level.setupGate(WHITE)
hero_sprites, ghost_sprites = level.setupPlayers(HEROPATH, [BlinkyPATH, ClydePATH, InkyPATH, PinkyPATH])
food_sprites = level.setupFood(YELLOW, WHITE)
is_clearance = False
whileTrue:
forevent inpygame.event.get:
ifevent.type == pygame.QUIT:
sys.exit( -1)
pygame.quit
ifevent.type == pygame.KEYDOWN:
ifevent.key == pygame.K_LEFT:
forhero inhero_sprites:
hero.changeSpeed([ -1, 0])
hero.is_move = True
elifevent.key == pygame.K_RIGHT:
forhero inhero_sprites:
hero.changeSpeed([ 1, 0])
hero.is_move = True
elifevent.key == pygame.K_UP:
forhero inhero_sprites:
hero.changeSpeed([ 0, -1])
hero.is_move = True
elifevent.key == pygame.K_DOWN:
forhero inhero_sprites:
hero.changeSpeed([ 0, 1])
hero.is_move = True
ifevent.type == pygame.KEYUP:
if(event.key == pygame.K_LEFT) or(event.key == pygame.K_RIGHT) or(event.key == pygame.K_UP) or(event.key == pygame.K_DOWN):
hero.is_move = False
screen.fill(BLACK)
forhero inhero_sprites:
hero.update(wall_sprites, gate_sprites)
hero_sprites.draw(screen)
forhero inhero_sprites:
food_eaten = pygame.sprite.spritecollide(hero, food_sprites, True)
SCORE += len(food_eaten)
wall_sprites.draw(screen)
gate_sprites.draw(screen)
food_sprites.draw(screen)
forghost inghost_sprites:
# 幽靈隨機運動(效果不好且有BUG)
'''
res = ghost.update(wall_sprites, None)
while not res:
ghost.changeSpeed(ghost.randomDirection)
res = ghost.update(wall_sprites, None)
'''
# 指定幽靈運動路徑
ifghost.tracks_loc[ 1] < ghost.tracks[ghost.tracks_loc[ 0]][ 2]:
ghost.changeSpeed(ghost.tracks[ghost.tracks_loc[ 0]][ 0: 2])
ghost.tracks_loc[ 1] += 1
else:
ifghost.tracks_loc[ 0] < len(ghost.tracks) - 1:
ghost.tracks_loc[ 0] += 1
elifghost.role_name == 'Clyde':
ghost.tracks_loc[ 0] = 2
else:
ghost.tracks_loc[ 0] = 0
ghost.changeSpeed(ghost.tracks[ghost.tracks_loc[ 0]][ 0: 2])
ghost.tracks_loc[ 1] = 0
ifghost.tracks_loc[ 1] < ghost.tracks[ghost.tracks_loc[ 0]][ 2]:
ghost.changeSpeed(ghost.tracks[ghost.tracks_loc[ 0]][ 0: 2])
else:
ifghost.tracks_loc[ 0] < len(ghost.tracks) - 1:
loc0 = ghost.tracks_loc[ 0] + 1
elifghost.role_name == 'Clyde':
loc0 = 2
else:
loc0 = 0
ghost.changeSpeed(ghost.tracks[loc0][ 0: 2])
ghost.update(wall_sprites, None)
ghost_sprites.draw(screen)
score_text = font.render( "Score: %s"% SCORE, True, RED)
screen.blit(score_text, [ 10, 10])
iflen(food_sprites) == 0:
is_clearance = True
break
ifpygame.sprite.groupcollide(hero_sprites, ghost_sprites, False, False):
is_clearance = False
break
pygame.display.flip
clock.tick( 10)
returnis_clearance
'''顯示文字'''
defshowText(screen, font, is_clearance, flag=False):
clock = pygame.time.Clock
msg = 'Game Over!'ifnotis_clearance else'Congratulations, you won!'
positions = [[ 235, 233], [ 65, 303], [ 170, 333]] ifnotis_clearance else[[ 145, 233], [ 65, 303], [ 170, 333]]
surface = pygame.Surface(( 400, 200))
surface.set_alpha( 10)
surface.fill(( 128, 128, 128))
screen.blit(surface, ( 100, 200))
texts = [font.render(msg, True, WHITE),
font.render( 'Press ENTER to continue or play again.', True, WHITE),
font.render( 'Press ESCAPE to quit.', True, WHITE)]
whileTrue:
forevent inpygame.event.get:
ifevent.type == pygame.QUIT:
sys.exit
pygame.quit
ifevent.type == pygame.KEYDOWN:
ifevent.key == pygame.K_RETURN:
ifis_clearance:
ifnotflag:
return
else:
main(initialize)
else:
main(initialize)
elifevent.key == pygame.K_ESCAPE:
sys.exit
pygame.quit
foridx, (text, position) inenumerate(zip(texts, positions)):
screen.blit(text, position)
pygame.display.flip
clock.tick( 10)
'''初始化'''
definitialize:
pygame.init
icon_image = pygame.image.load(ICONPATH)
pygame.display.set_icon(icon_image)
screen = pygame.display.set_mode([ 606, 606])
pygame.display.set_caption( '吃豆人')
returnscreen
'''主函數'''
defmain(screen):
pygame.mixer.init
pygame.mixer.music.load(BGMPATH)
pygame.mixer.music.play( -1, 0.0)
pygame.font.init
font_small = pygame.font.Font(FONTPATH, 18)
font_big = pygame.font.Font(FONTPATH, 24)
fornum_level inrange( 1, Levels.NUMLEVELS+ 1):
ifnum_level == 1:
level = Levels.Level1
is_clearance = startLevelGame(level, screen, font_small)
ifnum_level == Levels.NUMLEVELS:
showText(screen, font_big, is_clearance, True)
else:
showText(screen, font_big, is_clearance)
最終運行程序結果如下:
源碼地址:
https://pan.baidu.com/s/128id8L-PDPgGOPuH-5uHDg
提取碼:rj9f
作者簡介:
李秋鍵,CSDN博客專家,CSDN達人課作者。碩士在讀於中國礦業大學,開發有taptap競賽獲獎等等。