昨天搞这个碰撞有点费神,今天调整了一些精灵属性,主要是移动速度、发射间隔、子弹速度、防御值等。
载入buff图片,一共设计四种buff:增加生命、增加火力、增加炸弹、增加防御。再增加一个缩小版的炸弹buff,用来显示数量。
new_life = pygame.image.load(os.path.join('image', 'new_life.png'))
defence_buff = pygame.image.load((os.path.join('image', 'defence_buff.png')))
bullet_buff = pygame.image.load(os.path.join('image', 'bullet_buff.png'))
bomb_buff = pygame.image.load(os.path.join('image', 'bomb_buff.png'))
bomb_buff_mini = pygame.transform.scale(bomb_buff, (12, 20))
完善mini_icom代码,这样右上角可以显示剩余飞机生命数量和剩余炸弹buff数量。
def mini_icon(surface):
for i in range(life_number):
surface.blit(player_plane_mini, (WIDTH - 35 - i * 35, 10))
for i in range(bomb_number):
surface.blit(bomb_buff_mini, (WIDTH - 25 - i * 35, 40))
写一个buff类:
class Buff(pygame.sprite.Sprite):
buff_dict = {
'bullet_buff': bullet_buff,
'bomb_buff': bomb_buff,
'new_life': new_life,
'defence_buff': defence_buff
}
def __init__(self, buff_type):
pygame.sprite.Sprite.__init__(self)
self.buff_type = buff_type
self.image = self.buff_dict[self.buff_type]
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = 10
self.speedx = random.randrange(-3, 3)
self.speedy = 5
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.left <= 0 or self.rect.right >= WIDTH:
self.speedx = -self.speedx
if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
self.speedy = - self.speedy
再定义一个buff产生方法,采用自定义事件,每隔30秒随机产生一个buff,这里重复写一个子弹buff,让子弹buff多一点机会出现:
def buff_produce():
global new_buff
if new_buff:
game_buff = Buff(random.choice(['bullet_buff', 'bullet_buff', 'defence_buff', 'new_life', 'bomb_buff']))
all_sprites.add(game_buff)
buff_group.add(game_buff)
new_buff = False
最后在主循环中和碰撞检测中完善buff,如果是子弹buff加强火力20秒,如果是防御buff,增加3000防御值。
hits = pygame.sprite.spritecollide(player, buff_group, True)
for hit in hits:
if hit.buff_type == 'bullet_buff':
bullet_buff_time = time.time()
if hit.buff_type == 'bomb_buff':
if bomb_number <= 5:
bomb_number += 1
if hit.buff_type == 'new_life' and life_number <= 5:
life_number += 1
if hit.buff_type == 'defence_buff':
player.defense += 3000
下面进入爆炸效果,准备了6张爆炸效果图片,建立一个爆炸的列表,依次导入图片,在爆炸是在依次播放图片,达到爆炸效果。
class Explosion:
def __init__(self, surface, x, y):
self.explosions = []
self.last_bom = 0
self.bom_reload = 200
self.surface = surface
for i in range(6):
self.image = pygame.image.load(os.path.join('image', 'bomb' + str(i + 1) + '.png'))
self.explosions.append(self.image)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def explosion(self):
now_time = pygame.time.get_ticks()
if now_time - self.last_bom >= self.bom_reload:
for i in range(len(self.explosions)):
self.surface.blit(self.explosions[i], (self.rect.x, self.rect.y))
self.last_bom = now_time
在碰撞检测函数中的每个精灵kill()时,实例化Explosion,并调用explosion()函数显示爆炸效果。
玩家飞机在死亡后如果还有life_number,会在原位置直接刷新玩家飞机,影响游戏体验,这里重新写一个玩家飞机产生方法,在死亡后让玩家飞机先消失,然后在底部初始位置重新出现。
def new_player(player):
if life_number > 0:
player.rect.centerx = WIDTH / 2
player.rect.bottom = HEIGHT - 10
player.hp = 100
player.defense = 500
all_sprites.add(player)
写个炸弹使用方法,按b键,删除掉所有飞机和子弹。
基本就这样了吧,再写一个重新启动游戏的方法,初始化所有属性。
def restart(surface):
global running, state1, state2, new_buff, level, score, life_number, bomb_number, enemy_time, bullet_buff, \
bullet_buff_time, defence_buff_time
surface.fill(BLACK)
all_sprites.empty()
enemies.empty()
player_bullets.empty()
enemy_bullets.empty()
running = True
state1 = True
state2 = False
new_buff = False
level = 0
score = 0
life_number = 3
bomb_number = 3
enemy_time = 0
bullet_buff_time = 0
defence_buff_time = 0
main()
简单测试了一下,还存在一些问题:
1、所有敌机子弹攻击都被限定为100了,这里优化一下,改成子弹的基本伤害值加上不同飞机的10%攻击加成。
2、结束界面增加最终成绩,并提示按r键重新开始。
3、boss机出场机制,设定新的自定义事件,事件设定为150秒,将随机出现蓝色或者绿色boss机。测试了一下,玩家火力太猛了,增加boss机防御值为10000和20000,并随level增加每次增加10000。
4、前面敌机刷新有点问题,限定敌机刷新时间,最低不能小于500ms。
5、简单写了个level >=2的,和1基本一样,可以有想法时候增加其它游戏内容。
6、简单写了玩家飞机射击声音、和敌机爆炸声音以及背景音乐。
7、其它部分的代码优化调整。
最后完整代码:
import random
import sys
import os
import pygame
import time
pygame.init()
pygame.mixer.init()
screen_info_height = pygame.display.Info().current_h
shoot_sound = pygame.mixer.Sound(os.path.join('sound', 'shoot_sound.mp3'))
bomb_sound = pygame.mixer.Sound(os.path.join('sound', 'bomb_sound.mp3'))
HEIGHT = int(screen_info_height * 0.9)
WIDTH = int(screen_info_height * 0.9 * 2 / 3)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
FPS = 60
game_window = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
running = True
state1 = True
state2 = False
new_buff = False
boss_time = False
level = 0
score = 0
life_number = 3
bomb_number = 3
enemy_time = 0
bullet_buff_time = 0
defence_buff_time = 0
player_plane = pygame.image.load(os.path.join('image', 'my_plane.png'))
player_plane_mini = pygame.transform.scale(player_plane, (30, 20))
player_bullet = pygame.image.load(os.path.join('image', 'player_bullet1.png'))
enemy_grey_plane = pygame.image.load(os.path.join('image', 'enemy_grey_plane.png'))
enemy_orange_plane = pygame.image.load(os.path.join('image', 'enemy_orange_plane.png'))
enemy_red_plane = pygame.image.load(os.path.join('image', 'enemy_red_plane.png'))
enemy_blue_plane = pygame.image.load(os.path.join('image', 'enemy_blue_plane.png'))
enemy_green_plane = pygame.image.load(os.path.join('image', 'enemy_green_plane.png'))
enemy_bullet = pygame.image.load(os.path.join('image', 'enemy_bullet1.png'))
new_life = pygame.image.load(os.path.join('image', 'new_life.png'))
defence_buff = pygame.image.load((os.path.join('image', 'defence_buff.png')))
bullet_buff = pygame.image.load(os.path.join('image', 'bullet_buff.png'))
bomb_buff = pygame.image.load(os.path.join('image', 'bomb_buff.png'))
bomb_buff_mini = pygame.transform.scale(bomb_buff, (12, 20))
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
player_bullets = pygame.sprite.Group()
enemy_bullets = pygame.sprite.Group()
buff_group = pygame.sprite.Group()
pygame.time.set_timer(pygame.USEREVENT + 1, 30 * 1000)
pygame.time.set_timer(pygame.USEREVENT + 2, 150 * 1000)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(player_plane, (WIDTH * 0.09, WIDTH * 0.09 * 2 / 3))
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.centery = HEIGHT - 50
self.speed = 10
self.hp = 100
self.defense = 300
self.last_shoot = 0
self.shoot_reload = 100
self.damage = 500
def update(self):
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_UP] and self.rect.top >= 50:
self.rect.y -= self.speed
if key_pressed[pygame.K_DOWN] and self.rect.bottom <= HEIGHT:
self.rect.y += self.speed
if key_pressed[pygame.K_LEFT] and self.rect.left >= -5:
self.rect.x -= self.speed
if key_pressed[pygame.K_RIGHT] and self.rect.right <= WIDTH + 5:
self.rect.x += self.speed
if key_pressed[pygame.K_SPACE]:
now_shoot_time = pygame.time.get_ticks()
if now_shoot_time - self.last_shoot >= self.shoot_reload:
self.shoot()
self.last_shoot = now_shoot_time
shoot_sound.set_volume(0.3)
shoot_sound.play()
def shoot(self):
bullet = Bullet('player_bullet', self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
player_bullets.add(bullet)
if int(time.time()) <= 20 + int(bullet_buff_time):
bullet1 = Bullet('player_bullet', self.rect.centerx - 20, self.rect.top)
bullet2 = Bullet('player_bullet', self.rect.centerx + 20, self.rect.top)
all_sprites.add(bullet1, bullet2)
player_bullets.add(bullet1, bullet2)
class Enemy(pygame.sprite.Sprite):
enemy_plane_dic = {
'grey': [enemy_grey_plane, 5, 100, 4000],
'orange': [enemy_orange_plane, 4, 200, 3000],
'red': [enemy_red_plane, 3, 400, 2000],
'blue': [enemy_blue_plane, 2, 10000, 1000],
'green': [enemy_green_plane, 2, 20000, 1000]
}
def __init__(self, color):
pygame.sprite.Sprite.__init__(self)
self.color = color
if self.color == 'blue' or self.color == 'green':
self.image = pygame.transform.scale(self.enemy_plane_dic[self.color][0], (WIDTH * 0.5, WIDTH * 0.5 * 2 / 3))
else:
self.image = pygame.transform.scale(self.enemy_plane_dic[self.color][0], (WIDTH * 0.1, WIDTH * 0.1 * 2 / 3))
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(5, 10)
self.speedx = random.randrange(-3, 3)
self.speedy = self.enemy_plane_dic[self.color][1]
self.hp = 100
if self.color == 'blue' or self.color == 'green':
self.defense = self.enemy_plane_dic[self.color][2] + 10000 * level
else:
self.defense = self.enemy_plane_dic[self.color][2]
self.damage = self.enemy_plane_dic[self.color][2]
self.score = int(self.enemy_plane_dic[self.color][2] / 10)
self.last_shoot = 0
self.shoot_reload = self.enemy_plane_dic[self.color][3]
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
blood_bar(game_window, self.hp, self.rect.x, self.rect.y - 20, self.rect.width, 3)
now_shoot_time = pygame.time.get_ticks()
if now_shoot_time - self.last_shoot >= self.shoot_reload:
self.shoot()
self.last_shoot = now_shoot_time
if self.rect.left <= 0 or self.rect.right >= WIDTH:
self.speedx = -self.speedx
if self.rect.bottom >= HEIGHT or self.rect.top <= 0:
if self.color == 'blue' or self.color == 'green':
self.speedy = -self.speedy
else:
self.kill()
def shoot(self):
bullet = Bullet('enemy_bullet', self.rect.centerx, self.rect.bottom)
bullet.damage = bullet.damage + int(self.damage / 10)
all_sprites.add(bullet)
enemy_bullets.add(bullet)
class Bullet(pygame.sprite.Sprite):
bullet_dict = {
'player_bullet': [player_bullet, 200, -15],
'enemy_bullet': [enemy_bullet, 20, 10]
}
def __init__(self, bullet_type, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = self.bullet_dict[bullet_type][0]
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
self.damage = self.bullet_dict[bullet_type][1]
self.speed = self.bullet_dict[bullet_type][2]
def update(self):
self.rect.centery += self.speed
if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
self.kill()
class Buff(pygame.sprite.Sprite):
buff_dict = {
'bullet_buff': bullet_buff,
'bomb_buff': bomb_buff,
'new_life': new_life,
'defence_buff': defence_buff
}
def __init__(self, buff_type):
pygame.sprite.Sprite.__init__(self)
self.buff_type = buff_type
self.image = self.buff_dict[self.buff_type]
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = 10
self.speedx = random.randrange(-3, 3)
self.speedy = 5
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.left <= 0 or self.rect.right >= WIDTH:
self.speedx = -self.speedx
if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
self.speedy = - self.speedy
class Explosion:
def __init__(self, surface, x, y):
self.explosions = []
self.last_bom = 0
self.bom_reload = 200
self.surface = surface
for i in range(6):
self.image = pygame.image.load(os.path.join('image', 'bomb' + str(i + 1) + '.png'))
self.explosions.append(self.image)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def explosion(self):
now_time = pygame.time.get_ticks()
if now_time - self.last_bom >= self.bom_reload:
for i in range(len(self.explosions)):
self.surface.blit(self.explosions[i], (self.rect.x, self.rect.y))
self.last_bom = now_time
class BackGround:
def __init__(self, surface):
self.surface = surface
self.image1 = pygame.transform.scale(pygame.image.load(os.path.join('image', 'bg.png')), (WIDTH, HEIGHT))
self.image2 = pygame.transform.scale(pygame.image.load(os.path.join('image', 'bg.png')), (WIDTH, HEIGHT))
self.rect1 = self.image1.get_rect()
self.rect2 = self.image2.get_rect()
self.rect1.x = 0
self.rect1.y = 0
self.rect2.x = 0
self.rect2.y = - HEIGHT
def background_move(self):
self.surface.blit(self.image1, (self.rect1.x, self.rect1.y))
self.surface.blit(self.image2, (self.rect2.x, self.rect2.y))
self.rect1.y += 1
if self.rect1.top >= HEIGHT:
self.rect1.top = -HEIGHT
self.rect2.y += 1
if self.rect2.top >= HEIGHT:
self.rect2.top = -HEIGHT
def mini_icon(surface):
for i in range(life_number):
surface.blit(player_plane_mini, (WIDTH - 35 - i * 35, 10))
for i in range(bomb_number):
surface.blit(bomb_buff_mini, (WIDTH - 25 - i * 35, 40))
def buff_produce():
global new_buff
if new_buff:
game_buff = Buff(random.choice(['bullet_buff', 'bullet_buff', 'defence_buff', 'new_life', 'bomb_buff']))
all_sprites.add(game_buff)
buff_group.add(game_buff)
new_buff = False
def new_player(player):
if life_number > 0:
player.rect.centerx = WIDTH / 2
player.rect.bottom = HEIGHT - 10
player.hp = 100
player.defense = 500
all_sprites.add(player)
def new_enemy():
global enemy_time, boss_time
now_enemy_time = pygame.time.get_ticks()
if 0 <= score < 1000:
color = random.choice(['grey'])
if 1000 <= score < 2500:
color = random.choice(['grey', 'orange'])
if score >= 2500:
color = random.choice(['grey', 'orange', 'red'])
if now_enemy_time - enemy_time >= max(1100 - level * 100, 500):
enemy = Enemy(color)
all_sprites.add(enemy)
enemies.add(enemy)
enemy_time = now_enemy_time
if boss_time:
enemy = Enemy(random.choice(['blue', 'green']))
all_sprites.add(enemy)
enemies.add(enemy)
boss_time = False
def bomb():
for b in enemy_bullets:
b.kill()
for e in enemies:
bom = Explosion(game_window, e.rect.x, e.rect.y)
bom.explosion()
e.hp -= 2000
if e.hp <= 0:
e.kill()
def draw_text(surface, text, x, y, color, size):
font = pygame.font.Font(os.path.join('font', 'simkai.ttf'), size)
font_surf = font.render(text, True, color)
font_surf_rect = font_surf.get_rect()
font_surf_rect.x = x
font_surf_rect.y = y
surface.blit(font_surf, (x - font_surf_rect.width / 2, y))
def background_music():
pygame.mixer.music.load(os.path.join('sound', 'bgm.mp3'))
pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play(-1)
def start_interface(surface):
if state1:
image1 = pygame.image.load(os.path.join('image', 'begin_button.png'))
image2 = pygame.image.load(os.path.join('image', 'exit_button0.png'))
else:
image1 = pygame.image.load(os.path.join('image', 'begin_button0.png'))
image2 = pygame.image.load(os.path.join('image', 'exit_button.png'))
rect1 = image1.get_rect()
rect1.x = WIDTH / 2 - rect1.width / 2
rect1.y = HEIGHT * 0.25
surface.blit(image1, (rect1.x, rect1.y))
rect2 = image2.get_rect()
rect2.x = WIDTH / 2 - rect2.width / 2
rect2.y = HEIGHT * 0.35
surface.blit(image2, (rect2.x, rect2.y))
draw_text(surface, '按Tab键选择,按Enter键确认', WIDTH / 2, HEIGHT * 0.5, WHITE, 20)
draw_text(surface, '按←↑↓→控制移动飞机', WIDTH / 2, HEIGHT * 0.55, WHITE, 20)
draw_text(surface, '按空格键发射子弹,按b键释放炸弹', WIDTH / 2, HEIGHT * 0.6, WHITE, 20)
def restart(surface):
global running, state1, state2, new_buff, level, score, life_number, bomb_number, enemy_time, bullet_buff, \
bullet_buff_time, defence_buff_time
surface.fill(BLACK)
all_sprites.empty()
enemies.empty()
player_bullets.empty()
enemy_bullets.empty()
running = True
state1 = True
state2 = False
new_buff = False
level = 0
score = 0
life_number = 3
bomb_number = 3
enemy_time = 0
bullet_buff_time = 0
defence_buff_time = 0
main()
def end_interface():
draw_text(game_window, 'GAME OVER', WIDTH / 2, 300, RED, 50)
draw_text(game_window, 'SCORE: ' + str(score), WIDTH / 2, 400, RED, 50)
draw_text(game_window, '按 b 键重新开始游戏', WIDTH / 2, 500, RED, 20)
def blood_bar(surface, hp, x, y, bar_length, bar_width):
hp_max = max(hp, 0)
blood_bar_fill = pygame.Rect(x, y, hp_max / 100 * bar_length, bar_width)
blood_bar_outline = pygame.Rect(x, y, bar_length, bar_width)
if hp_max >= 20:
pygame.draw.rect(surface, GREEN, blood_bar_fill)
else:
pygame.draw.rect(surface, RED, blood_bar_fill)
pygame.draw.rect(surface, WHITE, blood_bar_outline, 1)
def collision_detection(player):
global score, level, life_number, bullet_buff_time, defence_buff_time, bomb_number
hits = pygame.sprite.groupcollide(player_bullets, enemies, True, False, pygame.sprite.collide_mask)
for hit in hits:
if hits[hit][0].defense >= 0:
hits[hit][0].defense -= hit.damage
else:
hits[hit][0].hp -= hit.damage
if hits[hit][0].hp <= 0:
bom = Explosion(game_window, hits[hit][0].rect.x, hits[hit][0].rect.y)
bom.explosion()
hits[hit][0].kill()
bomb_sound.play()
score += hits[hit][0].score
level = score // 5000 + 1
hits = pygame.sprite.spritecollide(player, enemy_bullets, True, pygame.sprite.collide_mask)
for hit in hits:
if player.defense >= 0:
player.defense -= hit.damage
else:
player.hp -= hit.damage
if player.hp <= 0:
bom = Explosion(game_window, player.rect.x, player.rect.y)
bom.explosion()
player.kill()
new_player(player)
life_number -= 1
hits = pygame.sprite.spritecollide(player, enemies, False, pygame.sprite.collide_mask)
for hit in hits:
if hit.defense >= 0:
hit.defense -= player.damage
else:
hit.hp -= player.damage
if hit.hp <= 0:
bom = Explosion(game_window, hit.rect.x, hit.rect.y)
bom.explosion()
hit.kill()
bomb_sound.play()
score += hit.score
if player.defense >= 0:
player.defense -= hit.damage
else:
player.hp -= hit.damage
if player.hp <= 0:
bom = Explosion(game_window, player.rect.x, player.rect.y)
bom.explosion()
player.kill()
new_player(player)
life_number -= 1
hits = pygame.sprite.spritecollide(player, buff_group, True)
for hit in hits:
if hit.buff_type == 'bullet_buff':
bullet_buff_time = time.time()
if hit.buff_type == 'bomb_buff':
if bomb_number <= 5:
bomb_number += 1
if hit.buff_type == 'new_life' and life_number <= 5:
life_number += 1
if hit.buff_type == 'defence_buff':
player.defense += 3000
def control():
global running, state1, state2, level, new_buff, bomb_number, boss_time
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.USEREVENT + 1:
new_buff = True
if event.type == pygame.USEREVENT +2:
boss_time = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_TAB:
state1, state2 = state2, state1
if event.key == pygame.K_r and life_number <= 0:
restart(game_window)
if event.key == pygame.K_RETURN and state1 and level == 0:
level = 1
if event.key == pygame.K_RETURN and state2 and level == 0:
running = False
if event.key == pygame.K_b and life_number > 0 and bomb_number > 0:
bomb()
bomb_number -= 1
def main():
background = BackGround(game_window)
player = Player()
background_music()
while running:
control()
background.background_move()
if life_number > 0:
if level == 0:
start_interface(game_window)
if level == 1:
blood_bar(game_window, player.hp, 10, 40, 150, 10)
mini_icon(game_window)
new_enemy()
buff_produce()
all_sprites.add(player)
all_sprites.draw(game_window)
all_sprites.update()
collision_detection(player)
draw_text(game_window, 'SCORE: ' + str(score), 70, 10, RED, 25)
draw_text(game_window, '第一关', WIDTH / 2, 300, RED, 30)
if level >= 2:
blood_bar(game_window, player.hp, 10, 40, 150, 10)
mini_icon(game_window)
new_enemy()
buff_produce()
all_sprites.add(player)
all_sprites.draw(game_window)
all_sprites.update()
collision_detection(player)
draw_text(game_window, 'SCORE: ' + str(score), 70, 10, RED, 25)
draw_text(game_window, '第' + str(level) + '关', WIDTH / 2, 300, RED, 30)
else:
background.background_move()
end_interface()
clock.tick(FPS)
pygame.display.set_caption('飞机游戏 ' + 'FPS: ' + '{0:0.2F}'.format(clock.get_fps()))
pygame.display.update()
pygame.quit()
sys.exit()
if __name__ == '__main__':
main()
补几张游戏测试图片,飞机等元素图片都 来自网络,仅供个人学习用哦!