C++ SFML中采用过程式编程检测被点击按钮的实现方法
过程式SFML扫雷通用点击检测方案
全程不需要重构为OOP风格,完全兼容你已有的过程式游戏逻辑,所有点击检测逻辑只需要写一次,适配所有场景。
核心思路
定义通用的可点击元素结构体,每个场景维护一张当前场景所有可点击元素的列表,点击时统一遍历列表做碰撞检测,根据返回的元素类型触发对应逻辑。
实现步骤
1. 定义通用数据结构
不需要类和成员函数,纯C风格结构体即可:
#include <vector> // 所有可点击元素的类型枚举,新增按钮只需要在这里加项 enum ClickableType { CLICK_CELL, // 棋盘单元格 CLICK_RESTART, // 重启按钮 CLICK_SAVE, // 保存按钮 CLICK_EXIT // 退出按钮 }; // 可点击元素结构,存储边界、类型和附加参数 typedef struct { int x; // 左上角X坐标 int y; // 左上角Y坐标 int width; // 宽度 int height; // 高度 ClickableType type; int extra_row; // 单元格专属:存储行号,功能按钮可留空 int extra_col; // 单元格专属:存储列号,功能按钮可留空 } Clickable; // 全局变量存储当前场景的所有可点击元素 std::vector<Clickable> current_clickables;
2. 编写统一的点击检测函数
整个项目只需要写这一次检测逻辑,所有场景通用:
// 传入鼠标坐标,返回是否命中可点击元素,同时返回命中元素的类型和附加参数 bool check_click(int mouse_x, int mouse_y, ClickableType* out_type, int* out_row, int* out_col) { // 倒序遍历保证上层元素优先被命中(比如弹出菜单比底层元素优先级高) for (auto it = current_clickables.rbegin(); it != current_clickables.rend(); ++it) { const Clickable& item = *it; if (mouse_x >= item.x && mouse_x <= item.x + item.width && mouse_y >= item.y && mouse_y <= item.y + item.height) { *out_type = item.type; *out_row = item.extra_row; *out_col = item.extra_col; return true; } } return false; }
3. 场景切换时更新可点击元素列表
不需要修改检测逻辑,只需要在切换场景时重新填充current_clickables即可:
菜单场景加载示例
void load_menu_scene() { current_clickables.clear(); // 添加菜单页的功能按钮 current_clickables.push_back({100, 200, 200, 50, CLICK_RESTART, 0, 0}); current_clickables.push_back({100, 300, 200, 50, CLICK_SAVE, 0, 0}); current_clickables.push_back({100, 400, 200, 50, CLICK_EXIT, 0, 0}); }
游戏场景加载示例
// rows为棋盘行数,cols为列数,cell_size为单元格像素尺寸 void load_game_scene(int rows, int cols, int cell_size) { current_clickables.clear(); // 先加顶部功能按钮 current_clickables.push_back({10, 10, 100, 30, CLICK_RESTART, 0, 0}); current_clickables.push_back({120, 10, 100, 30, CLICK_SAVE, 0, 0}); // 再加所有棋盘单元格 const int board_top_offset = 50; // 棋盘顶部偏移,避开功能按钮 for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { int cell_x = c * cell_size; int cell_y = board_top_offset + r * cell_size; current_clickables.push_back({cell_x, cell_y, cell_size, cell_size, CLICK_CELL, r, c}); } } }
4. SFML事件中统一调用处理
不需要分场景写检测逻辑,事件处理代码全局通用:
// SFML窗口事件循环中处理鼠标点击 if (event.type == sf::Event::MouseButtonPressed) { int mouse_x = event.mouseButton.x; int mouse_y = event.mouseButton.y; ClickableType hit_type; int hit_row, hit_col; if (check_click(mouse_x, mouse_y, &hit_type, &hit_row, &hit_col)) { switch (hit_type) { case CLICK_CELL: // 直接调用你已经写好的过程式扫雷逻辑即可 process_cell_click(hit_row, hit_col); break; case CLICK_RESTART: restart_game(); break; case CLICK_SAVE: save_game(); break; case CLICK_EXIT: window.close(); break; } } }
优势
- 完全兼容已有过程式代码,不需要重构现有游戏逻辑
- 新增按钮/场景只需要修改对应场景的列表填充代码,不需要修改检测逻辑
- 支持元素层级控制,倒序遍历可以保证后加入的上层元素优先响应点击
内容的提问来源于stack exchange,提问作者Sang Phạm
相关产品推荐
相关产品推荐

