当前位置:网站首页>太空射击第14课: 玩家生命
太空射击第14课: 玩家生命
2022-07-28 18:26:00 【acktomas】
太空射击第14课: 玩家生命
这是我们“Shmup”项目的第11部分。如果您尚未通读前面的部分,请从第 1 部分开始。在本课中,我们将为玩家添加多个生命值,并在玩家死亡时添加一个漂亮的爆炸。
视频
您可以在此处观看本课程的视频:
玩家爆炸
我们将使用不同的爆炸动画来表示玩家的死亡,来自相同的Kenny Game Art包,称为“声波爆炸”。
单击此处下载这些图像的压缩包。
我们只需要加载这些框架,就像我们为其他爆炸所做的那样。我们只需添加另一个名为'player'
爆炸类型并将其加载到同一循环中,因为它具有与我们正在加载的其他爆炸相同的帧数。现在我们的加载代码将如下所示:
explosion_anim = {
}
explosion_anim['lg'] = []
explosion_anim['sm'] = []
explosion_anim['player'] = []
for i in range(9):
filename = 'regularExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
img_lg = pygame.transform.scale(img, (75, 75))
explosion_anim['lg'].append(img_lg)
img_sm = pygame.transform.scale(img, (32, 32))
explosion_anim['sm'].append(img_sm)
filename = 'sonicExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
explosion_anim['player'].append(img)
我们不需要在Explosion
精灵类中更改任何内容,因此我们只需要在玩家的护盾用完时创建一个玩家爆炸。我们可以在游戏循环中添加这一点,在该循环中,我们将检查玩家与流星碰撞:
# check to see if a mob hit the player
hits = pygame.sprite.spritecollide(player, mobs, True, pygame.sprite.collide_circle)
for hit in hits:
player.shield -= hit.radius * 2
expl = Explosion(hit.rect.center, 'sm')
all_sprites.add(expl)
newmob()
if player.shield <= 0:
death_explosion = Explosion(player.rect.center, 'player')
all_sprites.add(death_explosion)
running = False
但是,如果您运行该程序,您会发现我们现在遇到了一个问题:当玩家死亡时,我们设置running
为False
游戏结束,然后我们才有机会看到我们漂亮的爆炸!
为了解决这个问题,我们需要在爆炸结束之前不要结束游戏。因此,我们删除玩家,但在爆炸消失之前不会设置running
:
if player.shield <= 0:
death_explosion = Explosion(player.rect.center, 'player')
all_sprites.add(death_explosion)
player.kill()
# if the player died and the explosion has finished playing
if not player.alive() and not death_explosion.alive():
running = False
alive()
函数仅返回特定子画面是否处于活动状态。由于我们在它完成游戏时kill()
爆炸,我们现在可以在爆炸完成时结束游戏。
生命
现在我们要给玩家多重生命。我们可以使用变量跟踪它,但我们也希望能够在屏幕上显示它。与其只显示一个数字,不如使用玩家飞船的较小图像来指示还剩下多少条生命,这样看起来会更好。首先,我们将创建较小的映像:
player_img = pygame.image.load(path.join(img_dir, "playerShip1_orange.png")).convert()
player_mini_img = pygame.transform.scale(player_img, (25, 19))
player_mini_img.set_colorkey(BLACK)
现在,我们将向该Player
类添加一些新参数,__init__()
:一个生命计数器,一个标志(可以是True
或False
的变量)来隐藏/显示玩家,以及一个计时器来控制我们保持玩家隐藏多长时间:
self.lives = 3
self.hidden = False
self.hide_timer = pygame.time.get_ticks()
现在,当玩家死亡时,我们将隐藏玩家并从 中减去 1,而不是使用kill()
。我们将lives
减1,为新的生命重置了盾牌:
if player.shield <= 0:
death_explosion = Explosion(player.rect.center, 'player')
all_sprites.add(death_explosion)
player.hide()
player.lives -= 1
player.shield = 100
# if the player died and the explosion has finished playing
if player.lives == 0 and not death_explosion.alive():
running = False
接下来,我们需要定义如何隐藏玩家。回到Player()
类,我们将添加一个隐藏方法,该方法将标志hidden
设置为True
并启动计时器。我们还需要确保在玩家隐藏时,它不会被流星击中。我们有几种方法可以做到这一点,但是一个不需要从组中添加/删除等的简单方法是将玩家暂时移出屏幕底部:
def hide(self):
# hide the player temporarily
self.hidden = True
self.hide_timer = pygame.time.get_ticks()
self.rect.center = (WIDTH / 2, HEIGHT + 200)
在玩家的update()
方法中,我们需要经过足够的时间取消隐藏(我们现在使用1秒):
def update(self):
# unhide if hidden
if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000:
self.hidden = False
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 10
生命计数器显示
为了显示生命,我们将创建一个类似于我们的draw_shield_bar()
函数,它将允许我们将生命计数器放置在给定的位置:
def draw_lives(surf, x, y, lives, img):
for i in range(lives):
img_rect = img.get_rect()
img_rect.x = x + 30 * i
img_rect.y = y
surf.blit(img, img_rect)
我们只是循环了'生命数'
次,然后将每个图像间隔30像素(player_mini_img
宽度为25像素,因此它们之间会留下一个小空间)。
然后,在游戏循环的 draw
部分添加对此函数的调用:
draw_lives(screen, WIDTH - 100, 5, player.lives,
player_mini_img)
现在我们可以看到最终结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VTtA1UTt-1658991354598)(http://kidscancode.org/blog/img/shmup_part11_1.gif)]
在下一课中,我们将为游戏添加一些道具。
此部分的完整代码
# KidsCanCode - Game Development with Pygame video series
# Shmup game - part 11
# Video link: https://www.youtube.com/watch?v=G5-4nV6LxgU
# Explosions
# Frozen Jam by tgfcoder <https://twitter.com/tgfcoder> licensed under CC-BY-3
# Art from Kenney.nl
import pygame
import random
from os import path
img_dir = path.join(path.dirname(__file__), 'img')
snd_dir = path.join(path.dirname(__file__), 'snd')
WIDTH = 480
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
# initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Shmup!")
clock = pygame.time.Clock()
font_name = pygame.font.match_font('arial')
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.midtop = (x, y)
surf.blit(text_surface, text_rect)
def newmob():
m = Mob()
all_sprites.add(m)
mobs.add(m)
def draw_shield_bar(surf, x, y, pct):
if pct < 0:
pct = 0
BAR_LENGTH = 100
BAR_HEIGHT = 10
fill = (pct / 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, x, y, lives, img):
for i in range(lives):
img_rect = img.get_rect()
img_rect.x = x + 30 * i
img_rect.y = y
surf.blit(img, img_rect)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(player_img, (50, 38))
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.radius = 20
# pygame.draw.circle(self.image, RED, self.rect.center, self.radius)
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
self.shield = 100
self.shoot_delay = 250
self.last_shot = pygame.time.get_ticks()
self.lives = 3
self.hidden = False
self.hide_timer = pygame.time.get_ticks()
def update(self):
# unhide if hidden
if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000:
self.hidden = False
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -8
if keystate[pygame.K_RIGHT]:
self.speedx = 8
if keystate[pygame.K_SPACE]:
self.shoot()
self.rect.x += self.speedx
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shot > self.shoot_delay:
self.last_shot = now
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
shoot_sound.play()
def hide(self):
# hide the player temporarily
self.hidden = True
self.hide_timer = pygame.time.get_ticks()
self.rect.center = (WIDTH / 2, HEIGHT + 200)
class Mob(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image_orig = random.choice(meteor_images)
self.image_orig.set_colorkey(BLACK)
self.image = self.image_orig.copy()
self.rect = self.image.get_rect()
self.radius = int(self.rect.width * .85 / 2)
# pygame.draw.circle(self.image, RED, self.rect.center, self.radius)
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.bottom = random.randrange(-80, -20)
self.speedy = random.randrange(1, 8)
self.speedx = random.randrange(-3, 3)
self.rot = 0
self.rot_speed = random.randrange(-8, 8)
self.last_update = pygame.time.get_ticks()
def rotate(self):
now = pygame.time.get_ticks()
if now - self.last_update > 50:
self.last_update = now
self.rot = (self.rot + self.rot_speed) % 360
new_image = pygame.transform.rotate(self.image_orig, self.rot)
old_center = self.rect.center
self.image = new_image
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
self.rotate()
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.top > HEIGHT + 10 or self.rect.left < -100 or self.rect.right > WIDTH + 100:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
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.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
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 = explosion_anim[self.size][0]
self.rect = self.image.get_rect()
self.rect.center = center
self.frame = 0
self.last_update = pygame.time.get_ticks()
self.frame_rate = 75
def update(self):
now = pygame.time.get_ticks()
if now - self.last_update > self.frame_rate:
self.last_update = now
self.frame += 1
if self.frame == len(explosion_anim[self.size]):
self.kill()
else:
center = self.rect.center
self.image = explosion_anim[self.size][self.frame]
self.rect = self.image.get_rect()
self.rect.center = center
# Load all game graphics
background = pygame.image.load(path.join(img_dir, "starfield.png")).convert()
background_rect = background.get_rect()
player_img = pygame.image.load(path.join(img_dir, "playerShip1_orange.png")).convert()
player_mini_img = pygame.transform.scale(player_img, (25, 19))
player_mini_img.set_colorkey(BLACK)
bullet_img = pygame.image.load(path.join(img_dir, "laserRed16.png")).convert()
meteor_images = []
meteor_list = ['meteorBrown_big1.png', 'meteorBrown_med1.png', 'meteorBrown_med1.png',
'meteorBrown_med3.png', 'meteorBrown_small1.png', 'meteorBrown_small2.png',
'meteorBrown_tiny1.png']
for img in meteor_list:
meteor_images.append(pygame.image.load(path.join(img_dir, img)).convert())
explosion_anim = {}
explosion_anim['lg'] = []
explosion_anim['sm'] = []
explosion_anim['player'] = []
for i in range(9):
filename = 'regularExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
img_lg = pygame.transform.scale(img, (75, 75))
explosion_anim['lg'].append(img_lg)
img_sm = pygame.transform.scale(img, (32, 32))
explosion_anim['sm'].append(img_sm)
filename = 'sonicExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
explosion_anim['player'].append(img)
# Load all game sounds
shoot_sound = pygame.mixer.Sound(path.join(snd_dir, 'pew.wav'))
expl_sounds = []
for snd in ['expl3.wav', 'expl6.wav']:
expl_sounds.append(pygame.mixer.Sound(path.join(snd_dir, snd)))
player_die_sound = pygame.mixer.Sound(path.join(snd_dir, 'rumble1.ogg'))
pygame.mixer.music.load(path.join(snd_dir, 'tgfcoder-FrozenJam-SeamlessLoop.ogg'))
pygame.mixer.music.set_volume(0.4)
all_sprites = pygame.sprite.Group()
mobs = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(8):
newmob()
score = 0
pygame.mixer.music.play(loops=-1)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
# Update
all_sprites.update()
# check to see if a bullet hit a mob
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
score += 50 - hit.radius
random.choice(expl_sounds).play()
expl = Explosion(hit.rect.center, 'lg')
all_sprites.add(expl)
newmob()
# check to see if a mob hit the player
hits = pygame.sprite.spritecollide(player, mobs, True, pygame.sprite.collide_circle)
for hit in hits:
player.shield -= hit.radius * 2
expl = Explosion(hit.rect.center, 'sm')
all_sprites.add(expl)
newmob()
if player.shield <= 0:
player_die_sound.play()
death_explosion = Explosion(player.rect.center, 'player')
all_sprites.add(death_explosion)
player.hide()
player.lives -= 1
player.shield = 100
# if the player died and the explosion has finished playing
if player.lives == 0 and not death_explosion.alive():
running = False
# Draw / render
screen.fill(BLACK)
screen.blit(background, background_rect)
all_sprites.draw(screen)
draw_text(screen, str(score), 18, WIDTH / 2, 10)
draw_shield_bar(screen, 5, 5, player.shield)
draw_lives(screen, WIDTH - 100, 5, player.lives, player_mini_img)
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()
第12部分:通电
边栏推荐
- Quick sort template
- Install keras, tensorflow, and add the virtual environment to the Jupiter notebook
- Wust-ctf2021-re school match WP
- User, user group related operations
- Explain RESNET residual network in detail
- Raspberry Pie 3 connected to WiFi
- Use of DDR3 (axi4) in Xilinx vivado (5) board test
- Raspberry pie uses the command line to configure WiFi connections
- Commands related to obtaining administrator permissions
- Vivado design single cycle CPU
猜你喜欢
【CodeForces】Educational Codeforces Round 132 (Rated for Div. 2)
Linxu 【基本指令】
Configure Windows Server + install MySQL database on the server + Remote Access database
Nocturnal simulator settings agent cannot be saved
Linxu 【权限,粘滞位】
Use of DDR3 (axi4) in Xilinx vivado (5) board test
The privatized instant messaging platform protects the security of enterprise mobile business
DSACTF7月re
Scheduled backup of MySQL database under Windows system
CNN convolution neural network learning process (weight update)
随机推荐
Other IPS cannot connect to the local redis problem solving and redis installation
熊市下PLATO如何通过Elephant Swap,获得溢价收益?
Raspberry pie 4B parsing PWM
Using typedef in C language to change the name of data type
Durham High Lord (classic DP)
Related concepts of multitasking programming
[task03: complex query methods - views, subqueries, functions, etc.]
进制及数的表示 2
The privatized instant messaging platform protects the security of enterprise mobile business
读取json配置文件,实现数据驱动测试
Solve the problem that jupyter cannot import new packages
Nocturnal simulator settings agent cannot be saved
C language data 3 (2)
FPGA programming experience
Extract China map from global.Nc data and calculate regional CO2 value based on acgis
Vivado designs PC and ram
Music says
Practice of real-time push demo of three web messages: long polling, iframe and SSE
Solve the problem of adding the least number of parentheses (interval DP)
Raspberry pie 4B ffmpeg RTMP streaming