欢迎光临散文网 会员登陆 & 注册

爆炸与宝箱,从零开始用Python制作飞机大战 P9

2023-08-24 21:16 作者:我真帅啊kk  | 我要投稿

'''编完之后回头看有看不懂代码的意义?来看看我的加注释版吧,标记了各个代码的意义,如有问题敬请指出,共同探讨'''

# Time : 2023/6/30 22:08

# !/user/bin/env python3

# -*- coding: utf-8 -*-

import random # 导入random库,进行随机运算


import pygame # 导入pygame库


import os # 导入os库进行文件操作


# 定义常量

WIDTH = 500 # 窗口的宽

HEIGHT = 600 # 窗口的高

WHITE = (255, 255, 255) # 白色RGB常量

GREEN = (0, 255, 0) # 绿色RGB常量

RED = (255, 0, 0) # 红石RGB常量

YELLOW = (255, 255, 0) # 黄色RGB常量

BLACK = (0, 0, 0) # 黑色RGB常量


# 游戏初始化

pygame.init() # 游戏初始化

pygame.mixer.init() # 音频初始化

screen = pygame.display.set_mode((WIDTH, HEIGHT)) # 设置界面宽高

pygame.display.set_caption('飞机大战(plane_war)') # 设置标题

clock = pygame.time.Clock() # 创建时钟对象


# 加载图片

background_img = pygame.image.load(os.path.join('img', 'background.png')).convert() # 背景图

player_img = pygame.image.load(os.path.join('img', 'player.png')).convert() # 加载飞机图片

player_mini_img = pygame.transform.scale(player_img, (25, 20)) # 缩小飞机图片得到生命显示的小飞机

player_mini_img.set_colorkey(BLACK) # 将小飞机图片中黑色改为透明

rock_img = pygame.image.load(os.path.join('img', 'rock.png')).convert() # 加载陨石图片

bullet_img = pygame.image.load(os.path.join('img', 'bullet.png')).convert() # 加载子弹图片


rock_imges = [] # 创建陨石图片列表

for i in range(7): # for循环遍历

rock_imges.append(pygame.image.load(os.path.join('img', f'rock{i}.png')).convert())

# 格式化字符串,遍历出每一个图片素材并加进陨石图片列表


expl_anim = {}

expl_anim['lg'] = []

expl_anim['sm'] = []

expl_anim['player'] = []

for i in range(9):

expl_img = pygame.image.load(os.path.join('img', f'expl{i}.png')).convert()

expl_img.set_colorkey(BLACK)

expl_anim['lg'].append(pygame.transform.scale(expl_img, (75, 75)))

expl_anim['sm'].append(pygame.transform.scale(expl_img, (30, 30)))

player_expl_img = pygame.image.load(os.path.join('img', f'expl{i}.png')).convert()

player_expl_img.set_colorkey(BLACK)

expl_anim['player'].append(player_expl_img)


power_imgs = {}

power_imgs['shield'] = pygame.image.load(os.path.join('img', 'shield.png')).convert()

power_imgs['gun'] = pygame.image.load(os.path.join('img', 'gun.png')).convert()


# 加载声音

shoot_sound = pygame.mixer.Sound(os.path.join('sound', 'shoot.wav')) # 加载射击音效

# 加载陨石爆炸音效,组成列表

expl_sounds = [

pygame.mixer.Sound(os.path.join('sound', 'expl0.wav')),

pygame.mixer.Sound(os.path.join('sound', 'expl1.wav'))

]


die_sound = pygame.mixer.Sound(os.path.join('sound', 'rumble.ogg'))

shield_sound = pygame.mixer.Sound(os.path.join('sound', 'pow0.wav'))

gun_sound = pygame.mixer.Sound(os.path.join('sound', 'pow1.wav'))


pygame.mixer.music.load(os.path.join('sound', 'background.ogg')) # 加载背景音乐

pygame.mixer.music.set_volume(0.5) # 调整背景音乐音量


