《用Python+PyGame开发双人生存游戏!源码解析+完整开发思路分享》

导语

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


        哈哈,怪物可以换成同学 的qq头像

游戏内容如下:

一、游戏展示 & 核心功能
  1. 游戏截图/GIF动图
    (建议添加游戏实际运行画面,展示双人操作、敌人生成、技能特效等)

  2. 核心玩法特性

    • 双人本地合作模式(WASD vs 方向键控制)
    • 两种可选角色:四方向攻击 vs 对角线攻击
    • 动态敌人系统:普通敌人 + 精英Boss
    • 角色成长体系:经验值升级/属性强化
    • 时间限制生存模式(5分钟倒计时)

二、技术实现亮点
  1. 技术栈

    • 语言:Python 3.x
    • 核心库:PyGame
    • 开发周期:约10小时
  2. 关键技术点

    - 精灵(Sprite)系统:玩家/敌人/子弹的统一管理
    - 基于三角函数的子弹轨迹计算(8方向射击)
    - 敌人AI:自动追踪玩家 + 精英怪弹幕攻击
    - 动态难度系统:敌人生成速度随玩家等级提升
    - 经验球漂浮动画(正弦函数实现)
    - 多菜单系统:主菜单/角色选择/游戏内HUD

三、代码结构解析

python

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

四、关键代码解读
  1. 角色控制系统

    python

    class Player(pygame.sprite.Sprite):def get_attack_directions(self):# 角色1:四方向射击 | 角色2:对角线射击return ["up", "down", "left", "right"] if self.role_type == 1 else ["up_left", "up_right", ...]
  2. 精英敌人弹幕算法

     

    python

    class EliteEnemy(Enemy):def shoot(self, target):# 45度间隔的8方向弹幕for angle in range(0, 360, 45):rad = math.radians(angle)bullet = EliteBullet(..., math.cos(rad)*speed, math.sin(rad)*speed)
  3. 动态难度机制

     

    python

    # 敌人生成速度随玩家等级提升
    enemy_spawn_interval = 60 - (sum(p.level for p in players) * 2)

五、如何运行游戏
  1. 环境准备

     

    bash

    pip install pygame
  2. 文件结构要求

    project/
    ├── main.py
    └── image/├── role1.png    # 角色1素材├── enemy.png    # 敌人素材└── ...
  3. 启动命令

     

    bash

    python main.py

六、开发心得 & 优化方向
  1. 踩坑经验

    • PyGame精灵组的碰撞检测优化
    • 双人模式下的事件冲突处理
    • 游戏节奏平衡性调试
  2. 待优化项

    • 添加音效系统
    • 实现网络联机功能
    • 增加更多角色/技能树
    • 开发关卡编辑器

七、完整代码获取

"关注+私信回复【生存游戏】获取完整代码包和素材资源!"

骗你的,代码就在这,复制就能用!

# -*- coding: utf-8 -*-
import os
import pygame
import random
import math# 初始化配置
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
IMAGE_DIR = os.path.join(BASE_DIR, 'image')RESOURCES = {'role1': os.path.join(IMAGE_DIR, 'role1.png'),'role2': os.path.join(IMAGE_DIR, 'role2.png'),'enemy': os.path.join(IMAGE_DIR, 'enemy.png'),'elite': os.path.join(IMAGE_DIR, 'elite.png'),'bullet': os.path.join(IMAGE_DIR, 'bullet.png'),'exp_orb': os.path.join(IMAGE_DIR, 'exp_orb.png'),'background': os.path.join(IMAGE_DIR, 'background.png')
}pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()# 颜色定义
WHITE = (255, 255, 255)
GRAY = (100, 100, 100)
BUTTON_COLOR = (50, 150, 50)
HOVER_COLOR = (70, 170, 70)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
ELITE_BULLET_COLOR = (255, 165, 0)font = pygame.font.Font(None, 24)def load_image(path, default_size=(30, 30)):try:image = pygame.image.load(path).convert_alpha()return pygame.transform.scale(image, default_size)except:surf = pygame.Surface(default_size)surf.fill(RED)return surfGAME_IMAGES = {'role1': load_image(RESOURCES['role1']),'role2': load_image(RESOURCES['role2']),'enemy': load_image(RESOURCES['enemy'], (20, 20)),'elite': load_image(RESOURCES['elite'], (40, 40)),'bullet': load_image(RESOURCES['bullet'], (10, 10)),'exp_orb': load_image(RESOURCES['exp_orb'], (10, 10)),'background': load_image(RESOURCES['background'], (WIDTH, HEIGHT))
}class Button:def __init__(self, text, x, y, w, h):self.rect = pygame.Rect(x, y, w, h)self.text = textself.color = BUTTON_COLORself.hover = Falsedef draw(self, surface):color = HOVER_COLOR if self.hover else BUTTON_COLORpygame.draw.rect(surface, color, self.rect, border_radius=5)text_surf = font.render(self.text, True, WHITE)text_rect = text_surf.get_rect(center=self.rect.center)surface.blit(text_surf, text_rect)def check_hover(self, mouse_pos):self.hover = self.rect.collidepoint(mouse_pos)class Player(pygame.sprite.Sprite):def __init__(self, controls, role_type, pos_offset=0, is_player2=False):super().__init__()self.role_type = role_typeself.image = GAME_IMAGES['role2' if role_type == 2 else 'role1']self.rect = self.image.get_rect(center=(WIDTH // 2 + pos_offset, HEIGHT // 2))self.speed = 5self.health = 100self.exp = 0self.level = 1self.max_exp = 100self.kills = 0self.controls = controlsdef update(self, keys):if keys[self.controls['up']]: self.rect.y -= self.speedif keys[self.controls['down']]: self.rect.y += self.speedif keys[self.controls['left']]: self.rect.x -= self.speedif keys[self.controls['right']]: self.rect.x += self.speedself.rect.clamp_ip(screen.get_rect())def get_attack_directions(self):return ["up", "down", "left", "right"] if self.role_type == 1 else ["up_left", "up_right", "down_left","down_right"]class Bullet(pygame.sprite.Sprite):def __init__(self, x, y, direction):super().__init__()self.image = GAME_IMAGES['bullet']self.rect = self.image.get_rect(center=(x, y))self.speed = 8self.start_pos = (x, y)self.max_distance = 300self.penetration = 2dir_mapping = {"up": (0, -1), "down": (0, 1),"left": (-1, 0), "right": (1, 0),"up_left": (-math.sqrt(0.5), -math.sqrt(0.5)),"up_right": (math.sqrt(0.5), -math.sqrt(0.5)),"down_left": (-math.sqrt(0.5), math.sqrt(0.5)),"down_right": (math.sqrt(0.5), math.sqrt(0.5))}dx_mult, dy_mult = dir_mapping[direction]self.dx = dx_mult * self.speedself.dy = dy_mult * self.speeddef update(self):self.rect.x += self.dxself.rect.y += self.dyif math.hypot(self.rect.x - self.start_pos[0], self.rect.y - self.start_pos[1]) > self.max_distance:self.kill()class Enemy(pygame.sprite.Sprite):def __init__(self):super().__init__()self.image = GAME_IMAGES['enemy']self.rect = self.image.get_rect(center=(random.choice([-100, WIDTH + 100]), random.randint(0, HEIGHT)))self.speed = 2def update(self, targets):if not targets: returnnearest = min(targets, key=lambda t: math.hypot(t.rect.x - self.rect.x, t.rect.y - self.rect.y))dx = nearest.rect.x - self.rect.xdy = nearest.rect.y - self.rect.ydist = math.hypot(dx, dy)if dist != 0:self.rect.x += dx / dist * self.speedself.rect.y += dy / dist * self.speedclass EliteEnemy(pygame.sprite.Sprite):def __init__(self):super().__init__()self.image = GAME_IMAGES['elite']self.rect = self.image.get_rect(center=(random.choice([-100, WIDTH + 100]), random.randint(0, HEIGHT)))self.speed = 1.5self.health = 50self.max_health = 50self.shoot_timer = 0self.bullet_speed = 5def update(self, targets):if not targets: returnnearest = min(targets, key=lambda t: math.hypot(t.rect.x - self.rect.x, t.rect.y - self.rect.y))dx = nearest.rect.x - self.rect.xdy = nearest.rect.y - self.rect.ydist = math.hypot(dx, dy)if dist != 0:self.rect.x += dx / dist * self.speedself.rect.y += dy / dist * self.speedself.shoot_timer += 1if self.shoot_timer >= 60:self.shoot(nearest)self.shoot_timer = 0def shoot(self, target):for angle in range(0, 360, 45):rad = math.radians(angle)bullet = EliteBullet(self.rect.centerx,self.rect.centery,math.cos(rad) * self.bullet_speed,math.sin(rad) * self.bullet_speed)bullets.add(bullet)all_sprites.add(bullet)class EliteBullet(pygame.sprite.Sprite):def __init__(self, x, y, dx, dy):super().__init__()self.image = pygame.Surface((15, 15))self.image.fill(ELITE_BULLET_COLOR)self.rect = self.image.get_rect(center=(x, y))self.dx = dxself.dy = dyself.max_distance = 400self.start_pos = (x, y)def update(self):self.rect.x += self.dxself.rect.y += self.dyif math.hypot(self.rect.x - self.start_pos[0], self.rect.y - self.start_pos[1]) > self.max_distance:self.kill()class ExpOrb(pygame.sprite.Sprite):def __init__(self, x, y):super().__init__()self.image = GAME_IMAGES['exp_orb']self.rect = self.image.get_rect(center=(x, y))self.float_timer = 0def update(self):self.float_timer += 1self.rect.y += math.sin(self.float_timer * 0.1) * 0.5def draw_hud(surface, players, time_left):time_text = font.render(f"Time: {time_left // 60:02}:{time_left % 60:02}", True, WHITE)surface.blit(time_text, (WIDTH // 2 - 60, 10))for i, player in enumerate(players):y_offset = 40 + i * 80pygame.draw.rect(surface, GRAY, (10, y_offset, 100, 10))health_width = int((player.health / 100.0) * 100)pygame.draw.rect(surface, RED, (10, y_offset, health_width, 10))pygame.draw.rect(surface, GRAY, (10, y_offset + 20, 100, 10))exp_width = int((player.exp / float(player.max_exp)) * 100)pygame.draw.rect(surface, YELLOW, (10, y_offset + 20, exp_width, 10))info_text = font.render(f"P{i + 1} Lv{player.level} K{player.kills}", True, WHITE)surface.blit(info_text, (10, y_offset + 40))def role_selection_menu(player_count):roles = []buttons = []descriptions = ["Role 1: 4-Direction Attack","Role 2: Diagonal Attack"]for i in range(player_count):y_base = 150 + i * 150buttons.append([Button(f"Player{i + 1} Role1", WIDTH // 2 - 250, y_base, 200, 50),Button(f"Player{i + 1} Role2", WIDTH // 2 + 50, y_base, 200, 50)])confirm_btn = Button("Start Game", WIDTH // 2 - 100, HEIGHT - 100, 200, 50)while True:screen.fill((30, 30, 30))mouse_pos = pygame.mouse.get_pos()for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()return []if event.type == pygame.MOUSEBUTTONDOWN:for i, pair in enumerate(buttons):for j, btn in enumerate(pair):if btn.rect.collidepoint(mouse_pos):roles = roles[:i] + [j + 1] + roles[i + 1:] if len(roles) > i else roles + [j + 1]if confirm_btn.rect.collidepoint(mouse_pos) and len(roles) == player_count:return rolesdesc_y = 100for desc in descriptions:text = font.render(desc, True, WHITE)screen.blit(text, (WIDTH // 2 - text.get_width() // 2, desc_y))desc_y += 30for i, pair in enumerate(buttons):for j, btn in enumerate(pair):btn.check_hover(mouse_pos)btn.draw(screen)if i < len(roles) and roles[i] == j + 1:pygame.draw.rect(screen, YELLOW, btn.rect.inflate(10, 10), 3, border_radius=7)confirm_btn.check_hover(mouse_pos)confirm_btn.draw(screen)pygame.display.flip()clock.tick(30)def main_menu():buttons = [Button("1 Player", WIDTH // 2 - 100, HEIGHT // 2 - 50, 200, 50),Button("2 Players", WIDTH // 2 - 100, HEIGHT // 2 + 20, 200, 50)]while True:screen.fill((30, 30, 30))mouse_pos = pygame.mouse.get_pos()for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()return (0, [])if event.type == pygame.MOUSEBUTTONDOWN:for i, btn in enumerate(buttons):if btn.rect.collidepoint(mouse_pos):roles = role_selection_menu(i + 1)if roles:return (i + 1, roles)title = font.render("Vampire Survivors", True, WHITE)screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 100))for btn in buttons:btn.check_hover(mouse_pos)btn.draw(screen)pygame.display.flip()clock.tick(30)def game_loop(player_count, roles):background = GAME_IMAGES['background']controls = [{'up': pygame.K_w, 'down': pygame.K_s, 'left': pygame.K_a, 'right': pygame.K_d},{'up': pygame.K_UP, 'down': pygame.K_DOWN, 'left': pygame.K_LEFT, 'right': pygame.K_RIGHT}]players = pygame.sprite.Group()for i in range(player_count):player = Player(controls=controls[i],role_type=roles[i],pos_offset=-50 + i * 100,is_player2=(i == 1))players.add(player)all_sprites = pygame.sprite.Group(players)enemies = pygame.sprite.Group()bullets = pygame.sprite.Group()exp_orbs = pygame.sprite.Group()enemy_spawn_timer = 0attack_timer = 0start_ticks = pygame.time.get_ticks()time_limit = 300running = Truewhile running:screen.blit(background, (0, 0))keys = pygame.key.get_pressed()elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000time_left = max(time_limit - elapsed_seconds, 0)if time_left <= 0:running = Falsefor event in pygame.event.get():if event.type == pygame.QUIT:running = Falseenemy_spawn_timer += 1if enemy_spawn_timer >= 60 - (sum(p.level for p in players) * 2):if random.random() < 0.1:enemy = EliteEnemy()else:enemy = Enemy()enemies.add(enemy)all_sprites.add(enemy)enemy_spawn_timer = 0attack_timer += 1if attack_timer >= 30:for player in players:for direction in player.get_attack_directions():bullet = Bullet(player.rect.centerx, player.rect.centery, direction)bullets.add(bullet)all_sprites.add(bullet)attack_timer = 0for player in players:player.update(keys)enemies.update(players)bullets.update()exp_orbs.update()for bullet in bullets:hits = pygame.sprite.spritecollide(bullet, enemies, False)if hits:bullet.penetration -= 1for enemy in hits:if isinstance(enemy, EliteEnemy):enemy.health -= 2if enemy.health <= 0:enemy.kill()for _ in range(5):exp_orb = ExpOrb(enemy.rect.centerx, enemy.rect.centery)exp_orbs.add(exp_orb)all_sprites.add(exp_orb)else:enemy.kill()exp_orb = ExpOrb(enemy.rect.centerx, enemy.rect.centery)exp_orbs.add(exp_orb)all_sprites.add(exp_orb)for player in players:if math.hypot(player.rect.x - enemy.rect.x, player.rect.y - enemy.rect.y) < 100:player.kills += 1if bullet.penetration <= 0:bullet.kill()for player in players:hits = pygame.sprite.spritecollide(player, exp_orbs, True)if hits:player.exp += 10 * len(hits)if player.exp >= player.max_exp:player.level += 1player.exp -= player.max_expplayer.max_exp = int(player.max_exp * 1.5)player.speed += 0.5if pygame.sprite.spritecollide(player, enemies, True):player.health -= 10bullet_hits = pygame.sprite.spritecollide(player, bullets, True)if bullet_hits:player.health -= 5alive_players = [p for p in players if p.health > 0]if not alive_players or time_left <= 0:running = Falseall_sprites.draw(screen)draw_hud(screen, players, time_left)pygame.display.flip()clock.tick(30)pygame.quit()if __name__ == "__main__":player_count, roles = main_menu()if player_count > 0:game_loop(player_count, roles)

 下面是python2的代码。方便不同环境的兄弟们运行:

# -*- coding: utf-8 -*-
import os
import pygame
import random
import math
from datetime import datetime# 初始化路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
IMAGE_DIR = os.path.join(BASE_DIR, 'image')RESOURCES = {'role1': os.path.join(IMAGE_DIR, 'role1.png'),'role2': os.path.join(IMAGE_DIR, 'role2.png'),'enemy': os.path.join(IMAGE_DIR, 'enemy.png'),'bullet': os.path.join(IMAGE_DIR, 'bullet.png'),'exp_orb': os.path.join(IMAGE_DIR, 'exp_orb.png'),'background': os.path.join(IMAGE_DIR, 'background.png')
}pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()# 颜色定义
WHITE = (255, 255, 255)
GRAY = (100, 100, 100)
BUTTON_COLOR = (50, 150, 50)
HOVER_COLOR = (70, 170, 70)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)font = pygame.font.Font(None, 24)def load_image(path, default_size=(30, 30)):try:image = pygame.image.load(path).convert_alpha()return pygame.transform.scale(image, default_size)except:surf = pygame.Surface(default_size)surf.fill(RED)return surfGAME_IMAGES = {'role1': load_image(RESOURCES['role1']),'role2': load_image(RESOURCES['role2']),'enemy': load_image(RESOURCES['enemy'], (20, 20)),'bullet': load_image(RESOURCES['bullet'], (10, 10)),'exp_orb': load_image(RESOURCES['exp_orb'], (10, 10)),'background': load_image(RESOURCES['background'], (WIDTH, HEIGHT))
}class Button:def __init__(self, text, x, y, w, h):self.rect = pygame.Rect(x, y, w, h)self.text = textself.color = BUTTON_COLORself.hover = Falsedef draw(self, surface):color = HOVER_COLOR if self.hover else BUTTON_COLORpygame.draw.rect(surface, color, self.rect, border_radius=5)text_surf = font.render(self.text, True, WHITE)text_rect = text_surf.get_rect(center=self.rect.center)surface.blit(text_surf, text_rect)def check_hover(self, mouse_pos):self.hover = self.rect.collidepoint(mouse_pos)class Player(pygame.sprite.Sprite):def __init__(self, controls, role_type, pos_offset=0, is_player2=False):pygame.sprite.Sprite.__init__(self)self.role_type = role_typeself.image = GAME_IMAGES['role2' if role_type == 2 else 'role1']self.rect = self.image.get_rect(center=(WIDTH//2 + pos_offset, HEIGHT//2))self.speed = 5self.health = 100self.exp = 0self.level = 1self.max_exp = 100self.kills = 0self.controls = controlsdef update(self, keys):if keys[self.controls['up']]:self.rect.y -= self.speedif keys[self.controls['down']]:self.rect.y += self.speedif keys[self.controls['left']]:self.rect.x -= self.speedif keys[self.controls['right']]:self.rect.x += self.speedself.rect.clamp_ip(screen.get_rect())def get_attack_directions(self):if self.role_type == 1:return ["up", "down", "left", "right"]else:return ["up_left", "up_right", "down_left", "down_right"]class Bullet(pygame.sprite.Sprite):def __init__(self, x, y, direction):pygame.sprite.Sprite.__init__(self)self.image = GAME_IMAGES['bullet']self.rect = self.image.get_rect(center=(x, y))self.speed = 8self.start_pos = (x, y)self.max_distance = 300self.penetration = 2self.dx, self.dy = 0, 0dir_mapping = {"up": (0, -1),"down": (0, 1),"left": (-1, 0),"right": (1, 0),"up_left": (-math.sqrt(0.5), -math.sqrt(0.5)),"up_right": (math.sqrt(0.5), -math.sqrt(0.5)),"down_left": (-math.sqrt(0.5), math.sqrt(0.5)),"down_right": (math.sqrt(0.5), math.sqrt(0.5))}dx_mult, dy_mult = dir_mapping[direction]self.dx = dx_mult * self.speedself.dy = dy_mult * self.speeddef update(self):self.rect.x += self.dxself.rect.y += self.dyif math.hypot(self.rect.x - self.start_pos[0], self.rect.y - self.start_pos[1]) > self.max_distance:self.kill()class Enemy(pygame.sprite.Sprite):def __init__(self):pygame.sprite.Sprite.__init__(self)self.image = GAME_IMAGES['enemy']self.rect = self.image.get_rect(center=(random.choice([-100, WIDTH+100]), random.randint(0, HEIGHT)))self.speed = 2def update(self, targets):if not targets: returnnearest = min(targets, key=lambda t: math.hypot(t.rect.x-self.rect.x, t.rect.y-self.rect.y))dx = nearest.rect.x - self.rect.xdy = nearest.rect.y - self.rect.ydist = math.hypot(dx, dy)if dist != 0:self.rect.x += dx / dist * self.speedself.rect.y += dy / dist * self.speedclass ExpOrb(pygame.sprite.Sprite):def __init__(self, x, y):pygame.sprite.Sprite.__init__(self)self.image = GAME_IMAGES['exp_orb']self.rect = self.image.get_rect(center=(x, y))self.float_timer = 0def update(self):self.float_timer += 1self.rect.y += math.sin(self.float_timer * 0.1) * 0.5def draw_hud(surface, players, time_left):time_text = font.render("Time: {:02}:{:02}".format(time_left//60, time_left%60), True, WHITE)surface.blit(time_text, (WIDTH//2 - 60, 10))for i, player in enumerate(players):y_offset = 40 + i*80pygame.draw.rect(surface, GRAY, (10, y_offset, 100, 10))health_width = int((player.health / 100.0) * 100)pygame.draw.rect(surface, RED, (10, y_offset, health_width, 10))pygame.draw.rect(surface, GRAY, (10, y_offset+20, 100, 10))exp_width = int((player.exp / float(player.max_exp)) * 100)pygame.draw.rect(surface, YELLOW, (10, y_offset+20, exp_width, 10))info_text = font.render("P{} Lv{} K{}".format(i+1, player.level, player.kills), True, WHITE)surface.blit(info_text, (10, y_offset+40))def role_selection_menu(player_count):roles = []buttons = []descriptions = ["Role 1: 4-Direction Attack","Role 2: Diagonal Attack"]for i in range(player_count):y_base = 150 + i*150buttons.append([Button("Player{} Role1".format(i+1), WIDTH//2-250, y_base, 200, 50),Button("Player{} Role2".format(i+1), WIDTH//2+50, y_base, 200, 50)])confirm_btn = Button("Start Game", WIDTH//2-100, HEIGHT-100, 200, 50)while True:screen.fill((30, 30, 30))mouse_pos = pygame.mouse.get_pos()for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()return []if event.type == pygame.MOUSEBUTTONDOWN:for i, pair in enumerate(buttons):for j, btn in enumerate(pair):if btn.rect.collidepoint(mouse_pos):if len(roles) <= i:roles.append(j+1)else:roles[i] = j+1if confirm_btn.rect.collidepoint(mouse_pos) and len(roles) == player_count:return rolesdesc_y = 100for desc in descriptions:text = font.render(desc, True, WHITE)screen.blit(text, (WIDTH//2 - text.get_width()//2, desc_y))desc_y += 30for i, pair in enumerate(buttons):for j, btn in enumerate(pair):btn.check_hover(mouse_pos)btn.draw(screen)if i < len(roles) and roles[i] == j+1:pygame.draw.rect(screen, YELLOW, btn.rect.inflate(10,10), 3, border_radius=7)confirm_btn.check_hover(mouse_pos)confirm_btn.draw(screen)pygame.display.flip()clock.tick(30)def main_menu():buttons = [Button("1 Player", WIDTH//2-100, HEIGHT//2-50, 200, 50),Button("2 Players", WIDTH//2-100, HEIGHT//2+20, 200, 50)]while True:screen.fill((30, 30, 30))mouse_pos = pygame.mouse.get_pos()for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()return (0, [])if event.type == pygame.MOUSEBUTTONDOWN:for i, btn in enumerate(buttons):if btn.rect.collidepoint(mouse_pos):selected = i+1roles = role_selection_menu(selected)if roles:return (selected, roles)title = font.render("Vampire Survivors", True, WHITE)screen.blit(title, (WIDTH//2 - title.get_width()//2, 100))for btn in buttons:btn.check_hover(mouse_pos)btn.draw(screen)pygame.display.flip()clock.tick(30)def game_loop(player_count, roles):background = GAME_IMAGES['background']controls = [{'up': pygame.K_w, 'down': pygame.K_s, 'left': pygame.K_a, 'right': pygame.K_d},{'up': pygame.K_UP, 'down': pygame.K_DOWN, 'left': pygame.K_LEFT, 'right': pygame.K_RIGHT}]players = pygame.sprite.Group()for i in range(player_count):player = Player(controls=controls[i],role_type=roles[i],pos_offset=-50 + i*100,is_player2=(i==1))players.add(player)all_sprites = pygame.sprite.Group(players)enemies = pygame.sprite.Group()bullets = pygame.sprite.Group()exp_orbs = pygame.sprite.Group()enemy_spawn_timer = 0attack_timer = 0start_ticks = pygame.time.get_ticks()time_limit = 300running = Truewhile running:screen.blit(background, (0, 0))keys = pygame.key.get_pressed()elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000time_left = max(time_limit - elapsed_seconds, 0)if time_left <= 0:running = Falsefor event in pygame.event.get():if event.type == pygame.QUIT:running = Falseenemy_spawn_timer += 1if enemy_spawn_timer >= 60 - (sum(p.level for p in players)*2):enemy = Enemy()enemies.add(enemy)all_sprites.add(enemy)enemy_spawn_timer = 0attack_timer += 1if attack_timer >= 30:for player in players:directions = player.get_attack_directions()for direction in directions:bullet = Bullet(player.rect.centerx, player.rect.centery, direction)bullets.add(bullet)all_sprites.add(bullet)attack_timer = 0for player in players:player.update(keys)enemies.update(players)bullets.update()exp_orbs.update()for bullet in bullets:hits = pygame.sprite.spritecollide(bullet, enemies, False)if hits:bullet.penetration -= 1for enemy in hits:enemy.kill()exp_orb = ExpOrb(enemy.rect.centerx, enemy.rect.centery)exp_orbs.add(exp_orb)all_sprites.add(exp_orb)for player in players:if math.hypot(player.rect.x-enemy.rect.x, player.rect.y-enemy.rect.y) < 100:player.kills += 1if bullet.penetration <= 0:bullet.kill()for player in players:hits = pygame.sprite.spritecollide(player, exp_orbs, True)if hits:player.exp += 10 * len(hits)if player.exp >= player.max_exp:player.level += 1player.exp -= player.max_expplayer.max_exp = int(player.max_exp * 1.5)player.speed += 0.5alive_players = [p for p in players if p.health > 0]for player in alive_players:if pygame.sprite.spritecollide(player, enemies, True):player.health -= 10if len(alive_players) == 0 or time_left <= 0:running = Falseall_sprites.draw(screen)draw_hud(screen, players, time_left)pygame.display.flip()clock.tick(30)pygame.quit()if __name__ == "__main__":player_count, roles = main_menu()if player_count > 0:game_loop(player_count, roles) 

互动引导

  1. 投票:
    "如果让你添加新功能,你会选择?
    A) 联机对战 B) 技能组合 C) BOSS战 D) 自定义角色"

  2. 讨论:
    "你在用Python开发游戏时遇到过哪些难题?欢迎评论区交流!"


版权声明

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/28680.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AutoGen学习笔记系列(一)Tutorial - Model

这个系列文章记录了学习微软 AutoGen 的过程&#xff0c;与 smolagents 学习笔记系列一样&#xff0c;仍然以官方教程自己的理解为主线&#xff0c;中间可能穿插几个番外支线的形式写博客。 【注意】&#xff1a;在阅读这篇文章之前需要确保已经按照其 Installation 小节完成必…

探秘基带算法:从原理到5G时代的通信变革【八】QAM 调制 / 解调

文章目录 2.7 QAM 调制 / 解调2.7.1 概述2.7.2 星座图星座图的结构与性能发射端的信息编码与接收端的解码差分编码的分类与实现差分编码的模4格雷加法器公式16QAM星座图与映射关系 2.7.3 信号表达式正交振幅调制的基本原理与系统分析相位误差对QAM性能的影响多电平正交振幅调制…

本地YARN集群部署

请先完成HDFS的前置部署&#xff0c;部署方式可查看:本地部署HDFS集群https://blog.csdn.net/m0_73641796/article/details/145998092?spm1001.2014.3001.5502 部署说明 组件配置文件启动进程备注Hadoop HDFS需修改 需启动: NameNode作为主节点 DataNode作为从节点 Secondary…

vivado 充分利用 IP 核

充分利用 IP 核 使用预先验证的 IP 核能够大幅减少设计和验证工作量&#xff0c;从而加速产品上市进程。如需了解更多有利用 IP 的信息&#xff0c;请参 阅以下资源&#xff1a; • 《 Vivado Design Suite 用户指南&#xff1a;采用 IP 进行设计》 (UG896) [ 参照 1…

Python-07PDF转Word

2025-03-04-PDF转Word DeepSeek等大模型从来都不是简单的写一个静态博客这么肤浅&#xff08;太多博主都只讲这个内容了&#xff09;借助全网大神的奇思妙想&#xff0c;拓展我狭隘的思维边界。 文章目录 2025-03-04-PDF转Word [toc]1-参考网址2-学习要点3-核心逻辑4-核心代码 …

android11使用gpio口控制led状态灯

目录 一、简介 二、解决方法 A、底层驱动 B、上层调用 C、验证 一、简介 1、需求&#xff1a;这里是用2个gpio口来控制LED灯&#xff0c;开机时默认亮蓝灯&#xff0c;按开机键&#xff0c;休眠亮红灯&#xff0c;唤醒亮蓝灯。 原理图&#xff1a; 这里由于主板上电阻R63…

el-input 设置类型为number时,输入中文后光标会上移,并且会出现上下箭头

光标上移 设置 el-input 的 typenumber后&#xff0c;只能输入数字&#xff0c;输入中文后会自动清空&#xff0c;但是会出现一个问题&#xff1a;【光标会上移&#xff0c;如下图】 解决方法&#xff1a;修改样式 注意&#xff1a;需要使用样式穿透 :deep( ) /*解决el-in…

el-table一格两行;概率;find

样式&#xff1a; 根据概率表头关键代码&#xff1a;rateRanges&#xff1b; scope.row.targetHitTable.find((target:any) > target.targetHitRate > range.min && target.targetHitRate < range.max)!.targetHitNum &#xff08;1&#xff09;!.是TypeScri…

CSS定位详解上

1. 相对定位 1.1 如何设置相对定位&#xff1f; 给元素设置 position:relative 即可实现相对定位。 可以使用 left 、 right 、 top 、 bottom 四个属性调整位置。 1.2 相对定位的参考点在哪里&#xff1f; 相对自己原来的位置 1.3 相对定位的特点&#xff1…

二、Java-封装playwright UI自动化(根据官网执行步骤,首先封装BrowserFactory枚举类及BrowserManager)

前言 查看playwright官网&#xff0c;api文档了解到&#xff0c;playwright的基本步骤&#xff1a; 1、实例化一个playwright 2、启动一个浏览器类型 3、打开一个页面 所以&#xff0c;在封装时需要有一个浏览器工厂类&#xff0c;定义不同的浏览器类型&#xff0c;在配置文…

【问题解决】Jenkins使用File的exists()方法判断文件存在,一直提示不存在的问题

小剧场 最近为了给项目组提供一个能给Java程序替换前端、后端的增量的流水线&#xff0c;继续写上了声明式流水线。 替换增量是根据JSON配置文件去增量目录里去取再替换到对应位置的&#xff0c;替换前需要判断增量文件是否存在。 判断文件是否存在&#xff1f;作为一个老Ja…

神策数据严正声明

近日&#xff0c;我们发现有不法分子冒充神策网络科技&#xff08;北京&#xff09;有限公司&#xff08;以下简称”神策数据”&#xff09;的名义&#xff0c;发布虚假的招聘广告、面试邀请&#xff0c;企图误导求职者并进行不法行为。对此&#xff0c;我司发布严正声明&#…

ICLR 2025|香港浸会大学可信机器学习和推理课题组专场

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; AITIME 01 ICLR 2025预讲会团队专场 AITIME 02 专场信息 01 Noisy Test-Time Adaptation in Vision-Language Models 讲者&#xff1a;曹晨涛&#xff0c;HKBU TMLR Group一年级博士生&#xff0c;目前关注基础…

MySQL增量更新数据:高效同步策略与PanguSync实战指南

Mysql增量更新数据软件下载https://pan.baidu.com/s/1WesHaKGO7uQMhPNE-BTDmg?pwdabcd#list/path%2F 在数据驱动的商业环境中&#xff0c;实时数据同步已成为企业数字化转型的关键。本文将深入探讨MySQL增量更新的核心技术&#xff0c;并详细解析如何通过PanguSync工具实现高…

