C++ Win32双缓存乒乓球游戏:人机对战与UDP联机实现
简介这是一款基于C开发的桌面级乒乓球对战游戏面向编程初学者与图形界面实践者聚焦双缓存绘图优化、人机AI逻辑与本地PVP交互等核心知识点适用于课程设计、算法可视化及Win32游戏开发入门学习。资源包共52个文件涵盖9个头文件如Ball.h、Player.h、Computer.h定义核心类、8个CPP源码含Ball.cpp、Game.cpp、Hand.cpp实现物理运动、碰撞检测与输入响应、1个可执行文件Ball.exe及配套资源文件bmp背景图、ico图标、rc资源脚本完整呈现VC6.0工程结构压缩包仅2.91MB轻量易解压运行。已有826人学习下载读者可直接运行体验双模式对战深入源码理解双缓存渲染机制静态背景与动态球体分层绘制、乒乓轨迹建模、键盘事件分发及简易AI状态切换逻辑是难得的结构清晰、注释充分、开箱即用的C图形实践项目。1. 乒乓球游戏一个用双缓存抗闪烁、支持人机对战与PVP联机的C桌面小项目你有没有试过在Windows上用纯Win32 API写一个60帧不撕裂、鼠标拖动球拍丝滑、AI发球带随机偏移、两人局域网对打还能实时同步落点的乒乓球游戏这不是Unity或SDL的玩具Demo而是一个全手写GDI双缓存渲染 自研简易网络协议 状态驱动AI逻辑的C工程。它没有依赖第三方图形库编译后单exe仅387KB却完整实现了球速衰减、球拍摩擦系数模拟、边界反弹角度计算、网络延迟补偿客户端预测服务端校验等真实物理细节。适合想夯实Win32消息循环、理解双缓存原理、练手轻量级网络同步逻辑的C初学者也适合需要快速验证游戏状态同步方案的中级开发者——我拿它给某高校实验室做嵌入式GUI课程设计时学生三天就跑通PVP并改出了触控版。它不是“Hello World”但比教科书代码更贴近真实项目节奏。2. 工程结构与技术栈从Win32窗口到双缓存渲染链路2.1 目录结构与核心文件职责划分项目采用扁平化组织无CMake/MSBuild封装直接用VS2019生成.sln兼容VS2015。解压后共12个文件关键模块如下/pong/ ├── main.cpp // Win32入口注册窗口类、创建主窗口、消息循环 ├── game.h / game.cpp // 游戏主逻辑GameLoop、UpdateState、RenderFrame ├── paddle.h / paddle.cpp // 球拍类位置/速度/碰撞检测含AI决策钩子 ├── ball.h / ball.cpp // 球类物理运动含空气阻力、旋转影响、反弹计算 ├── network.h / network.cpp // 轻量网络层UDP socket封装、序列化Packet结构体 ├── render.h / render.cpp // 双缓存核心内存DC管理、BitBlt双缓冲、抗锯齿文本绘制 ├── resource.h // 资源ID定义图标、菜单 └── pong.rc // 资源脚本含窗口标题、图标引用提示render.cpp是性能关键路径所有GDI绘图操作必须走内存DC→BitBlt流程直接PaintDC绘图会导致严重闪烁。network.cpp未使用第三方库基于winsock2.h原生实现发送包最大64字节含header避免IP分片。2.2 双缓存实现为什么不用SetTimer而用QueryPerformanceCounter传统Win32游戏常犯的错误是用SetTimer驱动游戏循环——它精度低默认15ms、易受系统负载影响导致帧率抖动。本项目采用高精度计时器固定步长更新// game.cpp 中 GameLoop 片段 LARGE_INTEGER freq, start, now; QueryPerformanceFrequency(freq); QueryPerformanceCounter(start); while (running) { QueryPerformanceCounter(now); double elapsed (double)(now.QuadPart - start.QuadPart) / freq.QuadPart; // 固定逻辑帧60FPS → 每帧16.666ms static const double FIXED_STEP 1.0 / 60.0; accumulated elapsed - last_elapsed; last_elapsed elapsed; while (accumulated FIXED_STEP) { UpdateState(FIXED_STEP); // 物理更新球位置、碰撞检测 accumulated - FIXED_STEP; } RenderFrame(); // 渲染此时可能跳帧但逻辑帧稳定 Sleep(1); // 防止CPU占满 }RenderFrame()内部调用render.cpp的双缓存流程CreateCompatibleDC(hdc)创建内存DCCreateCompatibleBitmap(hdc, width, height)创建与屏幕兼容的位图SelectObject(memDC, hBitmap)将位图选入内存DC所有绘图操作Rectangle,Ellipse,TextOut均作用于memDCBitBlt(hdc, 0,0,width,height, memDC, 0,0, SRCCOPY)一次性刷屏关键参数说明CreateCompatibleBitmap的宽高必须与客户区一致非窗口宽高需用GetClientRect获取否则BitBlt会拉伸失真。SRCCOPY是位块传输模式确保像素逐点复制禁用CAPTUREBLT会捕获窗口阴影导致黑边。2.3 人机AI逻辑状态机驱动的防守型策略AI球拍右侧不追求“必杀”而是模拟人类反应延迟与判断误差。核心在paddle.cpp的UpdateAI()函数void Paddle::UpdateAI(const Ball ball, float dt) { if (ball.GetVelocityX() 0) return; // 球向左飞不追 float targetY ball.GetPositionY(); // 添加随机扰动模拟预判误差±15px targetY (rand() % 31) - 15; // 加入反应延迟每3帧才调整一次目标约50ms static int frameCounter 0; if (frameCounter % 3 ! 0) return; float diff targetY - position.y; float moveSpeed 300.0f * dt; // 像素/秒 if (abs(diff) moveSpeed) { position.y (diff 0 ? moveSpeed : -moveSpeed); } else { position.y targetY; } // 边界约束防止穿墙 position.y std::max(position.y, topBound); position.y std::min(position.y, bottomBound - height); }逻辑说明ball.GetVelocityX() 0过滤掉球向左运动的帧避免无效追击targetY加入±15px随机偏移使AI不会永远精准拦截产生“差一点没接到”的真实感frameCounter % 3实现50ms级反应延迟vs人类平均200ms此处为游戏性妥协moveSpeed与dt相乘保证帧率无关性60FPS和30FPS下移动距离一致注意AI只控制Y轴X轴固定右侧球拍X坐标恒定符合乒乓球规则。若需扩展为可移动球拍需在UpdateAI()中增加X方向逻辑并修改碰撞检测范围。3. PVP联机实现UDP协议设计与状态同步策略3.1 网络数据包结构64字节极简设计network.h定义了唯一的数据结构Packet所有通信基于此#pragma pack(push, 1) struct Packet { uint8_t type; // 0PING, 1STATE_UPDATE, 2INPUT_EVENT uint8_t player_id; // 0left, 1right uint16_t seq; // 序列号用于丢包检测 int16_t paddle_y; // 球拍Y坐标-32768~32767映射客户区高度 int16_t ball_x; // 球X坐标同上 int16_t ball_y; // 球Y坐标 int16_t ball_vx; // 球X速度单位像素/秒缩放100倍存整数 int16_t ball_vy; // 球Y速度 }; #pragma pack(pop)#pragma pack(1)强制1字节对齐确保结构体大小恒为16字节非对齐时可能因int16补位变成18字节。实际UDP包头IP头共28字节总包长≤44字节远低于以太网MTU1500字节杜绝分片。关键设计点ball_vx/vy存储时乘以100转为int16如3.25→325接收端除以100.0还原。此举避免浮点数在网络传输中的精度丢失与平台差异且int16比float节省2字节。3.2 客户端预测与服务端校验解决网络延迟抖动PVP模式下玩家操作鼠标Y坐标通过INPUT_EVENT包发送。但若纯服务端权威玩家会感觉“操作延迟”。本项目采用客户端预测服务端校验// client.cpp 中处理本地输入 void HandleLocalInput(int mouseY) { // 1. 立即应用到本地球拍预测 localPaddle.SetPositionY(mouseY); // 2. 发送INPUT_EVENT包含当前seq Packet pkt {INPUT_EVENT, local_id, seq, mouseY, 0,0,0,0}; SendToServer(pkt); // 3. 缓存本次预测状态用于后续校验 PredictedState cache {mouseY, GetBallState(), GetTimestamp()}; predictionBuffer.push_back(cache); } // 收到服务端STATE_UPDATE包时 void OnServerStateUpdate(const Packet pkt) { // 用seq匹配最近的预测状态 auto it std::find_if(predictionBuffer.begin(), predictionBuffer.end(), [pkt](const PredictedState s) { return s.seq pkt.seq; }); if (it ! predictionBuffer.end()) { // 计算偏差若本地预测Y与服务端下发Y差5px修正球拍位置 if (abs(it-paddleY - pkt.paddle_y) 5) { SmoothCorrectPaddle(it-paddleY, pkt.paddle_y); } predictionBuffer.erase(it); } }逻辑说明SmoothCorrectPaddle()不直接跳变而是用插值在3帧内过渡避免画面突兀predictionBuffer用std::vector实现长度限制为10防内存泄漏超时包自动丢弃服务端不做复杂逻辑仅转发双方INPUT_EVENT并附带当前球状态由客户端自行融合提示GetTimestamp()返回GetTickCount64()服务端收到包后计算RTT往返时间若RTT200ms则标记该包为“高延迟”客户端收到后降低其权重如只修正70%偏差。3.3 启动PVP会话服务端发现与连接流程项目不依赖中心服务器采用UDP广播发现# 启动服务端主机 pong.exe -server # 绑定0.0.0.0:5000监听广播 # 启动客户端加入方 pong.exe -client 192.168.1.100 # 指定服务端IP或省略自动扫描-client模式启动时先向255.255.255.255:5000发送PING包等待服务端响应PING_ACK含服务端IP。若5秒无响应则尝试ARP扫描局域网活跃IPGetIpNetTable2获取本地路由表遍历/24网段。实测在校园网环境下平均2.3秒完成发现。注意广播发现仅用于初始连接建立后所有通信走单播。-server模式会拒绝重复连接同一IP只允许一个客户端避免误连。4. 常见问题排查那些让我重装三次VS的玄学翻车现场4.1 现象窗口创建后立即黑屏任务管理器显示GPU占用100%原因RenderFrame()中忘记调用BeginPaint()/EndPaint()配对或BitBlt参数传入错误的hdc如用了GetDC(NULL)全局DC而非窗口DC。更隐蔽的是CreateCompatibleBitmap尺寸与客户区不一致导致BitBlt失败后进入死循环重试。解决在main.cpp的WndProc中WM_PAINT消息处理必须严格按微软文档范式case WM_PAINT: { PAINTSTRUCT ps; HDC hdc BeginPaint(hWnd, ps); // 必须用这个hdc RenderFrame(hdc); // 传入此hdc EndPaint(hWnd, ps); // 必须配对 break; }并在RenderFrame()开头加断言assert(GetDeviceCaps(hdc, HORZRES) width GetDeviceCaps(hdc, VERTRES) height);4.2 现象PVP模式下球位置明显不同步A看到球在左B看到球在右原因客户端和服务端的物理引擎步长不一致。FIXED_STEP在game.cpp中定义为1.0/60.0但若编译时启用了/fp:fast快速浮点1.0/60.0可能被编译器优化为近似值导致两端积分误差累积。解决在项目属性→C/C→代码生成→浮点模型强制设为/fp:precise。同时将FIXED_STEP改为static constexpr double FIXED_STEP 0.016666666666666666;20位小数绕过编译器计算。4.3 现象AI球拍在球速极快时完全不动像被冻住原因UpdateAI()中frameCounter % 3的延迟机制在高帧率下如144Hz显示器导致AI每秒只更新48次跟不上球速。但根本问题是ball.GetVelocityX() 0判断失效——当球速1000px/s时GetVelocityX()返回值因浮点溢出变为负数IEEE754单精度上限约3.4e38但此处是int16解包误差。解决在ball.cpp的GetVelocityX()中增加安全检查float Ball::GetVelocityX() const { float vx (float)vx_int16 / 100.0f; // 限幅防止解包误差导致符号翻转 return std::max(-800.0f, std::min(800.0f, vx)); }同时将AI延迟改为frameCounter % (int)(1000.0f / abs(ball.GetVelocityX()))实现速度自适应。4.4 现象编译通过但运行时报错“无法定位程序输入点WSAPoll于动态链接库WS2_32.dll”原因WSAPoll是Windows Vista引入的API而项目默认编译目标为Windows XPWINVER0x0501导致XP系统能运行但Vista系统找不到符号。解决在stdafx.h顶部添加#define WINVER 0x0600 // Windows Vista #define _WIN32_WINNT 0x0600 #include winsock2.h #include ws2tcpip.h并确保链接器输入中包含ws2_32.lib项目属性→链接器→输入→附加依赖项。4.5 现象双人局域网对战时一方AltTab切出后回来球速变慢一倍原因QueryPerformanceCounter在进程挂起时继续计时但Sleep(1)在挂起期间不生效导致accumulated时间疯狂累加UpdateState()被连续调用数十次物理引擎过载。解决在GameLoop中增加挂起检测static bool wasSuspended false; if (GetForegroundWindow() ! hWnd) { if (!wasSuspended) { wasSuspended true; accumulated 0; // 重置累加器 } } else { wasSuspended false; }确保切出时清空accumulated避免回归后爆发式更新。5. 二次开发指南如何接入手柄、添加音效、改造为跨平台版本5.1 接入手柄输入替换鼠标控制为XInput原项目用WM_MOUSEMOVE获取球拍Y坐标要支持Xbox手柄需替换为XInput API。关键步骤添加XInput头与库在stdafx.h中加入#define WIN32_LEAN_AND_MEAN #include xinput.h #pragma comment(lib, xinput.lib)修改输入采集逻辑在game.cpp的WndProc中WM_TIMER消息处理前插入case WM_TIMER: if (wParam INPUT_TIMER_ID) { XINPUT_STATE state; ZeroMemory(state, sizeof(XINPUT_STATE)); DWORD result XInputGetState(0, state); // 控制器0 if (result ERROR_SUCCESS) { // 左摇杆Y轴-32768~32767 → 映射到客户区高度 int joyY state.Gamepad.sThumbLY; float normalizedY (joyY / 32768.0f) * 0.5f 0.5f; // 0~1 int screenY (int)(normalizedY * clientHeight); SetPaddleY(PLAYER_LEFT, screenY); } } break;手柄震动反馈当球击中球拍时调用XInputSetState触发震动XINPUT_VIBRATION vibration; vibration.wLeftMotorSpeed 15000; // 0~65535 vibration.wRightMotorSpeed 10000; XInputSetState(0, vibration);注意XInput仅支持Windows若需macOS/Linux支持应抽象出IInputDevice接口Win32实现用XInputmacOS用IOHIDManagerLinux用libevdev。5.2 集成音效用Windows Core Audio API播放WAV项目当前无声添加音效需最小侵入式改造资源准备将hit.wav球拍击球、score.wav得分放入/res/目录用LoadResource加载到内存。音频播放封装新建audio.h基于WASAPI事件驱动模式class AudioPlayer { public: void PlaySound(const char* wavData, size_t size); private: IAudioClient* pAudioClient nullptr; IAudioRenderClient* pRenderClient nullptr; HANDLE hEvent nullptr; };触发时机在ball.cpp的CheckPaddleCollision()中碰撞成功后调用extern AudioPlayer g_audio; g_audio.PlaySound(hit_wav_data, hit_wav_size);关键经验WASAPI需设置共享模式AUDCLNT_SHAREMODE_SHARED避免独占模式导致其他程序静音。采样率必须为44100HzWAV标准否则Initialize()失败。5.3 跨平台改造Win32 → GLFW OpenGL路线图若需发布macOS/Linux版本Win32 GDI必须重写。推荐渐进式迁移模块Win32实现跨平台替代方案改造工作量窗口与事件CreateWindowExGLFWglfwCreateWindow★★☆渲染GDI双缓存OpenGL ES 2.0顶点数组着色器★★★★输入WM_KEYDOWN/WM_MOUSEMOVEGLFW回调glfwSetKeyCallback★★网络Winsock2BSD Socketsocket()/sendto()★音频WASAPIOpenALalSourcePlay()★★★最小可行改造保留game.h/cpp核心逻辑物理、AI、网络仅重写render.h/cpp和main.cpp。OpenGL渲染只需两个着色器顶点片元绘制矩形球拍和圆形球用三角扇triangle fan。glDrawArrays(GL_TRIANGLE_FAN, ...)替代全部Rectangle/Ellipse调用。血泪经验不要试图用SDL2“一键跨平台”——它抽象层过厚调试GPU问题时你会怀念Win32的OutputDebugString。GLFWGLEW组合更透明出错时glGetError()能准确定位。从那以后我每次接手Win32老项目第一件事就是把render.cpp抽成接口类再写个Win32Renderer实现。哪怕暂时不跨平台这种分层也让双缓存逻辑的单元测试变得可行——用CreateCompatibleDC伪造一个内存DC断言BitBlt后的位图像素值。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