font_name = pygame.font.match_font('artal') # 查找字体文件路径



def draw_text(surf, text, size, x, y): # 定义绘制积分函数

font = pygame.font.Font(font_name, size) # 创建字体对象

text_surface = font.render(text, True, WHITE) # 渲染,开启抗锯齿,文本白色

text_rect = text_surface.get_rect() # 获取矩形轮廓

text_rect.centerx = x # 设置x坐标

text_rect.centery = y # 设置y坐标

surf.blit(text_surface, text_rect) # 绘制文字



def draw_health(surf, hp, x, y): # 定义绘制血条函数

if hp < 0: # 判断血量是否为负数

hp = 0 # 如果是,那么将hp设为0


BAR_LENGTH = 100 # 设置血条长度

BAR_HEIGHT = 10 # 设置血条长度

fill = (hp / 100) * BAR_LENGTH # 设置内填充长度

outline_rect = pygame.Rect(x, y, BAR_LENGTH, BAR_HEIGHT) # 创建外围矩形,表示血条的外框

fill_rect = pygame.Rect(x, y, fill, BAR_HEIGHT) # 创建内填充矩形,表示血条的内部填充

pygame.draw.rect(surf, GREEN, fill_rect) # 绘制内部填充血条

pygame.draw.rect(surf, WHITE, outline_rect, 2) # 绘制外围轮廓



def draw_lives(surf, lives, img, x, y): # 定义显示生命函数

for i in range(lives): # 循环生命数次

img_rect = img.get_rect() # 获取矩形轮廓

img_rect.x = x + 30 * i # 设置x坐标

img_rect.y = y # 设置y坐标

surf.blit(img, img_rect) # 绘制图像



def new_rock():

rock = Rock() # 创建陨石对象

all_sprites.add(rock) # add:增加,将陨石对象加进角色列表

rocks.add(rock) # 将陨石对象加进陨石列表



class Player(pygame.sprite.Sprite): # 创建玩家类

def __init__(self): # 初始化函数

pygame.sprite.Sprite.__init__(self)

self.image = pygame.transform.scale(player_img, (50, 40)) # 角色设置成飞机图片,并调整图片大小

self.image.set_colorkey(BLACK) # 将素材中的黑色改透明


self.rect = self.image.get_rect() # 获取矩形轮廓

self.radius = 23 # 设置半径

# pygame.draw.circle(self.image, RED, self.rect.center, self.radius)


self.rect.centerx = WIDTH / 2 # 设置x中心

self.rect.bottom = HEIGHT - 20 # 底部坐标


self.speedx = 8 # 设置x速度

self.health = 100 # 设置玩家血量

self.lives = 3 # 设置玩家生命数量

self.hidden = False # 隐藏状态设为false

self.gun = 1


def update(self): # 位置更新

key_pressed = pygame.key.get_pressed() # 键状态的列表

if key_pressed[pygame.K_RIGHT]: # 按下右方向键向右移动

self.rect.x += self.speedx


if key_pressed[pygame.K_LEFT]: # 检测按下左方向键

self.rect.x -= self.speedx # 将x减speedx,相当于向左移动


if self.rect.right > WIDTH: # 检测是否超过右边缘

self.rect.right = WIDTH # 条件达成则回到右边缘


if self.rect.left < 0: # 检测是否超过左边缘

self.rect.left = 0 # 条件达成则回到左边缘


if self.hidden and pygame.time.get_ticks() - self.hide_time > 1000: # 判断隐身结束

self.hidden = False # 将隐藏状态设置为False

self.rect.centerx = WIDTH / 2 # 设置x中心

self.rect.bottom = HEIGHT - 20 # 底部坐标


self.image.set_alpha(int(self.health * 10)) # 设置透明度,根据血量变话,课程外代码


if self.gun > 2 and pygame.time.get_ticks() - self.gun_time > 5000: # 判断强化子弹时间是否超过5秒

