
说实话这两周我只攒下 10 个小时。标题里的 osgearthqt 和 ue 独立游戏是这个周期里我真正动手碰的两条技术线后面的 6735 小时是我那个一万小时计划的总账余额——到目前这个周期结束我已经投进去 3265 小时。10 小时摊到 14 天里看确实寒酸但复盘完我发现这轮 10 小时的产出密度比很多号称学了 20 小时的周次都要高。原因只有一个我把每段可支配时间都砍成能直接开工的颗粒度而不是坐到屏幕前再纠结要干什么。这 14 天里我实际完成的事情并不算多osgEarth 和 Qt 的窗口集成跑通了三维地球能住进 Qt 的窗口并正常响应鼠标UE 那边修掉了一个动画蓝图的状态切换 Bug用 ForEach Loop with Break 做了一个不会重复触发的击退原型另外花了一个小时清理环境问题。这些都是很小很小的进展但它们实打实落在了代码和备忘录里而不是停留在收藏夹和网盘。1. 两周10小时的账目碎片时间里的产出模型1.1 时间都去哪了一张明细表每次复盘我都会先记账。这 10 小时我分成了三块账目如下任务投入时长实际产出osgEarth Qt 集成6 小时地球渲染窗口嵌入 QOpenGLWidget支持旋转缩放可加载本地影像和高程数据UE 独立游戏原型3 小时修复 HitReact 状态切换问题完成基于 Overlap 的击退原型环境排查与交叉编译预演1 小时解决 Qt 版本混装和 linuxfb 插件缺失验证 ARM Linux 下的编译方案这张表不是写完就扔的。我会把它和上一周期的表做对比重点不是看时长变化而是看产出项有没有变化。如果连续两个周期都是同一批产出项那就说明我在原地打转接下来必须换任务或者换方法。1.2 时间块策略的调整上个周期我试过在工作日晚上抽 30 分钟零散学习结果发现 30 分钟刚进入状态就到了收工时间经常是环境还没热起来就关了终端。这周期我改成工作日早上一小时 周末两小时大块时间的组合效果立刻不一样。早上一小时的思路是前一天晚上把任务拆成打开哪个工程、改哪个函数、验证哪个现象这种级别早上纯粹按步骤执行。周末的两小时块则用来处理需要连续上下文的事情比如 osgEarth 的接口调试、UE 动画蓝图的链路排查。这次 10 小时能出活靠的不是意志力而是把任务颗粒度提前切好了。注意时间账要记的是产出不是时长。学了三小时但什么都没跑通账面上只值 0跑通一个再小的功能单位时间产出才是正的。这也是为什么我不太信那些只晒学习时长不晒作品的学习记录。2. osgEarthQt让三维地球住进Qt窗口2.1 技术选型动机不是为了两个引擎打架先说动机免得有人觉得在 UE 之外碰 osgEarth 是绕路。我做独立游戏的方向里有一个偏飞行模拟和星球尺度地形的玩法预设。UE 的优势在角色表现、动画系统和玩法逻辑但处理全球范围的多分辨率地形数据时它的 Landscape 工作流更适合中小尺度场景。osgEarth 恰好擅长 GIS 级数据调度能按视点动态加载不同 LOD 的影像和高程瓦片。所以我的规划不是让 osgEarth 替代 UE而是让 osgEarth 承担地球数据底座 编辑器预览的角色UE 承担真正的游戏运行时。选 Qt 做界面层是因为后续我想给这套工具做一个跨平台的编辑器壳子Qt 的信号槽和控件体系比直接写原生命令行舒服得多。这周期 6 个小时基本都花在打通Qt 窗口 - osgViewer - osgEarth 场景这条链路上。2.2 集成的四个关键动作osgEarth 官方其实带过一个基于 osgQt 的示例但 osgQt 在 osgEarth 3.x 时代维护得很一般。我最后没用 osgQt直接继承 QOpenGLWidget 来写 EarthWidget思路更符合 Qt 5.15 的年代感。核心就四步第一步在 initializeGL 里创建 osgViewer::Viewer配置 GraphicsContext 的 traits。注意这个阶段的顺序很重要不能在构造函数里初始化 GL 相关资源因为此时 QOpenGLWidget 的上下文还没准备好。第二步把 osg 的图形窗口绑定到 Qt 窗口。这里我用的是 GraphicsWindowEmbedded它会基于当前 widget 的窗口句柄创建一个 osg 可见的图形上下文。这一步最容易出问题如果 traits 里的设备参数和 QOpenGLWidget 的上下文参数对不上画面要么黑屏要么花屏。第三步给 viewer 设置 EarthManipulator。这是 osgEarth 的地球交互器处理鼠标旋转、缩放、平移都在这一层。如果忘了设置程序能跑但你拖动鼠标的时候地球纹丝不动。第四步把渲染循环塞进 paintGL。也就是在 paintGL 里调用 viewer-frame()每次 Qt 重绘都推进一帧。resizeGL 里同步更新 camera viewport否则窗口拉大后画面会变形。这四步跑通后我在 Qt 窗口里加载一个最简单的 earth 文件就能看到地球了。2.3 earth文件与数据路径的坑osgEarth 的场景入口是 earth 文件我这次用的是下面这种最简单的配置map nameSandbox options profileglobal-geodetic/profile /options image drivergdal namebase urldata/world.tif/url /image /map数据源我用了本地一个中等分辨率的全球影像 tif用 GDAL 驱动加载。之所以不一开始就上在线图源是因为本地数据可控调试网络和瓦片调度问题时不会引入外部变量。这里有一个容易踩的坑GDAL 读取驱动不是装上就觉得能用的需要确认 osgEarth 编译时确实带上了 GDAL 插件并且运行时插件目录能被正确找到。我在 Linux 下调试时程序经常报no readerWriter found或者干脆静默不显示影像最后发现是 plugin 搜索路径没指向 lib 目录下的 osgPlugins。另外提醒一句earth 文件里的路径不要写绝对路径尤其如果你准备在 Windows 和 Linux 之间来回倒。用相对路径并且和可执行文件的运行目录做好约定能省掉很多跨平台的莫名其妙问题。3. UE独立游戏线动画调试与击退原型的完成与失败3.1 动画蓝图Debug的三板斧UE 侧这周期 3 小时一半花在动画蓝图调试上。具体问题是角色受到攻击后进入 HitReact 状态但经常卡在那个状态里出不来表现就是角色一直保持僵直移动输入无效。一开始我怀疑是动画资源本身的问题但排查后确认资源没坏。后来我在动画蓝图的 Debug 模式里打开 AnimGraph Preview盯着状态机看了几轮才发现问题出在转换条件上HitReact 往 Idle/Run 的转换条件要求击退通知已经结束但蒙太奇里的 AnimNotify 触发时机比我预想的晚了几帧导致转换条件长时间不成立。如果把排查过程压成经验就是三板斧第一先在动画蓝图编辑器里开启 Anim Preview手动播放蒙太奇观察状态机行为不要一上来就改代码。第二用 Debugger 的 State Machine 输出面板看每个状态当前满足哪些转换条件不满足的显示成红色。第三在关键 AnimNotify 里加调试打印确认通知时机和状态切换的先后顺序很多卡状态问题其实是时序竞争。3.2 ForEach Loop with Break实现单次击退击退原型我放在了第三人称角色上。需求很直接角色挥拳时检测 360 度范围里的可交互敌人把最近的一个或者多个敌人沿角色朝向推开。但如果多个敌人同时进入范围每次帧更新都触发击退的话敌人会被反复弹开看起来像抽搐。解决办法就是 ForEach Loop with Break。每次触发击退时先获取范围内的所有可交互 Actor遍历数组做距离和角度判定命中第一个满足条件的目标后就 Break 跳出循环同时用一个冷却变量锁住后续帧的重复触发。这个模式在 UE 蓝图里非常通用遍历数组做筛选找到第一个符合条件的结果就立刻跳出避免后续无用计算。很多人写蓝图时习惯不加 Break数据量小没问题一旦 Overlap 的 Actor 数量上来性能和逻辑都容易出现怪问题。需要注意的是 Break 之后要对没找到目标的情况做处理否则冷却变量可能永远不清除。3.3 独立游戏需要一点技术美术的意识这次动画蓝图的坑让我重新确认了一件事独立游戏开发者哪怕不专攻 TA也得有基本的技术美术意识。动画蓝图状态机、蒙太奇通知、物理动画混合这些概念决定的是手感的上限。我见过很多独立游戏原型玩法逻辑写得很完整但角色动作要么滑步要么瞬移就是因为动画系统没和玩法数据打通。UE 里的技术美术分工很细不是要求全都学。但至少应该理解动画驱动用的是哪套数据、蒙太奇在哪个时机通知玩法层、物理模拟和动画混合的优先级。有了这层基础做击退、受击、翻滚这些交互时蓝图和动画资产才能互相咬合。后面我打算补充一些动画通知驱动玩法事件的练习把击退原型扩展到格挡和连招。4. 环境问题吃掉的那一小时三个报错的现场回放4.1 库版本混装的经典死法先看第一个现场。我在调试 osgEarth 程序时程序一启动就崩溃控制台弹出fatal: cannot mix incompatible Qt library (version ex50601) with this librar...这个报错里的版本串指向 Qt 5.6.1。我当前项目明明用的 Qt 5.15.2为什么程序里会混进 5.6.1 的东西排查时我先检查了 PATH 环境变量发现系统里某个旧工具目录下确实带了一份 Qt 5.6.1 的 DLL而它排在 Qt 5.15.2 的 bin 目录之前。程序运行时动态加载了这份旧库于是和编译时头文件对应的新库产生了冲突。处理的顺序是先从 PATH 里移除旧 Qt 目录再用 CMake 重新配置工程把 Qt5_DIR 明确指向 Qt 5.15.2 的 CMake 目录最后清理整个构建缓存后重新编译。这里特别强调清理缓存因为 CMake 会把 Qt 路径写死在缓存里你只改环境变量不动缓存的话链接阶段依然可能抓到旧库。经验遇到 Qt 版本混装报错先查环境变量和动态库搜索路径不要急着重装 Qt。报错里的版本串是定位线索记录完就可以顺着它去看是哪个目录的库先被加载了。4.2 linuxfb平台插件缺失第二个现场是在 ARM Linux 环境调试 Qt 程序时遇到的qt.qpa.plugin: could not find the qt platform plugin linuxfb in...这类报错说的是运行时找不到 linuxfb 平台插件。嵌入式 Linux 设备通常没有完整桌面环境Qt 在启动时需要明确用哪个 QPA 平台插件。如果程序默认找 xcb而系统里没有 xcb 运行库就很容易出这种提示。解法分两层。第一层是确保编译 Qt 时勾选了 linuxfb 插件编译产物中有 plugins/platforms/libqlinuxfb.so。第二层是运行时设置两个环境变量QT_QPA_PLATFORM_PLUGIN_PATH 指向插件目录QT_QPA_PLATFORMlinuxfb 强制指定平台。如果忽略第一层光设环境变量也没用因为文件根本不存在。这个小问题在开发机上不起眼一交叉到 ARM Linux 设备上就会爆属于典型的提前预演能省一小时的坑。4.3 组件缺失与链接失败第三类是编译期的两个问题放在一起说。一个朋友的项目报:-1: error: unknown module(s) in Qt: webenginewidgets这通常是安装 Qt 时没勾选 WebEngine 组件导致的。Windows 上用 Qt Maintenance Tool 把对应组件的 WebEngine 模块勾上就能解决但如果用的是离线安装包安装时没选就挺麻烦。这个报错的意义更多是提醒Qt 的模块安装是分件分包的默认安装不会包含全部模块。另一个是cannot find -lpublic这是链接器找不到名为 public 的库。表面看是库名写错实际问题往往是 Makefile 或 CMake 里多了一行无用的 LIBS -lpublic而它对应的库文件根本不存在。遇到这类报错先去项目文件里搜所有 -l 参数确认每个库名都对应真实存在的 .so 或 .lib 文件不要对着报错干猜。4.4 交叉编译环境下的一次预演这 14 天里我还花了一点时间做交叉编译的预演目标平台是 ARM 架构的 Linux 开发板。Qt 在交叉编译时的关键在 sysroot 里的依赖库是否齐全比如连接 ODBC 数据库前得先确认 unixODBC 的头文件和库文件都被安装到了 sysroot 里否则第三个阶段的 configure 就会提前报错。这次预演没有把整个 Qt 重编完只验证了工具链可行性和插件路径的部署方式算是埋伏笔。我见过不少崩溃类的 Qt 软件问题现象是程序运行到某个操作就闪退还报 0x0000005 访问违例排查到最后往往不是业务逻辑而是库路径混用导致的内存错误。环境问题看着小但它一旦发作消耗的时间可能比写功能还多。5. 6735小时怎么保质保量10000小时计划的下半场5.1 老实算一算这笔账这个周期结束余额还剩 6735 小时。按当前两周 10 小时的速率算是一个不太好看的数字日均不到 1 小时打完 6735 小时需要二十多年。这个账必须摆到台面上算清楚因为它能快速戳破我在坚持长线学习的幻觉。一万小时定律的真正含义不是熬够时间而是在有效反馈中反复训练足够长时间。如果我每个周期都只有 10 小时且大部分时间耗在环境配置和踩坑上那这一万小时就算熬满质量也堪忧。所以下半场的重点不是盯着 6735 这个数字而是把每个周期的有效产出密度抬上来。5.2 三条技术线的里程碑和验收标准为了让后续每个周期都有明确的完成定义我把三条线各自的下一步里程碑定下来了技术线下一个里程碑验收标准osgEarth Qt把本地影像换成自有高程瓦片并加一个 Qt 侧的基础参数面板程序能从 earth 文件读取配置面板可切换图层可见性UE 独立游戏完成进入区域触发敌人 - 受击 - 击退 - 动作反馈的完整闭环玩家能体感出击退反馈动画不卡状态、不重复触发Qt 工具链用 MVVM 框架搭一个简单的图层属性编辑面板接入 QChart 显示高程剖面面板数据和图层状态实时双向同步这三条线不是平均用力。下个周期我会把大头留给 UE 闭环osgEarth 侧只做维护性小改动。独立游戏终究要靠玩法和手感立住地图数据工具是辅助线不能反客为主。5.3 复盘规则与时间黑洞止损这几年的学习记录让我养成了一套止损规则专门针对时间黑洞。所谓时间黑洞就是那种一钻进去就出不来、而且对最终产出没有直接帮助的事情常见的包括某个第三方库编译不过、某个版本兼容问题、某个 UE 报错看不懂。我的止损线是 45 分钟。问题超过 45 分钟还没有明确头绪立刻把现象和已尝试的方案写进踩坑笔记然后切到另一条任务线。过几天带着新鲜感回来往往十几分钟就能解决因为潜意识已经帮你过滤掉了一半的错误方向。这周期 osgEarth 集成里确实有一个多小时耗在插件路径上但因为我按止损规则反复切换整体还在可控范围内。复盘动作我现在固定成每周一次只做三件事看本周记录、对比任务账里的产出项、确定下周任务清单。记录格式就三行做了什么、产出什么、卡在哪。简单到不会让人产生抗拒心理才能长期坚持。最后说几句实在的这 10 小时的周期让我印象最深的不是哪个技术点跑通了而是记账这件事本身的价值。把时间和产出对应起来会逼你面对一个事实很多你以为的在学习其实只是待在舒适区看资料和调工具。现在我更愿意把时间花在能产生可运行成果的任务上哪怕成果很小。下个周期我会继续按这个格式更新重点放在 UE 击退闭环和 osgEarth 图层面板上。也希望正在做长期自学计划的朋友把时间账和任务账分开记前者反映你的投入后者反映你的产出两者对不上时优先保产出。6735 小时这个数字没什么值得骄傲的真正值得盯着的是下一个 10 小时能留下几条代码、几个修掉的 Bug、几个跑得通的流程。