无人机灯光原理与应用解析!

工作原理 LED光源&#xff1a;LED因其高效、长寿命、高亮度和轻便等优点&#xff0c;成为无人机灯光的主要光源。 电路控制&#xff1a;通过微控制器或飞行控制器调节电流&#xff0c;控制灯光的开关、亮度和颜色。 信号传输&#xff1a;灯光控制信号通过无线或有线方式从遥…

51c自动驾驶~合集52

我自己的原文哦~ https://blog.51cto.com/whaosoft/13383340 #世界模型如何推演未来的千万种可能 驾驶世界模型&#xff08;DWM&#xff09;&#xff0c;专注于预测驾驶过程中的场景演变&#xff0c;已经成为追求自动驾驶的一种有前景的范式。这些方法使自动驾驶系统能够更…

lamp平台介绍

一、lamp介绍 网站&#xff1a; 静态 动态 php语言 .php 作用&#xff1a;运行php语言编写动态网站应用 lamp Linux Apache MySQL PHP PHP是作为httpd的一个功能模块存在的 二、部署lamp平台 1、测试httpd是否可正常返回PHP的响应 2、测试PHP代码是否可正常连接数据…

ROM修改进阶教程------修改安卓机型SELinux宽容的几种方式方法 以及第三方系统中如何关闭SELinux宽容

SELinux是一种强制访问控制安全机制,用于增强Linux系统的安全性。在某些情况下,可能需要对 SELinux 进行宽容设置,以满足特定的应用需求。当SELinux处于宽容模式时,系统允许违反安全策略的行为发生,但不会阻止这些行为,通常会在日志中记录这些违规事件。这与强制模式不同…

如何在 Aptos 上铸造 NFT

今天文章的第一部分将演示如何在 Aptos 上铸造 NFT&#xff0c;并使用 Moralis 轻松获取所有交易详细信息。在这里&#xff0c;我们将指导您完成使用 Aptos CLI 铸造活动门票 NFT 基本代币的过程。您还将了解如何完成初始设置步骤。此外&#xff0c;您还将了解如何获取 Moralis…

Springboot 梳理

一、Springboot 特性 方便创建可独立运行的spring应用程序直接内嵌Tomcat等服务简化了项目的构建配置为spring及第三方库提供自动配置提供生产级特性无需生成代码或者进行xml配置 二、四大核心 自动配置起步依赖命令行界面Actuator - 生成级的特性 三、自动配置的实现原理 …