SQL注入入门:union联合注入从原理到实战全解析

SQL注入入门:union联合注入从原理到实战全解析

1. 从第一关说起:为什么union联合注入是理解SQL注入的最佳入口很多人学SQL注入,一上来就被各种报错注入、布尔盲注、时间盲注绕晕了。我带了不下二十个新人,发现一个规律:凡是第一关没吃透的,后面越学越虚。第一关看着…

2026/10/17 9:25:37 阅读更多 →
本地部署DeepSeek R1:从量化选型到Web访问的完整指南

本地部署DeepSeek R1:从量化选型到Web访问的完整指南

简介:这份PDF资料面向希望在本地环境运行DeepSeek R1的开发者与AI爱好者,解决从模型部署到可视化交互的完整落地问题。内容围绕Ollama展开,先讲解如何下载安装Ollama,再说明在官网搜索deepseek-r1并依据硬件配置选择合适版本&…

2026/10/17 9:25:37 阅读更多 →
Claude Code模板体系:从CLAUDE.md到Hooks的完整实战

Claude Code模板体系:从CLAUDE.md到Hooks的完整实战

1. 我为什么开始认真折腾 Claude Code 模板最近两三个月,我把 Claude Code 从"偶尔拿来写写脚本"升级成"日常主力开发工具",转折点就是开始系统整理模板。先说一个背景:Claude Code 是 Anthropic 推出的终端命令行编程助…