self.gun = 1 # 将gun变量设为1,正常模式

def shoot(self):

if not (self.hidden): # 判断是否不为隐藏状态

if self.gun == 1: # 判断gun是否为1,执行正常状态的发射子弹

bullet = Bullet(self.rect.centerx, self.rect.centery) # 创建子弹对象

all_sprites.add(bullet) # 将子弹对象加进角色列表中

bullets.add(bullet) # 将子弹对象加进子弹角色列表中

shoot_sound.play() # 播放子弹发射的声音

elif self.gun >= 2: # 判断gun是否2,执行增益发射子弹

self.gun_time = pygame.time.get_ticks()

bullet1 = Bullet(self.rect.left, self.rect.centery) # 创建子弹对象

bullet2 = Bullet(self.rect.right, self.rect.centery) # 创建子弹对象

all_sprites.add(bullet1) # 将子弹对象加进角色列表中

bullets.add(bullet1) # 将子弹对象加进子弹角色列表中

shoot_sound.play() # 播放子弹发射的声音

all_sprites.add(bullet2) # 将子弹对象加进角色列表中

bullets.add(bullet2) # 将子弹对象加进子弹角色列表中


def hide(self):

self.hidden = True # 将隐藏状态变量设置为true,用于判断

self.hide_time = pygame.time.get_ticks() # 将当前时间赋值给变量

self.rect.center = (WIDTH / 2, HEIGHT + 50000) # 将位置移动到舞台外


def gunup(self):

self.gun += 1 # 将gun变量+1,强化子弹




class Rock(pygame.sprite.Sprite): # 创建陨石类

def __init__(self): # 初始化函数

pygame.sprite.Sprite.__init__(self)

self.image_origin = random.choice(rock_imges) # 将角色设置成陨石图片

self.image_origin.set_colorkey(BLACK) # 将素材中的黑色改为透明

self.image = self.image_origin.copy() # 拷贝原本图片到变量


self.rect = self.image.get_rect() # 获取矩形轮廓


self.radius = self.rect.width / 2.3 # 设置半径

# pygame.draw.circle(self.image, RED, self.rect.center, self.radius)


self.rect.x = random.randrange(0, WIDTH - self.rect.width) # 设置陨石掉下来的x

self.rect.y = random.randrange(-180, -100) # 设置陨石初始y


# self.speedy = random.randrange(2, 5) # 随机设置掉下速度

self.speedy = 1

self.speedx = random.randrange(-3, 3) # 随机左右移动


self.rot_degree = random.randrange(-3, 3) # 随机设置累加速度的值

self.total_degree = 0 # 初始化旋转的速度


self.rock_hp = self.radius # 设置血量


def rotate(self):

self.total_degree += self.rot_degree # 累加速度

self.total_degree = self.total_degree % 360 # 将旋转速度设为旋转速度除360的余数,以防旋转速度超过360

self.image = pygame.transform.rotate(self.image_origin, self.total_degree) # 图片转动

center = self.rect.center # 将中心点位置保存在变量中

self.rect = self.image.get_rect() # 获取矩形轮廓

self.rect.center = center # 将之前保存的中心点重新赋值给self.rect.center,以保持图像的中心位置不变。


def update(self): # 位置更新

self.rotate() # 调用陨石转动方法

self.rect.y += self.speedy # 陨石不断增加y坐标

self.rect.x += self.speedx # 陨石不断更新x坐标

if self.rect.top > HEIGHT or self.rect.left > WIDTH or self.rect.right < 0: # 判断矩形轮廓超出边缘就重新回到上面

self.rect.x = random.randrange(0, WIDTH - self.rect.width) # 设置陨石掉下来的x

self.rect.y = random.randrange(-100, -40) # 设置陨石初始y


self.speedy = random.randrange(2, 10) # 随机设置掉下速度

self.speedx = random.randrange(-3, 3) # 随机左右移动



class Bullet(pygame.sprite.Sprite): # 创建子弹类

def __init__(self, x, y): # 初始化函数

pygame.sprite.Sprite.__init__(self)

self.image = bullet_img # 将角色设置成子弹图片

self.image.set_colorkey(BLACK) # 将素材中的黑色改为透明


self.rect = self.image.get_rect() # 获取矩形轮廓

self.rect.x = x # 设置子弹初始的的x,与飞机x一样

self.rect.y = y # 设置子弹初始的的y,与飞机y一样


self.speedy = -10 # 子弹飞行速度(y变化的跨度)


def update(self): # 位置更新

self.rect.y += self.speedy # 子弹不断增加y坐标

if self.rect.bottom < 0: # 检测子弹超过边缘,则清除

self.kill() # 清除角色



class Explosion(pygame.sprite.Sprite): # 创建爆炸效果类

def __init__(self, center, size): # 初始化函数

pygame.sprite.Sprite.__init__(self)

self.size = size # 设置类型

self.image = expl_anim[self.size][0] # 将角色设置成子弹图片


self.rect = self.image.get_rect() # 获取矩形轮廓

self.rect.center = center # 设置位置

self.frame = 0 # 帧数

self.last_update = pygame.time.get_ticks() # 设置上一次更新的时间


def update(self): # 位置更新

now = pygame.time.get_ticks() # 现在的时间

if now - self.last_update > 50: # 检测现在的时间比上次更新的时间是否超过了50

self.last_update = now # 将上次更新的时间设为现在时间

self.frame = self.frame + 1 # 帧数+1

if self.frame == len(expl_anim[self.size]): # 判断帧数是否等于这个爆炸效果类型的总帧数,也就是是否爆炸效果播放完了

self.kill() # 清除对象

else:

self.image = expl_anim[self.size][self.frame] # 如果没播放完爆炸效果

center = self.rect.center # 将当前中心位置赋值给center

self.rect = self.image.get_rect() # 设置矩形轮廓

self.rect.center = center # 将现在中心位置改为center



class Power(pygame.sprite.Sprite): # 创建增益效果类

def __init__(self, center): # 初始化函数

pygame.sprite.Sprite.__init__(self)

self.type = random.choice(['shield', 'gun']) # 创建增益类时,随机选择盾牌或子弹增益

self.image = power_imgs[self.type] # 将角色设置成子弹图片

self.image.set_colorkey(BLACK) # 将素材中的黑色改为透明

if self.type == 'shield': # 判断是否为盾牌类型

shield_sound.play() # 播放盾牌声音

elif self.type == 'gun': # 判断是否为子弹增益

gun_sound.play() # 播放子弹增益音效


self.rect = self.image.get_rect() # 获取矩形轮廓

self.rect.center = center # 将中心设为center


self.speedy = 3 # 子弹飞行速度(y变化的跨度)


def update(self): # 位置更新

self.rect.y += self.speedy # 子弹不断增加y坐标

if self.rect.top > HEIGHT: # 检测子弹超过边缘,则清除

self.kill() # 清除角色



all_sprites = pygame.sprite.Group() # 创建所有角色表

rocks = pygame.sprite.Group() # 创建陨石角色表

bullets = pygame.sprite.Group() # 创建子弹角色表

powers = pygame.sprite.Group() # 创建增益效果角色表


player = Player() # 创建玩家类对象

all_sprites.add(player) # 将创建的玩家类对象放入角色列表


score = 0 # 初始化分数


pygame.mixer.music.play(-1) # 循环播放背景音乐


for item in range(10): # 循环10次,共创建10个陨石对象

new_rock() # 创建陨石对象



running = True # 定义工具变量用于控制循环

while running: # 主循环,循环至关闭窗口


clock.tick(60) # 设置帧数


for event in pygame.event.get(): # 获取事件


if event.type == pygame.QUIT: # 判断是否为退出事件

running = False # 如果是,则将变量设为False,停止循环


