最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python通過(guò)Pygame實(shí)現(xiàn)一個(gè)簡(jiǎn)化版彈幕射擊躲避游戲

 更新時(shí)間:2026年06月24日 09:10:22   作者:Wang ruoxi  
本文介紹使用用Pygame實(shí)現(xiàn)的彈幕射擊游戲,融合五種彈幕模式與血量系統(tǒng),玩家需通過(guò)鼠標(biāo)操作在滿(mǎn)屏彈幕中生存,游戲難度遞增,提供豐富操作體驗(yà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_RBULLET_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)文章

最新評(píng)論

从化市| 南溪县| 天门市| 清丰县| 瓦房店市| 潜山县| 内丘县| 枣阳市| 安阳市| 鲜城| 柘荣县| 米脂县| 和林格尔县| 体育| 留坝县| 大荔县| 读书| 遵化市| 五常市| 台中市| 昭平县| 芷江| 东源县| 隆子县| 昌吉市| 长白| 尖扎县| 龙岩市| 桐柏县| 噶尔县| 怀柔区| 行唐县| 武清区| 科技| 锦屏县| 嘉鱼县| 公安县| 公安县| 乐陵市| 通州市| 海口市|