Python通過(guò)Pygame實(shí)現(xiàn)一個(gè)簡(jiǎn)化版彈幕射擊躲避游戲
項(xiàng)目概述
本文通過(guò) Pygame 實(shí)現(xiàn)一個(gè)簡(jiǎn)化版彈幕射擊躲避游戲(Bullet Hell)。
游戲以鼠標(biāo)控制玩家小點(diǎn),在持續(xù)涌來(lái)的滿(mǎn)屏彈幕中生存盡可能長(zhǎng)的時(shí)間。彈幕按階段切換模式,難度隨生存時(shí)間線性提升,血量耗盡即游戲結(jié)束。其中:
- 鼠標(biāo)操控:玩家小點(diǎn)實(shí)時(shí)跟隨鼠標(biāo)位置移動(dòng),操作直覺(jué)自然,無(wú)需學(xué)習(xí)成本。
- 五種彈幕模式:散射、追蹤、旋轉(zhuǎn)、扇形、密集,每隔約 5 秒自動(dòng)切換,節(jié)奏變化豐富。
- 判定碰撞:玩家判定圓半徑僅 8px,子彈半徑 5px,碰撞采用圓心距離精確計(jì)算,手感寬松不誤判。
- 無(wú)敵幀機(jī)制:受傷后有 90 幀(約 1.5 秒)無(wú)敵時(shí)間,玩家有機(jī)會(huì)脫離危險(xiǎn)區(qū)域。
- 血量系統(tǒng):5 點(diǎn) HP,以圓形圖標(biāo)逐個(gè)展示,低血量時(shí)視覺(jué)警示明顯。
- 變速機(jī)制:子彈基礎(chǔ)速度 2.5px/幀,隨分?jǐn)?shù)線性遞增(每分 +0.008px/幀),越到后期越考驗(yàn)走位。
- 鍵盤(pán)操作:鼠標(biāo)點(diǎn)擊:開(kāi)始/重新開(kāi)始;ESC:退出。
游戲?qū)崿F(xiàn)

