这次我们来拆一个很适合开学练手的 Python 项目用 Python 写一版拳皇格斗小游戏。这类项目不涉及复杂的 AI 模型也不吃显卡核心是 pygame 游戏开发里最常见的一套东西——角色类设计、状态机、碰撞检测、AI 行为控制和动画切换。如果你正在找 Python 项目实战案例或者想从控制台程序跳到图形界面游戏开发这个练手项目比写管理系统更有即时反馈。先给结论这个项目的重点不在于招式多华丽而是把格斗游戏的最小闭环打通。玩家主控、AI 对手、血条扣减、攻击判定、胜负判断这五件事全部处理完你就拥有一个能实际对战、能反复扩展的拳皇风格小游戏。下面会从环境准备、项目结构、核心代码、运行验证到常见问题排查完整走一遍。1. 拳皇小游戏核心能力速览能力项说明项目类型Python 游戏开发实战格斗对战类小游戏核心语言Python 3.x主要依赖pygame 图形库纯 CPU 运行无显卡要求核心玩法玩家控制角色移动、跳跃、攻击与 AI 对手对战关键机制角色状态机、碰撞检测、攻击判定、血条管理、胜负判定适合人群有 Python 基础、想练面向对象设计和游戏逻辑的初学者运行平台Windows / macOS / Linux 均可启动方式命令行执行主程序文件是否需要 GPU不需要是否支持 API不支持游戏本体项目扩展方向双人对战、多角色、必杀技、音效、动画帧替换、招式表从表格可以看出来这个项目定位很明确不吃硬件、依赖少、逻辑密度高。你不需要为它准备 8G 显存或者 CUDA 环境一台普通笔记本就够跑。2. 玩法拆解与使用边界2.1 拳皇小游戏的核心玩法拳皇类格斗游戏和普通平台跳跃游戏的区别在于“对抗性”。整个游戏围绕两个核心对象展开玩家角色由键盘控制可以左右移动、跳跃、出拳、出腿核心操作是靠近对手并发动攻击。AI 对手不需要键盘控制根据与玩家的距离自动做出行为选择可能是靠近、防御或主动攻击。胜利条件很简单谁先把对方的血量打到 0谁就赢。因此游戏的主循环必须持续判断两件事——攻击是否命中以及血量是否归零。2.2 版权与合规边界这里特别提醒一点拳皇The King of Fighters是 SNK 的经典游戏 IP。作为 Python 练手项目你可以用自己的方式实现“格斗小游戏”的玩法逻辑也可以参考拳皇的对战模式设计但如果要公开发布或商用不要直接使用拳皇的官方角色立绘、名称、音乐等素材。更稳妥的做法是用自制方块人、占位角色和独立世界观保证玩法学习与素材版权两者互不干扰。2.3 技术边界从技术范围看这个项目适合解决以下问题理解面向对象编程中“类”与“对象”的实战用法。理解游戏循环中的输入、更新、渲染三层结构。理解碰撞检测中的矩形相交判断。理解简单 AI 状态机的编写。不适合做的是网络联机对战、复杂物理效果、3D 渲染。这些需要额外引入网络框架或游戏引擎已经超出“Python 小游戏练手”的范畴。3. 环境准备与依赖安装在写代码之前先把运行环境准备好。这个项目对环境的要求非常低核心就两样Python 3.x 和 pygame 库。3.1 检查 Python 版本打开终端或命令行窗口输入以下命令查看当前 Python 版本python --version如果提示找不到命令可以尝试python3 --version。建议使用 Python 3.8 以上版本pygame 对新老版本都兼容得比较好。3.2 安装 pygamepygame 是 Python 社区最常用的 2D 游戏开发库负责窗口创建、图片绘制、键盘事件监听、矩形碰撞检测等底层工作。安装命令pip install pygame如果你的机器同时存在 Python 2 和 Python 3需要指定 pip 版本python3 -m pip install pygame3.3 验证安装结果安装完成后输入以下命令确认 pygame 可以正常导入import pygame print(pygame.__version__)如果能输出版本号说明环境已经可用。至此环境准备工作完成。4. 项目结构设计与文件组织不要把所有代码写进一个文件里硬扛后面维护会非常痛苦。建议从第一步就按模块拆分。一个结构清晰的拳皇小游戏项目可以长这样kof_game/ ├── main.py # 游戏入口负责主循环 ├── settings.py # 全局配置窗口尺寸、帧率、颜色、速度常量 ├── fighter.py # 角色类移动、跳跃、攻击、受击逻辑 ├── ai.py # AI 对手行为控制 ├── weapon.py # 攻击判定框与伤害结算可选也可并入 fighter.py └── assets/ ├── bg.png # 背景图 ├── player.png # 玩家角色图 ├── enemy.png # 对手角色图 └── sound/ # 音效目录如果暂时没有美术素材可以直接用 pygame 的矩形色块表示两个角色。先跑通逻辑再替换素材是最稳妥的开发顺序。4.1 settings.py 示例把游戏中的魔法数字集中到一个配置文件中可以避免在主程序里到处修改数字。# settings.py SCREEN_WIDTH 800 SCREEN_HEIGHT 450 FPS 60 GROUND_Y 350 # 地面高度 PLAYER_SPEED 200 # 像素/秒 JUMP_SPEED -300 # 起跳初速度负数表示向上 GRAVITY 600 # 重力加速度像素/秒² PLAYER_COLOR (50, 150, 255) ENEMY_COLOR (255, 80, 80) BG_COLOR (30, 30, 30)这样在后面实现角色移动和跳跃时能直接引用配置而不是到处写数字。5. 角色类与状态机设计格斗游戏里角色不是一个孤立的图形对象而是一个“有状态的对象”。站立、移动、攻击、被击中、倒地这些状态决定角色的行为逻辑。实现时可以用状态机方式管理。5.1 角色类核心属性# fighter.py import pygame from settings import GROUND_Y class Fighter: def __init__(self, x, color, controlsNone): self.x x self.y GROUND_Y self.vx 0 self.vy 0 self.width 60 self.height 120 self.color color self.health 100 self.facing 1 # 1 朝右-1 朝左 self.state stand # stand / walk / jump / attack / hit self.grounded True self.attack_timer 0 self.hit_timer 0 self.controls controls or {}从类的设计可以看到角色至少需要记录位置、速度、朝向、血量、当前状态和攻击计时。这些属性是所有后续逻辑的基础。5.2 状态切换规则状态不能任意跳转比如攻击过程中不应立刻切入跳跃否则游戏手感会很飘。因此要定义基本的切换规则stand可以切换到walk、jump、attack、hit。walk可以切换到stand、jump、attack、hit。jump在落地前不应再次起跳落地后回到stand。attack在攻击动画结束前不重复攻击结束后回到stand。hit表示受击硬直硬直结束后回到stand。一个简单的判断方法只有在非攻击、非受击状态下才能发起新的移动和攻击。这样既保留操作自由度又不会让角色失控。5.3 角色更新逻辑骨架def update(self, dt): # 水平移动 self.x self.vx * dt # 垂直移动与重力 if not self.grounded: self.vy GRAVITY * dt self.y self.vy * dt if self.y GROUND_Y: self.y GROUND_Y self.vy 0 self.grounded True if self.state jump: self.state stand # 攻击计时 if self.state attack: self.attack_timer - dt if self.attack_timer 0: self.state stand # 受击计时 if self.state hit: self.hit_timer - dt if self.hit_timer 0: self.state stand这一段就是角色行为变化的核心。注意dt是上一帧到当前帧的时间差以秒为单位用它乘以速度才能保证不同帧率下的移动速度一致。6. 玩家按键控制与招式触发玩家控制通过 pygame 的键盘事件实现。这里推荐使用pygame.key.get_pressed()来读取持续按下的按键状态优点是代码直观、不容易丢帧。6.1 输入处理示例# main.py 中的玩家输入处理 keys pygame.key.get_pressed() # 只有站立和行走状态下才能重新设置水平速度 if player.state in (stand, walk): player.vx 0 if keys[pygame.K_a]: player.vx -PLAYER_SPEED player.facing -1 player.state walk elif keys[pygame.K_d]: player.vx PLAYER_SPEED player.facing 1 player.state walk # 跳跃 if keys[pygame.K_w] and player.grounded: player.vy JUMP_SPEED player.grounded False player.state jump # 攻击 if keys[pygame.K_j]: start_attack(player) elif keys[pygame.K_k]: start_attack(player, damage12, range70)6.2 攻击动作初始化def start_attack(fighter, damage8, range60): if fighter.state not in (attack, hit): fighter.state attack fighter.attack_timer 0.3 fighter.attack_damage damage fighter.attack_range range这里把攻击作为一个“动作状态”处理攻击开始后进入attack状态持续 0.3 秒左右期间攻击判定框会出现一次之后状态自动回到stand。通过不同的按键绑定不同伤害和范围就能实现拳和腿的区分。7. 攻击判定与伤害结算格斗游戏是否“有手感”很大程度取决于攻击判定做得好不好。这里采用最简单也最实用的方法把角色身体和攻击范围都抽象成矩形然后用colliderect做相交判断。7.1 获取角色碰撞框def get_rect(self): return pygame.Rect(self.x - self.width // 2, self.y - self.height, self.width, self.height)角色中心点存储在x, y中绘制时以中心点为基准向左右上下展开这样在屏幕上也容易对齐。7.2 获取攻击判定框def get_attack_rect(self): # 攻击框出现在角色朝向的一侧 if self.facing 1: return pygame.Rect(self.x, self.y - self.height, self.attack_range, self.height) else: return pygame.Rect(self.x - self.attack_range, self.y - self.height, self.attack_range, self.height)7.3 伤害结算时机攻击框并不是整个攻击状态都有效否则会出现“攻击已收招但对方还被打中”的体验问题。更合理的做法是只在攻击状态中的某一个时间点执行一次判定def check_hit(attacker, defender): # 只在攻击状态且计时器位于判定区间时触发 if attacker.state attack: if 0.1 attacker.attack_timer 0.2: attack_rect attacker.get_attack_rect() if attack_rect.colliderect(defender.get_rect()): defender.health - attacker.attack_damage defender.state hit defender.hit_timer 0.2这样每次攻击只会结算一次不会出现同一拳连续多帧扣血的 bug。受击后对手进入hit状态短暂硬直硬直结束才能继续行动。7.4 血条与胜负判断def draw_health_bar(screen, x, y, health, color): bar_width 200 bar_height 20 fill_width int(bar_width * max(health, 0) / 100) pygame.draw.rect(screen, (60, 60, 60), pygame.Rect(x, y, bar_width, bar_height)) pygame.draw.rect(screen, color, pygame.Rect(x, y, fill_width, bar_height))在每个角色更新完血量后主循环判断血量是否小于等于 0是则结束游戏并展示胜负结果。8. AI 对手的行为逻辑AI 的作用是在没有第二个玩家的前提下让游戏依然能对战。这个项目的 AI 不需要很强只需要表现出“会移动、会攻击、会偶尔防御”的简单策略即可。8.1 状态机 AI 设计AI 可以按以下逻辑驱动计算自身与玩家的距离。距离远时向玩家靠近。距离近时按概率决定攻击还是后退。受击硬直期间不做任何选择。# ai.py import random from settings import PLAYER_SPEED def ai_update(enemy, player, dt): if enemy.state in (attack, hit): return distance player.x - enemy.x # 太远就靠近 if abs(distance) 100: enemy.vx PLAYER_SPEED * (1 if distance 0 else -1) enemy.facing 1 if distance 0 else -1 enemy.state walk return # 近距离随机决策 action random.choices( [attack, walk, back, stand], weights[30, 20, 20, 30], k1 )[0] if action attack: enemy.vx 0 start_attack(enemy) elif action back: enemy.vx -PLAYER_SPEED * enemy.facing enemy.state walk else: enemy.vx 0 enemy.state stand这里使用了random.choices加权重的方式让 AI 的表现不是完全固定而是有一定的随机性和可读性。你可以继续调权重让对手更激进或更保守。8.2 AI 避不开的碰撞问题注意AI 只是根据位置做决策游戏本身没有物理阻挡。也就是说AI 可能直接走过玩家角色。对于练手项目这是可以接受的如果你希望更真实可以在检测到两个角色矩形重叠时强制把他们的 x 坐标推开做一个简单的“碰撞推开”逻辑。9. 游戏主循环、绘制与刷新到这里主角、AI、攻击判定都有了最后只需要把这一切放进游戏主循环里持续运行。9.1 主循环基本框架# main.py import pygame from settings import * from fighter import Fighter from fighter import start_attack from ai import ai_update def main(): pygame.init() screen pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption(Python 拳皇小游戏 - 练手版) clock pygame.time.Clock() player Fighter(200, PLAYER_COLOR) enemy Fighter(600, ENEMY_COLOR) enemy.facing -1 running True while running: dt clock.tick(FPS) / 1000 for event in pygame.event.get(): if event.type pygame.QUIT: running False # 玩家输入 keys pygame.key.get_pressed() handle_player_input(keys, player) # AI ai_update(enemy, player, dt) # 更新 player.update(dt) enemy.update(dt) # 攻击判定 check_hit(player, enemy) check_hit(enemy, player) # 绘制 screen.fill(BG_COLOR) pygame.draw.rect(screen, (0, 0, 0), pygame.Rect(0, GROUND_Y, SCREEN_WIDTH, 50)) draw_fighter(screen, player) draw_fighter(screen, enemy) draw_health_bar(screen, 50, 30, player.health, PLAYER_COLOR) draw_health_bar(screen, SCREEN_WIDTH - 250, 30, enemy.health, ENEMY_COLOR) # 胜负判断 if player.health 0: show_result(screen, AI 获胜) running False elif enemy.health 0: show_result(screen, 玩家获胜) running False pygame.display.flip() pygame.quit() if __name__ __main__: main()9.2 角色绘制函数def draw_fighter(screen, fighter): rect fighter.get_rect() pygame.draw.rect(screen, fighter.color, rect)当然后续你可以用角色图片替换这个色块。替换时只需要在绘制阶段加载pygame.image.load()然后screen.blit()即可角色逻辑代码完全不需要改动。10. 运行测试与效果验证10.1 启动游戏在项目根目录执行python main.py如果环境正常会弹出一个 800×450 的黑色游戏窗口两个不同颜色的角色出现在屏幕两侧顶部有两个血条。10.2 功能验证清单启动后按以下步骤逐项验证验证项操作预期结果左右移动按 A / D 键玩家角色沿水平方向移动跳跃按 W 键角色向上跳起随后落回地面攻击按 J / K 键角色进入攻击状态若靠近对手则对手扣血并硬直AI 行为不操作角色AI 会主动靠近并尝试攻击玩家血条更新让角色被 AI 打中血条按伤害值减少胜负判定把 AI 血量打到 0显示“玩家获胜”并结束游戏10.3 调试信息输出如果觉得角色状态切换不直观可以在fighter.update()中临时加一行控制台输出方便观察状态变化if self.state ! self.last_state: print(self.state) self.last_state self.state这一步不是必需但对理解状态机非常有帮助。调完逻辑后再删掉即可。11. 常见问题与排查方法问题现象可能原因排查方式解决方案运行提示 ModuleNotFoundError: pygamepygame 未安装pip show pygame执行pip install pygame窗口一闪而过主循环异常退出查看终端错误堆栈检查代码中是否有语法错误或空引用角色移动速度不一样没有使用 dt 计算位移检查更新函数是否乘以时间差把x vx * dt应用到所有移动逻辑攻击打不到人攻击范围过小或朝向不对打印攻击判定框坐标调大attack_range检查facing方向一次攻击多次扣血攻击判定持续触发检查伤害结算条件只在攻击计时的判定区间执行一次结算跳跃像瞬移帧率不稳定且未用 dt检查主循环clock.tick(60)统一使用 dt 计算位移AI 一直贴脸不走开AI 决策逻辑没有随机后退检查 AI 行动分支在近距离分支增加后退或站立选项角色重叠严重没有做物理推开检查位置更新顺序在更新阶段检测矩形重叠并修正 x 坐标这组排查表基本覆盖了初学者最容易遇到的问题。实际开发中绝大多数 bug 都出在“状态没有切换干净”和“位移没有乘时间差”这两件事上。12. 扩展方向与工程化建议12.1 从练手到完整的五步扩展素材替换用角色行走图、攻击图、背景图替换色块游戏观感会立刻提升。招式机制加入能量条和必杀技按能量状态触发不同攻击效果。双人对战增加第二套按键控制让两个玩家在同一键盘上对战。音效与反馈攻击命中时播放音效受击时角色变色或闪烁。帧动画系统把角色的每个状态对应到多帧图片列表按帧率逐帧切换。12.2 工程化建议不要一上来就往工程里加复杂设计。先保证最小可用版本能跑通再做增量修改。几个具体建议保持settings.py独立所有速度、颜色、尺寸都放进去方便调参。每次只改一个功能例如先只做移动验证移动再做跳跃验证跳跃再做攻击。定期备份可运行版本把“能正常对战”的版本标记为稳定版再去尝试新功能。目录管理素材、代码、输出分开存放后续加资源不会混乱。逻辑与渲染分离角色属性、血量、状态逻辑写在fighter.py绘制逻辑写在主程序的绘制函数中。后续替换成图片素材时不需要改动逻辑代码。12.3 关于安全使用的补充最后再补充一点如果是学习练手项目可以放 GitHub 或 CSDN 分享但要注意不要使用官方拳皇的 IP 素材也不要将他人的美术资源、音频资源打包发布到公开仓库。用自制素材或纯色块完成逻辑演示既合规又完全够用。13. 下一步怎么玩最值得先验证的永远是“攻击判定是否稳定”。整个项目的成败就在这一环状态切换得干净攻击结算只执行一次受击硬直能正常结束游戏的手感就基本合格了。最容易踩的坑则是状态机混乱和位移不乘帧时间差遇到问题优先去检查这两个地方。跑通这一版之后你手里就有一套完整的格斗游戏最小框架。后续无论是给角色换美术素材、增加必杀技系统还是改成双人对战模式都只是在现有结构里加模块不需要推翻重来。把这一版完整写出来、运行一遍、把 bug 调完Python 面向对象、事件处理和基础游戏循环的能力也就真正落地了。建议收藏这篇按章节一步步实现有问题随时对照排查表找原因。
阅读完成 · 觉得有帮助?