热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词专为Python游戏开发设计,能够根据用户需求生成结构完整、可运行的PyGame游戏代码。通过智能分析游戏类型、核心玩法和视觉风格等关键要素,提供从基础框架到完整功能的代码实现。提示词采用分步推理机制,确保代码逻辑严谨、注释清晰,支持多种游戏类型的开发需求,包括但不限于射击游戏、平台跳跃、益智解谜等。特别适合编程初学者学习游戏开发基础,也可帮助有经验的开发者快速实现原型设计。输出代码包含完整的游戏循环、事件处理、碰撞检测等核心模块,并遵循PyGame最佳实践规范。
## 游戏代码概述 - 游戏类型说明 - 平台跳跃(Platformer),像素风格,单关卡 - 核心功能特点 - 方向键左右移动,向上键或空格跳跃 - 收集星星计分 - 敌人简单巡逻与重力,碰到敌人会复位并统计死亡次数 - 关卡内使用矩形碰撞(平台、敌人、星星) - 收集完所有星星后出口开启,到达出口即关卡结束 - 结束界面显示:用时、收集数、死亡次数与简单评价,支持重新开始 - 技术实现要点 - 使用PyGame精灵系统和主循环 - 基于低分辨率虚拟画面+整数缩放的像素风渲染 - 轴向分离的矩形碰撞处理 - 巡逻敌人的边缘检测(前方悬空或撞墙时反向) - 关卡字符串解析生成Tile、道具、敌人、玩家与出口 ## 完整代码 ```python # 完整的PyGame平台跳跃游戏代码(像素风) # 需求特性: # - 方向键移动与跳跃 # - 收集星星 # - 敌人简单巡逻 # - 主循环内处理键盘事件与矩形碰撞 # - 关卡结束显示得分与简单报告提示 # - 像素风格(低分辨率渲染到高分辨率窗口) import pygame import sys from pygame.math import Vector2 # ---------------------------- # 基本设置(像素风低分辨率 + 整数放大) # ---------------------------- pygame.init() VIRTUAL_W, VIRTUAL_H = 320, 180 # 虚拟渲染分辨率(像素风) SCALE = 3 # 窗口缩放倍数(整数,保证像素清晰) WIN_W, WIN_H = VIRTUAL_W * SCALE, VIRTUAL_H * SCALE TILE = 16 # 瓦片大小(像素) FPS = 60 # 颜色定义(简洁像素调色) COL_BG = (25, 25, 35) # 背景深蓝灰 COL_PLATFORM = (60, 60, 80) # 平台块深灰 COL_PLAYER = (90, 200, 255) # 玩家青蓝 COL_PLAYER_OUTLINE = (20, 40, 55) COL_ENEMY = (240, 90, 90) # 敌人桃红 COL_STAR = (255, 220, 90) # 星星黄 COL_STAR_GLOW = (255, 255, 180) COL_TEXT = (240, 240, 240) # 文本 COL_DOOR_LOCK = (120, 120, 160) COL_DOOR_OPEN = (120, 200, 120) # ---------------------------- # 关卡数据(20列 x 11行) # 字符说明: # . 空 | X 平台 | S 星星 | E 敌人 | P 玩家出生点 | D 出口 # ---------------------------- LEVEL_DATA = [ "....................", "...S....XXX....S....", "...X.........E......", "...........XXX......", "..XXX.............S.", "...........S........", ".P....XXX...........", "XXXX.........XXXX...", ".....S....E.........", ".................D..", "XXXXXXXXXXXXXXXXXXXX", ] ROWS = len(LEVEL_DATA) COLS = len(LEVEL_DATA[0]) # ---------------------------- # 工具函数:创建简单像素块贴图 # ---------------------------- def make_block_surface(w, h, color, outline=None): surf = pygame.Surface((w, h), pygame.SRCALPHA) surf.fill(color) if outline: pygame.draw.rect(surf, outline, surf.get_rect(), 1) return surf def clamp(v, lo, hi): return max(lo, min(hi, v)) # ---------------------------- # 精灵类定义 # ---------------------------- class Platform(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.image = make_block_surface(TILE, TILE, COL_PLATFORM) self.rect = self.image.get_rect(topleft=(x, y)) class Star(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() # 星星是小块,加轻微发光边 self.base = make_block_surface(TILE, TILE, (0, 0, 0, 0)) pygame.draw.rect(self.base, COL_STAR, pygame.Rect(5, 5, 6, 6)) pygame.draw.rect(self.base, COL_STAR_GLOW, pygame.Rect(4, 4, 8, 8), 1) self.image = self.base.copy() self.rect = self.image.get_rect(topleft=(x, y)) self.t = 0.0 def update(self, dt): # 简单闪烁动画 self.t += dt pulse = (pygame.math.sin(self.t * 6.0) + 1.0) * 0.5 # 0..1 # 通过Alpha实现闪烁 self.image = self.base.copy() self.image.set_alpha(180 + int(75 * pulse)) class Enemy(pygame.sprite.Sprite): def __init__(self, x, y, solids_map): super().__init__() # 敌人是16x12的小矩形,带描边 self.image = make_block_surface(TILE, TILE - 4, COL_ENEMY, outline=(80, 20, 20)) self.rect = self.image.get_rect(midbottom=(x + TILE // 2, y + TILE)) self.pos = Vector2(self.rect.x, self.rect.y) self.vel = Vector2(40.0, 0.0) # 初速度向右 self.solids_map = solids_map self.gravity = 900.0 self.speed = 40.0 def is_solid_at(self, px, py): tx = int(px // TILE) ty = int(py // TILE) if 0 <= tx < COLS and 0 <= ty < ROWS: return self.solids_map[ty][tx] return False def update(self, dt, platforms): # 重力 self.vel.y += self.gravity * dt # 水平巡逻:前方阻挡或前方地面缺失则反向 dir_sign = 1 if self.vel.x >= 0 else -1 ahead_x = self.rect.centerx + dir_sign * (self.rect.width // 2 + 1) foot_y = self.rect.bottom + 1 # 检查前方是否墙 if self.is_solid_at(ahead_x, self.rect.centery) or self.is_solid_at(ahead_x, self.rect.top + 1): self.vel.x *= -1 else: # 检查前方脚下是否有地面 if not self.is_solid_at(ahead_x, foot_y): self.vel.x *= -1 # 应用水平位移 self.pos.x += self.vel.x * dt self.rect.x = int(self.pos.x) # 水平方向碰撞(防止嵌入墙体) hits = pygame.sprite.spritecollide(self, platforms, False) for p in hits: if self.vel.x > 0: self.rect.right = p.rect.left elif self.vel.x < 0: self.rect.left = p.rect.right self.pos.x = self.rect.x self.vel.x *= -1 # 撞墙反向 # 竖直方向 self.pos.y += self.vel.y * dt self.rect.y = int(self.pos.y) hits = pygame.sprite.spritecollide(self, platforms, False) for p in hits: if self.vel.y > 0: self.rect.bottom = p.rect.top elif self.vel.y < 0: self.rect.top = p.rect.bottom self.pos.y = self.rect.y self.vel.y = 0.0 class Player(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() # 玩家:14x16,有外描边 self.image = make_block_surface(14, 16, COL_PLAYER, outline=COL_PLAYER_OUTLINE) self.rect = self.image.get_rect(midbottom=(x + TILE // 2, y + TILE)) self.pos = Vector2(self.rect.x, self.rect.y) self.vel = Vector2(0.0, 0.0) self.move_speed = 110.0 self.gravity = 900.0 self.jump_speed = 270.0 self.on_ground = False # 跳跃辅助(容错) self.coyote_time = 0.1 self.coyote_timer = 0.0 self.jump_buffer_time = 0.12 self.jump_buffer_timer = 0.0 def handle_input(self, dt): keys = pygame.key.get_pressed() left = keys[pygame.K_LEFT] right = keys[pygame.K_RIGHT] self.vel.x = 0.0 if left: self.vel.x -= self.move_speed if right: self.vel.x += self.move_speed def queue_jump(self): self.jump_buffer_timer = self.jump_buffer_time def update(self, dt, platforms): # 输入 self.handle_input(dt) # 更新计时器 if self.on_ground: self.coyote_timer = self.coyote_time else: self.coyote_timer = max(0.0, self.coyote_timer - dt) self.jump_buffer_timer = max(0.0, self.jump_buffer_timer - dt) # 跳跃判定(缓冲 + 土狼时间) if self.jump_buffer_timer > 0 and self.coyote_timer > 0: self.vel.y = -self.jump_speed self.on_ground = False self.jump_buffer_timer = 0.0 # 重力 self.vel.y += self.gravity * dt self.vel.y = clamp(self.vel.y, -1000, 1000) # X轴移动 + 碰撞 self.pos.x += self.vel.x * dt self.rect.x = int(self.pos.x) hits = pygame.sprite.spritecollide(self, platforms, False) for p in hits: if self.vel.x > 0: self.rect.right = p.rect.left elif self.vel.x < 0: self.rect.left = p.rect.right self.pos.x = self.rect.x # Y轴移动 + 碰撞 self.pos.y += self.vel.y * dt self.rect.y = int(self.pos.y) hits = pygame.sprite.spritecollide(self, platforms, False) self.on_ground = False for p in hits: if self.vel.y > 0: self.rect.bottom = p.rect.top self.on_ground = True elif self.vel.y < 0: self.rect.top = p.rect.bottom self.pos.y = self.rect.y self.vel.y = 0.0 # 限制在屏幕内(虚拟坐标) self.rect.left = max(0, self.rect.left) self.rect.right = min(VIRTUAL_W, self.rect.right) self.pos.x = self.rect.x # 若掉出屏幕底部,交由Game处理复位 class Door(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.closed_img = make_block_surface(TILE, TILE*2, COL_DOOR_LOCK, outline=(50, 50, 70)) self.open_img = make_block_surface(TILE, TILE*2, COL_DOOR_OPEN, outline=(50, 70, 50)) self.image = self.closed_img self.rect = self.image.get_rect(bottomleft=(x, y + TILE)) # 门两格高,底对齐地面 self.is_open = False def set_open(self, open_state: bool): self.is_open = open_state self.image = self.open_img if self.is_open else self.closed_img # ---------------------------- # 游戏主类 # ---------------------------- class Game: def __init__(self): pygame.display.set_caption("像素平台跳跃 - 收集星星") self.screen = pygame.display.set_mode((WIN_W, WIN_H)) self.canvas = pygame.Surface((VIRTUAL_W, VIRTUAL_H)) self.clock = pygame.time.Clock() self.font = pygame.font.SysFont("arial", 10, bold=False) self.running = True self.state = "playing" # "playing" or "finished" # 关卡与对象容器 self.platforms = pygame.sprite.Group() self.stars = pygame.sprite.Group() self.enemies = pygame.sprite.Group() self.all_sprites = pygame.sprite.Group() self.door = None self.player = None # 统计 self.star_total = 0 self.star_collected = 0 self.deaths = 0 self.time_elapsed = 0.0 # solids map(用于快速检测瓦片是否为实心) self.solids_map = [[False for _ in range(COLS)] for _ in range(ROWS)] self.load_level() self.reset_runtime_stats() def reset_runtime_stats(self): self.state = "playing" self.star_collected = 0 self.deaths = 0 self.time_elapsed = 0.0 def load_level(self): # 清空 self.platforms.empty() self.stars.empty() self.enemies.empty() self.all_sprites.empty() self.door = None self.player = None # 重建solids map for r in range(ROWS): for c in range(COLS): self.solids_map[r][c] = (LEVEL_DATA[r][c] == 'X') # 解析关卡 spawn_pos = (0, 0) door_pos = None for r, row in enumerate(LEVEL_DATA): for c, ch in enumerate(row): x, y = c * TILE, r * TILE if ch == 'X': block = Platform(x, y) self.platforms.add(block) elif ch == 'S': star = Star(x, y) self.stars.add(star) self.all_sprites.add(star) elif ch == 'E': enemy = Enemy(x, y, self.solids_map) self.enemies.add(enemy) self.all_sprites.add(enemy) elif ch == 'P': spawn_pos = (x, y) elif ch == 'D': door_pos = (x, y) self.star_total = len(self.stars) # 创建玩家与门 self.player = Player(*spawn_pos) self.all_sprites.add(self.player) if door_pos: self.door = Door(*door_pos) self.all_sprites.add(self.door) # 平台需要在独立组中用于碰撞 # 绘制时两者都画到canvas上 def respawn_player(self): # 找到出生点 for r, row in enumerate(LEVEL_DATA): for c, ch in enumerate(row): if ch == 'P': x, y = c * TILE, r * TILE self.player.rect.midbottom = (x + TILE // 2, y + TILE) self.player.pos = Vector2(self.player.rect.x, self.player.rect.y) self.player.vel = Vector2(0, 0) return def finish_level(self): self.state = "finished" def draw_text(self, surf, text, x, y, color=COL_TEXT, align="topleft"): img = self.font.render(text, True, color) rect = img.get_rect(**{align: (x, y)}) surf.blit(img, rect) def update_play(self, dt): self.time_elapsed += dt # 处理事件(跳跃按键在事件循环里加缓冲) # 移动输入在Player内部处理 # 更新对象 for star in self.stars: star.update(dt) for enemy in self.enemies: enemy.update(dt, self.platforms) self.player.update(dt, self.platforms) # 掉出屏幕底部 -> 复位并计死亡 if self.player.rect.top > VIRTUAL_H + 50: self.deaths += 1 self.respawn_player() # 碰到敌人 -> 复位并计死亡 if pygame.sprite.spritecollide(self.player, self.enemies, False): self.deaths += 1 self.respawn_player() # 收集星星 hits = pygame.sprite.spritecollide(self.player, self.stars, dokill=True) if hits: self.star_collected += len(hits) # 门状态 if self.door: self.door.set_open(self.star_collected >= self.star_total) # 开门后碰撞即通关 if self.door.is_open and self.player.rect.colliderect(self.door.rect): self.finish_level() def draw_world(self): # 背景 self.canvas.fill(COL_BG) # 绘制平台 for p in self.platforms: self.canvas.blit(p.image, p.rect) # 绘制道具与敌人、玩家、门 # 控制层级:星星 -> 敌人 -> 门 -> 玩家(随意,像素风简单) for star in self.stars: self.canvas.blit(star.image, star.rect) for enemy in self.enemies: self.canvas.blit(enemy.image, enemy.rect) if self.door: self.canvas.blit(self.door.image, self.door.rect) self.canvas.blit(self.player.image, self.player.rect) # UI self.draw_text(self.canvas, f"Stars: {self.star_collected}/{self.star_total}", 4, 4) self.draw_text(self.canvas, f"Deaths: {self.deaths}", 4, 16) self.draw_text(self.canvas, f"Time: {self.time_elapsed:0.2f}s", 4, 28) # 像素放大绘制到窗口 surf_scaled = pygame.transform.scale(self.canvas, (WIN_W, WIN_H)) self.screen.blit(surf_scaled, (0, 0)) pygame.display.flip() def draw_finish(self): # 背景 + 结束面板 self.canvas.fill(COL_BG) # 简单报告 total = self.star_total got = self.star_collected deaths = self.deaths time_used = self.time_elapsed # 简单评级(仅提示) rank = "S" if deaths >= 3 or time_used > 60: rank = "B" elif deaths >= 1 or time_used > 40: rank = "A" # 面板 panel = pygame.Surface((VIRTUAL_W - 40, VIRTUAL_H - 60)) panel.fill((35, 35, 50)) pygame.draw.rect(panel, (80, 80, 110), panel.get_rect(), 2) # 文本内容 cx = panel.get_width() // 2 self.draw_text(panel, "关卡完成!", cx, 14, align="midtop") self.draw_text(panel, f"用时: {time_used:0.2f} 秒", cx, 38, align="midtop") self.draw_text(panel, f"收集: {got}/{total} 颗星星", cx, 54, align="midtop") self.draw_text(panel, f"死亡: {deaths} 次", cx, 70, align="midtop") self.draw_text(panel, f"评价: {rank}", cx, 86, align="midtop") self.draw_text(panel, "按 R 重新开始,Esc 退出", cx, panel.get_height() - 22, color=(200, 220, 255), align="midtop") self.canvas.blit(panel, (20, 30)) surf_scaled = pygame.transform.scale(self.canvas, (WIN_W, WIN_H)) self.screen.blit(surf_scaled, (0, 0)) pygame.display.flip() def run(self): while self.running: dt = self.clock.tick(FPS) / 1000.0 # 事件处理 for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False elif event.type == pygame.KEYDOWN: if self.state == "playing": if event.key in (pygame.K_UP, pygame.K_SPACE): self.player.queue_jump() if event.key == pygame.K_ESCAPE: self.running = False elif self.state == "finished": if event.key == pygame.K_r: # 重置关卡(重新加载星星与敌人,统计归零) self.load_level() self.reset_runtime_stats() if event.key == pygame.K_ESCAPE: self.running = False # 更新与绘制 if self.state == "playing": self.update_play(dt) self.draw_world() else: self.draw_finish() pygame.quit() sys.exit() if __name__ == "__main__": Game().run() ``` ## 代码说明 - 关键函数和类的作用说明 - Game:游戏主控制器,负责主循环、状态管理(playing/finished)、事件处理、关卡加载、绘制与统计 - Player:玩家角色,支持左右移动与跳跃,带“土狼时间”和“跳跃缓冲”,进行轴向分离的矩形碰撞 - Enemy:敌人带重力,沿地面巡逻;遇到墙或前方悬空时反向;与玩家碰撞会触发玩家复位并统计死亡 - Star:收集物,带简单闪烁动画;玩家碰撞后被移除并加分 - Platform:静态平台块,用于碰撞 - Door:出口;在收集完全部星星后开启;玩家接触开启的出口后进入结束界面 - LEVEL_DATA:关卡字符数组,解析为对象;X为平台,S为星星,E为敌人,P为玩家出生点,D为出口 - 像素风渲染:先绘制到320x180虚拟画布,再使用整数倍缩放到窗口,保证像素清晰 - 运行要求和依赖说明 - Python 3.8+(建议) - pygame 2.x - 运行方式:安装pygame后,直接执行该脚本 - pip install pygame - python game.py - 可能的扩展和改进建议 - 多关卡与关卡切换:将LEVEL_DATA列表扩展为多个关卡,完成一关后进入下一关 - 更完善的敌人交互:例如允许从上方踩敌人让其短暂眩晕,而非直接复位 - 动画与特效:为玩家、敌人、星星与门添加更丰富的逐帧动画和粒子效果 - 音效与背景音乐:收集星星、跳跃、通关等加入音效与简单BGM(使用自制或开源音源) - 相机与大地图:加入摄像机跟随以支持大尺寸关卡滚动 - UI完善:添加生命值、暂停菜单、设置选项、可视化按键提示 - 性能优化:将平台碰撞切分为网格桶(spatial hashing)以支持更大更复杂的关卡 本代码严格遵循矩形碰撞与主循环最佳实践,逻辑清晰、即开即玩,适合学习与原型开发。
## 游戏代码概述 - 游戏类型说明 - 俯视角“霓虹能量”射击(非暴力表现):玩家发射光子脉冲去“标记/关闭”几何体无人机(两种类型),更强调技巧和机制对比。 - 核心功能特点 - WASD移动,鼠标指向瞄准,左键连发光子脉冲 - 按 T 切换两种射击机制(A/B 对比) - A:反弹模式(脉冲可在墙面反弹,击中目标后消失) - B:穿透模式(脉冲可穿透多个目标,不在墙面反弹) - 两种敌人 - Seeker:持续追踪玩家 - Dasher:墙面弹射移动并周期性朝玩家加速突进 - 波次与计时 - 显示累计时间、当前波次、击中计分 - 波次随时间或清场自动推进,数量与速度逐步增加 - 简约霓虹视觉:黑底+加法混合的发光圆与线条 - 技术实现要点 - 使用 pygame.sprite 精灵与 collide_circle 实现圆形碰撞 - 自定义“霓虹光晕”缓存,减少重复绘制开销 - 子弹内置命中冷却,避免穿透模式多帧重复伤害同一目标 - 场景管理:主循环、波次管理、对象组更新与渲染分离 ## 完整代码 ```python # -*- coding: utf-8 -*- # 霓虹俯视射击(非暴力) - PyGame 单文件可运行示例 # 控制: # - 移动:WASD # - 射击:鼠标左键 # - 切换模式:T(反弹/穿透) # - 退出:ESC 或 关闭窗口 # # 依赖:pygame # 安装:pip install pygame # 运行:python neon_topdown.py import math import random import sys import pygame # ----------------------- # 全局配置 # ----------------------- WIDTH, HEIGHT = 960, 600 FPS = 60 BG_COLOR = (5, 5, 10) # 深色底 NEON_CYAN = (0, 255, 255) NEON_MAGENTA = (255, 0, 200) NEON_YELLOW = (255, 255, 0) NEON_WHITE = (255, 255, 255) NEON_GREEN = (0, 255, 160) PLAYER_COLOR = NEON_CYAN SEEKER_COLOR = NEON_MAGENTA DASHER_COLOR = NEON_YELLOW BULLET_COLOR = NEON_WHITE from pygame.math import Vector2 as Vec2 # ----------------------- # 霓虹绘制与缓存 # ----------------------- class NeonCache: circle_cache = {} # key: (color, radius, glow), value: Surface @staticmethod def get_glow_circle(color, radius, glow=10): key = (color, radius, glow) if key in NeonCache.circle_cache: return NeonCache.circle_cache[key] size = (radius + glow) * 2 + 2 surf = pygame.Surface((size, size), pygame.SRCALPHA) cx, cy = size // 2, size // 2 # 多层圆模拟光晕(从外到内,透明度递增,半径递减) layers = max(4, glow // 2) for i in range(layers, 0, -1): # 插值半径与透明度 ri = radius + int(glow * (i / layers)) alpha = int(30 * (i / layers)) # 外圈更淡 col = (color[0], color[1], color[2], alpha) pygame.draw.circle(surf, col, (cx, cy), ri) # 最内层实体圆 pygame.draw.circle(surf, (*color, 240), (cx, cy), radius) NeonCache.circle_cache[key] = surf return surf def blit_neon_circle(surface, pos, color, radius, glow=10): surf = NeonCache.get_glow_circle(color, radius, glow) rect = surf.get_rect(center=(int(pos[0]), int(pos[1]))) # 使用加法混合让光叠加 surface.blit(surf, rect, special_flags=pygame.BLEND_ADD) def draw_neon_line(surface, color, start, end, width=2, glow=6): # 用一个临时透明层绘制多层线条,再以加法混合叠加 temp = pygame.Surface(surface.get_size(), pygame.SRCALPHA) layers = max(3, glow // 2) for i in range(layers, 0, -1): w = width + i * 2 alpha = int(40 * (i / layers)) col = (color[0], color[1], color[2], alpha) pygame.draw.line(temp, col, start, end, w) # 实体线 pygame.draw.line(temp, (*color, 240), start, end, width) surface.blit(temp, (0, 0), special_flags=pygame.BLEND_ADD) # ----------------------- # 工具函数 # ----------------------- def clamp(value, a, b): return max(a, min(b, value)) def random_pos_away_from(center: Vec2, min_dist=120): for _ in range(64): x = random.uniform(40, WIDTH - 40) y = random.uniform(40, HEIGHT - 40) p = Vec2(x, y) if p.distance_to(center) >= min_dist: return p return Vec2(random.uniform(80, WIDTH - 80), random.uniform(80, HEIGHT - 80)) # ----------------------- # 精灵基类与实体 # ----------------------- class GlowSprite(pygame.sprite.Sprite): def __init__(self): super().__init__() # 用于 collide_circle self.radius = 10 self.image = pygame.Surface((2 * self.radius, 2 * self.radius), pygame.SRCALPHA) self.rect = self.image.get_rect() def draw(self, surface): pass class Player(GlowSprite): def __init__(self, pos: Vec2): super().__init__() self.pos = Vec2(pos) self.vel = Vec2(0, 0) self.speed = 280 self.radius = 14 self.image = pygame.Surface((2 * self.radius, 2 * self.radius), pygame.SRCALPHA) self.rect = self.image.get_rect(center=self.pos) self.color = PLAYER_COLOR self.shoot_cooldown = 0.12 # 连发频率 self.shoot_timer = 0.0 self.bullet_mode = "bounce" # "bounce" or "pierce" def toggle_mode(self): self.bullet_mode = "pierce" if self.bullet_mode == "bounce" else "bounce" def handle_input(self, dt): keys = pygame.key.get_pressed() move = Vec2(0, 0) if keys[pygame.K_w]: move.y -= 1 if keys[pygame.K_s]: move.y += 1 if keys[pygame.K_a]: move.x -= 1 if keys[pygame.K_d]: move.x += 1 if move.length_squared() > 0: move = move.normalize() self.vel = move * self.speed self.pos += self.vel * dt self.pos.x = clamp(self.pos.x, 20, WIDTH - 20) self.pos.y = clamp(self.pos.y, 20, HEIGHT - 20) self.rect.center = (int(self.pos.x), int(self.pos.y)) def try_shoot(self, dt, bullets_group): self.shoot_timer -= dt if self.shoot_timer > 0: return mouse_buttons = pygame.mouse.get_pressed(num_buttons=3) if mouse_buttons[0]: mouse_pos = Vec2(pygame.mouse.get_pos()) dir_vec = mouse_pos - self.pos if dir_vec.length_squared() == 0: return direction = dir_vec.normalize() # 发射位置略微离开玩家,避免与自身碰撞 spawn_pos = self.pos + direction * (self.radius + 10) speed = 520 if self.bullet_mode == "bounce": bullets_group.add(Bullet(spawn_pos, direction * speed, mode="bounce", bounces=3)) else: bullets_group.add(Bullet(spawn_pos, direction * speed, mode="pierce")) self.shoot_timer = self.shoot_cooldown def update(self, dt, bullets_group): self.handle_input(dt) self.try_shoot(dt, bullets_group) def draw(self, surface): # 玩家主体 blit_neon_circle(surface, self.pos, self.color, self.radius, glow=10) # 指向线(短线) mouse_pos = Vec2(pygame.mouse.get_pos()) dir_vec = mouse_pos - self.pos if dir_vec.length_squared() > 0: direction = dir_vec.normalize() a = self.pos + direction * (self.radius + 4) b = self.pos + direction * 28 draw_neon_line(surface, NEON_CYAN, a, b, width=2, glow=8) # 鼠标瞄准目标圈 blit_neon_circle(surface, mouse_pos, (80, 200, 255), 4, glow=6) class Bullet(GlowSprite): def __init__(self, pos: Vec2, velocity: Vec2, mode="bounce", bounces=3): super().__init__() self.pos = Vec2(pos) self.vel = Vec2(velocity) self.mode = mode # "bounce" or "pierce" self.radius = 4 self.image = pygame.Surface((2 * self.radius, 2 * self.radius), pygame.SRCALPHA) self.rect = self.image.get_rect(center=self.pos) self.color = BULLET_COLOR self.lifetime = 4.0 if mode == "bounce" else 2.6 self.bounces_left = bounces if mode == "bounce" else 0 self.hit_cooldowns = {} # enemy_id -> remaining_time (用于穿透模式的多帧重复命中抑制) def can_hit(self, enemy): eid = id(enemy) return self.hit_cooldowns.get(eid, 0) <= 0 def register_hit(self, enemy, cooldown=0.12): eid = id(enemy) self.hit_cooldowns[eid] = cooldown def update(self, dt): self.lifetime -= dt if self.lifetime <= 0: self.kill() return # 冷却计时更新 if self.hit_cooldowns: to_del = [] for k in list(self.hit_cooldowns.keys()): self.hit_cooldowns[k] -= dt if self.hit_cooldowns[k] <= 0: to_del.append(k) for k in to_del: del self.hit_cooldowns[k] # 移动 self.pos += self.vel * dt # 边界处理 if self.mode == "bounce": bounced = False if self.pos.x <= self.radius or self.pos.x >= WIDTH - self.radius: self.vel.x *= -1 self.pos.x = clamp(self.pos.x, self.radius, WIDTH - self.radius) bounced = True if self.pos.y <= self.radius or self.pos.y >= HEIGHT - self.radius: self.vel.y *= -1 self.pos.y = clamp(self.pos.y, self.radius, HEIGHT - self.radius) bounced = True if bounced: self.bounces_left -= 1 if self.bounces_left < 0: self.kill() return else: # 穿透模式越界直接销毁 if (self.pos.x < -20 or self.pos.x > WIDTH + 20 or self.pos.y < -20 or self.pos.y > HEIGHT + 20): self.kill() return self.rect.center = (int(self.pos.x), int(self.pos.y)) def draw(self, surface): blit_neon_circle(surface, self.pos, self.color, self.radius, glow=6) class Enemy(GlowSprite): def __init__(self, pos: Vec2, color, radius=12, health=2): super().__init__() self.pos = Vec2(pos) self.color = color self.radius = radius self.health = health self.image = pygame.Surface((2 * self.radius, 2 * self.radius), pygame.SRCALPHA) self.rect = self.image.get_rect(center=self.pos) self.hit_flash = 0.0 def take_hit(self, dmg=1): self.health -= dmg self.hit_flash = 0.12 if self.health <= 0: self.kill() return True return False def update(self, dt, player_pos: Vec2): pass def draw(self, surface): # 击中闪烁叠加绿色 base_color = self.color if self.hit_flash > 0: self.hit_flash -= 0.016 flash_color = ( min(255, base_color[0] + 40), min(255, base_color[1] + 80), min(255, base_color[2] + 40), ) blit_neon_circle(surface, self.pos, flash_color, self.radius + 1, glow=10) blit_neon_circle(surface, self.pos, base_color, self.radius, glow=10) class Seeker(Enemy): def __init__(self, pos: Vec2, wave=1): # 追踪者:速度随波次略增,血量随波次每隔几波+1 base_speed = 70 + wave * 6 health = 2 + (wave // 4) super().__init__(pos, SEEKER_COLOR, radius=12, health=health) self.speed = base_speed def update(self, dt, player_pos: Vec2): dir_vec = player_pos - self.pos if dir_vec.length_squared() > 1: self.pos += dir_vec.normalize() * self.speed * dt self.pos.x = clamp(self.pos.x, self.radius, WIDTH - self.radius) self.pos.y = clamp(self.pos.y, self.radius, HEIGHT - self.radius) self.rect.center = (int(self.pos.x), int(self.pos.y)) class Dasher(Enemy): def __init__(self, pos: Vec2, wave=1): # 弹射者:平时匀速移动,撞墙弹射;周期性朝玩家加速 base_speed = 110 + wave * 6 dash_speed = 260 + wave * 8 health = 2 + (wave // 5) super().__init__(pos, DASHER_COLOR, radius=12, health=health) angle = random.uniform(0, math.tau) self.vel = Vec2(math.cos(angle), math.sin(angle)) * base_speed self.base_speed = base_speed self.dash_speed = dash_speed self.dash_cooldown = random.uniform(1.4, 2.1) self.dash_timer = self.dash_cooldown def update(self, dt, player_pos: Vec2): # 定时指向玩家的冲刺 self.dash_timer -= dt if self.dash_timer <= 0: dir_vec = player_pos - self.pos if dir_vec.length_squared() > 1: self.vel = dir_vec.normalize() * self.dash_speed self.dash_timer = self.dash_cooldown # 逐步回落到基础速度(避免一直超高速) if self.vel.length() > self.base_speed: self.vel.scale_to_length(max(self.base_speed, self.vel.length() - 120 * dt)) # 移动与墙面弹射 self.pos += self.vel * dt bounced = False if self.pos.x <= self.radius or self.pos.x >= WIDTH - self.radius: self.vel.x *= -1 self.pos.x = clamp(self.pos.x, self.radius, WIDTH - self.radius) bounced = True if self.pos.y <= self.radius or self.pos.y >= HEIGHT - self.radius: self.vel.y *= -1 self.pos.y = clamp(self.pos.y, self.radius, HEIGHT - self.radius) bounced = True self.rect.center = (int(self.pos.x), int(self.pos.y)) # 弹墙微弱加速感 if bounced and self.vel.length() < self.base_speed * 1.2: self.vel *= 1.05 class Particle(GlowSprite): def __init__(self, pos: Vec2, color, radius=10, life=0.3): super().__init__() self.pos = Vec2(pos) self.color = color self.radius = radius self.life = life self.image = pygame.Surface((2, 2), pygame.SRCALPHA) self.rect = self.image.get_rect(center=self.pos) # 随机运动 ang = random.uniform(0, math.tau) self.vel = Vec2(math.cos(ang), math.sin(ang)) * random.uniform(20, 80) def update(self, dt): self.life -= dt if self.life <= 0: self.kill() return self.pos += self.vel * dt self.vel *= (1 - 2.2 * dt) # 快速阻尼 self.rect.center = (int(self.pos.x), int(self.pos.y)) def draw(self, surface): alpha = max(0, min(255, int(255 * (self.life / 0.3)))) col = (self.color[0], self.color[1], self.color[2], alpha) # 临时绘制,粒子较小可直接简单画 temp = pygame.Surface((16, 16), pygame.SRCALPHA) pygame.draw.circle(temp, col, (8, 8), max(1, int(self.radius * (self.life / 0.3)))) surface.blit(temp, (self.pos.x - 8, self.pos.y - 8), special_flags=pygame.BLEND_ADD) # ----------------------- # 游戏主类(循环、波次、碰撞、渲染) # ----------------------- class Game: def __init__(self): pygame.init() self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Neon Top-Down (A/B 模式对比)") self.clock = pygame.time.Clock() self.font = pygame.font.SysFont("Consolas", 20) if self.font is None: self.font = pygame.font.SysFont(None, 20) # 精灵组 self.all_sprites = pygame.sprite.Group() self.enemies = pygame.sprite.Group() self.bullets = pygame.sprite.Group() self.particles = pygame.sprite.Group() # 玩家 self.player = Player(Vec2(WIDTH / 2, HEIGHT / 2)) self.all_sprites.add(self.player) # 计分与时间 self.score = 0 self.elapsed = 0.0 # 波次管理 self.wave = 0 self.wave_timer = 0.0 self.wave_duration = 18.0 # 每波基准时长(也可清场提前进入下一波) self.start_next_wave() def start_next_wave(self): self.wave += 1 self.wave_timer = 0.0 # 根据波次生成敌人 # 数量与难度递增 num_seekers = 3 + self.wave // 1 num_dashers = max(1, self.wave // 2) # 稍微随机 num_seekers += random.randint(0, 1) num_dashers += random.randint(0, 1) for _ in range(num_seekers): pos = random_pos_away_from(self.player.pos, min_dist=180) e = Seeker(pos, wave=self.wave) self.enemies.add(e) self.all_sprites.add(e) for _ in range(num_dashers): pos = random_pos_away_from(self.player.pos, min_dist=200) e = Dasher(pos, wave=self.wave) self.enemies.add(e) self.all_sprites.add(e) def spawn_hit_particles(self, pos, base_color): for _ in range(6): self.particles.add(Particle(pos, base_color, radius=6, life=0.25)) def handle_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: self.quit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_t: self.player.toggle_mode() def update(self, dt): self.elapsed += dt self.wave_timer += dt # 更新玩家和子弹 self.player.update(dt, self.bullets) for b in list(self.bullets): b.update(dt) # 更新敌人 for e in list(self.enemies): e.update(dt, self.player.pos) # 更新粒子 for p in list(self.particles): p.update(dt) # 碰撞:子弹 vs 敌人(圆碰撞) collisions = pygame.sprite.groupcollide(self.bullets, self.enemies, False, False, collided=pygame.sprite.collide_circle) for bullet, enemy_list in collisions.items(): for enemy in enemy_list: if bullet.mode == "pierce": if not bullet.can_hit(enemy): continue destroyed = enemy.take_hit(1) bullet.register_hit(enemy) self.spawn_hit_particles(enemy.pos, NEON_GREEN) if destroyed: self.score += 1 else: # 反弹模式:命中即销毁子弹 destroyed = enemy.take_hit(1) self.spawn_hit_particles(enemy.pos, NEON_GREEN) bullet.kill() if destroyed: self.score += 1 # 波次推进:清场或时间到 if (len(self.enemies) == 0 and self.wave_timer > 1.0) or self.wave_timer >= self.wave_duration: self.start_next_wave() def draw_grid(self, surface): # 轻微网格,增强科技感 step = 40 grid_color = (20, 20, 30, 40) temp = pygame.Surface(surface.get_size(), pygame.SRCALPHA) for x in range(0, WIDTH, step): pygame.draw.line(temp, grid_color, (x, 0), (x, HEIGHT), 1) for y in range(0, HEIGHT, step): pygame.draw.line(temp, grid_color, (0, y), (WIDTH, y), 1) surface.blit(temp, (0, 0)) def draw_ui(self, surface): mode_text = f"模式: {'反弹(A)' if self.player.bullet_mode=='bounce' else '穿透(B)'} [T 切换]" time_text = f"时间: {self.elapsed:.1f}s" wave_text = f"波次: {self.wave}" score_text = f"得分: {self.score}" def draw_label(txt, x, y, color=NEON_CYAN): surf = self.font.render(txt, True, color) # 轻微光晕 glow = self.font.render(txt, True, (min(255, color[0]//2+60), min(255, color[1]//2+60), min(255, color[2]//2+60))) surface.blit(glow, (x-1, y-1)) surface.blit(glow, (x+1, y+1)) surface.blit(surf, (x, y)) draw_label(time_text, 14, 10, NEON_CYAN) draw_label(wave_text, WIDTH - 160, 10, NEON_YELLOW) draw_label(score_text, 14, 36, NEON_GREEN) draw_label(mode_text, 14, HEIGHT - 32, NEON_WHITE) def draw(self): self.screen.fill(BG_COLOR) self.draw_grid(self.screen) # 绘制顺序:粒子(底层)-> 敌人 -> 子弹 -> 玩家 for p in self.particles: p.draw(self.screen) for e in self.enemies: e.draw(self.screen) for b in self.bullets: b.draw(self.screen) self.player.draw(self.screen) self.draw_ui(self.screen) pygame.display.flip() def quit(self): pygame.quit() sys.exit(0) def run(self): while True: dt = self.clock.tick(FPS) / 1000.0 self.handle_events() self.update(dt) self.draw() def main(): Game().run() if __name__ == "__main__": main() ``` ## 代码说明 - 关键函数和类的作用说明 - NeonCache / blit_neon_circle / draw_neon_line:构建霓虹风格的发光绘制,使用加法混合模拟光晕,并做了圆形的缓存避免重复生成。 - Player:玩家实体,WASD移动、鼠标瞄准,左键发射光子脉冲;用 T 切换反弹/穿透两种发射机制。 - Bullet:子弹(光子脉冲),支持反弹和穿透模式;穿透模式带命中冷却,防止同一敌人多帧重复伤害。 - Seeker / Dasher:两种敌人,分别为追踪型和弹射突进型;参数随波次提升(速度、生命),用于快速测试迭代。 - Particle:被命中后的轻量粒子效果,增强反馈。 - Game:主循环(事件-更新-渲染)、波次管理、碰撞检测、UI绘制(时间、波次、模式、得分)。 - 运行要求和依赖说明 - Python 3.8+(建议) - 第三方库:pygame - 安装:pip install pygame - 运行:python neon_topdown.py - 可能的扩展和改进建议 - 增加敌人种类(例如护盾型、分裂型),或加入无碰撞幽灵型增强多样性。 - 扩展更多 A/B 测试机制,如不同的射速、子弹速度/尺寸、命中反馈强度等,以便教学或玩法验证。 - 增加玩家护盾与生命系统、敌人生成的“安全区提示圈”等,便于更长时间的生存挑战。 - 增加音效(射击、命中、波次开始),可用纯代码合成短音或加载免版权音效。 - 对霓虹绘制进行进一步优化:将线条也做局部缓存(或使用更小的局部临时 Surface),在移动端/低功耗设备上保持更高帧率。
## 游戏代码概述 - 游戏类型说明 - 益智解谜(推箱子核心玩法)。玩家在网格地图中推动箱子至目标点,完成关卡。提供简单/中等/困难三档难度与计时显示。 - 核心功能特点 - 三档难度:内置参数控制地图尺寸、箱子数量与默认障碍密度。 - 计时与统计:显示用时、步数、撤销次数。 - 课堂即改参数(实时调整,无需重启): - 步数上限([ 与 ] 调整) - 撤销次数上限(; 与 ' 调整) - 障碍密度(, 与 . 调整,用于随机生成新关) - 关卡加载与自动验证: - 支持文本关卡文件拖拽(.txt)即加载 - 自动验证:结构合法性、基础死锁检测,以及启发式 BFS 推动作求解验证(小规模关卡可验证可解) - 随机关卡生成:按难度与障碍密度随机生成,自动多次尝试直至找到可解关卡或耗尽次数 - 撤销/重做:Z 撤销,Y 重做(受限于撤销上限) - 事件队列与优化示范: - 使用事件过滤(set_allowed / set_blocked) - 自定义定时事件刷新计时 - 脏矩形更新(Dirty Rects),预渲染静态层(背景/墙/目标),仅重绘变动单元 - 视觉风格(无版权资源):内置主题可切换(1=Pastel,2=Neon,3=Mono)。若用户输入 #{visual_style} 未指定,将默认 Pastel。 - 技术实现要点 - 采用网格地图与集合结构(walls/goals/boxes)实现快速查询 - 使用“宏推”BFS:以“推箱动作”为边,只在玩家可到达相邻位置时尝试推箱,显著缩小状态空间 - 死锁检测:箱子陷于非目标角落立即剪枝 - 预渲染与脏矩形更新,降低逐帧渲染成本 - 事件队列过滤与自定义计时事件,减少无关事件开销 ## 完整代码 ```python # 完整的PyGame游戏代码 # 推箱子(Sokoban)教学版:三档难度、计时、参数即改、随机生成、关卡加载与验证、事件队列与渲染优化 # 依赖:pygame # 运行:python this_file.py import os import sys import time import random from collections import deque import pygame # --------------------------- # 全局设置与常量 # --------------------------- pygame.init() pygame.display.set_caption("推箱子教学版 - 难度&计时&参数即改&验证&优化示范") # 事件类型(自定义) TICK_EVENT = pygame.USEREVENT + 1 # 计时每秒刷新 PERF_EVENT = pygame.USEREVENT + 2 # 性能统计更新 # 允许的事件类型:减少事件队列冗余(优化示范) pygame.event.set_blocked(None) pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP, pygame.VIDEORESIZE, pygame.WINDOWFOCUSGAINED, pygame.WINDOWFOCUSLOST, pygame.DROPFILE, TICK_EVENT, PERF_EVENT]) # 屏幕与帧率 BASE_WIDTH, BASE_HEIGHT = 900, 700 FLAGS = pygame.RESIZABLE | pygame.SCALED screen = pygame.display.set_mode((BASE_WIDTH, BASE_HEIGHT), FLAGS) clock = pygame.time.Clock() TARGET_FPS = 60 # 网格绘制相关 MARGIN = 10 # 画布边距像素 # --------------------------- # 主题与颜色 # --------------------------- def theme_pastel(): return { "bg": (248, 248, 255), "wall": (180, 200, 230), "goal": (255, 210, 120), "floor": (240, 240, 240), "box": (255, 160, 160), "box_on_goal": (180, 230, 180), "player": (140, 180, 255), "grid": (220, 220, 220), "text": (40, 40, 40), "hud_bg": (255, 255, 255, 180), } def theme_neon(): return { "bg": (10, 10, 15), "wall": (35, 50, 65), "goal": (255, 85, 0), "floor": (25, 25, 35), "box": (0, 200, 255), "box_on_goal": (0, 255, 120), "player": (255, 0, 155), "grid": (55, 55, 80), "text": (230, 230, 240), "hud_bg": (20, 20, 30, 190), } def theme_mono(): return { "bg": (250, 250, 250), "wall": (110, 110, 110), "goal": (80, 80, 80), "floor": (230, 230, 230), "box": (150, 150, 150), "box_on_goal": (100, 100, 100), "player": (50, 50, 50), "grid": (200, 200, 200), "text": (10, 10, 10), "hud_bg": (255, 255, 255, 180), } THEMES = [theme_pastel(), theme_neon(), theme_mono()] THEME_NAMES = ["Pastel", "Neon", "Mono"] # --------------------------- # 配置(可课堂即改) # --------------------------- class Config: def __init__(self): self.difficulty = "easy" # "easy" | "medium" | "hard" # 三档缺省参数(仅用于随机生成时提供建议) self.diff_params = { "easy": {"w": 9, "h": 9, "boxes": 2, "density": 0.08}, "medium": {"w": 11, "h": 11, "boxes": 3, "density": 0.11}, "hard": {"w": 13, "h": 13, "boxes": 4, "density": 0.14}, } self.step_limit = 300 self.undo_limit = 20 self.obstacle_density = 0.10 # 用于随机生成 self.theme_index = 0 # 0 Pastel, 1 Neon, 2 Mono self.show_help = True self.validate_timeout_sec = 1.2 # 求解器最大时长 self.validate_node_limit = 50000 # 求解器最大节点展开 def apply_difficulty(self, name): name = name.lower() if name in self.diff_params: self.difficulty = name p = self.diff_params[name] self.obstacle_density = p["density"] def adjust_step_limit(self, delta): self.step_limit = max(10, min(9999, self.step_limit + delta)) def adjust_undo_limit(self, delta): self.undo_limit = max(0, min(999, self.undo_limit + delta)) def adjust_density(self, delta): self.obstacle_density = max(0.00, min(0.40, round(self.obstacle_density + delta, 3))) def cycle_theme(self, idx=None): if idx is None: self.theme_index = (self.theme_index + 1) % len(THEMES) else: self.theme_index = idx % len(THEMES) CONFIG = Config() # --------------------------- # 工具:方向与网格帮助 # --------------------------- DIRS = { pygame.K_UP: (0, -1), pygame.K_w: (0, -1), pygame.K_DOWN: (0, 1), pygame.K_s: (0, 1), pygame.K_LEFT: (-1, 0), pygame.K_a: (-1, 0), pygame.K_RIGHT: (1, 0), pygame.K_d: (1, 0), } VEC4 = [(0,-1), (0,1), (-1,0), (1,0)] def add(p, d): return (p[0]+d[0], p[1]+d[1]) # --------------------------- # 关卡对象 # --------------------------- class Level: def __init__(self, name=""): self.name = name self.w = 0 self.h = 0 self.walls = set() self.goals = set() self.boxes = set() self.player = None self.raw_lines = [] self._bg_cached_surface = None # 预渲染静态层缓存 self._tile_size = 32 self._origin = (0, 0) # 网格起点在窗口坐标 # 游戏运行态 self.steps = 0 self.start_ticks = pygame.time.get_ticks() self.undos_used = 0 self.undo_stack = [] # [(player, frozenset(boxes))] self.redo_stack = [] # 解析文本关卡 @staticmethod def from_text(text, name="FromText"): lvl = Level(name) lines = [ln.rstrip("\n") for ln in text.splitlines()] if not lines: raise ValueError("空关卡") width = max(len(ln) for ln in lines) height = len(lines) lvl.w, lvl.h = width, height lvl.raw_lines = lines player_count = 0 for y, ln in enumerate(lines): for x in range(width): ch = ln[x] if x < len(ln) else " " if ch == "#": lvl.walls.add((x, y)) elif ch in ".G": lvl.goals.add((x, y)) elif ch == "*": lvl.goals.add((x, y)) lvl.boxes.add((x, y)) elif ch == "B": lvl.boxes.add((x, y)) elif ch == "+": lvl.goals.add((x, y)) lvl.player = (x, y); player_count += 1 elif ch == "P": lvl.player = (x, y); player_count += 1 # 其他字符默认地板或空白 if player_count == 0: raise ValueError("未找到玩家 P") if player_count > 1: raise ValueError("玩家数量超过1") if not lvl.boxes: raise ValueError("未找到箱子 B 或 *") if len(lvl.goals) != len(lvl.boxes): raise ValueError("目标与箱子数量不一致") return lvl # 基础结构验证与简单死锁检测 def validate_structure(self): # 检查边界:如果外围没有墙,允许通过,但提示 warnings = [] # 角落死锁检查(非目标) for bx, by in self.boxes: if (bx, by) not in self.goals: # 是否在两面夹墙 up = (bx, by-1) in self.walls down = (bx, by+1) in self.walls left = (bx-1, by) in self.walls right = (bx+1, by) in self.walls # 角落 if (up and left) or (up and right) or (down and left) or (down and right): warnings.append(f"箱子({bx},{by})位于角落且非目标,可能无解") return warnings def is_completed(self): return self.boxes == self.goals def clone_state(self): return (self.player, frozenset(self.boxes)) def restore_state(self, state): self.player = state[0] self.boxes = set(state[1]) def reset_run_state(self): self.steps = 0 self.undos_used = 0 self.undo_stack.clear() self.redo_stack.clear() self.start_ticks = pygame.time.get_ticks() # 尝试移动/推箱 def try_move(self, dx, dy, step_limit, undo_limit): if self.is_completed(): return False, "completed" if self.steps >= step_limit: return False, "step_limit_reached" px, py = self.player np = (px + dx, py + dy) pushed = False # 墙阻挡 if np in self.walls: return False, "wall" # 如果下一格是箱子,检查再下一格是否可推 if np in self.boxes: b2 = (np[0] + dx, np[1] + dy) if b2 in self.walls or b2 in self.boxes: return False, "box_blocked" # 记录状态以便撤销 if len(self.undo_stack) < undo_limit: self.undo_stack.append(self.clone_state()) else: # 撤销栈到达上限时,仍允许移动,但无法撤销更多 pass self.redo_stack.clear() # 推箱 self.boxes.remove(np) self.boxes.add(b2) self.player = np pushed = True self.steps += 1 return True, "pushed" else: # 空地移动 if len(self.undo_stack) < undo_limit: self.undo_stack.append(self.clone_state()) self.redo_stack.clear() self.player = np self.steps += 1 return True, "moved" def undo(self): if not self.undo_stack: return False prev = self.undo_stack.pop() self.redo_stack.append(self.clone_state()) self.restore_state(prev) self.undos_used += 1 return True def redo(self): if not self.redo_stack: return False nxt = self.redo_stack.pop() self.undo_stack.append(self.clone_state()) self.restore_state(nxt) return True # --------------------------- # 渲染相关 # --------------------------- def compute_layout(self, surf_size): # 根据窗口尺寸计算tile size与原点,使网格居中 win_w, win_h = surf_size avail_w = max(100, win_w - 2 * MARGIN) avail_h = max(100, win_h - 2 * MARGIN) tile = min(avail_w // self.w, avail_h // self.h) tile = max(16, min(64, tile)) grid_w = tile * self.w grid_h = tile * self.h ox = (win_w - grid_w) // 2 oy = (win_h - grid_h) // 2 self._tile_size = tile self._origin = (ox, oy) def grid_to_screen(self, cell): x, y = cell return (self._origin[0] + x * self._tile_size, self._origin[1] + y * self._tile_size) def pre_render_static(self, theme, target_surface): # 预渲染静态层(地板、墙、目标、网格线) self.compute_layout(target_surface.get_size()) tile = self._tile_size ox, oy = self._origin bg = pygame.Surface(target_surface.get_size()).convert() bg.fill(theme["bg"]) # 网格地板 floor_col = theme["floor"] grid_col = theme["grid"] for y in range(self.h): for x in range(self.w): rx, ry = ox + x*tile, oy + y*tile pygame.draw.rect(bg, floor_col, (rx, ry, tile, tile)) # 网格线 pygame.draw.rect(bg, grid_col, (rx, ry, tile, tile), 1) # 目标点 for (x, y) in self.goals: rx, ry = ox + x*tile, oy + y*tile gcol = theme["goal"] inset = max(2, tile // 6) pygame.draw.rect(bg, gcol, (rx+inset, ry+inset, tile-2*inset, tile-2*inset), border_radius=max(2, tile//8)) # 墙 for (x, y) in self.walls: rx, ry = ox + x*tile, oy + y*tile wcol = theme["wall"] pygame.draw.rect(bg, wcol, (rx, ry, tile, tile)) self._bg_cached_surface = bg def draw_dynamic(self, target_surface, theme, dirty_rects=None): # 绘制动态对象(箱子、玩家) tile = self._tile_size ox, oy = self._origin def rect_of(c): rx, ry = ox + c[0]*tile, oy + c[1]*tile return pygame.Rect(rx, ry, tile, tile) # 箱子 for (x, y) in self.boxes: rc = rect_of((x, y)) col = theme["box_on_goal"] if (x, y) in self.goals else theme["box"] inset = max(2, tile // 10) pygame.draw.rect(target_surface, col, rc.inflate(-2*inset, -2*inset), border_radius=max(2, tile//10)) if dirty_rects is not None: dirty_rects.append(rc) # 玩家 rc = rect_of(self.player) pcol = theme["player"] inset = max(2, tile // 8) pygame.draw.ellipse(target_surface, pcol, rc.inflate(-2*inset, -2*inset)) if dirty_rects is not None: dirty_rects.append(rc) # --------------------------- # 求解器(宏推 BFS) # --------------------------- def reachable_cells(player, walls, boxes, w, h): # 计算玩家可到达区域(不穿墙与箱子) q = deque([player]) seen = {player} blocked = walls.union(boxes) while q: x, y = q.popleft() for dx, dy in VEC4: nx, ny = x+dx, y+dy if 0 <= nx < w and 0 <= ny < h and (nx, ny) not in blocked and (nx, ny) not in seen: seen.add((nx, ny)) q.append((nx, ny)) return seen def is_deadlock_box(pos, walls, goals): if pos in goals: return False x, y = pos up = (x, y-1) in walls down = (x, y+1) in walls left = (x-1, y) in walls right = (x+1, y) in walls if (up and left) or (up and right) or (down and left) or (down and right): return True return False def solve_sokoban(level, time_limit_sec=1.2, node_limit=50000): # 返回 (solvable: bool, steps: int or None) start_time = time.time() walls = set(level.walls) goals = set(level.goals) boxes = frozenset(level.boxes) w, h = level.w, level.h # 初步死锁检查 for b in boxes: if is_deadlock_box(b, walls, goals): return False, None # 状态:仅用箱子位置(宏推),玩家位置用于可达性判定 # 队列元素:(boxes_frozenset, player_pos) from_state = {} start_state = (boxes, level.player) q = deque([start_state]) seen = set([boxes]) # 仅按箱子集去重 expansions = 0 while q: if time.time() - start_time > time_limit_sec: return False, None if expansions > node_limit: return False, None cur_boxes, cur_player = q.popleft() expansions += 1 # 终止条件 if set(cur_boxes) == goals: return True, None # 玩家可达区域 reach = reachable_cells(cur_player, walls, set(cur_boxes), w, h) # 尝试每个箱子的四向推 for bx, by in cur_boxes: for dx, dy in VEC4: behind = (bx - dx, by - dy) # 玩家站位 ahead = (bx + dx, by + dy) # 箱子推后位置 # 先判断 ahead 合法,behind 可达 if not (0 <= ahead[0] < w and 0 <= ahead[1] < h): continue if ahead in walls or ahead in cur_boxes: continue if behind not in reach: continue # 死锁剪枝(推后) if is_deadlock_box(ahead, walls, goals): continue new_boxes = set(cur_boxes) new_boxes.remove((bx, by)) new_boxes.add(ahead) new_boxes_fs = frozenset(new_boxes) if new_boxes_fs in seen: continue seen.add(new_boxes_fs) # 推后玩家位置在原箱子位置 q.append((new_boxes_fs, (bx, by))) return False, None # --------------------------- # 随机关卡生成 # --------------------------- def generate_random_level(name, width, height, box_count, obstacle_density, max_tries=200): # 基本框架:外墙 + 内部障碍(随机),随机放置目标/箱子/玩家,验证可解 # 为确保可解,尝试多次生成 for attempt in range(1, max_tries+1): lvl = Level(f"{name}#{attempt}") lvl.w, lvl.h = width, height # 外墙 for x in range(width): lvl.walls.add((x, 0)) lvl.walls.add((x, height-1)) for y in range(height): lvl.walls.add((0, y)) lvl.walls.add((width-1, y)) # 内障碍 cell_count = (width-2)*(height-2) obstacles = int(cell_count * obstacle_density) # 为避免过度碎片化,先随机挑格子 candidates = [(x, y) for x in range(1, width-1) for y in range(1, height-1)] random.shuffle(candidates) for i in range(obstacles): lvl.walls.add(candidates[i]) # 可放置区域 free = [(x, y) for x in range(1, width-1) for y in range(1, height-1) if (x, y) not in lvl.walls] if len(free) < (2*box_count + 1): continue random.shuffle(free) # 放目标 goals = free[:box_count] lvl.goals = set(goals) # 放箱子(与目标不同位置,允许部分重叠也可,但更难) p2 = free[box_count:] if len(p2) < box_count + 1: continue random.shuffle(p2) boxes = [] # 尽量避免初始就角落死锁 for cell in p2: if len(boxes) >= box_count: break if not is_deadlock_box(cell, lvl.walls, lvl.goals): boxes.append(cell) if len(boxes) < box_count: # 容错:允许少量角落,但降低成功率 boxes = p2[:box_count] lvl.boxes = set(boxes) # 放玩家 p3 = [c for c in p2 if c not in boxes][:1] if not p3: continue lvl.player = p3[0] # 验证结构与求解 try: warns = lvl.validate_structure() except Exception: continue solvable, _ = solve_sokoban(lvl, time_limit_sec=CONFIG.validate_timeout_sec, node_limit=CONFIG.validate_node_limit) if solvable: return lvl, warns raise RuntimeError("随机生成失败:未能在限定次数内生成可解关卡,请降低障碍密度或减少箱子数") # --------------------------- # 内置示例关卡 # --------------------------- BUILTIN_LEVELS = { "easy": [ """######### # . # # # # B # # P # # B # # # # . # #########""", """######### # . # # B # # B # # P # # # # . # # # #########""" ], "medium": [ """########### # . . # # B # # B # # ### # # P # # B # # . # ###########""" ], "hard": [ """############# # . # . # # B B # # ### # # P B # # # B # # . # . # #############""" ], } def load_builtin_level(diff): levels = BUILTIN_LEVELS.get(diff, []) if not levels: return None text = random.choice(levels) return Level.from_text(text, name=f"{diff}_builtin") # --------------------------- # HUD / 文本绘制 # --------------------------- def draw_hud(surface, theme, level, width, height, msg=""): font = pygame.font.SysFont("arial", 18) font_small = pygame.font.SysFont("arial", 14) # 背景 hud = pygame.Surface((width, 100), pygame.SRCALPHA) hud.fill(theme["hud_bg"]) # 时间 elapsed_ms = pygame.time.get_ticks() - level.start_ticks sec = elapsed_ms // 1000 mm, ss = divmod(sec, 60) time_str = f"{mm:02d}:{ss:02d}" # 状态文本 lines = [ f"难度: {CONFIG.difficulty} 主题: {THEME_NAMES[CONFIG.theme_index]}", f"用时: {time_str} 步数: {level.steps}/{CONFIG.step_limit} 撤销: {level.undos_used}/{CONFIG.undo_limit}", f"障碍密度: {CONFIG.obstacle_density:.3f} 关卡: {level.name}", "操作: 方向/WASD移动 Z撤销 Y重做 R重开 V验证 G随机新关 L拖拽txt加载 M菜单 1/2/3主题", "课堂即改: [/]步数上限 ;/'撤销上限 ,/.障碍密度 F1/F2/F3选择难度", ] if msg: lines.append(f"提示: {msg}") # 绘制文字 y = 5 color = theme["text"] for ln in lines: surf = font.render(ln, True, color) hud.blit(surf, (10, y)) y += 20 surface.blit(hud, (0, 0)) def draw_center_text(surface, text, theme, y_offset=0, big=False): font = pygame.font.SysFont("arial", 42 if big else 32, bold=big) surf = font.render(text, True, theme["text"]) rect = surf.get_rect(center=(surface.get_width()//2, surface.get_height()//2 + y_offset)) surface.blit(surf, rect) # --------------------------- # 游戏状态管理 # --------------------------- class Game: def __init__(self): self.theme = THEMES[CONFIG.theme_index] self.mode = "menu" # menu | playing | complete | failed | paused self.level = None self.info_msg = "" self.performance = {"fps": 0.0, "event_queue": 0} self.last_dirty = [] # 上一帧脏矩形,用于增量更新 # 定时事件 pygame.time.set_timer(TICK_EVENT, 1000) # 每秒刷新计时显示 pygame.time.set_timer(PERF_EVENT, 1000) # 每秒更新性能指标 def new_level_builtin(self, diff): CONFIG.apply_difficulty(diff) self.theme = THEMES[CONFIG.theme_index] try: lvl = load_builtin_level(CONFIG.difficulty) if lvl is None: raise RuntimeError("无内置关卡") warns = lvl.validate_structure() self.level = lvl self.level.reset_run_state() self.level.pre_render_static(self.theme, screen) self.info_msg = "; ".join(warns) if warns else "已载入内置关卡" self.mode = "playing" except Exception as e: self.info_msg = f"载入失败: {e}" def new_level_random(self): p = CONFIG.diff_params[CONFIG.difficulty] try: lvl, warns = generate_random_level( name=f"{CONFIG.difficulty}_rand", width=p["w"], height=p["h"], box_count=p["boxes"], obstacle_density=CONFIG.obstacle_density, max_tries=200 ) self.level = lvl self.level.reset_run_state() self.level.pre_render_static(self.theme, screen) self.info_msg = "随机关卡成功" + ((" | " + "; ".join(warns)) if warns else "") self.mode = "playing" except Exception as e: self.info_msg = f"随机生成失败: {e}" def load_level_from_file(self, path): try: with open(path, "r", encoding="utf-8") as f: text = f.read() lvl = Level.from_text(text, name=os.path.basename(path)) warns = lvl.validate_structure() self.level = lvl self.level.reset_run_state() self.level.pre_render_static(self.theme, screen) self.info_msg = f"文件载入成功: {path}" + ((" | " + "; ".join(warns)) if warns else "") self.mode = "playing" except Exception as e: self.info_msg = f"文件载入失败: {e}" def validate_current(self): if self.level is None: self.info_msg = "无关卡" return st = time.time() solvable, _ = solve_sokoban(self.level, time_limit_sec=CONFIG.validate_timeout_sec, node_limit=CONFIG.validate_node_limit) dt = time.time() - st if solvable: self.info_msg = f"验证结果:可解({dt*1000:.0f}ms)" else: self.info_msg = f"验证结果:未证可解({dt*1000:.0f}ms)" def restart_level(self): if self.level is None: return # 重新从原始文本加载,确保初始状态一致 try: base = Level.from_text("\n".join(self.level.raw_lines), name=self.level.name) self.level = base self.level.reset_run_state() self.level.pre_render_static(self.theme, screen) self.info_msg = "已重开关卡" except Exception: # 随机生成或未保存的关卡,简单清空运行态 self.level.reset_run_state() self.level.pre_render_static(self.theme, screen) self.info_msg = "已重开(运行态复位)" def handle_event(self, e): if e.type == pygame.QUIT: pygame.quit() sys.exit(0) elif e.type == pygame.VIDEORESIZE: # 重新绘制静态层 if self.level: self.level.pre_render_static(self.theme, screen) elif e.type == pygame.DROPFILE: self.load_level_from_file(e.file) elif e.type == TICK_EVENT: # 计时每秒刷新:不必做事,HUD会读取系统计时 pass elif e.type == PERF_EVENT: self.performance["fps"] = clock.get_fps() self.performance["event_queue"] = pygame.event.peek() # 简要查看队列 elif e.type == pygame.KEYDOWN: self.on_keydown(e) def on_keydown(self, e): k = e.key mods = pygame.key.get_mods() shift = mods & pygame.KMOD_SHIFT if self.mode == "menu": if k == pygame.K_F1: self.new_level_builtin("easy") elif k == pygame.K_F2: self.new_level_builtin("medium") elif k == pygame.K_F3: self.new_level_builtin("hard") elif k == pygame.K_g: self.new_level_random() elif k in (pygame.K_1, pygame.K_2, pygame.K_3): CONFIG.cycle_theme({pygame.K_1:0, pygame.K_2:1, pygame.K_3:2}[k]) self.theme = THEMES[CONFIG.theme_index] elif k == pygame.K_m: # 留在菜单 pass elif k == pygame.K_ESCAPE: pygame.quit(); sys.exit(0) return # playing/complete/failed/paused 公共快捷键 if k == pygame.K_m: self.mode = "menu" self.info_msg = "返回菜单" return if k in (pygame.K_1, pygame.K_2, pygame.K_3): CONFIG.cycle_theme({pygame.K_1:0, pygame.K_2:1, pygame.K_3:2}[k]) self.theme = THEMES[CONFIG.theme_index] if self.level: self.level.pre_render_static(self.theme, screen) return if k == pygame.K_F1: self.new_level_builtin("easy"); return if k == pygame.K_F2: self.new_level_builtin("medium"); return if k == pygame.K_F3: self.new_level_builtin("hard"); return if k == pygame.K_g: self.new_level_random(); return if k == pygame.K_r: self.restart_level(); return if k == pygame.K_v: self.validate_current(); return if k == pygame.K_ESCAPE: pygame.quit(); sys.exit(0) # 参数调整 if k == pygame.K_LEFTBRACKET: # [ CONFIG.adjust_step_limit(-50 if shift else -10); self.info_msg = f"步数上限: {CONFIG.step_limit}" return if k == pygame.K_RIGHTBRACKET: # ] CONFIG.adjust_step_limit(50 if shift else +10); self.info_msg = f"步数上限: {CONFIG.step_limit}" return if k == pygame.K_SEMICOLON: # ; CONFIG.adjust_undo_limit(-5 if shift else -1); self.info_msg = f"撤销上限: {CONFIG.undo_limit}" return if k == pygame.K_QUOTE: # ' CONFIG.adjust_undo_limit(+5 if shift else +1); self.info_msg = f"撤销上限: {CONFIG.undo_limit}" return if k == pygame.K_COMMA: # , CONFIG.adjust_density(-0.02 if shift else -0.01); self.info_msg = f"障碍密度: {CONFIG.obstacle_density:.3f}" return if k == pygame.K_PERIOD: # . CONFIG.adjust_density(+0.02 if shift else +0.01); self.info_msg = f"障碍密度: {CONFIG.obstacle_density:.3f}" return # 游戏动作(playing) if self.mode == "playing" and self.level: if k in (pygame.K_z,): if self.level.undo(): self.info_msg = "撤销" else: self.info_msg = "无法撤销" return if k in (pygame.K_y,): if self.level.redo(): self.info_msg = "重做" else: self.info_msg = "无法重做" return if k in DIRS: dx, dy = DIRS[k] moved, reason = self.level.try_move(dx, dy, CONFIG.step_limit, CONFIG.undo_limit) # 更新绘制脏矩形:上一帧的动态清空,新帧绘制时会加入 if moved: # 判断胜利/失败 if self.level.is_completed(): self.mode = "complete" self.info_msg = "成功!按G随机新关,或M回菜单" elif reason == "step_limit_reached" or self.level.steps >= CONFIG.step_limit: self.mode = "failed" self.info_msg = "步数用尽!按R重开,G随机新关,或M回菜单" else: if reason == "wall": self.info_msg = "前方是墙" elif reason == "box_blocked": self.info_msg = "箱子后有阻挡" return # complete/failed 模式下额外键位 if self.mode in ("complete", "failed"): if k == pygame.K_r: self.restart_level() self.mode = "playing" elif k == pygame.K_g: self.new_level_random() return # --------------------------- # 渲染 # --------------------------- def render(self): self.theme = THEMES[CONFIG.theme_index] dirty = [] # 背景 if self.level and self.level._bg_cached_surface: screen.blit(self.level._bg_cached_surface, (0, 0)) else: screen.fill(self.theme["bg"]) # 动态层 if self.level: self.level.draw_dynamic(screen, self.theme, dirty_rects=dirty) # HUD if self.level: draw_hud(screen, self.theme, self.level, screen.get_width(), screen.get_height(), self.info_msg) else: # 菜单页HUD font = pygame.font.SysFont("arial", 18) hud = pygame.Surface((screen.get_width(), 100), pygame.SRCALPHA) hud.fill(self.theme["hud_bg"]) tx = "F1简 F2中 F3难 G随机 拖拽txt加载 1/2/3主题 ESC退出" hud.blit(font.render(tx, True, self.theme["text"]), (10, 10)) screen.blit(hud, (0, 0)) # 中央提示 if self.mode == "menu": draw_center_text(screen, "推箱子教学版", self.theme, y_offset=-40, big=True) draw_center_text(screen, "选择难度:F1/F2/F3,或按 G 随机生成", self.theme, y_offset=10) draw_center_text(screen, "支持拖拽txt关卡文件加载", self.theme, y_offset=50) elif self.mode == "complete": draw_center_text(screen, "关卡完成!", self.theme, y_offset=-20, big=True) draw_center_text(screen, "R重开 G随机新关 M菜单", self.theme, y_offset=20) elif self.mode == "failed": draw_center_text(screen, "失败:步数用尽", self.theme, y_offset=-20, big=True) draw_center_text(screen, "R重开 G随机新关 M菜单", self.theme, y_offset=20) # 性能角标 font = pygame.font.SysFont("consolas", 14) perf = f"{self.performance['fps']:.0f} FPS | EQ:{self.performance['event_queue']}" ps = font.render(perf, True, self.theme["text"]) screen.blit(ps, (screen.get_width()-ps.get_width()-8, screen.get_height()-ps.get_height()-6)) pygame.display.update(dirty if dirty else None) # --------------------------- # 主循环 # --------------------------- def main(): game = Game() # 默认进入菜单;若需要,可直接加载一个内置关卡 # game.new_level_builtin("easy") while True: # 事件处理 for e in pygame.event.get(): game.handle_event(e) # 渲染 game.render() # 帧率 clock.tick(TARGET_FPS) if __name__ == "__main__": main() ``` ## 代码说明 - 关键函数和类的作用说明 - Config:全局配置,支持课堂即时调整参数(步数上限、撤销上限、障碍密度)、难度与主题。 - Level: - from_text:解析文本关卡(字符:#墙,.或G目标,B箱子,*目标上箱子,P玩家,+目标上玩家)。 - validate_structure:基础结构验证(玩家/箱子/目标数量、角落死锁警告)。 - try_move/undo/redo:移动与推箱逻辑、撤销与重做。 - pre_render_static/draw_dynamic:预渲染静态层与动态渲染(脏矩形优化)。 - solve_sokoban:宏推BFS求解,进行基础死锁剪枝与玩家可达性判定,适合小规模盒子数量(2-4)验证可解性。 - generate_random_level:随机生成可解关卡(外墙 + 随机内障碍 + 随机目标/箱子/玩家),多次尝试与自动验证。 - Game:整体流程控制(菜单/游戏/完成/失败)、事件处理(含事件过滤与自定义计时事件)、渲染。 - 运行要求和依赖说明 - 需要 Python 3.8+ 与 pygame(pip install pygame) - 直接运行脚本即可启动。窗口支持拖拽 txt 文件载入关卡。 - 关卡字符说明: - # 墙 - . 或 G 目标 - B 箱子 - * 目标上箱子 - P 玩家 - + 目标上玩家 - 常用按键: - F1/F2/F3:选择难度(简/中/难) - G:按当前难度与障碍密度随机生成可解关卡 - 方向/WASD:移动/推箱 - Z:撤销(受撤销上限限制) - Y:重做 - R:重开关卡 - V:验证当前关卡可解性(限时/节点上限) - L:拖拽 txt 文件到窗口加载关卡 - [ / ]:调整步数上限(Shift 加大步幅) - ; / ':调整撤销上限(Shift 加大步幅) - , / .:调整障碍密度(Shift 加大步幅) - 1/2/3:切换主题(Pastel/Neon/Mono) - M:返回主菜单 - ESC:退出 - 可能的扩展和改进建议 - 更强的求解器:加入更完善的死锁模式库(沿墙死锁、推入死巷死锁),启用双向搜索或IDA*以验证更大规模关卡。 - 关卡集管理:支持 levels/ 目录批量加载与关卡选择菜单。 - 计时与排行榜:记录各难度下的最佳用时、步数,用 JSON 存档。 - 动画与音效:加入推箱/完成音效、平滑移动动画与粒子特效。 - 触屏与手柄:扩展手柄控制映射与触屏滑动输入。 - 关卡编辑器:内置简单编辑器,课堂现场制作关卡并一键验证保存。 - 更多渲染优化:使用pygame.sprite.DirtySprite体系、对大地图使用分块重绘与相机裁剪。
借助模板快速完成课程作业或个人项目,理解游戏循环、事件处理与碰撞检测,交付可玩的成品并撰写报告。
把玩法想法在数分钟内变成可玩Demo,AB对比不同机制与视觉风格,用于内测与投资人展示,快速迭代验证。
批量生成不同难度的教学案例与作业素材,课堂即跑即改,拆解架构与优化思路,提升授课效率与学习效果。
验证核心循环与留存点,构建交互原型做用户测试,收集反馈后迅速调整关卡与道具,降低试错成本。
快速产出教程配套代码与讲解大纲,录制教学视频一键生成示例,稳定更新节奏,提升内容质量。
在可运行框架中替换素材,实时查看节奏与风格效果,验证美术方向,缩短与程序协作沟通链路。
搭建可控的交互环境做实验,灵活调整规则与反馈,复现实验场景,快速完成原型与论文附录演示。
赛期内迅速组装基础玩法,集中精力打磨世界观与关卡,提升完成度与提交质量,增强团队协作。
把你的游戏创意在极短时间内变成“可玩”的PyGame作品,助你从学习入门到原型验证一路加速。核心目标: - 根据“游戏类型/核心玩法/视觉风格”三要素,自动生成可运行、结构清晰的完整代码 - 给出清楚的主循环、场景管理、对象交互与碰撞处理方案,拒绝拼凑式模板 - 以丰富注释+使用说明,帮助初学者边做边学,帮助进阶者高效迭代原型 - 覆盖射击、平台跳跃、益智解谜等常见2D类型,快速对齐玩法方向 - 提供扩展建议(关卡、道具、音效、难度曲线),让作品更易打磨 - 内置合规与安全规则,规避不当题材与版权风险 适用场景:编程教学课件、个人练习与作品集、Game Jam 快速成型、立项与评审Demo、课程/训练营实操项目。通过试用体验“从描述到可玩”的直观跃迁,进而升级到深度定制与持续迭代版本。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
免费获取高级提示词-优惠即将到期