初始化與基礎(chǔ)設(shè)置
游戲啟動(dòng)時(shí)初始化 Pygame,定義窗口尺寸和顏色常量。
pygame.init() W, H = 600, 680 FPS = 60 PLAYER_R = 8 BULLET_R = 5 INVINCIBLE_TIME = 90 # 幀
PLAYER_R 和 BULLET_R 分別定義玩家與子彈的碰撞半徑,兩者之和(13px)即為觸發(fā)碰撞的最大圓心間距。INVINCIBLE_TIME = 90 對(duì)應(yīng) 60FPS 下約 1.5 秒的無(wú)敵窗口,保證受擊后有足夠時(shí)間脫身。
顏色定義
C_BG = (8, 5, 20) C_PLAYER = (80, 220, 255) C_BULLET = [(255, 80, 120), (255, 180, 50), (120, 255, 150), (180, 100, 255)] C_HP = (80, 200, 120) C_HP_LOW = (220, 80, 60) C_SHIELD = (100, 180, 255) C_STAR = (255, 240, 100)
整體延續(xù)深色宇宙風(fēng)格:接近純黑的深藍(lán)背景搭配高飽和霓虹色子彈,四種彈幕顏色隨階段輪換,讓玩家在混亂中仍能直覺(jué)分辨當(dāng)前彈幕模式;玩家以冷藍(lán)色呈現(xiàn),在暖色彈幕中突出醒目。
子彈類(lèi)設(shè)計(jì)
class Bullet:
def __init__(self, x, y, vx, vy, color_idx=0, r=BULLET_R):
self.x, self.y = float(x), float(y)
self.vx, self.vy = vx, vy
self.color = C_BULLET[color_idx % len(C_BULLET)]
self.r = r
self.trail = []
def update(self):
self.trail.append((self.x, self.y))
if len(self.trail) > 6:
self.trail.pop(0)
self.x += self.vx
self.y += self.vy
def alive(self):
return -50 < self.x < W+50 and -50 < self.y < H+50
trail 列表保存最近 6 幀的歷史坐標(biāo),形成拖尾軌跡。每幀先追加當(dāng)前坐標(biāo)再移動(dòng),保證拖尾始終落后于彈頭。alive() 在邊界外 50px 才判定為失效,避免子彈在畫(huà)面邊緣突然消失的視覺(jué)跳變。
子彈渲染
def draw(self, surf):
for i, (tx, ty) in enumerate(self.trail):
a = int(40 * (i+1) / len(self.trail))
r = max(1, self.r - 2)
s = pygame.Surface((r*2+1, r*2+1), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, a), (r, r), r)
surf.blit(s, (tx-r, ty-r))
pygame.draw.circle(surf, self.color, (int(self.x), int(self.y)), self.r)
pygame.draw.circle(surf, (255,255,255,200), (int(self.x), int(self.y)), max(1, self.r-2), 1)
拖尾透明度按序號(hào)線性遞增(最老的幀 alpha 最低),形成漸隱效果。彈頭上額外疊加一圈白色高光描邊,使子彈在任意背景色下保持清晰可辨的立體感。
粒子系統(tǒng)
class Particle:
def __init__(self, x, y, color=(255,255,255)):
self.x, self.y = x, y
a = random.uniform(0, math.pi*2)
s = random.uniform(1, 5)
self.vx, self.vy = math.cos(a)*s, math.sin(a)*s
self.life = 1.0
self.color = color
self.r = random.randint(2, 5)
def update(self):
self.x += self.vx
self.y += self.vy
self.vx *= 0.92
self.vy *= 0.92
self.life -= 0.03
return self.life > 0
粒子初速度方向隨機(jī)均勻分布于整個(gè)圓周(全向爆炸感),速度大小 1–5px/幀。每幀對(duì)速度分量乘以摩擦系數(shù) 0.92,模擬空氣阻力減速,使粒子在靠近中心時(shí)自然剎車(chē)而非驟停。life 約 33 幀(0.55 秒)歸零,同步控制透明度淡出。
受傷時(shí)一次性生成 20 顆紅色粒子,數(shù)量與顏色共同強(qiáng)調(diào)沖擊感:
for _ in range(20):
particles.append(Particle(px, py, (255, 100, 100)))
五種彈幕模式
散射(phase 0)
def spawn_scatter(cx, cy, count, speed, ci):
bullets = []
for i in range(count):
a = (2*math.pi*i/count)
bullets.append(Bullet(cx, cy, math.cos(a)*speed, math.sin(a)*speed, ci))
return bullets
以固定角度間隔在 360° 內(nèi)均勻散射 count 顆子彈,從畫(huà)面隨機(jī)邊緣點(diǎn)發(fā)射。發(fā)射源位置隨機(jī)、圓心均勻,玩家需時(shí)刻注意來(lái)球方向。隨分?jǐn)?shù)提升 count 增大(8 + score//60),后期散射圈數(shù)更密。
追蹤扇形(phase 1)
def spawn_aimed(px, py, ox, oy, speed, spread=3, ci=0):
dx, dy = px-ox, py-oy
for s in range(-spread, spread+1):
a = math.atan2(dy, dx) + s * 0.15
bullets.append(Bullet(ox, oy, math.cos(a)*speed, math.sin(a)*speed, ci))
以 atan2 實(shí)時(shí)計(jì)算玩家方向角,在此基礎(chǔ)上以 0.15 弧度(約 8.6°)間隔生成 2*spread+1 顆子彈構(gòu)成扇形。追蹤彈從屏幕左右兩側(cè)發(fā)射,迫使玩家保持橫向走位。
旋轉(zhuǎn)(phase 2)
spin_angle += 0.3
for j in range(3):
a = spin_angle + j * 2.1
bullets.append(Bullet(W//2, H//2, math.cos(a)*spd, math.sin(a)*spd, ci))
從畫(huà)面中心每幀旋轉(zhuǎn) 0.3 弧度(約 17°/幀),以 120° 間隔同時(shí)發(fā)射 3 顆,形成旋轉(zhuǎn)三叉星彈幕。spin_angle 在主循環(huán)中持續(xù)累加,彈幕軌跡不斷螺旋外擴(kuò),玩家須繞圈走位。
頂部扇形(phase 3)
def spawn_fan(cx, cy, base_angle, count, spread, speed, ci):
for i in range(count):
a = base_angle - spread/2 + spread*i/(max(1,count-1))
bullets.append(Bullet(cx, cy, math.cos(a)*speed, math.sin(a)*speed, ci))
在頂部隨機(jī) x 坐標(biāo)向下發(fā)射扇形子彈群,基準(zhǔn)角加上隨機(jī)偏轉(zhuǎn)(±0.5 弧度),每次出現(xiàn)方向略有不同。扇形寬度 1.2 弧度、7 顆子彈,覆蓋約 69° 的扇面,迫使玩家左右機(jī)動(dòng)躲避。
密集追蹤(phase 4)
edge = random.randint(0, 3)
# 根據(jù) edge 決定四條邊上的隨機(jī)發(fā)射點(diǎn)
dx, dy = px-cx, py-cy
a = math.atan2(dy, dx)
for k in range(-1, 2):
bullets.append(Bullet(cx, cy, math.cos(a+k*0.25)*speed, math.sin(a+k*0.25)*speed, ci))
從四條邊隨機(jī)位置向玩家發(fā)射三聯(lián)彈,0.25 弧度(約 14°)偏角形成小扇面覆蓋,容錯(cuò)空間極小。四邊同時(shí)可能出現(xiàn)發(fā)射源,疊加前期殘余彈幕,后期畫(huà)面彈幕密度最高。
彈幕調(diào)度與難度曲線
phase_timer += 1
if phase_timer > 300 + phase * 20:
phase = (phase + 1) % 5
phase_timer = 0
interval = max(8, 30 - score//30)
spd = 2.5 + score * 0.008
每個(gè)階段持續(xù)時(shí)間隨階段編號(hào)遞增(300、320、340……幀),使前期階段切換更頻繁、后期單一模式持續(xù)更久,考驗(yàn)玩家對(duì)特定彈幕的應(yīng)對(duì)深度。發(fā)射間隔從 30 幀壓縮至最低 8 幀(密度上限約為原來(lái)的 4 倍);速度線性遞增無(wú)上限,理論上游戲難度可以無(wú)限提升。
碰撞檢測(cè)
if math.hypot(b.x-px, b.y-py) < PLAYER_R + b.r - 2:
bullets.remove(b)
hp -= 1
invincible = INVINCIBLE_TIME
...
使用 math.hypot 計(jì)算圓心距,與兩圓半徑之和比較。-2 的容差有意將有效碰撞半徑縮小 2px,給玩家略多的"擦邊而過(guò)"空間,降低誤判帶來(lái)的挫敗感——這是彈幕游戲的通行設(shè)計(jì)慣例。無(wú)敵幀期間完全跳過(guò)碰撞檢測(cè),受擊子彈不被移除(避免無(wú)敵時(shí)無(wú)意"吃掉"更多子彈后重置無(wú)敵計(jì)時(shí))。
玩家渲染與無(wú)敵反饋
pc = C_PLAYER if invincible == 0 else (
C_SHIELD if shield > 0 else
((255,255,255) if (invincible//5)%2 else C_PLAYER)
)
# 光暈
glow = pygame.Surface((PLAYER_R*6, PLAYER_R*6), pygame.SRCALPHA)
pygame.draw.circle(glow, (*pc, 40), (PLAYER_R*3, PLAYER_R*3), PLAYER_R*3)
screen.blit(glow, (px-PLAYER_R*3, py-PLAYER_R*3))
pygame.draw.circle(screen, pc, (px, py), PLAYER_R)
pygame.draw.circle(screen, C_PLAYER_C, (px, py), 3)
無(wú)敵狀態(tài)下玩家每 5 幀切換一次顏色(白色/原色交替),產(chǎn)生規(guī)律閃爍,讓玩家和觀看者清晰感知無(wú)敵持續(xù)時(shí)間。內(nèi)圈疊加淺色中心點(diǎn)強(qiáng)調(diào)判定核心位置,提醒玩家真正需要避開(kāi)的是這個(gè)核心。半徑 3 倍的半透明光暈以 alpha=40 疊加,增強(qiáng)玩家在深色背景上的辨識(shí)度。
星空背景
STARS = [(random.randint(0,W), random.randint(0,H), random.random()*2+1) for _ in range(80)]
def draw_stars(surf, t):
for sx, sy, sr in STARS:
a = int((math.sin(t*0.02 + sx) * 0.5 + 0.5) * 180 + 60)
pygame.draw.circle(surf, (a, a, a), (sx, sy), int(sr))
80 顆星點(diǎn)在初始化時(shí)一次性隨機(jī)生成并固定位置,運(yùn)行時(shí)零額外內(nèi)存分配。亮度以 math.sin(t*0.02 + sx) 隨幀數(shù)緩慢變化,每顆星的 sx 作為相位偏移,使星點(diǎn)彼此錯(cuò)開(kāi)閃爍節(jié)奏而非同步。亮度映射到 60–240 區(qū)間,避免全滅或過(guò)曝。
繪制層次
繪制順序?yàn)椋盒强毡尘?→ 粒子 → 子彈(含拖尾)→ 玩家(光暈 + 實(shí)體) → UI(分?jǐn)?shù)/HP/階段提示/最高分)→ 游戲結(jié)束遮罩。嚴(yán)格保證層次正確,玩家始終在子彈之上以便定位,UI 文字不被彈幕遮擋。
HP 圓形圖標(biāo)
for i in range(max_hp):
hc = (C_HP if i < hp else (40, 40, 60))
pygame.draw.circle(screen, hc, (20 + i*26, 24), 10)
if i < hp:
pygame.draw.circle(screen, (200,255,220), (20 + i*26, 24), 4)
以離散圓形代替條狀血條,每格代表一條命,視覺(jué)上比百分比更直觀。現(xiàn)存 HP 內(nèi)疊加淺色中心點(diǎn),視覺(jué)上強(qiáng)調(diào)"滿(mǎn)格"狀態(tài);空格以暗色填充表示消耗,無(wú)需額外文字說(shuō)明。
游戲結(jié)束彈窗
lines = [
(FONT_LG, "GAME OVER", C_BULLET[0], -55),
(FONT_MD, f"生存時(shí)間:{score} 分", C_TEXT, 0),
(FONT_MD, f"最高紀(jì)錄:{best} 分", C_STAR, 35),
(FONT_SM, "點(diǎn)擊鼠標(biāo)重新開(kāi)始", C_GREY, 80),
]
for font, text, color, dy in lines:
t = font.render(text, True, color)
screen.blit(t, t.get_rect(centerx=W//2, centery=H//2+dy))
best 變量在整個(gè)進(jìn)程生命周期內(nèi)保留最高分(死亡后不重置),鼓勵(lì)玩家反復(fù)挑戰(zhàn)自己記錄。彈窗背景以半透明黑色遮罩疊加在游戲畫(huà)面之上而非清空屏幕,讓玩家在結(jié)算界面仍能看到"最后的彈幕",增強(qiáng)現(xiàn)場(chǎng)感。
主循環(huán):
while True:
mx, my = pygame.mouse.get_pos()
dt = clock.tick(FPS) / 1000.0
frame += 1
score = frame // 6
score 直接由幀數(shù)整除 6 換算(60FPS 下約每 0.1 秒 +1 分),避免浮點(diǎn)累加誤差,也使分?jǐn)?shù)增長(zhǎng)速率在任何幀率下保持一致。鼠標(biāo)坐標(biāo)每幀頂部獲取一次,保證玩家位置與系統(tǒng)光標(biāo)嚴(yán)格同步。
全部代碼
"""
躲避彈幕 — 操控小點(diǎn)在滿(mǎn)屏彈幕中生存
操作:鼠標(biāo)移動(dòng)控制玩家,生存越久分越高
彈幕模式隨時(shí)間切換:散射、旋轉(zhuǎn)、追蹤、扇形
"""
import pygame
import sys
import random
import math
import time
pygame.init()
W, H = 600, 680
FPS = 60
C_BG = (8, 5, 20)
C_PLAYER = (80, 220, 255)
C_PLAYER_C = (200, 240, 255)
C_BULLET = [(255, 80, 120), (255, 180, 50), (120, 255, 150), (180, 100, 255)]
C_TEXT = (220, 220, 255)
C_GREY = (100, 100, 140)
C_HP = (80, 200, 120)
C_HP_LOW = (220, 80, 60)
C_SHIELD = (100, 180, 255)
C_STAR = (255, 240, 100)
CHINESE_FONT_PATH = r"C:/Windows/Fonts/simsun.ttc"
FONT_LG = pygame.font.Font(CHINESE_FONT_PATH, 32)
FONT_MD = pygame.font.Font(CHINESE_FONT_PATH, 20)
FONT_SM = pygame.font.Font(CHINESE_FONT_PATH, 15)
PLAYER_R = 8
BULLET_R = 5
INVINCIBLE_TIME = 90 # 幀
SHIELD_TIME = 180
class Bullet:
def __init__(self, x, y, vx, vy, color_idx=0, r=BULLET_R):
self.x, self.y = float(x), float(y)
self.vx, self.vy = vx, vy
self.color = C_BULLET[color_idx % len(C_BULLET)]
self.r = r
self.trail = []
def update(self):
self.trail.append((self.x, self.y))
if len(self.trail) > 6:
self.trail.pop(0)
self.x += self.vx
self.y += self.vy
def alive(self):
return -50 < self.x < W+50 and -50 < self.y < H+50
def draw(self, surf):
for i, (tx, ty) in enumerate(self.trail):
a = int(40 * (i+1) / len(self.trail))
r = max(1, self.r - 2)
s = pygame.Surface((r*2+1, r*2+1), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, a), (r, r), r)
surf.blit(s, (tx-r, ty-r))
pygame.draw.circle(surf, self.color, (int(self.x), int(self.y)), self.r)
pygame.draw.circle(surf, (255,255,255,200), (int(self.x), int(self.y)), max(1, self.r-2), 1)
class Particle:
def __init__(self, x, y, color=(255,255,255)):
self.x, self.y = x, y
a = random.uniform(0, math.pi*2)
s = random.uniform(1, 5)
self.vx, self.vy = math.cos(a)*s, math.sin(a)*s
self.life = 1.0
self.color = color
self.r = random.randint(2, 5)
def update(self):
self.x += self.vx
self.y += self.vy
self.vx *= 0.92
self.vy *= 0.92
self.life -= 0.03
return self.life > 0
def draw(self, surf):
a = int(self.life * 255)
s = pygame.Surface((self.r*2+1, self.r*2+1), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, a), (self.r, self.r), self.r)
surf.blit(s, (int(self.x)-self.r, int(self.y)-self.r))
# ── 彈幕模式 ─────────────────────────────────────────────────────────
def spawn_scatter(cx, cy, count, speed, ci):
bullets = []
for i in range(count):
a = (2*math.pi*i/count)
bullets.append(Bullet(cx, cy, math.cos(a)*speed, math.sin(a)*speed, ci))
return bullets
def spawn_aimed(px, py, ox, oy, speed, spread=3, ci=0):
bullets = []
dx, dy = px-ox, py-oy
dist = math.hypot(dx, dy) or 1
for s in range(-spread, spread+1):
a = math.atan2(dy, dx) + s * 0.15
bullets.append(Bullet(ox, oy, math.cos(a)*speed, math.sin(a)*speed, ci))
return bullets
def spawn_fan(cx, cy, base_angle, count, spread, speed, ci):
bullets = []
for i in range(count):
a = base_angle - spread/2 + spread*i/(max(1,count-1))
bullets.append(Bullet(cx, cy, math.cos(a)*speed, math.sin(a)*speed, ci))
return bullets
# ── 星星背景 ─────────────────────────────────────────────────────────
STARS = [(random.randint(0,W), random.randint(0,H), random.random()*2+1) for _ in range(80)]
def draw_stars(surf, t):
for sx, sy, sr in STARS:
a = int((math.sin(t*0.02 + sx) * 0.5 + 0.5) * 180 + 60)
pygame.draw.circle(surf, (a, a, a), (sx, sy), int(sr))
# ── 主循環(huán) ────────────────────────────────────────────────────────────
def main():
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption("躲避彈幕")
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
bullets = []
particles = []
score = 0
best = 0
hp = 5
max_hp = 5
invincible = 0
shield = 0
frame = 0
game_over = False
started = False
spawn_timer = 0
phase_timer = 0
phase = 0
spin_angle = 0
px, py = W//2, H//2
def reset():
nonlocal bullets, particles, score, hp, invincible, shield
nonlocal frame, game_over, started, spawn_timer, phase_timer, phase, spin_angle
bullets.clear()
particles.clear()
score = 0; hp = max_hp; invincible = 0; shield = 0
frame = 0; game_over = False; started = True
spawn_timer = 0; phase_timer = 0; phase = 0; spin_angle = 0
while True:
mx, my = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.mouse.set_visible(True)
pygame.quit(); sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.mouse.set_visible(True)
pygame.quit(); sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if not started or game_over:
reset()
if started and not game_over:
px, py = mx, my
frame += 1
score = frame // 6
if invincible > 0: invincible -= 1
if shield > 0: shield -= 1
# 切換彈幕階段
phase_timer += 1
if phase_timer > 300 + phase * 20:
phase = (phase + 1) % 5
phase_timer = 0
spawn_timer += 1
interval = max(8, 30 - score//30)
ci = phase % len(C_BULLET)
spd = 2.5 + score * 0.008
if spawn_timer >= interval:
spawn_timer = 0
if phase == 0: # 四面散射
cx, cy = random.choice([W//2, 0, W, 0, W]), random.choice([0, H, H//2, 0, H])
bullets += spawn_scatter(cx, cy, 8+score//60, spd, ci)
elif phase == 1: # 追蹤扇形
ex = random.choice([0, W])
ey = random.randint(100, H-100)
bullets += spawn_aimed(px, py, ex, ey, spd, 2, ci)
elif phase == 2: # 旋轉(zhuǎn)
spin_angle += 0.3
for j in range(3):
a = spin_angle + j * 2.1
bullets.append(Bullet(W//2, H//2, math.cos(a)*spd, math.sin(a)*spd, ci))
elif phase == 3: # 頂部扇形
ca = math.pi/2 + random.uniform(-0.5, 0.5)
bullets += spawn_fan(random.randint(50,W-50), -10, ca, 7+score//80, 1.2, spd, ci)
elif phase == 4: # 全方向密集
edge = random.randint(0, 3)
if edge==0: cx,cy=random.randint(0,W),0
elif edge==1: cx,cy=W,random.randint(0,H)
elif edge==2: cx,cy=random.randint(0,W),H
else: cx,cy=0,random.randint(0,H)
dx,dy=px-cx,py-cy; dist=math.hypot(dx,dy) or 1
a=math.atan2(dy,dx)
for k in range(-1,2):
bullets.append(Bullet(cx,cy, math.cos(a+k*0.25)*spd, math.sin(a+k*0.25)*spd, ci))
# 更新子彈
for b in bullets[:]:
b.update()
if not b.alive():
bullets.remove(b)
elif invincible == 0 and shield == 0:
if math.hypot(b.x-px, b.y-py) < PLAYER_R + b.r - 2:
bullets.remove(b)
hp -= 1
invincible = INVINCIBLE_TIME
for _ in range(20):
particles.append(Particle(px, py, (255,100,100)))
if hp <= 0:
game_over = True
best = max(best, score)
particles = [p for p in particles if p.update()]
# ── 繪制 ──────────────────────────────────────────────────────
screen.fill(C_BG)
draw_stars(screen, frame)
if started:
for p in particles: p.draw(screen)
for b in bullets: b.draw(screen)
# 玩家
if not game_over:
pc = C_PLAYER if invincible == 0 else (
C_SHIELD if shield > 0 else
((255,255,255) if (invincible//5)%2 else C_PLAYER)
)
# 光暈
glow = pygame.Surface((PLAYER_R*6, PLAYER_R*6), pygame.SRCALPHA)
pygame.draw.circle(glow, (*pc, 40), (PLAYER_R*3, PLAYER_R*3), PLAYER_R*3)
screen.blit(glow, (px-PLAYER_R*3, py-PLAYER_R*3))
pygame.draw.circle(screen, pc, (px, py), PLAYER_R)
pygame.draw.circle(screen, C_PLAYER_C, (px, py), 3)
# UI
st = FONT_LG.render(f"{score}", True, C_TEXT)
screen.blit(st, st.get_rect(centerx=W//2, y=8))
# HP
for i in range(max_hp):
hc = (C_HP if i < hp else (40, 40, 60))
pygame.draw.circle(screen, hc, (20 + i*26, 24), 10)
if i < hp:
pygame.draw.circle(screen, (200,255,220), (20 + i*26, 24), 4)
# 階段提示
phase_names = ["散射", "追蹤", "旋轉(zhuǎn)", "扇形", "密集"]
pt = FONT_SM.render(f"模式:{phase_names[phase]}", True, C_GREY)
screen.blit(pt, (W-120, 8))
# 最高分
bt = FONT_SM.render(f"最高:{best}", True, C_GREY)
screen.blit(bt, (W-100, 28))
# 游戲結(jié)束
if game_over:
ov = pygame.Surface((W, H), pygame.SRCALPHA)
ov.fill((0,0,0,150))
screen.blit(ov, (0,0))
box = pygame.Rect(W//2-160, H//2-100, 320, 210)
pygame.draw.rect(screen, (15,10,35), box, border_radius=16)
pygame.draw.rect(screen, C_BULLET[0], box, 2, border_radius=16)
lines = [
(FONT_LG, "GAME OVER", C_BULLET[0], -55),
(FONT_MD, f"生存時(shí)間:{score} 分", C_TEXT, 0),
(FONT_MD, f"最高紀(jì)錄:{best} 分", C_STAR, 35),
(FONT_SM, "點(diǎn)擊鼠標(biāo)重新開(kāi)始", C_GREY, 80),
]
for font, text, color, dy in lines:
t = font.render(text, True, color)
screen.blit(t, t.get_rect(centerx=W//2, centery=H//2+dy))
else:
# 開(kāi)始界面
t1 = FONT_LG.render("? 躲避彈幕", True, C_PLAYER)
t2 = FONT_MD.render("移動(dòng)鼠標(biāo)控制白點(diǎn)", True, C_TEXT)
t3 = FONT_SM.render("點(diǎn)擊鼠標(biāo)開(kāi)始", True, C_GREY)
screen.blit(t1, t1.get_rect(centerx=W//2, centery=H//2-60))
screen.blit(t2, t2.get_rect(centerx=W//2, centery=H//2))
screen.blit(t3, t3.get_rect(centerx=W//2, centery=H//2+50))
pygame.display.flip()
clock.tick(FPS)
if __name__ == "__main__":
main()
以上就是Python通過(guò)Pygame實(shí)現(xiàn)一個(gè)簡(jiǎn)化版彈幕射擊躲避游戲的詳細(xì)內(nèi)容,更多關(guān)于Python Pygame躲避彈幕游戲的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python 運(yùn)用Django 開(kāi)發(fā)后臺(tái)接口的實(shí)例
今天小編就為大家分享一篇python 運(yùn)用Django 開(kāi)發(fā)后臺(tái)接口的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
新手學(xué)習(xí)Python2和Python3中print不同的用法
在本篇文章里小編給大家分享的是關(guān)于Python2和Python3中print不同的用法,有興趣的朋友們可以學(xué)習(xí)下。2020-06-06
操作Windows注冊(cè)表的簡(jiǎn)單的Python程序制作教程
這篇文章主要介紹了操作Windows注冊(cè)表的簡(jiǎn)單的Python程序制作教程,包括遠(yuǎn)程對(duì)注冊(cè)表進(jìn)行修改的實(shí)現(xiàn),需要的朋友可以參考下2015-04-04
Python數(shù)據(jù)分析之分析千萬(wàn)級(jí)淘寶數(shù)據(jù)
網(wǎng)購(gòu)已經(jīng)成為人們生活不可或缺的一部分,本次項(xiàng)目基于淘寶app平臺(tái)數(shù)據(jù),通過(guò)相關(guān)指標(biāo)對(duì)用戶(hù)行為進(jìn)行分析,從而探索用戶(hù)相關(guān)行為模式。感興趣的可以學(xué)習(xí)一下2022-03-03
Python Selenium 設(shè)置元素等待的三種方式
這篇文章主要介紹了Python Selenium 設(shè)置元素等待的三種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
深入淺出Python中反射機(jī)制的原理和實(shí)戰(zhàn)
簡(jiǎn)單來(lái)說(shuō),反射是指程序在運(yùn)行時(shí)(Runtime)能夠檢查、修改自身結(jié)構(gòu)和行為的一種能力,本文介紹了Python反射機(jī)制的原理與應(yīng)用,有需要的小伙伴可以了解下2026-02-02
python單線程實(shí)現(xiàn)多個(gè)定時(shí)器示例
這篇文章主要介紹了python單線程實(shí)現(xiàn)多個(gè)定時(shí)器示例,需要的朋友可以參考下2014-03-03

