如何在Allegro 5中避免重绘康威生命游戏的静态背景?
解决Allegro中重复绘制静态表格边框的问题
我之前做康威生命游戏的Allegro复刻时也碰到过一模一样的问题——每次循环都重绘所有边框不仅浪费性能,还可能导致画面轻微闪烁。核心解决方案就是把静态的背景元素提前绘制到离屏缓冲区,之后每次只更新动态变化的内容,具体步骤如下:
1. 创建离屏背景位图
在程序初始化阶段(绘制初始表格之前),创建一个和游戏窗口尺寸一致的离屏位图,专门用来存放静态的表格边框和底色。这样我们只需要画一次边框,之后就能重复使用这个背景。
修改你的代码,先添加全局变量:
ALLEGRO_BITMAP *background; // 用于存储静态背景 int** board;
然后在初始化Allegro并创建显示窗口后,绘制静态背景:
// 创建和窗口同尺寸的离屏位图 background = al_create_bitmap(SCREEN_W * CELL_SIZE, SCREEN_H * CELL_SIZE); // 切换绘制目标到离屏位图(此时所有绘制操作都不会显示在屏幕上) al_set_target_bitmap(background); // 先填充白色底色 al_clear_to_color(WHITE); // 一次性绘制所有表格边框 for (int i = 0; i < SCREEN_H ; i++) { for (int j = 0; j < SCREEN_W; j++) { al_draw_rectangle( j * CELL_SIZE, i * CELL_SIZE, (j + 1) * CELL_SIZE, (i + 1) * CELL_SIZE, BLACK, 1.0 ); } } // 切换回屏幕缓冲区,准备显示初始画面 al_set_target_bitmap(al_get_backbuffer(display)); // 第一次把静态背景画到屏幕上 al_draw_bitmap(background, 0, 0, 0); al_flip_display();
2. 循环中只更新动态单元格
在while循环里,我们不再需要重复绘制边框了——只需要先把预先生成的背景画到屏幕上,然后只绘制状态变化的单元格填充色即可:
while (game_running) { // 处理输入事件(这里省略你的事件处理代码) // 根据生命游戏规则更新board状态 generate(); // 第一步:绘制静态背景(恢复边框和底色) al_draw_bitmap(background, 0, 0, 0); // 第二步:只绘制需要更新的单元格填充色 for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { if (board[i][j] == 1) { // 只有细胞存活时才绘制黑色填充,白色底色已经在背景里了 al_draw_filled_rectangle( j * CELL_SIZE, i * CELL_SIZE, (j + 1) * CELL_SIZE, (i + 1) * CELL_SIZE, BLACK ); } // 细胞死亡时无需绘制,背景的白色已经覆盖了上一帧的黑色 } } al_flip_display(); al_rest(0.1); // 控制帧率,可根据需求调整 }
3. 进阶优化:只重绘变化的单元格
如果你的生命游戏中每次迭代只有少数细胞状态变化,可以再进一步优化:维护一个上一帧的prev_board数组,每次generate()后对比prev_board和当前board,只重绘状态不同的单元格。这样能大幅减少绘制操作的数量,提升性能:
// 初始化时复制一份初始board到prev_board int** prev_board = malloc(rows * sizeof(int*)); for (int i = 0; i < rows; i++) { prev_board[i] = malloc(columns * sizeof(int)); memcpy(prev_board[i], board[i], columns * sizeof(int)); } // 循环内修改: generate(); al_draw_bitmap(background, 0, 0, 0); for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { if (prev_board[i][j] != board[i][j]) { // 只有状态变化时才重绘 ALLEGRO_COLOR color = (board[i][j] == 1) ? BLACK : WHITE; al_draw_filled_rectangle( j * CELL_SIZE, i * CELL_SIZE, (j + 1) * CELL_SIZE, (i + 1) * CELL_SIZE, color ); // 更新prev_board记录当前状态 prev_board[i][j] = board[i][j]; } } }
最后注意事项
- 程序退出时别忘了销毁离屏位图,避免内存泄漏:
al_destroy_bitmap(background); - 如果你的窗口尺寸可能变化,需要在窗口 resize 时重新生成背景位图。
内容的提问来源于stack exchange,提问作者gvd
相关产品推荐
相关产品推荐

