import pygame
from sys import exit
import math
import random
pygame.init()
========= 視窗 & 基本 =========
screen = pygame.display.set_mode((1000, 1000))
pygame.display.set_caption(‘Little Hero’)
clock = pygame.time.Clock()
test_font = pygame.font.Font(None, 50)
game_active = True
地圖邊界(用來 clamp)
MAP_RECT = pygame.Rect(0, 0, 1000, 1000)
========= 背景與文字 =========
background_surface = pygame.image.load(‘backgroud10001000.png’)
text_surface = test_font.render(‘Little Hero’, False, ‘Green’)
def load_pic(number,prefix,suffix,storge,anti_storge):
for i in range(1, number):
x = prefix+str(i)+suffix
storge.append(pygame.image.load(x))
anti_storge.append(pygame.transform.flip(pygame.image.load(x),True, False))
========= 怪物動畫 =========
main_monster_walk = []
anti_main_monster_walk = []
load_pic(8,“monster walk/monster walk-export”,“.png”,main_monster_walk,anti_main_monster_walk)
monster_surface = main_monster_walk[0]
main_monster_attack = []
anti_main_monster_attack = []
load_pic(11,“monster attack/monster attack-export”,“.png”,main_monster_attack,anti_main_monster_attack)
========= 玩家攻擊動畫(保留你的素材與偏移做法)=========
main_attack = []
anti_main_attack = []
load_pic(15,“main character attack (huge sword)/main charcterattacking-Recovered”,“.png”,main_attack,anti_main_attack)
main_katana = []
anti_main_katana = []
load_pic(16,“main character katana attack/main character normal katana-export”,“.png”,main_katana,anti_main_katana)
main_flame = []
anti_main_flame = []
load_pic(13,“main character flame katana attack/main character fire katana-export”,“.png”,main_flame,anti_main_flame)
========= 玩家走路動畫 =========
main_walk = []
anti_main_walk = []
load_pic(8,“main charector walk/main walk-export”,“.png”,main_walk,anti_main_walk)
========= 玩家狀態 =========
player_index = 0
player_surface = main_walk[player_index]
player_rect = player_surface.get_rect(midbottom=(200, 500))
speed = 3
speed_monster = 2
player_max_hp = 100
player_hp = player_max_hp
player_iframes = 0 # 玩家受擊後的無敵幀
facing = 1 # 1 右, -1 左(用於攻擊箱朝向)
========= 攻擊判定(獨立 Rect,不等於角色 Rect)=========
attack_cooldown = 0 # 按鍵後冷卻幀
attack_active_frames = 0 # 攻擊箱存在幀數
attack_rect = None
attack_damage = 18
current_attack_cfg = None # 記住這次攻擊使用的偏移/尺寸
========= 浮空傷害數字 =========
class FloatText:
def init(self, text, pos, life=30, color=(255, 80, 80)):
self.text = test_font.render(str(text), True, color)
self.x, self.y = pos
self.life = life
def update(self):
self.y -= 0.8
self.life -= 1
def draw(self, surf):
surf.blit(self.text, (self.x, self.y))
float_texts = []
========= 畫血條(角色頭上/或 UI)=========
def draw_hp_bar(surf, anchor_rect, hp, max_hp, w=60, h=8, offset_y=-10, color=(0, 200, 0)):
pct = 0 if max_hp <= 0 else max(0, min(1, hp / max_hp))
x = anchor_rect.centerx - w // 2
y = anchor_rect.top + offset_y
bg = pygame.Rect(x, y, w, h)
fg = pygame.Rect(x, y, int(w * pct), h)
pygame.draw.rect(surf, (50, 50, 50), bg, border_radius=3)
pygame.draw.rect(surf, color, fg, border_radius=3)
pygame.draw.rect(surf, (0, 0, 0), bg, 2, border_radius=3)
========= 敵人資料結構 =========
enemies = [] # 每個元素: {‘rect’: Rect, ‘hp’:int, ‘knock’:[vx,vy], ‘stun’:int, ‘iframes’:int, ‘atk_*’…}
def spawn_enemy(x, y):
r = monster_surface.get_rect(topleft=(x, y))
enemies.append({
‘rect’: r, ‘hp’: 40,
‘knock’:[0.0, 0.0], ‘stun’:0, ‘iframes’:0,
‘atk_cool’:0, ‘atk_active’:0, ‘atk_range’:48, ‘atk_damage’:10,
‘facing’:1
})
spawn_timer = 0
========= 動畫參數 =========
ANIMATION_SPEED = 4
ANIMATE_speed = 3
frame_count = 4
monster_index = 0
monster_frame_count = 0
========= 攻击判定框(玩家各招式配置) ========
OFFSETS = {
‘basic’: {‘up’: -4, ‘forward’: -16, ‘w’: 80, ‘h’: 44},
‘katana’: {‘up’: -10,‘forward’: -12, ‘w’: 100, ‘h’: 66},
‘flame’: {‘up’: -6, ‘forward’: -22, ‘w’: 140, ‘h’: 88},
}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
# ========== 每幀初始狀態 ========== monster_attack = False # 全局動畫旗標暫時保留(若要每隻怪獨立可再細化) monster_walk = True # ========== 輸入 ========== if game_active: keys = pygame.key.get_pressed() moving, attack, katana, stay, flame = False, False, False, False, False # 攻擊鍵(J/K/L) if keys[pygame.K_j] or keys[pygame.K_k] or keys[pygame.K_l]: if keys[pygame.K_j]: attack = True if keys[pygame.K_k]: katana = True if keys[pygame.K_l]: flame = True else: if keys[pygame.K_d]: player_rect.x += speed moving = True facing = 1 if keys[pygame.K_a]: player_rect.x -= speed moving = True facing = -1 if keys[pygame.K_w]: player_rect.y -= speed moving = True if keys[pygame.K_s]: player_rect.y += speed moving = True # ============= 攻击判定:選招式配置 ============ cfg = None if attack: cfg = OFFSETS['basic'] elif katana: cfg = OFFSETS['katana'] elif flame: cfg = OFFSETS['flame'] else: keys = pygame.key.get_pressed() if keys[pygame.K_r]: game_active = True player_hp = player_max_hp enemies.clear() # ========== 地圖邊界 ========== player_rect.clamp_ip(MAP_RECT) # ========== 生怪 ========== if game_active: spawn_timer += 1 if spawn_timer >= 90: # 大約每 1.5 秒(60fps 假設)刷一隻 side = random.choice(["top", "bottom", "left", "right"]) if side == "top": x, y = random.randint(0, 1000), -30 elif side == "bottom": x, y = random.randint(0, 1000), 1000 + 30 elif side == "left": x, y = -30, random.randint(0, 1000) else: # right x, y = 1000 + 30, random.randint(0, 1000) spawn_enemy(x, y) spawn_timer = 0 # ========== 生成 / 跟隨 攻擊判定 Rect ========== if game_active: # 生成 if cfg and attack_cooldown == 0 and attack_active_frames == 0: current_attack_cfg = cfg w, h = cfg['w'], cfg['h'] forward, up = cfg['forward'], cfg['up'] attack_rect = pygame.Rect(0, 0, w, h) attack_rect.center = ( player_rect.centerx + facing * (player_rect.width // 2 + forward), player_rect.centery + up ) attack_active_frames = 8 attack_cooldown = 20 # 跟隨 if attack_active_frames > 0 and attack_rect: attack_active_frames -= 1 if current_attack_cfg: fwd = current_attack_cfg['forward'] up = current_attack_cfg['up'] w, h = current_attack_cfg['w'], current_attack_cfg['h'] attack_rect.size = (w, h) attack_rect.center = ( player_rect.centerx + facing * (player_rect.width // 2 + fwd), player_rect.centery + up ) else: attack_rect = None current_attack_cfg = None if attack_cooldown > 0: attack_cooldown -= 1 # ========== 更新怪物(硬直 > 攻擊 > 進攻 > 追擊)========= if game_active: for e in enemies[:]: if e['hp'] <= 0: continue # 通用衰減 if e['iframes'] > 0: e['iframes'] -= 1 if e['atk_cool'] > 0: e['atk_cool'] -= 1 # 與玩家相對 dx = player_rect.centerx - e['rect'].centerx dy = player_rect.centery - e['rect'].centery dist = max(1, (dx*dx + dy*dy) ** 0.5) e['facing'] = 1 if dx >= 0 else -1 # ① 硬直/擊退優先 if e['stun'] > 0 or (abs(e['knock'][0]) + abs(e['knock'][1])) > 0.1: e['rect'].x += int(e['knock'][0]) e['rect'].y += int(e['knock'][1]) e['knock'][0] *= 0.8 e['knock'][1] *= 0.8 if e['stun'] > 0: e['stun'] -= 1 # ② 攻擊生效(有獨立攻擊判定框) elif e['atk_active'] > 0: fw, up, w, h = 16, 0, 40, 30 atk_rect = pygame.Rect(0, 0, w, h) atk_rect.center = ( e['rect'].centerx + e['facing'] * (e['rect'].width // 2 + fw), e['rect'].centery + up ) if player_iframes == 0 and atk_rect.colliderect(player_rect): player_hp -= e['atk_damage'] player_iframes = 30 float_texts.append(FloatText(-e['atk_damage'], (player_rect.centerx, player_rect.top - 10), color=(255, 220, 0))) if player_hp <= 0: game_active = False e['atk_active'] -= 1 # 調試可視化:pygame.draw.rect(screen, (255,150,0), atk_rect, 2) # ③ 進入攻擊 elif dist <= e['atk_range'] and e['atk_cool'] == 0: e['atk_active'] = 8 e['atk_cool'] = 30 monster_attack = True # 如果你想全局播放攻擊動畫;若要每隻獨立可改成 e['is_attacking_anim']=True monster_walk = False # ④ 普通追擊 else: ndx, ndy = dx / dist, dy / dist e['rect'].x += int(ndx * speed_monster) e['rect'].y += int(ndy * speed_monster) # 地圖邊界 e['rect'].clamp_ip(MAP_RECT) # ========== 玩家攻擊命中(怪物扣血 + 擊退 + 浮空數字)========= if game_active and attack_rect: for e in enemies: if e['hp'] > 0 and e['iframes'] == 0 and attack_rect.colliderect(e['rect']): e['hp'] -= attack_damage e['iframes'] = 12 # 擊退方向:玩家->怪物 dx = e['rect'].centerx - player_rect.centerx dy = e['rect'].centery - player_rect.centery dist = max(1, (dx*dx + dy*dy) ** 0.5) kb = 8.0 e['knock'] = [kb * dx / dist, kb * dy / dist] e['stun'] = 10 float_texts.append(FloatText(attack_damage, (e['rect'].centerx, e['rect'].top - 10))) # ========== 動畫更新 ========== if game_active: # 玩家 if keys := pygame.key.get_pressed(): if facing == 1: if moving: if player_index >= len(main_walk): player_index = 0 player_surface = main_walk[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(main_walk): player_index = 0 player_surface = main_walk[player_index] elif attack: if player_index >= len(main_attack): player_index = 0 player_surface = main_attack[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(main_attack): player_index = 0 player_surface = main_attack[player_index] elif katana: if player_index >= len(main_katana): player_index = 0 player_surface = main_katana[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(main_katana): player_index = 0 player_surface = main_katana[player_index] elif flame: if player_index >= len(main_flame): player_index = 0 player_surface = main_flame[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(main_flame): player_index = 0 player_surface = main_flame[player_index] else: frame_count = 5 player_index = 0 player_surface = main_walk[player_index] stay = True else: if moving: if player_index >= len(anti_main_walk): player_index = 0 player_surface = anti_main_walk[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(anti_main_walk): player_index = 0 player_surface = anti_main_walk[player_index] elif attack: if player_index >= len(anti_main_attack): player_index = 0 player_surface = anti_main_attack[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(anti_main_attack): player_index = 0 player_surface = anti_main_attack[player_index] elif katana: if player_index >= len(anti_main_katana): player_index = 0 player_surface = anti_main_katana[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(anti_main_katana): player_index = 0 player_surface = anti_main_katana[player_index] elif flame: if player_index >= len(anti_main_flame): player_index = 0 player_surface = anti_main_flame[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(anti_main_flame): player_index = 0 player_surface = anti_main_flame[player_index] else: frame_count = 5 player_index = 0 player_surface = anti_main_walk[player_index] stay = True # 怪物動畫(依據 monster_walk / monster_attack)— 這是全局版本;若要每隻獨立可再細化 monster_frame_count += 1 if monster_walk: if monster_frame_count >= ANIMATION_SPEED: monster_frame_count = 0 monster_index += 1 if monster_index >= len(main_monster_walk): monster_index = 0 monster_surface = main_monster_walk[monster_index] if monster_attack: if monster_frame_count >= ANIMATION_SPEED: monster_frame_count = 0 monster_index += 1 if monster_index >= len(main_monster_attack): monster_index = 0 monster_surface = main_monster_attack[monster_index] # ========== 繪製 ========== if game_active: screen.blit(background_surface, (0, 0)) screen.blit(text_surface, (400, 50)) # 怪物與血條(目前共用一張surface;若要每隻動畫獨立需再擴充) for e in enemies: if e['hp'] > 0: screen.blit(monster_surface, e['rect']) draw_hp_bar(screen, e['rect'], e['hp'], 40) # 玩家(保留你對 katana / flame 的偏移,避免動畫畫布不同造成的位置漂移) if 'attack' in locals() and (attack or moving or (locals().get('stay', False))): screen.blit(player_surface, player_rect) elif 'katana' in locals() and (katana or ('flame' in locals() and flame)): screen.blit(player_surface, [player_rect.centerx - 70, player_rect.centery - 90]) else: screen.blit(player_surface, player_rect) # 玩家血條(畫在玩家頭上) draw_hp_bar(screen, player_rect, player_hp, player_max_hp, w=120, h=12, offset_y=-18, color=(0,180,255)) # 攻擊箱(除錯可視化) if attack_rect: pygame.draw.rect(screen, (255, 0, 0), attack_rect, 2) # 浮空文字 for ft in float_texts[:]: ft.update() ft.draw(screen) if ft.life <= 0: float_texts.remove(ft) # 玩家受傷無敵幀閃爍 if player_iframes > 0: player_iframes -= 1 if (player_iframes // 3) % 2 == 0: s = pygame.Surface(player_rect.size, pygame.SRCALPHA) s.fill((255, 255, 255, 120)) screen.blit(s, player_rect.topleft) else: screen.fill('Red') msg = test_font.render('You Died - Press R to Restart', True, (255,255,255)) screen.blit(msg, (250, 480)) pygame.display.update() clock.tick(60)
在上列編程的基礎上,加入計算擊殺怪物的得分板
回答
要在遊戲中加入計算擊殺怪物的得分板,我們需要做以下幾件事:
新增一個變數來追蹤擊殺數量:我們可以使用一個整數變數 kills 來記錄玩家擊殺的怪物數量。
在怪物死亡時更新得分:當怪物的血量(hp)小於或等於 0 時,我們就增加 kills 的值。
在遊戲畫面上顯示得分:使用 Pygame 的字體渲染功能將得分顯示在畫面上。
以下是修改後的程式碼片段,加入了得分功能:
解釋:
kills = 0:這是用來追蹤玩家擊殺怪物數量的變數。
kill_font = pygame.font.Font(None, 40):建立一個字體用於顯示擊殺數。
kills += 1:當怪物被擊殺時,這行代碼會增加得分。
kill_text = kill_font.render(...):將擊殺數渲染成文字。
screen.blit(kill_text, (20, 20)):將得分顯示在螢幕的左上角。
結果:
玩家現在可以在遊戲中看到自己擊殺了多少隻怪物,這增加了遊戲的競爭性和趣味性。
相关提问
如何在得分板中加入最高分紀錄(High Score)?
如何將得分板與怪物種類結合,讓不同怪物有不同分數?
如何實現得分排行榜,讓玩家可以看到歷史得分?
import pygame
from sys import exit
import math
import random
pygame.init()
========= 視窗 & 基本 =========
screen = pygame.display.set_mode((1000, 1000))
pygame.display.set_caption(‘Little Hero’)
clock = pygame.time.Clock()
test_font = pygame.font.Font(None, 50)
game_active = True
地圖邊界(用來 clamp)
MAP_RECT = pygame.Rect(0, 0, 1000, 1000)
========= 背景與文字 =========
background_surface = pygame.image.load(‘backgroud10001000.png’)
text_surface = test_font.render(‘Little Hero’, False, ‘Green’)
def load_pic(number,prefix,suffix,storge,anti_storge):
for i in range(1, number):
x = prefix+str(i)+suffix
storge.append(pygame.image.load(x))
anti_storge.append(pygame.transform.flip(pygame.image.load(x),True, False))
========= 怪物動畫 =========
main_monster_walk = []
anti_main_monster_walk = []
load_pic(8,“monster walk/monster walk-export”,“.png”,main_monster_walk,anti_main_monster_walk)
monster_surface = main_monster_walk[0]
main_monster_attack = []
anti_main_monster_attack = []
load_pic(11,“monster attack/monster attack-export”,“.png”,main_monster_attack,anti_main_monster_attack)
========= 玩家攻擊動畫(保留你的素材與偏移做法)=========
main_attack = []
anti_main_attack = []
load_pic(15,“main character attack (huge sword)/main charcterattacking-Recovered”,“.png”,main_attack,anti_main_attack)
main_katana = []
anti_main_katana = []
load_pic(16,“main character katana attack/main character normal katana-export”,“.png”,main_katana,anti_main_katana)
main_flame = []
anti_main_flame = []
load_pic(13,“main character flame katana attack/main character fire katana-export”,“.png”,main_flame,anti_main_flame)
========= 玩家走路動畫 =========
main_walk = []
anti_main_walk = []
load_pic(8,“main charector walk/main walk-export”,“.png”,main_walk,anti_main_walk)
========= 玩家狀態 =========
player_index = 0
player_surface = main_walk[player_index]
player_rect = player_surface.get_rect(midbottom=(200, 500))
speed = 3
speed_monster = 2
player_max_hp = 100
player_hp = player_max_hp
player_iframes = 0 # 玩家受擊後的無敵幀
facing = 1 # 1 右, -1 左(用於攻擊箱朝向)
========= 攻擊判定(獨立 Rect,不等於角色 Rect)=========
attack_cooldown = 0 # 按鍵後冷卻幀
attack_active_frames = 0 # 攻擊箱存在幀數
attack_rect = None
attack_damage = 18
current_attack_cfg = None # 記住這次攻擊使用的偏移/尺寸
========= 浮空傷害數字 =========
class FloatText:
def init(self, text, pos, life=30, color=(255, 80, 80)):
self.text = test_font.render(str(text), True, color)
self.x, self.y = pos
self.life = life
def update(self):
self.y -= 0.8
self.life -= 1
def draw(self, surf):
surf.blit(self.text, (self.x, self.y))
float_texts = []
========= 畫血條(角色頭上/或 UI)=========
def draw_hp_bar(surf, anchor_rect, hp, max_hp, w=60, h=8, offset_y=-10, color=(0, 200, 0)):
pct = 0 if max_hp <= 0 else max(0, min(1, hp / max_hp))
x = anchor_rect.centerx - w // 2
y = anchor_rect.top + offset_y
bg = pygame.Rect(x, y, w, h)
fg = pygame.Rect(x, y, int(w * pct), h)
pygame.draw.rect(surf, (50, 50, 50), bg, border_radius=3)
pygame.draw.rect(surf, color, fg, border_radius=3)
pygame.draw.rect(surf, (0, 0, 0), bg, 2, border_radius=3)
========= 敵人資料結構 =========
enemies = [] # 每個元素: {‘rect’: Rect, ‘hp’:int, ‘knock’:[vx,vy], ‘stun’:int, ‘iframes’:int, ‘atk_*’…}
def spawn_enemy(x, y):
r = monster_surface.get_rect(topleft=(x, y))
enemies.append({
‘rect’: r, ‘hp’: 40,
‘knock’:[0.0, 0.0], ‘stun’:0, ‘iframes’:0,
‘atk_cool’:0, ‘atk_active’:0, ‘atk_range’:48, ‘atk_damage’:10,
‘facing’:1
})
spawn_timer = 0
========= 動畫參數 =========
ANIMATION_SPEED = 4
ANIMATE_speed = 3
frame_count = 4
monster_index = 0
monster_frame_count = 0
========= 攻击判定框(玩家各招式配置) ========
OFFSETS = {
‘basic’: {‘up’: -4, ‘forward’: -16, ‘w’: 80, ‘h’: 44},
‘katana’: {‘up’: -10,‘forward’: -12, ‘w’: 100, ‘h’: 66},
‘flame’: {‘up’: -6, ‘forward’: -22, ‘w’: 140, ‘h’: 88},
}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
========== 每幀初始狀態 ========== monster_attack = False # 全局動畫旗標暫時保留(若要每隻怪獨立可再細化) monster_walk = True # ========== 輸入 ========== if game_active: keys = pygame.key.get_pressed() moving, attack, katana, stay, flame = False, False, False, False, False # 攻擊鍵(J/K/L) if keys[pygame.K_j] or keys[pygame.K_k] or keys[pygame.K_l]: if keys[pygame.K_j]: attack = True if keys[pygame.K_k]: katana = True if keys[pygame.K_l]: flame = True else: if keys[pygame.K_d]: player_rect.x += speed moving = True facing = 1 if keys[pygame.K_a]: player_rect.x -= speed moving = True facing = -1 if keys[pygame.K_w]: player_rect.y -= speed moving = True if keys[pygame.K_s]: player_rect.y += speed moving = True # ============= 攻击判定:選招式配置 ============ cfg = None if attack: cfg = OFFSETS[‘basic’] elif katana: cfg = OFFSETS[‘katana’] elif flame: cfg = OFFSETS[‘flame’] else: keys = pygame.key.get_pressed() if keys[pygame.K_r]: game_active = True player_hp = player_max_hp enemies.clear() # ========== 地圖邊界 ========== player_rect.clamp_ip(MAP_RECT) # ========== 生怪 ========== if game_active: spawn_timer += 1 if spawn_timer >= 90: # 大約每 1.5 秒(60fps 假設)刷一隻 side = random.choice([“top”, “bottom”, “left”, “right”]) if side == “top”: x, y = random.randint(0, 1000), -30 elif side == “bottom”: x, y = random.randint(0, 1000), 1000 + 30 elif side == “left”: x, y = -30, random.randint(0, 1000) else: # right x, y = 1000 + 30, random.randint(0, 1000) spawn_enemy(x, y) spawn_timer = 0 # ========== 生成 / 跟隨 攻擊判定 Rect ========== if game_active: # 生成 if cfg and attack_cooldown == 0 and attack_active_frames == 0: current_attack_cfg = cfg w, h = cfg[‘w’], cfg[‘h’] forward, up = cfg[‘forward’], cfg[‘up’] attack_rect = pygame.Rect(0, 0, w, h) attack_rect.center = ( player_rect.centerx + facing * (player_rect.width // 2 + forward), player_rect.centery + up ) attack_active_frames = 8 attack_cooldown = 20 # 跟隨 if attack_active_frames > 0 and attack_rect: attack_active_frames -= 1 if current_attack_cfg: fwd = current_attack_cfg[‘forward’] up = current_attack_cfg[‘up’] w, h = current_attack_cfg[‘w’], current_attack_cfg[‘h’] attack_rect.size = (w, h) attack_rect.center = ( player_rect.centerx + facing * (player_rect.width // 2 + fwd), player_rect.centery + up ) else: attack_rect = None current_attack_cfg = None if attack_cooldown > 0: attack_cooldown -= 1 # ========== 更新怪物(硬直 > 攻擊 > 進攻 > 追擊)========= if game_active: for e in enemies[:]: if e[‘hp’] <= 0: continue # 通用衰減 if e[‘iframes’] > 0: e[‘iframes’] -= 1 if e[‘atk_cool’] > 0: e[‘atk_cool’] -= 1 # 與玩家相對 dx = player_rect.centerx - e[‘rect’].centerx dy = player_rect.centery - e[‘rect’].centery dist = max(1, (dxdx + dydy) ** 0.5) e[‘facing’] = 1 if dx >= 0 else -1 # ① 硬直/擊退優先 if e[‘stun’] > 0 or (abs(e[‘knock’][0]) + abs(e[‘knock’][1])) > 0.1: e[‘rect’].x += int(e[‘knock’][0]) e[‘rect’].y += int(e[‘knock’][1]) e[‘knock’][0] = 0.8 e[‘knock’][1] = 0.8 if e[‘stun’] > 0: e[‘stun’] -= 1 # ② 攻擊生效(有獨立攻擊判定框) elif e[‘atk_active’] > 0: fw, up, w, h = 16, 0, 40, 30 atk_rect = pygame.Rect(0, 0, w, h) atk_rect.center = ( e[‘rect’].centerx + e[‘facing’] * (e[‘rect’].width // 2 + fw), e[‘rect’].centery + up ) if player_iframes == 0 and atk_rect.colliderect(player_rect): player_hp -= e[‘atk_damage’] player_iframes = 30 float_texts.append(FloatText(-e[‘atk_damage’], (player_rect.centerx, player_rect.top - 10), color=(255, 220, 0))) if player_hp <= 0: game_active = False e[‘atk_active’] -= 1 # 調試可視化:pygame.draw.rect(screen, (255,150,0), atk_rect, 2) # ③ 進入攻擊 elif dist <= e[‘atk_range’] and e[‘atk_cool’] == 0: e[‘atk_active’] = 8 e[‘atk_cool’] = 30 monster_attack = True # 如果你想全局播放攻擊動畫;若要每隻獨立可改成 e[‘is_attacking_anim’]=True monster_walk = False # ④ 普通追擊 else: ndx, ndy = dx / dist, dy / dist e[‘rect’].x += int(ndx * speed_monster) e[‘rect’].y += int(ndy * speed_monster) # 地圖邊界 e[‘rect’].clamp_ip(MAP_RECT) # ========== 玩家攻擊命中(怪物扣血 + 擊退 + 浮空數字)========= if game_active and attack_rect: for e in enemies: if e[‘hp’] > 0 and e[‘iframes’] == 0 and attack_rect.colliderect(e[‘rect’]): e[‘hp’] -= attack_damage e[‘iframes’] = 12 # 擊退方向:玩家->怪物 dx = e[‘rect’].centerx - player_rect.centerx dy = e[‘rect’].centery - player_rect.centery dist = max(1, (dxdx + dydy) ** 0.5) kb = 8.0 e[‘knock’] = [kb * dx / dist, kb * dy / dist] e[‘stun’] = 10 float_texts.append(FloatText(attack_damage, (e[‘rect’].centerx, e[‘rect’].top - 10))) # ========== 動畫更新 ========== if game_active: # 玩家 if keys := pygame.key.get_pressed(): if facing == 1: if moving: if player_index >= len(main_walk): player_index = 0 player_surface = main_walk[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(main_walk): player_index = 0 player_surface = main_walk[player_index] elif attack: if player_index >= len(main_attack): player_index = 0 player_surface = main_attack[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(main_attack): player_index = 0 player_surface = main_attack[player_index] elif katana: if player_index >= len(main_katana): player_index = 0 player_surface = main_katana[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(main_katana): player_index = 0 player_surface = main_katana[player_index] elif flame: if player_index >= len(main_flame): player_index = 0 player_surface = main_flame[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(main_flame): player_index = 0 player_surface = main_flame[player_index] else: frame_count = 5 player_index = 0 player_surface = main_walk[player_index] stay = True else: if moving: if player_index >= len(anti_main_walk): player_index = 0 player_surface = anti_main_walk[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(anti_main_walk): player_index = 0 player_surface = anti_main_walk[player_index] elif attack: if player_index >= len(anti_main_attack): player_index = 0 player_surface = anti_main_attack[player_index] frame_count += 1 if frame_count >= ANIMATION_SPEED: frame_count = 0 player_index += 1 if player_index >= len(anti_main_attack): player_index = 0 player_surface = anti_main_attack[player_index] elif katana: if player_index >= len(anti_main_katana): player_index = 0 player_surface = anti_main_katana[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(anti_main_katana): player_index = 0 player_surface = anti_main_katana[player_index] elif flame: if player_index >= len(anti_main_flame): player_index = 0 player_surface = anti_main_flame[player_index] frame_count += 1 if frame_count >= ANIMATE_speed: frame_count = 0 player_index += 1 if player_index >= len(anti_main_flame): player_index = 0 player_surface = anti_main_flame[player_index] else: frame_count = 5 player_index = 0 player_surface = anti_main_walk[player_index] stay = True # 怪物動畫(依據 monster_walk / monster_attack)— 這是全局版本;若要每隻獨立可再細化 monster_frame_count += 1 if monster_walk: if monster_frame_count >= ANIMATION_SPEED: monster_frame_count = 0 monster_index += 1 if monster_index >= len(main_monster_walk): monster_index = 0 monster_surface = main_monster_walk[monster_index] if monster_attack: if monster_frame_count >= ANIMATION_SPEED: monster_frame_count = 0 monster_index += 1 if monster_index >= len(main_monster_attack): monster_index = 0 monster_surface = main_monster_attack[monster_index] # ========== 繪製 ========== if game_active: screen.blit(background_surface, (0, 0)) screen.blit(text_surface, (400, 50)) # 怪物與血條(目前共用一張surface;若要每隻動畫獨立需再擴充) for e in enemies: if e[‘hp’] > 0: screen.blit(monster_surface, e[‘rect’]) draw_hp_bar(screen, e[‘rect’], e[‘hp’], 40) # 玩家(保留你對 katana / flame 的偏移,避免動畫畫布不同造成的位置漂移) if ‘attack’ in locals() and (attack or moving or (locals().get(‘stay’, False))): screen.blit(player_surface, player_rect) elif ‘katana’ in locals() and (katana or (‘flame’ in locals() and flame)): screen.blit(player_surface, [player_rect.centerx - 70, player_rect.centery - 90]) else: screen.blit(player_surface, player_rect) # 玩家血條(畫在玩家頭上) draw_hp_bar(screen, player_rect, player_hp, player_max_hp, w=120, h=12, offset_y=-18, color=(0,180,255)) # 攻擊箱(除錯可視化) if attack_rect: pygame.draw.rect(screen, (255, 0, 0), attack_rect, 2) # 浮空文字 for ft in float_texts[:]: ft.update() ft.draw(screen) if ft.life <= 0: float_texts.remove(ft) # 玩家受傷無敵幀閃爍 if player_iframes > 0: player_iframes -= 1 if (player_iframes // 3) % 2 == 0: s = pygame.Surface(player_rect.size, pygame.SRCALPHA) s.fill((255, 255, 255, 120)) screen.blit(s, player_rect.topleft) else: screen.fill(‘Red’) msg = test_font.render(‘You Died - Press R to Restart’, True, (255,255,255)) screen.blit(msg, (250, 480)) pygame.display.update() clock.tick(60)
在上列編程的基礎上,加入計算擊殺怪物的得分板,并且記錄每次擊殺數