Skip to content

Repository files navigation

GEOM AIM

本地单机 + 局域网联机伪 3D 第一人称射击训练器。射线渲染(raycasting),Python + pygame,无外部素材(音效由 numpy 合成)。包含靶场练习、3v3 回合对抗、5v5 积分赛三种模式,以及同 Wi-Fi 下多人联机的局域网房间。

运行

各系统双击 / 运行对应启动器,脚本会自动检查并安装依赖后再启动:

系统 启动器
macOS 双击 启动游戏.command
Windows 双击 start.bat
Linux 终端执行 ./start.sh(首次需 chmod +x start.sh

命令行(任意系统):

pip install -r requirements.txt
python3 main.py

macOS:提示"来自身份不明的开发者"无法打开

浏览器下载的 ZIP 里的文件都带 macOS 隔离标记(quarantine),无签名的 .command 脚本会被 Gatekeeper 拦下。任选其一解除:

方法 A(推荐,一次解决整个文件夹):打开「终端」,执行(文件夹不在下载目录就先 cd 过去,或把文件夹拖进终端窗口)。同时解隔离标记 + 补回可执行权限(GitHub 的 Download ZIP 不保留 +x):

cd ~/Downloads/geom-aim-main
xattr -cr .
chmod +x 启动游戏.command start.sh

之后双击 启动游戏.command 即可。

方法 B(不敲命令):双击被拦后,打开 系统设置 → 隐私与安全性 → 滚到最下面, 点「仍要打开」并输入密码。

方法 C:用 git clone 拿代码(clone 下来的文件没有隔离标记,天然不触发此问题):

git clone https://github.com/Franky100-pig/geom-aim.git
cd geom-aim && ./启动游戏.command

启动后进入标题界面,按 1 / 2 / 3 选择模式。

模式

模式 说明
1 练习 5 种靶场训练(见下)
2 对战 3v3 回合制对抗,含经济与买枪
3 积分赛 5v5 连续重生 TDM,先到 25 杀获胜
4 创建局域网房间 你当主机,跑权威模拟,朋友加入一起打
5 加入局域网房间 输入主机 IP 或 .local 主机名加入

对战与积分赛地图每局随机生成 180° 旋转对称 的掩体(半高箱 / 高箱);练习模式为固定柱。

操作

按键 作用
W A S D 移动
鼠标 转视角(含上下)
鼠标左键 开火(点射 / 按住连发)
鼠标右键 开镜(仅 SNIPER 练习模式,轻点切换)
E 蹲(缩小受击轮廓、降低被命中率)
空格 跳(短暂抬升视角,可越过半高箱探头)
G 扔烟雾弹(练习免费;对战需购买;积分赛不可用)
15 练习:切模式;对战:买枪阶段买枪;积分赛:随时换枪
C 对战:在已买的枪里循环切换
R 对战:换弹(2 秒)
M 3v3 / 积分赛:小地图放大一倍(再按还原)
6 对战买枪阶段:买烟雾弹
H 返回标题(任意状态)
4 标题界面:创建局域网房间(主机)
5 标题界面:输入主机地址加入(客户端)
ESC 菜单(灵敏度 / FOV / 准星 / 音效);对战内退回标题;房间界面取消 / 客户端断开
R 重开(模式结束后)
[ ] 鼠标灵敏度(游戏中可调,±15% 步进)
- = 视场角 FOV
M 静音
F 全屏(菜单内)

对战模式(3v3 回合制)

你 + 2 名队友 AI 对抗 3 名敌人 AI,双方均会交火、买枪、使用烟雾弹。

  • 赛制:best of 7,先到 4 回合胜。
  • 流程:买枪(prep, 10s, 可移动) → 交火(live, 90s) → 结算(2.5s) → 下一回合。
  • 死亡:一回合一条命。阵亡后先向后倒、视线朝天停留约 1 秒,再切到存活队友第一视角;观战期间视角锁定在队友身上,移动鼠标不会晃动画面。
  • 胜负:一方 3 人全灭则另一方得分;时间到则存活多者胜,相同判平。
  • 经济:起始 $800;胜 +3250 / 败 +1400;连败补偿递增;击杀按枪械分档奖励;上限 $16000。AI 按资金买枪。
  • 武器库存每回合重置——只带免费手枪上场(CS 式经济局)。同一回合内买的多把枪用 C 循环切换,买枪阶段和交火中都行;15 只在买枪阶段负责购买。
  • 弹药与换弹:每把枪一个弹匣(手枪 12 / 冲锋枪·步枪 30 / 连狙 20 / 狙击 5)。打空后自动换弹,也可按 R 主动换;换弹耗时 2 秒且期间不能开火,切枪会打断换弹(余弹保留)。右下角显示余弹与换弹进度。练习模式和积分赛不受弹药限制。
  • 武器:手枪 / 冲锋枪 / 步枪 / 连狙 / 狙击,共 5 把(无护甲、无投掷物,烟雾弹单独购买)。爆头乘伤害倍率。
  • 难度:4 档 + 逐回合自适应(依近 3 回合 KD 与胜率 ±20% 微调)。
反应延迟 瞄准误差 横移
简单 0.55s 6.5°
普通 0.38s 4.0°
困难 0.25s 2.2°
专家 0.16s 1.2°
  • AI 判定:敌我共用同一套命中几何,瞄准点带高斯误差,超出人形轮廓即打偏;走位与掩体可真实降低被命中率。
  • 小地图:左上角圆形小地图,只显示自己和队友(敌人位置保密);随视角旋转——正前方永远朝上,自己是中间的白色箭头。画出墙 / 柱子 / 半高箱 / 高箱的色块,防止在拼接地图里迷路;按 M 放大一倍。自由练习模式不显示
  • AI 战术(偷背身 / 绕后包抄):目标把背露给 AI 时,AI 会压近偷袭而非原地横移;目标正面对枪、压不上去时,高难度 AI 会按冷却绕到目标身后包抄,到位/超时/低血/贴脸即收回正面对枪。可经 config.pyAI_FLANK_* 系列参数调节(触发概率、冷却、包抄半径、最长时长、血量下限、开关)。
  • 战绩:命中率 / KD / 爆头率,回合与整场结束均展示。

积分赛(5v5 连续重生 TDM)

你 + 4 名队友 AI 对抗 5 名敌人 AI,无经济、可随时换枪。

  • 胜负:先累计 25 击杀 的队伍获胜。
  • 重生:阵亡后约 1.5s 重生,重生后 3s 无敌。阵亡观战规则同对战:先倒地 1 秒,再切到队友第一视角(鼠标锁定)。
  • 换枪15 随时免费换枪,无需购买。
  • 小地图:与 3v3 同一套(左上角圆形,只显示队友,随视角旋转,M 放大一倍)。
  • 地图:3×3 拼接(房间 25×17),随机对称掩体;玩家无烟雾弹,但 AI 会按判断使用烟雾弹——仅在中远距离封视线、低血撤退、或低频掩护推进时出手,且受开局冷静期与队伍配额限制,不会一开局就铺满。
  • 命中 / 难度 / 战绩逻辑同对战模式。

局域网联机(LAN)

同 Wi-Fi 下多人一起玩:一人当主机(跑权威模拟),其他人各开一份客户端加入。基于 UDP(JSON),零第三方依赖,纯标准库。

  • 模式:人类 vs AI 协作。你当主机开一局 5v5 积分赛,房间最多 3 名真人(含你),空位由 AI 补满。
  • 拓扑:客户端-服务器。主机跑全部模拟逻辑(移动、命中、AI、胜负);客户端只把本地键鼠输入上行、收到 30Hz 快照后在自己屏幕上渲染——所以即使某台机器很弱也不影响公平性。
  • 加入方式:标题界面按 5 → 输入主机的 IP(如 192.168.1.20)或 .local 主机名(如 Frankys-Mac.local,macOS 自带,无需记 IP)→ 回车连接。主机可在房间界面看到本机局域网地址提示。
  • 房间流程:主机按 4 建房 → 等朋友加入(房间界面显示已连玩家)→ 回车 / 空格开局;按 Esc 取消。
  • 朝向:服务器权威 yaw(防作弊 + 简单),客户端本地只算俯仰(pitch)做视觉。
  • 断线:客户端 Esc 退回标题即断开;主机侧超时(默认 4s 无输入)自动踢人并补回 AI。

快照按局域网 1500 MTU 自动 BASE64 分片重组,单包约 1.1KB,普通 Wi-Fi/Ethernet 下稳定送达;丢片则当帧丢弃、等下一帧快照。

练习模式(5 种)

模式 训练
AIM BOTZ 甩枪点靶,5–10 靶同时,清掉补位,进阶混入横移靶
REFLEX 闪现反应,单靶限时 0.7s→0.32s 递减并缩小
TRACKING 跟枪压枪,黏住高速乱跑的靶,断 0.85s 连击清零
PEEK 掩体探头,靶从柱后滑出才可击中
SNIPER 远距狙击,右键开/关镜 4x,左键单发需上栓

枪感与调参

AK 手感:600 RPM,前几发近直上,后向右摆再回摆(player.pySPRAY_YAW_PATTERN)。连发越久扩散越大,移动附加扩散,站定最准。爆头区在身高 78%–94%(250 分),躯干 100 分,连击第二起每级 +5%(封顶 2.5x)。

主要参数在 config.py,改完重开生效:

  • MOUSE_SENS:灵敏度([ ] 游戏内可调)
  • H_FOV_DEG:水平视场角,默认 90(- = 可调)
  • RECOIL_PX / RECOIL_RECOVER_PX:后坐力上抬与回正
  • SPRAY_PER_SHOT / SPRAY_MAX:连发扩散
  • RENDER_COL_STEP:射线精度,默认 2(卡则改 3/4,求锐利试 1)
  • AI_DIFF_* / ECON_* / MATCH_* / SCORE_*:对战与积分赛数值
  • TERRAIN_*:地形起伏开关 / 幅度 / 频率 / 种子(见下节)
  • 游戏内 L 热重载配色

地形上下起伏

地图叠了一层平滑高度场(值噪声,与掩体种子独立、可复现):

  • 玩起来的感觉:上下坡时相机会真实地升降(上坡视线抬高、下坡压低); 山脊能挡住视线、洼地能藏人——蹲在坡后是真掩体。
  • AI 同样受地形影响:会按脚下高度开火与判视线,翻山越岭时和玩家同规则。
  • 渲染:地面网格与墙脚跟着地形起伏,人和靶子会"踩"在坡面上。

config.py

TERRAIN_ENABLED = True    # 关掉即回到完全平整的旧地图
TERRAIN_AMP = 0.55        # 起伏幅度(墙高=1.0;想要更夸张可以试 0.8)
TERRAIN_FREQ = 0.16       # 越小山头越大越平缓
TERRAIN_SEED = 1337       # 换个数字换一张"地形脸"

掩体系统

对战与积分赛地图每局随机生成,满足:180° 旋转对称、全图连通、出生点四周留空。

  • 半高箱(高 0.70):高于站立眼高,站立不可穿透;蹲下(头顶 0.65 < 0.70)完全藏住,站立露头可见;跳起平射可越过。
  • 高箱(高 1.0):等同满高墙,绝对遮挡。

满高外墙与房间墙始终为绝对遮挡,跳跃无法越过。

场地规模

地图由 ARENA_TILES_X × ARENA_TILES_Y 个房间拼接,房间开门洞连通,外圈封外墙。改 config.py 顶部 ARENA_*

  • ARENA_TILES_X / ARENA_TILES_Y:房间数(默认 4×3 = 12)
  • ARENA_ROOM_W / ARENA_ROOM_H:单房间尺寸

默认约 109×57 格。对战 / 积分赛另有各自的 MATCH_* / SCORE_* 尺寸参数。

狙击配置

  • ADS_ZOOM:开镜倍率,默认 4
  • BOLT_TIME:栓动延迟,默认 0.9s(设 0 变连点)
  • SNIPER_SENS_MUL:开镜灵敏度乘子,默认 0.28
  • SNIPER_DIST_MIN / SNIPER_DIST_MAX:远距靶刷新距离

代码结构

main.py      主循环、命中判定、计分、标题三模式、对战/积分赛分支、阵亡观战
engine.py    伪 3D 射线渲染:地图 / 相机 / 投影 / DDA / 精灵可见性 / 掩体高度
player.py    移动碰撞、后坐力、弹道扩散、蹲跳、Weapon 抽象
targets.py   靶子与 5 种练习模式生成、移动、生命周期
weapons.py   Weapon 数据类 + 5 把枪数值
ai.py        Agent 状态机 + BFS 寻路 + AI 命中判定
match.py     Match:对战/积分赛状态机、队伍、经济、胜负、重生、观战
net.py       局域网联机网络层(UDP/JSON、分片、主机/客户端)
hud.py       准星、计分板、枪模型、菜单、开镜镜框、对战/积分赛 HUD、标题
effects.py   曳光弹、枪口火光、碎片、飘字、命中标记
audio.py     numpy 合成枪声与命中音
config.py    全部可调参数(AI_DIFF_* / ECON_* / MATCH_* / SCORE_* / COVER_* / TERRAIN_*)
tests/       无头自检套件(练习 / 对战 / 积分赛 / 掩体 / 烟雾 / 返回标题)

自检

python3 tests/test_core.py         # 练习模式
python3 tests/test_match.py        # 对战模式
python3 tests/test_score.py        # 积分赛
python3 tests/test_cover.py        # 掩体对称/连通/遮挡
python3 tests/test_nades.py        # 烟雾弹
python3 tests/test_title_return.py # H 返回标题
python3 tests/test_net.py          # 局域网联机:握手/输入/快照分片/超时
python3 tests/bench.py             # 渲染开销基准

全部为无头(dummy 驱动)测试,无需显示设备。改练习逻辑跑 test_core,改对战 / 积分赛跑对应用例,改联机跑 test_net

性能

python3 tests/bench.py(1280x720,CPU 侧,dummy 驱动):

天花板/地板渐变   0.068 ms
地板网格          0.158 ms
墙体光栅化        2.343 ms   ← 主要开销,随 RENDER_COL_STEP 线性变化
HUD + 准星 + 枪   0.133 ms
整帧 update+draw  2.736 ms   → 约 365 fps(拼接大地图)

对比 60 fps 的 16.7 ms 预算余量充足。

渲染数学约定

engine.py 前先看文件头注释。核心三条:

  • 射线 ray = dir + plane_unit * sdir 分量恒为 1,DDA 参数即垂直距离,无鱼眼。
  • 屏幕纵坐标 y = horizon + (H * vscale / d) * (EYE_HEIGHT - h)vscale = W / (2 * L * H) 保证像素密度一致。
  • 上下看靠平移地平线:horizon = H/2 + pitch_px;准星在深度 d 处的世界高度为 EYE_HEIGHT + pitch_px * d / (H * vscale),命中判定用之。

注意事项(易错点)

  • pygame 2 窗口事件扁平:用 pygame.WINDOWFOCUSLOST / pygame.WINDOWMINIMIZED,无 pygame.WINDOWEVENT 子属性写法,否则切窗口瞬间崩。
  • Renderer 建 Surface 前需检查 pygame.display.get_init():无头环境 Surface.convert() 会抛异常。

About

A good practice for those wanting to play shooting games on Mac.

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages