本文基於win7(64) + py3.5(64)環境。
本文是這里的一篇學習筆記。加入了自己的理解。
本文最終目的是實現一個飛機躲避導彈的游戲。
1、核心概念
pygame 的核心概念有:
- Surface 對象(一個容器,一個載體,可以是空白的矩形區域,亦可是圖片)
- Surface 對象的矩形區域(用 Surface 實例的
get_rect()
方法獲得) - 屏幕對象的
blit()
方法用於放置 Surface 對象 - display 模塊的
flip()
方法用於重繪游戲界面 - 窗口主循環
import pygame, sys
from pygame.locals import * # 全局常量
# 初始化
pygame.init()
# 屏幕對象
screen = pygame.display.set_mode((400,270)) # 尺寸
# Surface對象
surf = pygame.Surface((50,50)) # 長、寬
surf.fill((255,255,255)) # 顏色
# Surface對象的矩形區域
rect = surf.get_rect()
# 窗口主循環
while True:
# 遍歷事件隊列
for event in pygame.event.get():
if event.type == QUIT: # 點擊右上角的'X',終止主循環
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE: # 按下'ESC'鍵,終止主循環
pygame.quit()
sys.exit()
# 放置Surface對象
screen.blit(surf, ((400-50)//2, (270-50)//2)) # 窗口正中
#screen.blit(surf, rect) # surf的左上角
# 重繪界面
pygame.display.flip()
效果圖
2、引入精靈
上面的代碼很能體現pygame的核心概念,但不利於規模比較大的游戲開發。
為此,就需要繼承 Sprite 類,並且設置屬性surf
為Surface對象。同時設置rect
屬性。
調用屏幕對象的blit
方法時,以精靈實例的surf
屬性和rect
屬性為參數。
import pygame, sys
from pygame.locals import *
import random
'''玩家隨着方向鍵運動'''
# 玩家
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.surf = pygame.Surface((75,25))
self.surf.fill((255,255,255))
self.rect = self.surf.get_rect()
def update(self, key):
# 隨着方向鍵運動
if key[K_UP]:
self.rect.move_ip(0,-5)
if key[K_DOWN]:
self.rect.move_ip(0,5)
if key[K_LEFT]:
self.rect.move_ip(-5,0)
if key[K_RIGHT]:
self.rect.move_ip(5,0)
# 限定player在屏幕中
if self.rect.left < 0:
self.rect.left = 0
elif self.rect.right > 800:
self.rect.right = 800
if self.rect.top <= 0:
self.rect.top = 0
elif self.rect.bottom >= 600:
self.rect.bottom = 600
# 初始化
pygame.init()
# 屏幕對象
screen = pygame.display.set_mode((800,600)) # 尺寸
# 玩家精靈對象
player = Player()
# 窗口主循環
while True:
# 遍歷事件隊列
for event in pygame.event.get():
if event.type == QUIT: # 點擊右上角的'X',終止主循環
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE: # 按下'ESC'鍵,終止主循環
pygame.quit()
sys.exit()
# 更新屏幕
screen.fill((0,0,0))
# 獲得按鍵
key = pygame.key.get_pressed()
# 更新玩家
player.update(key)
# 放置玩家
screen.blit(player.surf, player.rect)
# 更新界面
pygame.display.flip()
效果圖
3、引入敵人
由於敵人很多,最好的方式是用Group來管理那么多的精靈。
import pygame, sys
from pygame.locals import *
import random
# 玩家
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.surf = pygame.Surface((75,25))
self.surf.fill((255,255,255))
self.rect = self.surf.get_rect()
def update(self, key):
if key[K_UP]:
self.rect.move_ip(0,-5)
if key[K_DOWN]:
self.rect.move_ip(0,5)
if key[K_LEFT]:
self.rect.move_ip(-5,0)
if key[K_RIGHT]:
self.rect.move_ip(5,0)
# 限定player在屏幕中
if self.rect.left < 0:
self.rect.left = 0
elif self.rect.right > 800:
self.rect.right = 800
if self.rect.top <= 0:
self.rect.top = 0
elif self.rect.bottom >= 600:
self.rect.bottom = 600
# 敵人
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.surf = pygame.Surface((20, 10))
self.surf.fill((255, 255, 255))
self.rect = self.surf.get_rect(center=(820, random.randint(0, 600)))
self.speed = random.randint(5, 20)
def update(self):
self.rect.move_ip(-self.speed, 0) # 從右向左
if self.rect.right < 0:
self.kill() # Sprite 的內建方法
# 初始化
pygame.init()
# 屏幕對象
screen = pygame.display.set_mode((800,600)) # 尺寸
# 自定義事件
ADDENEMY = pygame.USEREVENT + 1 # 事件本質上就是整數常量。比 USEREVENT 小的數值已經對應內置事件,因此任何自定義事件都要比 USEREVENT 大
pygame.time.set_timer(ADDENEMY, 250) # 每隔 250 毫秒(四分之一秒) 觸發
# 玩家
player = Player()
# 兩個精靈組
enemies = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
# 窗口主循環
while True:
# 遍歷事件隊列
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
elif event.type == ADDENEMY: # 自定義事件
new_enemy = Enemy()
enemies.add(new_enemy)
all_sprites.add(new_enemy)
# 更新屏幕
screen.fill((0,0,0))
# 更新玩家
key = pygame.key.get_pressed()
player.update(key)
screen.blit(player.surf, player.rect)
# 更新敵人
for enemy in enemies:
enemy.update()
screen.blit(enemy.surf, enemy.rect)
# 碰撞檢測(靈魂所在)
if pygame.sprite.spritecollideany(player, enemies):
player.kill()
print('發生碰撞!')
# 重繪界面
pygame.display.flip()
效果圖
4、使用圖片
上面的例子是丑陋的黑白界面,需要進行美化。好的方式是使用圖片來代替。可以去網上找素材,也可以自己畫。
為了讓游戲更好看,增添了雲朵精靈。
注意:下面的surf
屬性用image
來代替了。
為方便朋友們測試,下面給出代碼中用到的三張圖片素材:
import pygame, sys
from pygame.locals import *
import random
'''飛機躲避導彈'''
# 玩家
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('jet.png').convert() # load函數,返回一個 Surface 對象
self.image.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.image.get_rect()
def update(self, key):
if key[K_UP]:
self.rect.move_ip(0,-5)
if key[K_DOWN]:
self.rect.move_ip(0,5)
if key[K_LEFT]:
self.rect.move_ip(-5,0)
if key[K_RIGHT]:
self.rect.move_ip(5,0)
# 限定player在屏幕中
if self.rect.left < 0:
self.rect.left = 0
elif self.rect.right > 800:
self.rect.right = 800
if self.rect.top <= 0:
self.rect.top = 0
elif self.rect.bottom >= 600:
self.rect.bottom = 600
# 敵人
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('missile.png').convert()
self.image.set_colorkey((255, 255, 255), RLEACCEL)
self.rect = self.image.get_rect(center=(820, random.randint(0, 600)))
self.speed = random.randint(5, 20)
def update(self):
self.rect.move_ip(-self.speed, 0) # 從右向左
if self.rect.right < 0:
self.kill() # Sprite 的內建方法
# 白雲
class Cloud(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('cloud.png').convert()
self.image.set_colorkey((0,0,0),RLEACCEL)
self.rect = self.image.get_rect(
center = (random.randint(820,900),random.randint(0,600))
)
def update(self):
self.rect.move_ip(-5,0)
if self.rect.right < 0:
self.kill()
# 游戲初始化
pygame.init()
# 屏幕對象
screen = pygame.display.set_mode((800,600)) # 尺寸
# 背景Surface
background = pygame.Surface(screen.get_size())
background.fill((135, 206, 250)) # 淺藍色
# 兩個自定義事件
ADDENEMY = pygame.USEREVENT + 1 # 事件本質上就是整數常量。比 USEREVENT 小的數值已經對應內置事件,因此任何自定義事件都必須比 USEREVENT 大)
pygame.time.set_timer(ADDENEMY, 250) # 每隔 250 毫秒(四分之一秒) 觸發
ADDCLOUD = pygame.USEREVENT + 2
pygame.time.set_timer(ADDCLOUD, 1000)
# 玩家精靈對象
player = Player()
# 三個精靈組
enemies = pygame.sprite.Group()
clouds = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
# 窗口主循環
while True:
# 遍歷事件隊列
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
elif event.type == ADDENEMY: # 自定義事件
new_enemy = Enemy()
enemies.add(new_enemy)
all_sprites.add(new_enemy)
elif event.type == ADDCLOUD: # 自定義事件
new_cloud = Cloud()
clouds.add(new_cloud)
all_sprites.add(new_cloud)
# 背景
screen.blit(background, (0, 0))
# 獲取按鍵
key = pygame.key.get_pressed()
# 更新精靈(組)
player.update(key)
enemies.update()
clouds.update()
# 放置精靈
for sprite in all_sprites:
screen.blit(sprite.image, sprite.rect)
# 碰撞檢測(靈魂所在)
if pygame.sprite.spritecollideany(player, enemies):
player.kill()
#print('發生碰撞!')
# 重繪界面
pygame.display.flip()
效果圖
全都是類
所有的東西都放到類里面!難道這就是傳說中的面向對象編程???
import pygame, sys
from pygame.locals import *
import random
'''飛機躲避導彈'''
# 根據概率隨機選取
def choice(seq, prob):
p = random.random()
for i in range(len(seq)):
if sum(prob[:i]) < p < sum(prob[:i+1]):
return seq[i]
# 玩家
class PlayerSprite(pygame.sprite.Sprite):
speed = 5
def __init__(self):
super().__init__()
self.image = pygame.image.load('jet.png').convert() # load函數,返回一個 Surface 對象
self.image.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.image.get_rect()
def update(self, key):
if key[K_UP]:
self.rect.move_ip(0, -self.speed)
if key[K_DOWN]:
self.rect.move_ip(0, self.speed)
if key[K_LEFT]:
self.rect.move_ip(-self.speed, 0)
if key[K_RIGHT]:
self.rect.move_ip(self.speed, 0)
# 限定player在屏幕中
if self.rect.left < 0:
self.rect.left = 0
elif self.rect.right > 800:
self.rect.right = 800
if self.rect.top <= 0:
self.rect.top = 0
elif self.rect.bottom >= 600:
self.rect.bottom = 600
# 敵人
class EnemySprite(pygame.sprite.Sprite):
speed = choice([1,3,5], [0.5, 0.4, 0.1])
def __init__(self):
super().__init__()
self.image = pygame.image.load('missile.png').convert()
self.image.set_colorkey((255, 255, 255), RLEACCEL)
self.rect = self.image.get_rect(center=(820, random.randint(0, 600)))
#self.speed = random.randint(5, 20)
def update(self):
self.rect.move_ip(-self.speed, 0) # 從右向左
if self.rect.right < 0:
self.kill() # Sprite 的內建方法
# 白雲
class CloudSprite(pygame.sprite.Sprite):
speed = choice([1,2], [0.8, 0.2])
def __init__(self):
super().__init__()
self.image = pygame.image.load('cloud.png').convert()
self.image.set_colorkey((0,0,0),RLEACCEL)
self.rect = self.image.get_rect(
center = (random.randint(820,900),random.randint(0,600))
)
def update(self):
self.rect.move_ip(-self.speed,0)
if self.rect.right < 0:
self.kill()
# 背景
class BackgroundSprite(pygame.sprite.Sprite):
def __init__(self, size):
super().__init__()
self.image = pygame.Surface(size) # 實際上是surf,為了統一寫成image
self.image.fill((135, 206, 250)) # 淺藍色
self.rect = pygame.Rect(0, 0, *size)
def update(self):
pass
class Game():
def __init__(self):
# 游戲初始化
pygame.init()
# 屏幕大小及屏幕對象
self.size = self.width, self.height = 800, 600
self.screen = pygame.display.set_mode(self.size)
pygame.display.set_caption("Pygame 2D RPG !")
# 兩個自定義事件
self.ADDENEMY = pygame.USEREVENT + 1 # 事件本質上就是整數常量。比 USEREVENT 小的數值已經對應內置事件,因此任何自定義事件都必須比 USEREVENT 大)
pygame.time.set_timer(self.ADDENEMY, 250) # 每隔 250 毫秒(四分之一秒) 觸發
self.ADDCLOUD = pygame.USEREVENT + 2
pygame.time.set_timer(self.ADDCLOUD, 1000)
# 兩個精靈對象
self.background = BackgroundSprite(self.size)
self.player = PlayerSprite()
# 三個精靈組
self.enemies = pygame.sprite.Group()
self.clouds = pygame.sprite.Group()
self.all_sprites = pygame.sprite.Group()
self.all_sprites.add(self.player)
def run(self):
# 窗口主循環
while True:
# 遍歷事件隊列
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
elif event.type == self.ADDENEMY: # 自定義事件
new_enemy = EnemySprite()
self.enemies.add(new_enemy)
self.all_sprites.add(new_enemy)
elif event.type == self.ADDCLOUD: # 自定義事件
new_cloud = CloudSprite()
self.clouds.add(new_cloud)
self.all_sprites.add(new_cloud)
# 背景
self.screen.blit(self.background.image, self.background.rect)
# 獲取按鍵
key = pygame.key.get_pressed()
# 更新精靈(組)
self.player.update(key)
self.enemies.update()
self.clouds.update()
# 放置精靈
for sprite in self.all_sprites:
self.screen.blit(sprite.image, sprite.rect)
# 碰撞檢測(靈魂所在)
if pygame.sprite.spritecollideany(self.player, self.enemies):
self.player.kill()
print('發生碰撞!')
# 重繪界面
pygame.display.flip()
if __name__ == '__main__':
Game().run()