使用python的pygame模塊寫了一個窗口的飛機大戰遊戲,效果如圖。
使用上下左右控制,獲得藍色星星火力+20,黃色星星生命+1
以下是實現代碼,運行環境python3。
此代碼僅供學習與交流。請勿用於商業用途。
import random
import threading
from abc import ABCMeta, abstractmethod
from enum import Enum
import pygame
# 窗口寬高
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 654
# 背景,開始,暫停,遊戲結束圖片
background_image = pygame.image.load('image/background.png')
start_image = pygame.image.load('image/start.png')
pause_image = pygame.image.load('image/pause.png')
gameover_image = pygame.image.load('image/gameover.png')
# 英雄機圖片,兩張圖片切換
hero0_image = pygame.image.load('image/hero0.png')
hero1_image = pygame.image.load('image/hero1.png')
# 子彈圖片
bullet_image = pygame.image.load('image/bullet.png')
# 敵機,大敵機,敵機爆炸圖片
airplane_image = pygame.image.load('image/airplane.png')
airplane_rect = airplane_image.get_rect()
big_airplane_image = pygame.transform.scale(airplane_image, (airplane_rect.width * 2, airplane_rect.height * 2))
bomb_image = pygame.image.load('image/bomb.png')
bomb_rect = bomb_image.get_rect()
# 黃色星星,藍色星星圖片
yellow_star_image = pygame.image.load('image/star.png')
blue_star_image = pygame.image.load('image/blue_star.png')
class GameState(Enum):
"""遊戲狀態"""
start = 0
running = 1
gameover = 2
pause = 3
class Award(Enum):
"""獎勵類型"""
life = 0
bullet = 1
def hint(self):
"""獲得獎勵的提示"""
if self == Award.life:
return '生命+1'
elif self == Award.bullet:
return '火力+20'
class FlyingObject(pygame.sprite.Sprite):
"""飛行物"""
__metaclass__ = ABCMeta
def __init__(self):
super(FlyingObject, self).__init__()
self.image = None
self.rect = None
self.speed = 0
self.life = 1
@abstractmethod
def update(self):
"""更新當前對象狀態的方法,供子類實現"""
pass
class Hint(FlyingObject):
"""提示"""
def __init__(self, hint_str, pos):
super(Hint, self).__init__()
self.hint_str = hint_str
# 元組不能更改,轉換爲list
self.start_pos = list(pos)
self.pos = self.start_pos.copy()
def update(self):
"""上升一小段距離後消失"""
self.pos[1] -= 1
if self.start_pos[1] - self.pos[1] >= 40:
self.kill()
class Bullet(FlyingObject):
"""子彈"""
def __init__(self, pos):
super(Bullet, self).__init__()
self.image = bullet_image
self.rect = self.image.get_rect()
self.rect.topleft = (pos[0] - self.rect.width / 2, pos[1])
self.speed = 8
def update(self):
"""向上移動,出上邊界消失"""
self.rect.top -= self.speed
if self.rect.bottom < 0:
self.kill()
class Enemy(FlyingObject):
"""敵人"""
__metaclass__ = ABCMeta
def __init__(self):
super(Enemy, self).__init__()
self.score = 0
def update(self):
"""向下移動,出下邊界消失"""
self.rect.top += self.speed
if self.rect.top > SCREEN_HEIGHT:
self.kill()
def destroy(self):
"""置爲毀壞狀態"""
# 將圖片置爲bomb圖片,座標移動到正中間
x = self.rect.left + ((self.rect.width - bomb_rect.width) / 2)
y = self.rect.top + ((self.rect.height - bomb_rect.height) / 2)
self.rect.topleft = (x, y)
self.image = bomb_image
# 0.3秒後消失
timer = threading.Timer(0.3, self.kill)
timer.start()
class Airplane(Enemy):
"""敵機,敵人的子類"""
def __init__(self):
super(Airplane, self).__init__()
self.image = airplane_image
self.rect = self.image.get_rect()
self.rect.topleft = (random.randint(0, SCREEN_WIDTH - self.rect.width), (-self.rect.height))
self.speed = 4
self.score = 10
class BigAirplane(Enemy):
"""大敵機,敵人的子類"""
def __init__(self):
super(BigAirplane, self).__init__()
self.image = big_airplane_image
self.rect = self.image.get_rect()
self.rect.topleft = (random.randint(0, SCREEN_WIDTH - self.rect.width), (-self.rect.height))
self.speed = 3
self.life = 2
self.score = 30
class Star(FlyingObject):
"""星星,英雄機可以吃星星獲得獎勵"""
def __init__(self):
super(Star, self).__init__()
# 初始化獎勵類型
self.award = random.choices(list(Award), [1, 3])[0]
# 生命獎勵爲黃色星星,火力獎勵爲藍色星星
self.image = yellow_star_image if self.award == Award.life else blue_star_image
self.rect = self.image.get_rect()
self.rect.topleft = (random.randint(0, SCREEN_WIDTH - self.rect.width), (-self.rect.height))
self.speed_x = 2
self.speed_y = 4
def update(self):
"""斜向下移動"""
self.rect.left += self.speed_x
self.rect.top += self.speed_y
# 碰到左右邊界speed_x取反
if self.rect.left < 0 or self.rect.left > SCREEN_WIDTH - self.rect.width:
self.speed_x = -self.speed_x
if self.rect.top > SCREEN_HEIGHT:
self.kill()
class Hero(FlyingObject):
"""英雄機"""
def __init__(self):
super(Hero, self).__init__()
self.images = [hero0_image, hero1_image]
self.image = self.images[0]
self.rect = self.image.get_rect()
self.rect.topleft = (int(SCREEN_WIDTH / 2 - self.rect.width / 2), SCREEN_HEIGHT - self.rect.height - 30)
self.speed = 6
self.life = 3
self.init_directions()
self.double_fire = 0
# 控制圖片切換
self.index = 0
# 子彈列表
self.bullets = pygame.sprite.Group()
def shoot(self):
"""射擊"""
step_x = self.rect.width / 4
# 火力大於0時二連發
if self.double_fire > 0:
self.bullets.add(Bullet((self.rect.left + 1 * step_x, self.rect.top)))
self.bullets.add(Bullet((self.rect.left + 3 * step_x, self.rect.top)))
self.double_fire -= 2
# 沒有火力單發子彈
else:
self.bullets.add(Bullet(self.rect.midtop))
def set_direction_enabled(self, direction, enabled):
"""設置某個方向有效或無效"""
self.directions[direction] = enabled
def init_directions(self):
"""初始化方向的字典"""
self.directions = {pygame.K_LEFT: False, pygame.K_RIGHT: False, pygame.K_UP: False, pygame.K_DOWN: False}
def receive_award(self, award):
"""接受獎勵"""
if award == Award.life:
self.life += 1
elif award == Award.bullet:
self.double_fire += 20
def update(self):
# 切換圖片
self.image = self.images[self.index // 10 % len(self.images)]
self.index += 1
# 移動,不能超出邊界
x = self.rect.left + (self.directions[pygame.K_RIGHT] - self.directions[pygame.K_LEFT]) * self.speed
y = self.rect.top + (self.directions[pygame.K_DOWN] - self.directions[pygame.K_UP]) * self.speed
if x < 0:
x = 0
elif x > SCREEN_WIDTH - self.rect.width:
x = SCREEN_WIDTH - self.rect.width
if y < 0:
y = 0
elif y > SCREEN_HEIGHT - self.rect.height:
y = SCREEN_HEIGHT - self.rect.height
self.rect.left = x
self.rect.top = y
class Game:
"""遊戲類"""
def __init__(self):
"""初始化窗口"""
pygame.init()
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), 0, 32)
pygame.display.set_caption('shoot game')
self.state = GameState.start
self.init()
def init(self):
"""初始化數據,供遊戲第一次開始和死亡後重新開始時調用"""
# 計數
self.tick = 0
# 分數
self.point = 0
# 英雄機對象
self.hero = Hero()
# 敵機組
self.enemy_group = pygame.sprite.Group()
# 星星組
self.star_group = pygame.sprite.Group()
# 提示組
self.hint_group = pygame.sprite.Group()
def handle_event(self, event):
"""處理事件"""
if event.type == pygame.QUIT:
pygame.quit()
exit()
# 如果不是運行中,點擊回車,置爲運行中
if self.state != GameState.running:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
if self.state == GameState.gameover:
self.init()
self.hero.init_directions()
self.state = GameState.running
return
# 若在運行中,上下左右控制英雄方向,回車暫停
else:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
self.state = GameState.pause
elif event.key in self.hero.directions:
self.hero.set_direction_enabled(event.key, True)
elif event.type == pygame.KEYUP:
if event.key in self.hero.directions:
self.hero.set_direction_enabled(event.key, False)
def produce_flying_object(self):
"""產生飛行物"""
fly_class = random.choices([Airplane, BigAirplane, Star], [4, 2, 1])[0]
if fly_class == Airplane or fly_class == BigAirplane:
self.enemy_group.add(fly_class())
elif fly_class == Star:
self.star_group.add(fly_class())
def update(self):
"""子彈,敵機,提示,英雄 更新位置"""
self.hero.bullets.update()
self.enemy_group.update()
self.star_group.update()
for hint in self.hint_group.sprites():
hint.update()
self.hero.update()
def enemy_and_bullet_collide(self):
"""敵人和子彈碰撞 敵人生命-他撞到的子彈數量,若敵人生命=0則敵機毀滅然後獲得分數"""
enemy_and_bullect_list_dict = pygame.sprite.groupcollide(self.enemy_group, self.hero.bullets, False,
False)
if enemy_and_bullect_list_dict:
for enemy, bullect_list in enemy_and_bullect_list_dict.items():
# 敵機已經毀滅,則跳過
if enemy.life <= 0:
continue
for bullet in bullect_list:
enemy.life -= 1
bullet.kill()
if enemy.life <= 0:
enemy.destroy()
score = enemy.score
# 添加提示
self.hint_group.add(Hint('分數+%d' % score, enemy.rect.topleft))
self.point += score
def hero_and_star_collide(self):
"""星星和英雄機碰撞,獲得獎勵"""
star_list = pygame.sprite.spritecollide(self.hero, self.star_group, True)
if star_list:
for star in star_list:
# 添加提示
self.hint_group.add(Hint(star.award.hint(), star.rect.topleft))
# 英雄機獲得獎勵
self.hero.receive_award(star.award)
def hero_and_enemy_collide(self):
"""敵機和英雄碰撞,英雄機生命-1,敵機消失,若英雄機生命小於等於0遊戲結束"""
enemy_list = pygame.sprite.spritecollide(self.hero, self.enemy_group, True)
if enemy_list:
for enemy in enemy_list:
if enemy.life > 0:
self.hint_group.add(Hint('生命-1', self.hero.rect.topleft))
self.hero.life -= 1
enemy.kill()
if self.hero.life <= 0:
self.state = GameState.gameover
def draw_flying_object(self):
"""繪製飛行物(子彈,敵機,星星,提示,英雄機)"""
self.hero.bullets.draw(self.screen)
self.enemy_group.draw(self.screen)
self.star_group.draw(self.screen)
for hint in self.hint_group.sprites():
self.draw_string(hint.hint_str, 16, hint.pos)
self.screen.blit(self.hero.image, self.hero.rect)
def draw_string(self, s, size, pos, color=(255, 0, 0)):
"""繪製字符串"""
font = pygame.font.SysFont("SimHei", size)
text_surface = font.render(s, True, color)
self.screen.blit(text_surface, pos)
def draw_info(self):
"""繪製左上角的分數、生命、火力等信息"""
self.draw_string("分數:%d" % self.point, 16, (0, 0))
self.draw_string("生命:%d" % self.hero.life, 16, (80, 0))
self.draw_string("火力:%d" % self.hero.double_fire, 16, (150, 0))
def start(self):
"""遊戲開始"""
while True:
"""事件處理"""
for event in pygame.event.get():
self.handle_event(event)
"""畫背景後,判斷四個狀態分別繪製"""
# 畫背景
self.screen.blit(background_image, background_image.get_rect())
# 若爲開始界面,畫開始圖,寫提示
if self.state == GameState.start:
self.screen.blit(start_image, start_image.get_rect())
self.draw_string("press enter key to start", 30, (20, SCREEN_HEIGHT / 2 + 50), (150, 150, 150))
# 若爲暫停界面,畫暫停圖
elif self.state == GameState.pause:
self.screen.blit(pause_image, pause_image.get_rect())
# 若爲遊戲結束界面,畫遊戲結束圖、顯示得分
elif self.state == GameState.gameover:
self.screen.blit(gameover_image, gameover_image.get_rect())
self.draw_string("分數:%d" % self.point, 24, (140, SCREEN_HEIGHT / 2), (255, 0, 255))
# 若爲運行中
elif self.state == GameState.running:
# 每20個tick英雄射擊,每50個tick產生飛行物
if self.tick % 20 == 0:
self.hero.shoot()
if self.tick % 50 == 0:
self.produce_flying_object()
# 子彈,敵機,提示,英雄 更新位置
self.update()
# 敵人和子彈碰撞 敵人生命-1,若敵人生命=0則敵機毀滅然後獲得分數
self.enemy_and_bullet_collide()
# 星星和英雄機碰撞,獲得獎勵
self.hero_and_star_collide()
# 敵機和英雄碰撞,英雄機生命-1,敵機消失,若英雄機生命小於等於0遊戲結束
self.hero_and_enemy_collide()
# 繪製子彈,敵機,星星,提示,英雄機
self.draw_flying_object()
# 繪製左上角的分數、生命、火力
self.draw_info()
# 計數
self.tick += 1
pygame.display.flip()
if __name__ == '__main__':
Game().start()