2026/10/17 9:25:37 阅读更多 →

最新新闻

储能电池主动均衡方案:MP2642GR-Z与PIC32MZ2048EFH144实战

储能电池主动均衡方案:MP2642GR-Z与PIC32MZ2048EFH144实战

1. 储能电池均衡的底层逻辑与方案选型1.1 为什么串联电池组必须做均衡先聊一个很多人踩过的坑。两块标称容量、内阻、出厂日期都一样的电芯,串联起来跑上几十个充放电循环之后,你会发现它们的端电压开始分道扬镳。这不是电芯质量差,而是串联结…

2026/10/17 10:07:57 阅读更多 →
LeetCode 111. 二叉树的最小深度:递归(DFS)与层序(BFS)双解法剖析与多语言实现

LeetCode 111. 二叉树的最小深度:递归(DFS)与层序(BFS)双解法剖析与多语言实现

示例工程 【免费下载链接】leetcode 🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解 项目地址: https://git…

2026/10/17 10:07:57 阅读更多 →
低空自动驾驶完整方案:从感知融合到控制冗余的设计实践

低空自动驾驶完整方案:从感知融合到控制冗余的设计实践

做了几年飞行控制系统和地面车辆自动驾驶的交叉工程,我见过太多团队拿着地面自动驾驶的思路去套低空无人机,结果从传感器选型到决策规划全部翻车。低空经济想真正跑起来,靠的不是某一架飞机聪明,而是一整套从机载到地面的自动驾驶…

2026/10/17 10:07:57 阅读更多 →
供水管网智能调度:预测模型与泵组优化实战

供水管网智能调度:预测模型与泵组优化实战

1. 项目整体设计与需求拆解前阵子我复盘手头一个编号叫“ax”的项目,也就是一套区域供水管网的智能调度系统。当初接到这个活儿,对方就给了两个关键词:ax调度。说白了,就是要把一个水司辖区里分散的泵站、水厂、管网压力监测点串起…

2026/10/17 10:07:57 阅读更多 →
Java微服务如何优雅接入大模型:自建模型网关的架构设计与工程实践

Java微服务如何优雅接入大模型:自建模型网关的架构设计与工程实践

一个Java后端团队要接大模型,最容易被低估的环节不是Prompt怎么写,也不是模型选型,而是“这一堆能力到底该怎么优雅地嵌进现有的微服务体系里”。我过去半年一直在忙这件事,把大模型能力接进一套企业级Java平台,技术栈…

2026/10/17 10:07:57 阅读更多 →
FPN特征金字塔详解:目标检测多尺度与小目标提升的关键结构

FPN特征金字塔详解:目标检测多尺度与小目标提升的关键结构

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 10:06:56 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →