IT评测·应用市场-qidao123.com

标题: 《用Python+PyGame开发双人生存游戏!源码剖析+完整开发思绪分享》 [打印本页]

作者: 守听    时间: 2025-3-6 22:27
标题: 《用Python+PyGame开发双人生存游戏!源码剖析+完整开发思绪分享》
导语

"你是否想过用Python开发一款可玩性高的双人合作游戏?本文将分享怎样从零开始实现一款类《吸血鬼幸存者》的生存射击游戏!包含完整源码剖析、角色体系设计、敌人AI逻辑等核心技能点,文末提供完整代码包下载!"



        哈哈,怪物可以换成同学 的qq头像
游戏内容如下:

一、游戏展示 & 核心功能


二、技能实现亮点


三、代码结构剖析

python
  1. # 代码模块示意图
  2. ├── Assets/              # 资源文件夹
  3. │   ├── image/           # 游戏素材(角色/敌人/背景图)
  4. ├── main.py              # 主程序入口
  5. │   ├── 核心类:
  6. │   │   - Player        # 玩家角色(移动/攻击/成长)
  7. │   │   - Enemy         # 基础敌人AI
  8. │   │   - EliteEnemy    # 精英Boss(弹幕攻击)
  9. │   │   - Bullet        # 子弹物理系统
  10. │   │   - Button        # 交互式GUI按钮
  11. │   ├── 游戏流程:
  12. │   │   - main_menu()       # 主菜单
  13. │   │   - role_selection()  # 角色选择
  14. │   │   - game_loop()       # 核心游戏循环
复制代码

四、关键代码解读


五、怎样运行游戏


六、开发心得 & 优化方向


七、完整代码获取

"关注+私信回复【生存游戏】获取完整代码包和素材资源!"
骗你的,代码就在这,复制就能用!
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import pygame
  4. import random
  5. import math
  6. # 初始化配置
  7. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  8. IMAGE_DIR = os.path.join(BASE_DIR, 'image')
  9. RESOURCES = {
  10.     'role1': os.path.join(IMAGE_DIR, 'role1.png'),
  11.     'role2': os.path.join(IMAGE_DIR, 'role2.png'),
  12.     'enemy': os.path.join(IMAGE_DIR, 'enemy.png'),
  13.     'elite': os.path.join(IMAGE_DIR, 'elite.png'),
  14.     'bullet': os.path.join(IMAGE_DIR, 'bullet.png'),
  15.     'exp_orb': os.path.join(IMAGE_DIR, 'exp_orb.png'),
  16.     'background': os.path.join(IMAGE_DIR, 'background.png')
  17. }
  18. pygame.init()
  19. WIDTH, HEIGHT = 800, 600
  20. screen = pygame.display.set_mode((WIDTH, HEIGHT))
  21. clock = pygame.time.Clock()
  22. # 颜色定义
  23. WHITE = (255, 255, 255)
  24. GRAY = (100, 100, 100)
  25. BUTTON_COLOR = (50, 150, 50)
  26. HOVER_COLOR = (70, 170, 70)
  27. RED = (255, 0, 0)
  28. YELLOW = (255, 255, 0)
  29. ELITE_BULLET_COLOR = (255, 165, 0)
  30. font = pygame.font.Font(None, 24)
  31. def load_image(path, default_size=(30, 30)):
  32.     try:
  33.         image = pygame.image.load(path).convert_alpha()
  34.         return pygame.transform.scale(image, default_size)
  35.     except:
  36.         surf = pygame.Surface(default_size)
  37.         surf.fill(RED)
  38.         return surf
  39. GAME_IMAGES = {
  40.     'role1': load_image(RESOURCES['role1']),
  41.     'role2': load_image(RESOURCES['role2']),
  42.     'enemy': load_image(RESOURCES['enemy'], (20, 20)),
  43.     'elite': load_image(RESOURCES['elite'], (40, 40)),
  44.     'bullet': load_image(RESOURCES['bullet'], (10, 10)),
  45.     'exp_orb': load_image(RESOURCES['exp_orb'], (10, 10)),
  46.     'background': load_image(RESOURCES['background'], (WIDTH, HEIGHT))
  47. }
  48. class Button:
  49.     def __init__(self, text, x, y, w, h):
  50.         self.rect = pygame.Rect(x, y, w, h)
  51.         self.text = text
  52.         self.color = BUTTON_COLOR
  53.         self.hover = False
  54.     def draw(self, surface):
  55.         color = HOVER_COLOR if self.hover else BUTTON_COLOR
  56.         pygame.draw.rect(surface, color, self.rect, border_radius=5)
  57.         text_surf = font.render(self.text, True, WHITE)
  58.         text_rect = text_surf.get_rect(center=self.rect.center)
  59.         surface.blit(text_surf, text_rect)
  60.     def check_hover(self, mouse_pos):
  61.         self.hover = self.rect.collidepoint(mouse_pos)
  62. class Player(pygame.sprite.Sprite):
  63.     def __init__(self, controls, role_type, pos_offset=0, is_player2=False):
  64.         super().__init__()
  65.         self.role_type = role_type
  66.         self.image = GAME_IMAGES['role2' if role_type == 2 else 'role1']
  67.         self.rect = self.image.get_rect(center=(WIDTH // 2 + pos_offset, HEIGHT // 2))
  68.         self.speed = 5
  69.         self.health = 100
  70.         self.exp = 0
  71.         self.level = 1
  72.         self.max_exp = 100
  73.         self.kills = 0
  74.         self.controls = controls
  75.     def update(self, keys):
  76.         if keys[self.controls['up']]: self.rect.y -= self.speed
  77.         if keys[self.controls['down']]: self.rect.y += self.speed
  78.         if keys[self.controls['left']]: self.rect.x -= self.speed
  79.         if keys[self.controls['right']]: self.rect.x += self.speed
  80.         self.rect.clamp_ip(screen.get_rect())
  81.     def get_attack_directions(self):
  82.         return ["up", "down", "left", "right"] if self.role_type == 1 else ["up_left", "up_right", "down_left",
  83.                                                                             "down_right"]
  84. class Bullet(pygame.sprite.Sprite):
  85.     def __init__(self, x, y, direction):
  86.         super().__init__()
  87.         self.image = GAME_IMAGES['bullet']
  88.         self.rect = self.image.get_rect(center=(x, y))
  89.         self.speed = 8
  90.         self.start_pos = (x, y)
  91.         self.max_distance = 300
  92.         self.penetration = 2
  93.         dir_mapping = {
  94.             "up": (0, -1), "down": (0, 1),
  95.             "left": (-1, 0), "right": (1, 0),
  96.             "up_left": (-math.sqrt(0.5), -math.sqrt(0.5)),
  97.             "up_right": (math.sqrt(0.5), -math.sqrt(0.5)),
  98.             "down_left": (-math.sqrt(0.5), math.sqrt(0.5)),
  99.             "down_right": (math.sqrt(0.5), math.sqrt(0.5))
  100.         }
  101.         dx_mult, dy_mult = dir_mapping[direction]
  102.         self.dx = dx_mult * self.speed
  103.         self.dy = dy_mult * self.speed
  104.     def update(self):
  105.         self.rect.x += self.dx
  106.         self.rect.y += self.dy
  107.         if math.hypot(self.rect.x - self.start_pos[0], self.rect.y - self.start_pos[1]) > self.max_distance:
  108.             self.kill()
  109. class Enemy(pygame.sprite.Sprite):
  110.     def __init__(self):
  111.         super().__init__()
  112.         self.image = GAME_IMAGES['enemy']
  113.         self.rect = self.image.get_rect(
  114.             center=(random.choice([-100, WIDTH + 100]), random.randint(0, HEIGHT))
  115.         )
  116.         self.speed = 2
  117.     def update(self, targets):
  118.         if not targets: return
  119.         nearest = min(targets, key=lambda t: math.hypot(t.rect.x - self.rect.x, t.rect.y - self.rect.y))
  120.         dx = nearest.rect.x - self.rect.x
  121.         dy = nearest.rect.y - self.rect.y
  122.         dist = math.hypot(dx, dy)
  123.         if dist != 0:
  124.             self.rect.x += dx / dist * self.speed
  125.             self.rect.y += dy / dist * self.speed
  126. class EliteEnemy(pygame.sprite.Sprite):
  127.     def __init__(self):
  128.         super().__init__()
  129.         self.image = GAME_IMAGES['elite']
  130.         self.rect = self.image.get_rect(
  131.             center=(random.choice([-100, WIDTH + 100]), random.randint(0, HEIGHT))
  132.         )
  133.         self.speed = 1.5
  134.         self.health = 50
  135.         self.max_health = 50
  136.         self.shoot_timer = 0
  137.         self.bullet_speed = 5
  138.     def update(self, targets):
  139.         if not targets: return
  140.         nearest = min(targets, key=lambda t: math.hypot(t.rect.x - self.rect.x, t.rect.y - self.rect.y))
  141.         dx = nearest.rect.x - self.rect.x
  142.         dy = nearest.rect.y - self.rect.y
  143.         dist = math.hypot(dx, dy)
  144.         if dist != 0:
  145.             self.rect.x += dx / dist * self.speed
  146.             self.rect.y += dy / dist * self.speed
  147.         self.shoot_timer += 1
  148.         if self.shoot_timer >= 60:
  149.             self.shoot(nearest)
  150.             self.shoot_timer = 0
  151.     def shoot(self, target):
  152.         for angle in range(0, 360, 45):
  153.             rad = math.radians(angle)
  154.             bullet = EliteBullet(
  155.                 self.rect.centerx,
  156.                 self.rect.centery,
  157.                 math.cos(rad) * self.bullet_speed,
  158.                 math.sin(rad) * self.bullet_speed
  159.             )
  160.             bullets.add(bullet)
  161.             all_sprites.add(bullet)
  162. class EliteBullet(pygame.sprite.Sprite):
  163.     def __init__(self, x, y, dx, dy):
  164.         super().__init__()
  165.         self.image = pygame.Surface((15, 15))
  166.         self.image.fill(ELITE_BULLET_COLOR)
  167.         self.rect = self.image.get_rect(center=(x, y))
  168.         self.dx = dx
  169.         self.dy = dy
  170.         self.max_distance = 400
  171.         self.start_pos = (x, y)
  172.     def update(self):
  173.         self.rect.x += self.dx
  174.         self.rect.y += self.dy
  175.         if math.hypot(self.rect.x - self.start_pos[0], self.rect.y - self.start_pos[1]) > self.max_distance:
  176.             self.kill()
  177. class ExpOrb(pygame.sprite.Sprite):
  178.     def __init__(self, x, y):
  179.         super().__init__()
  180.         self.image = GAME_IMAGES['exp_orb']
  181.         self.rect = self.image.get_rect(center=(x, y))
  182.         self.float_timer = 0
  183.     def update(self):
  184.         self.float_timer += 1
  185.         self.rect.y += math.sin(self.float_timer * 0.1) * 0.5
  186. def draw_hud(surface, players, time_left):
  187.     time_text = font.render(f"Time: {time_left // 60:02}:{time_left % 60:02}", True, WHITE)
  188.     surface.blit(time_text, (WIDTH // 2 - 60, 10))
  189.     for i, player in enumerate(players):
  190.         y_offset = 40 + i * 80
  191.         pygame.draw.rect(surface, GRAY, (10, y_offset, 100, 10))
  192.         health_width = int((player.health / 100.0) * 100)
  193.         pygame.draw.rect(surface, RED, (10, y_offset, health_width, 10))
  194.         pygame.draw.rect(surface, GRAY, (10, y_offset + 20, 100, 10))
  195.         exp_width = int((player.exp / float(player.max_exp)) * 100)
  196.         pygame.draw.rect(surface, YELLOW, (10, y_offset + 20, exp_width, 10))
  197.         info_text = font.render(f"P{i + 1} Lv{player.level} K{player.kills}", True, WHITE)
  198.         surface.blit(info_text, (10, y_offset + 40))
  199. def role_selection_menu(player_count):
  200.     roles = []
  201.     buttons = []
  202.     descriptions = [
  203.         "Role 1: 4-Direction Attack",
  204.         "Role 2: Diagonal Attack"
  205.     ]
  206.     for i in range(player_count):
  207.         y_base = 150 + i * 150
  208.         buttons.append([
  209.             Button(f"Player{i + 1} Role1", WIDTH // 2 - 250, y_base, 200, 50),
  210.             Button(f"Player{i + 1} Role2", WIDTH // 2 + 50, y_base, 200, 50)
  211.         ])
  212.     confirm_btn = Button("Start Game", WIDTH // 2 - 100, HEIGHT - 100, 200, 50)
  213.     while True:
  214.         screen.fill((30, 30, 30))
  215.         mouse_pos = pygame.mouse.get_pos()
  216.         for event in pygame.event.get():
  217.             if event.type == pygame.QUIT:
  218.                 pygame.quit()
  219.                 return []
  220.             if event.type == pygame.MOUSEBUTTONDOWN:
  221.                 for i, pair in enumerate(buttons):
  222.                     for j, btn in enumerate(pair):
  223.                         if btn.rect.collidepoint(mouse_pos):
  224.                             roles = roles[:i] + [j + 1] + roles[i + 1:] if len(roles) > i else roles + [j + 1]
  225.                 if confirm_btn.rect.collidepoint(mouse_pos) and len(roles) == player_count:
  226.                     return roles
  227.         desc_y = 100
  228.         for desc in descriptions:
  229.             text = font.render(desc, True, WHITE)
  230.             screen.blit(text, (WIDTH // 2 - text.get_width() // 2, desc_y))
  231.             desc_y += 30
  232.         for i, pair in enumerate(buttons):
  233.             for j, btn in enumerate(pair):
  234.                 btn.check_hover(mouse_pos)
  235.                 btn.draw(screen)
  236.                 if i < len(roles) and roles[i] == j + 1:
  237.                     pygame.draw.rect(screen, YELLOW, btn.rect.inflate(10, 10), 3, border_radius=7)
  238.         confirm_btn.check_hover(mouse_pos)
  239.         confirm_btn.draw(screen)
  240.         pygame.display.flip()
  241.         clock.tick(30)
  242. def main_menu():
  243.     buttons = [
  244.         Button("1 Player", WIDTH // 2 - 100, HEIGHT // 2 - 50, 200, 50),
  245.         Button("2 Players", WIDTH // 2 - 100, HEIGHT // 2 + 20, 200, 50)
  246.     ]
  247.     while True:
  248.         screen.fill((30, 30, 30))
  249.         mouse_pos = pygame.mouse.get_pos()
  250.         for event in pygame.event.get():
  251.             if event.type == pygame.QUIT:
  252.                 pygame.quit()
  253.                 return (0, [])
  254.             if event.type == pygame.MOUSEBUTTONDOWN:
  255.                 for i, btn in enumerate(buttons):
  256.                     if btn.rect.collidepoint(mouse_pos):
  257.                         roles = role_selection_menu(i + 1)
  258.                         if roles:
  259.                             return (i + 1, roles)
  260.         title = font.render("Vampire Survivors", True, WHITE)
  261.         screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 100))
  262.         for btn in buttons:
  263.             btn.check_hover(mouse_pos)
  264.             btn.draw(screen)
  265.         pygame.display.flip()
  266.         clock.tick(30)
  267. def game_loop(player_count, roles):
  268.     background = GAME_IMAGES['background']
  269.     controls = [
  270.         {'up': pygame.K_w, 'down': pygame.K_s, 'left': pygame.K_a, 'right': pygame.K_d},
  271.         {'up': pygame.K_UP, 'down': pygame.K_DOWN, 'left': pygame.K_LEFT, 'right': pygame.K_RIGHT}
  272.     ]
  273.     players = pygame.sprite.Group()
  274.     for i in range(player_count):
  275.         player = Player(
  276.             controls=controls[i],
  277.             role_type=roles[i],
  278.             pos_offset=-50 + i * 100,
  279.             is_player2=(i == 1)
  280.         )
  281.         players.add(player)
  282.     all_sprites = pygame.sprite.Group(players)
  283.     enemies = pygame.sprite.Group()
  284.     bullets = pygame.sprite.Group()
  285.     exp_orbs = pygame.sprite.Group()
  286.     enemy_spawn_timer = 0
  287.     attack_timer = 0
  288.     start_ticks = pygame.time.get_ticks()
  289.     time_limit = 300
  290.     running = True
  291.     while running:
  292.         screen.blit(background, (0, 0))
  293.         keys = pygame.key.get_pressed()
  294.         elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000
  295.         time_left = max(time_limit - elapsed_seconds, 0)
  296.         if time_left <= 0:
  297.             running = False
  298.         for event in pygame.event.get():
  299.             if event.type == pygame.QUIT:
  300.                 running = False
  301.         enemy_spawn_timer += 1
  302.         if enemy_spawn_timer >= 60 - (sum(p.level for p in players) * 2):
  303.             if random.random() < 0.1:
  304.                 enemy = EliteEnemy()
  305.             else:
  306.                 enemy = Enemy()
  307.             enemies.add(enemy)
  308.             all_sprites.add(enemy)
  309.             enemy_spawn_timer = 0
  310.         attack_timer += 1
  311.         if attack_timer >= 30:
  312.             for player in players:
  313.                 for direction in player.get_attack_directions():
  314.                     bullet = Bullet(player.rect.centerx, player.rect.centery, direction)
  315.                     bullets.add(bullet)
  316.                     all_sprites.add(bullet)
  317.             attack_timer = 0
  318.         for player in players:
  319.             player.update(keys)
  320.         enemies.update(players)
  321.         bullets.update()
  322.         exp_orbs.update()
  323.         for bullet in bullets:
  324.             hits = pygame.sprite.spritecollide(bullet, enemies, False)
  325.             if hits:
  326.                 bullet.penetration -= 1
  327.                 for enemy in hits:
  328.                     if isinstance(enemy, EliteEnemy):
  329.                         enemy.health -= 2
  330.                         if enemy.health <= 0:
  331.                             enemy.kill()
  332.                             for _ in range(5):
  333.                                 exp_orb = ExpOrb(enemy.rect.centerx, enemy.rect.centery)
  334.                                 exp_orbs.add(exp_orb)
  335.                                 all_sprites.add(exp_orb)
  336.                     else:
  337.                         enemy.kill()
  338.                         exp_orb = ExpOrb(enemy.rect.centerx, enemy.rect.centery)
  339.                         exp_orbs.add(exp_orb)
  340.                         all_sprites.add(exp_orb)
  341.                     for player in players:
  342.                         if math.hypot(player.rect.x - enemy.rect.x, player.rect.y - enemy.rect.y) < 100:
  343.                             player.kills += 1
  344.                 if bullet.penetration <= 0:
  345.                     bullet.kill()
  346.         for player in players:
  347.             hits = pygame.sprite.spritecollide(player, exp_orbs, True)
  348.             if hits:
  349.                 player.exp += 10 * len(hits)
  350.                 if player.exp >= player.max_exp:
  351.                     player.level += 1
  352.                     player.exp -= player.max_exp
  353.                     player.max_exp = int(player.max_exp * 1.5)
  354.                     player.speed += 0.5
  355.             if pygame.sprite.spritecollide(player, enemies, True):
  356.                 player.health -= 10
  357.             bullet_hits = pygame.sprite.spritecollide(player, bullets, True)
  358.             if bullet_hits:
  359.                 player.health -= 5
  360.         alive_players = [p for p in players if p.health > 0]
  361.         if not alive_players or time_left <= 0:
  362.             running = False
  363.         all_sprites.draw(screen)
  364.         draw_hud(screen, players, time_left)
  365.         pygame.display.flip()
  366.         clock.tick(30)
  367.     pygame.quit()
  368. if __name__ == "__main__":
  369.     player_count, roles = main_menu()
  370.     if player_count > 0:
  371.         game_loop(player_count, roles)
复制代码
 下面是python2的代码。方便不同环境的兄弟们运行:
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import pygame
  4. import random
  5. import math
  6. from datetime import datetime
  7. # 初始化路径
  8. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  9. IMAGE_DIR = os.path.join(BASE_DIR, 'image')
  10. RESOURCES = {
  11.     'role1': os.path.join(IMAGE_DIR, 'role1.png'),
  12.     'role2': os.path.join(IMAGE_DIR, 'role2.png'),
  13.     'enemy': os.path.join(IMAGE_DIR, 'enemy.png'),
  14.     'bullet': os.path.join(IMAGE_DIR, 'bullet.png'),
  15.     'exp_orb': os.path.join(IMAGE_DIR, 'exp_orb.png'),
  16.     'background': os.path.join(IMAGE_DIR, 'background.png')
  17. }
  18. pygame.init()
  19. WIDTH, HEIGHT = 800, 600
  20. screen = pygame.display.set_mode((WIDTH, HEIGHT))
  21. clock = pygame.time.Clock()
  22. # 颜色定义
  23. WHITE = (255, 255, 255)
  24. GRAY = (100, 100, 100)
  25. BUTTON_COLOR = (50, 150, 50)
  26. HOVER_COLOR = (70, 170, 70)
  27. RED = (255, 0, 0)
  28. YELLOW = (255, 255, 0)
  29. font = pygame.font.Font(None, 24)
  30. def load_image(path, default_size=(30, 30)):
  31.     try:
  32.         image = pygame.image.load(path).convert_alpha()
  33.         return pygame.transform.scale(image, default_size)
  34.     except:
  35.         surf = pygame.Surface(default_size)
  36.         surf.fill(RED)
  37.         return surf
  38. GAME_IMAGES = {
  39.     'role1': load_image(RESOURCES['role1']),
  40.     'role2': load_image(RESOURCES['role2']),
  41.     'enemy': load_image(RESOURCES['enemy'], (20, 20)),
  42.     'bullet': load_image(RESOURCES['bullet'], (10, 10)),
  43.     'exp_orb': load_image(RESOURCES['exp_orb'], (10, 10)),
  44.     'background': load_image(RESOURCES['background'], (WIDTH, HEIGHT))
  45. }
  46. class Button:
  47.     def __init__(self, text, x, y, w, h):
  48.         self.rect = pygame.Rect(x, y, w, h)
  49.         self.text = text
  50.         self.color = BUTTON_COLOR
  51.         self.hover = False
  52.     def draw(self, surface):
  53.         color = HOVER_COLOR if self.hover else BUTTON_COLOR
  54.         pygame.draw.rect(surface, color, self.rect, border_radius=5)
  55.         text_surf = font.render(self.text, True, WHITE)
  56.         text_rect = text_surf.get_rect(center=self.rect.center)
  57.         surface.blit(text_surf, text_rect)
  58.     def check_hover(self, mouse_pos):
  59.         self.hover = self.rect.collidepoint(mouse_pos)
  60. class Player(pygame.sprite.Sprite):
  61.     def __init__(self, controls, role_type, pos_offset=0, is_player2=False):
  62.         pygame.sprite.Sprite.__init__(self)
  63.         self.role_type = role_type
  64.         self.image = GAME_IMAGES['role2' if role_type == 2 else 'role1']
  65.         self.rect = self.image.get_rect(center=(WIDTH//2 + pos_offset, HEIGHT//2))
  66.         self.speed = 5
  67.         self.health = 100
  68.         self.exp = 0
  69.         self.level = 1
  70.         self.max_exp = 100
  71.         self.kills = 0
  72.         self.controls = controls
  73.     def update(self, keys):
  74.         if keys[self.controls['up']]:
  75.             self.rect.y -= self.speed
  76.         if keys[self.controls['down']]:
  77.             self.rect.y += self.speed
  78.         if keys[self.controls['left']]:
  79.             self.rect.x -= self.speed
  80.         if keys[self.controls['right']]:
  81.             self.rect.x += self.speed
  82.         self.rect.clamp_ip(screen.get_rect())
  83.     def get_attack_directions(self):
  84.         if self.role_type == 1:
  85.             return ["up", "down", "left", "right"]
  86.         else:
  87.             return ["up_left", "up_right", "down_left", "down_right"]
  88. class Bullet(pygame.sprite.Sprite):
  89.     def __init__(self, x, y, direction):
  90.         pygame.sprite.Sprite.__init__(self)
  91.         self.image = GAME_IMAGES['bullet']
  92.         self.rect = self.image.get_rect(center=(x, y))
  93.         self.speed = 8
  94.         self.start_pos = (x, y)
  95.         self.max_distance = 300
  96.         self.penetration = 2
  97.         self.dx, self.dy = 0, 0
  98.         dir_mapping = {
  99.             "up": (0, -1),
  100.             "down": (0, 1),
  101.             "left": (-1, 0),
  102.             "right": (1, 0),
  103.             "up_left": (-math.sqrt(0.5), -math.sqrt(0.5)),
  104.             "up_right": (math.sqrt(0.5), -math.sqrt(0.5)),
  105.             "down_left": (-math.sqrt(0.5), math.sqrt(0.5)),
  106.             "down_right": (math.sqrt(0.5), math.sqrt(0.5))
  107.         }
  108.         dx_mult, dy_mult = dir_mapping[direction]
  109.         self.dx = dx_mult * self.speed
  110.         self.dy = dy_mult * self.speed
  111.     def update(self):
  112.         self.rect.x += self.dx
  113.         self.rect.y += self.dy
  114.         if math.hypot(self.rect.x - self.start_pos[0], self.rect.y - self.start_pos[1]) > self.max_distance:
  115.             self.kill()
  116. class Enemy(pygame.sprite.Sprite):
  117.     def __init__(self):
  118.         pygame.sprite.Sprite.__init__(self)
  119.         self.image = GAME_IMAGES['enemy']
  120.         self.rect = self.image.get_rect(
  121.             center=(random.choice([-100, WIDTH+100]), random.randint(0, HEIGHT))
  122.         )
  123.         self.speed = 2
  124.     def update(self, targets):
  125.         if not targets: return
  126.         nearest = min(targets, key=lambda t: math.hypot(t.rect.x-self.rect.x, t.rect.y-self.rect.y))
  127.         dx = nearest.rect.x - self.rect.x
  128.         dy = nearest.rect.y - self.rect.y
  129.         dist = math.hypot(dx, dy)
  130.         if dist != 0:
  131.             self.rect.x += dx / dist * self.speed
  132.             self.rect.y += dy / dist * self.speed
  133. class ExpOrb(pygame.sprite.Sprite):
  134.     def __init__(self, x, y):
  135.         pygame.sprite.Sprite.__init__(self)
  136.         self.image = GAME_IMAGES['exp_orb']
  137.         self.rect = self.image.get_rect(center=(x, y))
  138.         self.float_timer = 0
  139.     def update(self):
  140.         self.float_timer += 1
  141.         self.rect.y += math.sin(self.float_timer * 0.1) * 0.5
  142. def draw_hud(surface, players, time_left):
  143.     time_text = font.render("Time: {:02}:{:02}".format(time_left//60, time_left%60), True, WHITE)
  144.     surface.blit(time_text, (WIDTH//2 - 60, 10))
  145.    
  146.     for i, player in enumerate(players):
  147.         y_offset = 40 + i*80
  148.         pygame.draw.rect(surface, GRAY, (10, y_offset, 100, 10))
  149.         health_width = int((player.health / 100.0) * 100)
  150.         pygame.draw.rect(surface, RED, (10, y_offset, health_width, 10))
  151.         
  152.         pygame.draw.rect(surface, GRAY, (10, y_offset+20, 100, 10))
  153.         exp_width = int((player.exp / float(player.max_exp)) * 100)
  154.         pygame.draw.rect(surface, YELLOW, (10, y_offset+20, exp_width, 10))
  155.         
  156.         info_text = font.render("P{} Lv{} K{}".format(i+1, player.level, player.kills), True, WHITE)
  157.         surface.blit(info_text, (10, y_offset+40))
  158. def role_selection_menu(player_count):
  159.     roles = []
  160.     buttons = []
  161.     descriptions = [
  162.         "Role 1: 4-Direction Attack",
  163.         "Role 2: Diagonal Attack"
  164.     ]
  165.    
  166.     for i in range(player_count):
  167.         y_base = 150 + i*150
  168.         buttons.append([
  169.             Button("Player{} Role1".format(i+1), WIDTH//2-250, y_base, 200, 50),
  170.             Button("Player{} Role2".format(i+1), WIDTH//2+50, y_base, 200, 50)
  171.         ])
  172.    
  173.     confirm_btn = Button("Start Game", WIDTH//2-100, HEIGHT-100, 200, 50)
  174.    
  175.     while True:
  176.         screen.fill((30, 30, 30))
  177.         mouse_pos = pygame.mouse.get_pos()
  178.         
  179.         for event in pygame.event.get():
  180.             if event.type == pygame.QUIT:
  181.                 pygame.quit()
  182.                 return []
  183.             if event.type == pygame.MOUSEBUTTONDOWN:
  184.                 for i, pair in enumerate(buttons):
  185.                     for j, btn in enumerate(pair):
  186.                         if btn.rect.collidepoint(mouse_pos):
  187.                             if len(roles) <= i:
  188.                                 roles.append(j+1)
  189.                             else:
  190.                                 roles[i] = j+1
  191.                 if confirm_btn.rect.collidepoint(mouse_pos) and len(roles) == player_count:
  192.                     return roles
  193.         
  194.         desc_y = 100
  195.         for desc in descriptions:
  196.             text = font.render(desc, True, WHITE)
  197.             screen.blit(text, (WIDTH//2 - text.get_width()//2, desc_y))
  198.             desc_y += 30
  199.         
  200.         for i, pair in enumerate(buttons):
  201.             for j, btn in enumerate(pair):
  202.                 btn.check_hover(mouse_pos)
  203.                 btn.draw(screen)
  204.                 if i < len(roles) and roles[i] == j+1:
  205.                     pygame.draw.rect(screen, YELLOW, btn.rect.inflate(10,10), 3, border_radius=7)
  206.         
  207.         confirm_btn.check_hover(mouse_pos)
  208.         confirm_btn.draw(screen)
  209.         
  210.         pygame.display.flip()
  211.         clock.tick(30)
  212. def main_menu():
  213.     buttons = [
  214.         Button("1 Player", WIDTH//2-100, HEIGHT//2-50, 200, 50),
  215.         Button("2 Players", WIDTH//2-100, HEIGHT//2+20, 200, 50)
  216.     ]
  217.    
  218.     while True:
  219.         screen.fill((30, 30, 30))
  220.         mouse_pos = pygame.mouse.get_pos()
  221.         
  222.         for event in pygame.event.get():
  223.             if event.type == pygame.QUIT:
  224.                 pygame.quit()
  225.                 return (0, [])
  226.             if event.type == pygame.MOUSEBUTTONDOWN:
  227.                 for i, btn in enumerate(buttons):
  228.                     if btn.rect.collidepoint(mouse_pos):
  229.                         selected = i+1
  230.                         roles = role_selection_menu(selected)
  231.                         if roles:
  232.                             return (selected, roles)
  233.         
  234.         title = font.render("Vampire Survivors", True, WHITE)
  235.         screen.blit(title, (WIDTH//2 - title.get_width()//2, 100))
  236.         
  237.         for btn in buttons:
  238.             btn.check_hover(mouse_pos)
  239.             btn.draw(screen)
  240.         
  241.         pygame.display.flip()
  242.         clock.tick(30)
  243. def game_loop(player_count, roles):
  244.     background = GAME_IMAGES['background']
  245.     controls = [
  246.         {'up': pygame.K_w, 'down': pygame.K_s, 'left': pygame.K_a, 'right': pygame.K_d},
  247.         {'up': pygame.K_UP, 'down': pygame.K_DOWN, 'left': pygame.K_LEFT, 'right': pygame.K_RIGHT}
  248.     ]
  249.    
  250.     players = pygame.sprite.Group()
  251.     for i in range(player_count):
  252.         player = Player(
  253.             controls=controls[i],
  254.             role_type=roles[i],
  255.             pos_offset=-50 + i*100,
  256.             is_player2=(i==1)
  257.         )
  258.         players.add(player)
  259.    
  260.     all_sprites = pygame.sprite.Group(players)
  261.     enemies = pygame.sprite.Group()
  262.     bullets = pygame.sprite.Group()
  263.     exp_orbs = pygame.sprite.Group()
  264.    
  265.     enemy_spawn_timer = 0
  266.     attack_timer = 0
  267.     start_ticks = pygame.time.get_ticks()
  268.     time_limit = 300
  269.     running = True
  270.     while running:
  271.         screen.blit(background, (0, 0))
  272.         keys = pygame.key.get_pressed()
  273.         
  274.         elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000
  275.         time_left = max(time_limit - elapsed_seconds, 0)
  276.         if time_left <= 0:
  277.             running = False
  278.         for event in pygame.event.get():
  279.             if event.type == pygame.QUIT:
  280.                 running = False
  281.         enemy_spawn_timer += 1
  282.         if enemy_spawn_timer >= 60 - (sum(p.level for p in players)*2):
  283.             enemy = Enemy()
  284.             enemies.add(enemy)
  285.             all_sprites.add(enemy)
  286.             enemy_spawn_timer = 0
  287.         attack_timer += 1
  288.         if attack_timer >= 30:
  289.             for player in players:
  290.                 directions = player.get_attack_directions()
  291.                 for direction in directions:
  292.                     bullet = Bullet(player.rect.centerx, player.rect.centery, direction)
  293.                     bullets.add(bullet)
  294.                     all_sprites.add(bullet)
  295.             attack_timer = 0
  296.         for player in players:
  297.             player.update(keys)
  298.         enemies.update(players)
  299.         bullets.update()
  300.         exp_orbs.update()
  301.         for bullet in bullets:
  302.             hits = pygame.sprite.spritecollide(bullet, enemies, False)
  303.             if hits:
  304.                 bullet.penetration -= 1
  305.                 for enemy in hits:
  306.                     enemy.kill()
  307.                     exp_orb = ExpOrb(enemy.rect.centerx, enemy.rect.centery)
  308.                     exp_orbs.add(exp_orb)
  309.                     all_sprites.add(exp_orb)
  310.                     for player in players:
  311.                         if math.hypot(player.rect.x-enemy.rect.x, player.rect.y-enemy.rect.y) < 100:
  312.                             player.kills += 1
  313.                 if bullet.penetration <= 0:
  314.                     bullet.kill()
  315.         for player in players:
  316.             hits = pygame.sprite.spritecollide(player, exp_orbs, True)
  317.             if hits:
  318.                 player.exp += 10 * len(hits)
  319.                 if player.exp >= player.max_exp:
  320.                     player.level += 1
  321.                     player.exp -= player.max_exp
  322.                     player.max_exp = int(player.max_exp * 1.5)
  323.                     player.speed += 0.5
  324.         alive_players = [p for p in players if p.health > 0]
  325.         for player in alive_players:
  326.             if pygame.sprite.spritecollide(player, enemies, True):
  327.                 player.health -= 10
  328.         if len(alive_players) == 0 or time_left <= 0:
  329.             running = False
  330.         all_sprites.draw(screen)
  331.         draw_hud(screen, players, time_left)
  332.         pygame.display.flip()
  333.         clock.tick(30)
  334.     pygame.quit()
  335. if __name__ == "__main__":
  336.     player_count, roles = main_menu()
  337.     if player_count > 0:
  338.         game_loop(player_count, roles)
复制代码

互动引导


版权声明

"本项目为开源学习作品,遵照MIT协议,欢迎二次开发但需保留原作者信息"

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4