C语言SDL开发:文本按钮显示消失且仅响应点击区域问题求助
问题原因分析
你的问题核心是主循环只渲染背景图,没有重复绘制文本和START按钮:
- 文本通过
render_text逐字显示后,纹理被直接销毁,主循环里没法重新绘制这些文本 - START按钮的绘制代码只在主循环执行前跑了一次,进入循环后每次
SDL_RenderClear清空屏幕,只画背景图,自然看不到文本和按钮 - 按钮点击区域还能触发,是因为你在事件处理里依然用了原来的坐标判断,和画面渲染无关
修复步骤及代码调整
1. 修改render_text函数,保存最终文本纹理
让函数完成逐字动画后返回最终的文本纹理,方便主循环重复渲染:
SDL_Texture* render_text(SDL_Renderer* renderer, TTF_Font* font, const char* text, SDL_Color color, int x, int y, int delay) { int len = strlen(text); char buffer[len + 1]; memset(buffer, '\0', sizeof(buffer)); int i = 0; Uint32 ticks; SDL_Rect rect = { x, y, 0, 0 }; // 逐字显示动画 while (i < len) { ticks = SDL_GetTicks(); strncat(buffer, &text[i], 1); SDL_Surface* surface = TTF_RenderText_Solid(font, buffer, color); SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); rect.w = surface->w; rect.h = surface->h; // 每次动画先重绘背景,避免文本残影 SDL_RenderClear(renderer); SDL_RenderCopy(renderer, image3.texture, NULL, NULL); SDL_RenderCopy(renderer, texture, NULL, &rect); SDL_RenderPresent(renderer); SDL_FreeSurface(surface); SDL_DestroyTexture(texture); i++; SDL_Delay(delay - (SDL_GetTicks() - ticks)); } // 创建并保存最终的文本纹理 SDL_Surface* final_surface = TTF_RenderText_Solid(font, text, color); SDL_Texture* final_texture = SDL_CreateTextureFromSurface(renderer, final_surface); SDL_FreeSurface(final_surface); // 最后重绘一次完整画面 SDL_RenderClear(renderer); SDL_RenderCopy(renderer, image3.texture, NULL, NULL); SDL_QueryTexture(final_texture, NULL, NULL, &rect.w, &rect.h); SDL_RenderCopy(renderer, final_texture, NULL, &rect); SDL_RenderPresent(renderer); return final_texture; }
2. 在main中保存文本信息
定义结构体存储文本纹理和位置,调用render_text时记录这些数据:
// 新增:保存已渲染文本的结构体 typedef struct { SDL_Texture* texture; int x; int y; } RenderedText; int main(int argc, char* argv[]) { // ... 原有初始化代码不变 RenderedText story_texts[4]; int text_count = 0; SDL_Color color1 = {255, 0, 0, 255}; // 修正alpha值为不透明 // 逐字显示并保存文本纹理 story_texts[text_count].texture = render_text(renderer, font1, "You are caught in this castle!", color, 370, 300, delay); story_texts[text_count].x = 370; story_texts[text_count].y = 300; text_count++; int y_pos = 350; story_texts[text_count].texture = render_text(renderer, font2,"To escape", color, 470, y_pos, delay); story_texts[text_count].x = 470; story_texts[text_count].y = y_pos; text_count++; y_pos += 50; story_texts[text_count].texture = render_text(renderer, font1, "You need to", color, 470, y_pos, delay); story_texts[text_count].x = 470; story_texts[text_count].y = y_pos; text_count++; y_pos += 50; story_texts[text_count].texture = render_text(renderer, font2,"WIN this game", color1, 430, y_pos, delay); story_texts[text_count].x = 430; story_texts[text_count].y = y_pos; text_count++; // START按钮相关代码不变,保留text_texture和坐标变量 int oval_width = 100; int oval_height = 50; int oval_x = (SCREEN_WIDTH - oval_width) /2 +40; int oval_y = SCREEN_HEIGHT - oval_height - 10; SDL_Surface* text_surface = TTF_RenderText_Solid(font, "START", textColor); SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer, text_surface); SDL_Rect text_rect = { oval_x + (oval_width - text_surface->w) / 2, oval_y + (oval_height - text_surface->h) / 2, text_surface->w, text_surface->h }; // ... 事件循环前的其他代码不变
3. 修改主循环渲染逻辑,重复绘制文本和按钮
在主循环中,判断当前为初始界面时,重新绘制文本和START按钮:
SDL_Event event; bool done = false; while (!done) { while (SDL_PollEvent(&event)) { // 原有事件处理代码不变 } SDL_RenderClear(renderer); SDL_RenderCopy(renderer, current_image.texture, NULL, NULL); // 只有在初始界面时才绘制文本和按钮 if (current_image.texture == image3.texture) { // 绘制所有剧情文本 for (int i = 0; i < text_count; i++) { SDL_Rect rect = { story_texts[i].x, story_texts[i].y, 0, 0 }; SDL_QueryTexture(story_texts[i].texture, NULL, NULL, &rect.w, &rect.h); SDL_RenderCopy(renderer, story_texts[i].texture, NULL, &rect); } // 绘制START按钮 SDL_SetRenderDrawColor(renderer, 190, 140, 255, 255); SDL_Rect oval_rect = { oval_x, oval_y, oval_width, oval_height }; SDL_RenderFillRect(renderer, &oval_rect); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderDrawRect(renderer, &oval_rect); // 绘制START文本 SDL_RenderCopy(renderer, text_texture, NULL, &text_rect); } SDL_RenderPresent(renderer); }
4. 补充资源释放
在程序末尾添加文本纹理的释放代码,避免内存泄漏:
// 释放剧情文本纹理 for (int i = 0; i < text_count; i++) { SDL_DestroyTexture(story_texts[i].texture); } // 释放START按钮的文本纹理和表面 SDL_DestroyTexture(text_texture); SDL_FreeSurface(text_surface); // 原有资源释放代码不变 free_image(&image1); free_image(&image2); // ...
额外注意点
- 删掉没用的
Render_text函数,里面的renderer和font都是NULL,会导致程序崩溃 - 修正
TEXT_COLOR的alpha值(如果需要不透明文本):#define TEXT_COLOR {0, 0, 0, 255} - 检查字体文件路径是否正确,确保
arial.ttf在程序运行目录下
内容的提问来源于stack exchange,提问作者hoover
相关产品推荐
相关产品推荐