elif event.type == pygame.KEYDOWN: # 检测事件是否为按下按键


if event.key == pygame.K_SPACE: # 检测按下的按键是否为空格

player.shoot() # 如果是,调用发射子弹方法发射子弹


elif event.key == pygame.K_UP: # 新增代码,b站评论区中的用1000积分换生命,检测按下的按键是否为上箭头

if score > 999 and not player.lives >= 3: # 判断评分是否大于999,玩家生命数是否大等于3

player.lives += 1 # 将玩家命加一

score -= 1000 # 分数减1000

'割草机模式'

'''key_pressed = pygame.key.get_pressed()

if key_pressed[pygame.K_SPACE]: # 按下右方向键向右移动

player.shoot()'''


all_sprites.update() # 调用角色池里面的位置更新方法


hits_rock_and_bullet = pygame.sprite.groupcollide(rocks, bullets, False, True) # 检测陨石是否碰到子弹,

# 如果碰到则陨石扣血

for hit in hits_rock_and_bullet:

hit.rock_hp -= 20 # 扣的数相当于子弹威力

if hit.rock_hp <= 0: # 检测陨石血量是否低等于0

random.choice(expl_sounds).play() # 播放陨石被击碎的音频

score += int(hit.radius) # 分数增加陨石大小

expl = Explosion(hit.rect.center, 'lg') # 创建爆炸类型为lg的爆炸特效对象

all_sprites.add(expl) # 爆炸特效对象加进角色表中,以进行更新

new_rock() # 新建陨石

if random.random() > 0.1: # random.randonm()是弄一个0到1之间的随机小数,判断该小数是否大于0.1,概率为百分之90

p = Power(hit.rect.center) # 创建增益效果对象

all_sprites.add(p) # 在角色表中增加增益效果对象,以便进行更新

powers.add(p) # 在增益效果角色表中增加增益效果对象,以便单独操作

hit.kill() # 清除陨石


hits_player_and_rock = pygame.sprite.spritecollide(player, rocks, True, pygame.sprite.collide_circle) # 检测陨石碰到飞机

for hit in hits_player_and_rock:

player.health -= hit.radius # 血量减去陨石大小


new_rock() # 新建陨石


expl = Explosion(hit.rect.center, 'sm')

all_sprites.add(expl)

if player.health <= 0: # 判断检测血量小于等于0

death_expl = Explosion(player.rect.center, 'player') # 创建爆炸特效对象,类型为飞机爆炸

all_sprites.add(death_expl) # 在角色表中增加该爆炸特效对象

die_sound.play() # 播放飞机爆炸音效

player.lives -= 1 # 将命减一

player.hide() # 调用飞机隐藏方法隐藏

player.health = 100 # 血量回复到一百


if player.lives == 0: # 判断检测生命数等于零

running = False # 调整工具变量,结束游戏


hits_player_and_power = pygame.sprite.spritecollide(player, powers, True) # 检测增益效果碰到飞机

for hit in hits_player_and_power:

if hit.type == 'shield': # 判断增益效果是否为盾牌

player.health += 25 # 增加25滴血

if player.health > 100: # 如果增加后血量大于100

player.health = 100 # 将血量设置成100

elif hit.type == 'gun': # 判断增益效果是否为子弹增益

player.gunup() # 调用子弹增益方法



screen.fill(BLACK) # 设置窗口界面颜色,显示画面

screen.blit(background_img, (0, 0)) # 填充背景图片


all_sprites.draw(screen) # 绘制角色


draw_text(screen, str(score), 30, WIDTH / 2, 50) # 绘制分数


draw_health(screen, player.health, 10, 30) # 绘制血量


draw_lives(screen, player.lives, player_mini_img, WIDTH - 100, 15) # 绘制命数


pygame.display.update() # 更新状态


pygame.quit() # 退出


爆炸与宝箱,从零开始用Python制作飞机大战 P9的评论 (共 条)

分享到微博请遵守国家法律