马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
导语
"你是否想过用Python开发一款可玩性高的双人合作游戏?本文将分享怎样从零开始实现一款类《吸血鬼幸存者》的生存射击游戏!包含完整源码剖析、角色体系设计、敌人AI逻辑等核心技能点,文末提供完整代码包下载!"
哈哈,怪物可以换成同学 的qq头像
游戏内容如下:
一、游戏展示 & 核心功能
- 游戏截图/GIF动图
(发起添加游戏现实运行画面,展示双人利用、敌人生成、技能特效等)
- 核心玩法特性
- 双人本地合作模式(WASD vs 方向键控制)
- 两种可选角色:四方向攻击 vs 对角线攻击
- 动态敌人体系:平凡敌人 + 精英Boss
- 角色发展体系:经验值升级/属性强化
- 时间限制生存模式(5分钟倒计时)
二、技能实现亮点
- 技能栈
- 语言:Python 3.x
- 核心库:PyGame
- 开发周期:约10小时
- 关键技能点
- - 精灵(Sprite)系统:玩家/敌人/子弹的统一管理
- - 基于三角函数的子弹轨迹计算(8方向射击)
- - 敌人AI:自动追踪玩家 + 精英怪弹幕攻击
- - 动态难度系统:敌人生成速度随玩家等级提升
- - 经验球漂浮动画(正弦函数实现)
- - 多菜单系统:主菜单/角色选择/游戏内HUD
复制代码 三、代码结构剖析
python
- # 代码模块示意图
- ├── Assets/ # 资源文件夹
- │ ├── image/ # 游戏素材(角色/敌人/背景图)
- ├── main.py # 主程序入口
- │ ├── 核心类:
- │ │ - Player # 玩家角色(移动/攻击/成长)
- │ │ - Enemy # 基础敌人AI
- │ │ - EliteEnemy # 精英Boss(弹幕攻击)
- │ │ - Bullet # 子弹物理系统
- │ │ - Button # 交互式GUI按钮
- │ ├── 游戏流程:
- │ │ - main_menu() # 主菜单
- │ │ - role_selection() # 角色选择
- │ │ - game_loop() # 核心游戏循环
复制代码 四、关键代码解读
- 角色控制体系
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", ...]
复制代码 - 精英敌人弹幕算法
- [/code] python
- [code]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)
复制代码 - 动态难度机制
- [/code] python
- [code]# 敌人生成速度随玩家等级提升
- enemy_spawn_interval = 60 - (sum(p.level for p in players) * 2)
复制代码 五、怎样运行游戏
- 环境准备
- [/code] bash
- [code]pip install pygame
复制代码 - 文件结构要求
- project/
- ├── main.py
- └── image/
- ├── role1.png # 角色1素材
- ├── enemy.png # 敌人素材
- └── ...
复制代码 - 启动命令
- [/code] bash
- [code]python main.py
复制代码 六、开发心得 & 优化方向
- 踩坑经验
- PyGame精灵组的碰撞检测优化
- 双人模式下的变乱冲突处置惩罚
- 游戏节奏均衡性调试
- 待优化项
- 添加音效体系
- 实现网络联机功能
- 增加更多角色/技能树
- 开发关卡编辑器
七、完整代码获取
"关注+私信回复【生存游戏】获取完整代码包和素材资源!"
骗你的,代码就在这,复制就能用!
- # -*- 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 surf
- GAME_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 = text
- self.color = BUTTON_COLOR
- self.hover = False
- def draw(self, surface):
- color = HOVER_COLOR if self.hover else BUTTON_COLOR
- pygame.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_type
- self.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 = 5
- self.health = 100
- self.exp = 0
- self.level = 1
- self.max_exp = 100
- self.kills = 0
- self.controls = controls
- def update(self, keys):
- if keys[self.controls['up']]: self.rect.y -= self.speed
- if keys[self.controls['down']]: self.rect.y += self.speed
- if keys[self.controls['left']]: self.rect.x -= self.speed
- if keys[self.controls['right']]: self.rect.x += self.speed
- self.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 = 8
- self.start_pos = (x, y)
- self.max_distance = 300
- self.penetration = 2
- dir_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.speed
- self.dy = dy_mult * self.speed
- def update(self):
- self.rect.x += self.dx
- self.rect.y += self.dy
- if 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 = 2
- def update(self, targets):
- if not targets: return
- nearest = 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.x
- dy = nearest.rect.y - self.rect.y
- dist = math.hypot(dx, dy)
- if dist != 0:
- self.rect.x += dx / dist * self.speed
- self.rect.y += dy / dist * self.speed
- class 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.5
- self.health = 50
- self.max_health = 50
- self.shoot_timer = 0
- self.bullet_speed = 5
- def update(self, targets):
- if not targets: return
- nearest = 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.x
- dy = nearest.rect.y - self.rect.y
- dist = math.hypot(dx, dy)
- if dist != 0:
- self.rect.x += dx / dist * self.speed
- self.rect.y += dy / dist * self.speed
- self.shoot_timer += 1
- if self.shoot_timer >= 60:
- self.shoot(nearest)
- self.shoot_timer = 0
- def 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 = dx
- self.dy = dy
- self.max_distance = 400
- self.start_pos = (x, y)
- def update(self):
- self.rect.x += self.dx
- self.rect.y += self.dy
- if 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 = 0
- def update(self):
- self.float_timer += 1
- self.rect.y += math.sin(self.float_timer * 0.1) * 0.5
- def 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 * 80
- pygame.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 * 150
- buttons.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 roles
- desc_y = 100
- for desc in descriptions:
- text = font.render(desc, True, WHITE)
- screen.blit(text, (WIDTH // 2 - text.get_width() // 2, desc_y))
- desc_y += 30
- for 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 = 0
- attack_timer = 0
- start_ticks = pygame.time.get_ticks()
- time_limit = 300
- running = True
- while running:
- screen.blit(background, (0, 0))
- keys = pygame.key.get_pressed()
- elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000
- time_left = max(time_limit - elapsed_seconds, 0)
- if time_left <= 0:
- running = False
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- running = False
- enemy_spawn_timer += 1
- if 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 = 0
- attack_timer += 1
- if 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 = 0
- for 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 -= 1
- for enemy in hits:
- if isinstance(enemy, EliteEnemy):
- enemy.health -= 2
- if 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 += 1
- if 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 += 1
- player.exp -= player.max_exp
- player.max_exp = int(player.max_exp * 1.5)
- player.speed += 0.5
- if pygame.sprite.spritecollide(player, enemies, True):
- player.health -= 10
- bullet_hits = pygame.sprite.spritecollide(player, bullets, True)
- if bullet_hits:
- player.health -= 5
- alive_players = [p for p in players if p.health > 0]
- if not alive_players or time_left <= 0:
- running = False
- all_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 surf
- GAME_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 = text
- self.color = BUTTON_COLOR
- self.hover = False
- def draw(self, surface):
- color = HOVER_COLOR if self.hover else BUTTON_COLOR
- pygame.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_type
- self.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 = 5
- self.health = 100
- self.exp = 0
- self.level = 1
- self.max_exp = 100
- self.kills = 0
- self.controls = controls
- def update(self, keys):
- if keys[self.controls['up']]:
- self.rect.y -= self.speed
- if keys[self.controls['down']]:
- self.rect.y += self.speed
- if keys[self.controls['left']]:
- self.rect.x -= self.speed
- if keys[self.controls['right']]:
- self.rect.x += self.speed
- self.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 = 8
- self.start_pos = (x, y)
- self.max_distance = 300
- self.penetration = 2
- self.dx, self.dy = 0, 0
- dir_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.speed
- self.dy = dy_mult * self.speed
- def update(self):
- self.rect.x += self.dx
- self.rect.y += self.dy
- if 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 = 2
- def update(self, targets):
- if not targets: return
- nearest = 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.x
- dy = nearest.rect.y - self.rect.y
- dist = math.hypot(dx, dy)
- if dist != 0:
- self.rect.x += dx / dist * self.speed
- self.rect.y += dy / dist * self.speed
- class 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 = 0
- def update(self):
- self.float_timer += 1
- self.rect.y += math.sin(self.float_timer * 0.1) * 0.5
- def 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*80
- pygame.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*150
- buttons.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+1
- if confirm_btn.rect.collidepoint(mouse_pos) and len(roles) == player_count:
- return roles
-
- desc_y = 100
- for desc in descriptions:
- text = font.render(desc, True, WHITE)
- screen.blit(text, (WIDTH//2 - text.get_width()//2, desc_y))
- desc_y += 30
-
- for 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+1
- roles = 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 = 0
- attack_timer = 0
- start_ticks = pygame.time.get_ticks()
- time_limit = 300
- running = True
- while running:
- screen.blit(background, (0, 0))
- keys = pygame.key.get_pressed()
-
- elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000
- time_left = max(time_limit - elapsed_seconds, 0)
- if time_left <= 0:
- running = False
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- running = False
- enemy_spawn_timer += 1
- if enemy_spawn_timer >= 60 - (sum(p.level for p in players)*2):
- enemy = Enemy()
- enemies.add(enemy)
- all_sprites.add(enemy)
- enemy_spawn_timer = 0
- attack_timer += 1
- if 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 = 0
- for 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 -= 1
- for 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 += 1
- if 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 += 1
- player.exp -= player.max_exp
- player.max_exp = int(player.max_exp * 1.5)
- player.speed += 0.5
- alive_players = [p for p in players if p.health > 0]
- for player in alive_players:
- if pygame.sprite.spritecollide(player, enemies, True):
- player.health -= 10
- if len(alive_players) == 0 or time_left <= 0:
- running = False
- all_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)
复制代码 互动引导
- 投票:
"如果让你添加新功能,你会选择?
A) 联机对战 B) 技能组合 C) BOSS战 D) 自界说角色"
- 讨论:
"你在用Python开发游戏时遇到过哪些难题?欢迎评论区交换!"
版权声明
"本项目为开源学习作品,遵照MIT协议,欢迎二次开发但需保留原作者信息"
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |