Cool Speed Test

博主分享了一个有趣的测速活动,邀请读者参与测试自己的速度。

   今天在 LA.NET的blog 看见一个有趣的东西,如果有兴趣的话大家也去测试一下你们的   speed  吧!

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) 在上列編程的基礎上,加入計算擊殺怪物的得分板,并且記錄每次擊殺數
09-02
import pygame from sys import exit import math import random import os pygame.init() # ========= 視窗 & 基本 ========= screen = pygame.display.set_mode((1000, 1000)) pygame.display.set_caption("High Score Example") pygame.display.set_caption("High Score Example") clock = pygame.time.Clock() test_font = pygame.font.Font(None, 50) game_active = True # 地圖邊界(用來 clamp) MAP_RECT = pygame.Rect(0, 0, 1000, 1000) # ========= 遊戲初始化 ========= kills = 0 kill_font = pygame.font.Font(None, 40) # 用來顯示得分 # ========= 背景與文字 ========= 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 = 10000 player_hp = player_max_hp player_iframes = 0 # 玩家受擊後的無敵幀 facing = 1 # 1 右, -1 左(用於攻擊箱朝向) # ========= 攻擊判定(獨立 Rect,不等於角色 Rect)========= attack_cooldown = 0 # 按鍵後冷卻幀 attack_active_frames = 0 # 攻擊箱存在幀數 attack_rect = None current_attack_cfg = None # 記住這次攻擊使用的偏移/尺寸 attack_damage = 20 # ========= 浮空傷害數字 ========= 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 # 根據 kills 數量決定生成間隔 if kills <= 10: spawn_interval = 90 # 每 1.5 秒生成一隻 else: spawn_interval = 30 # 每 0.5 秒生成一隻 # 判斷是否達到生成條件 if spawn_timer >= spawn_interval: 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)) # 顯示擊殺數 kill_text = kill_font.render(f'Kills: {kills}', True, (255, 255, 255)) screen.blit(kill_text, (100, 200)) # 左上角顯示得分 # 怪物與血條(目前共用一張surface;若要每隻動畫獨立需再擴充) for e in enemies: if e['hp'] > 0: screen.blit(monster_surface, e['rect']) draw_hp_bar(screen, e['rect'], e['hp'], 40) if e['hp'] <= 0: enemies.remove(e) kills += 1 continue # 玩家(保留你對 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)) kills = 0 # 重置擊殺數 pygame.display.update() clock.tick(60) 設置kills=100是任務,達到任務後游戲會彈出完成任務。
09-02
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值