You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言贪吃蛇游戏添加TTF字体失败求助:问题排查与解决

SDL2贪吃蛇字体加载失败解决方案

问题概述

基于SDL2开发的C语言贪吃蛇游戏,尝试加载程序同目录下的字体文件用于显示分数和游戏结束文本,但加载失败。已确认字体路径正确、更换字体仍无效。

问题根源分析

从提供的代码中可定位三个核心问题:

  1. 未初始化SDL_ttf库:仅初始化了SDL视频模块,未调用TTF_Init()启动字体渲染库,这是SDL_ttf加载字体的必要前提。
  2. 局部变量覆盖全局变量:init()函数中重新声明了局部的TTF_Font* font,导致全局font指针始终为NULL,后续渲染文本时使用无效指针。
  3. 未清理字体资源:程序关闭时未调用TTF_CloseFont()释放字体资源,存在内存泄漏风险。

修复步骤

  1. 初始化SDL_ttf库:在init()函数中,SDL初始化完成后添加TTF初始化逻辑,并检查初始化结果。
  2. 修复变量覆盖:删除init()中TTF_Font* font的局部声明,直接给全局font变量赋值。
  3. 验证字体路径(可选):若仍加载失败,可打印当前工作目录(用getcwd())确认程序运行路径,或使用字体文件绝对路径测试。
  4. 添加资源清理:在close()函数中调用TTF_CloseFont(font)释放字体资源。

修改后的完整代码

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h> // 用于getcwd(),可选
#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <time.h>

// 常量定义
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int GRID_SIZE = 20;
const int GAME_SPEED = 150;

// 游戏状态变量
int snakeX[100], snakeY[100];
int snakeLength;
int fruitX, fruitY;
int score;
int direction;
int gameOver;
TTF_Font* font = NULL;

// SDL资源
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;

// 初始化函数
bool init() {
    // 初始化SDL视频模块
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        printf("SDL初始化失败! SDL_Error: %s\n", SDL_GetError());
        return false;
    }

    // 初始化SDL_ttf库
    if (TTF_Init() == -1) {
        printf("SDL_ttf初始化失败! TTF_Error: %s\n", TTF_GetError());
        return false;
    }

    // 打印当前工作目录(可选,用于验证字体路径)
    char cwd[1024];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("当前工作目录: %s\n", cwd);
    }

    // 创建窗口
    window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
    if (window == NULL) {
        printf("窗口创建失败! SDL_Error: %s\n", SDL_GetError());
        return false;
    }

    // 创建渲染器
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (renderer == NULL) {
        printf("渲染器创建失败! SDL_Error: %s\n", SDL_GetError());
        return false;
    }

    // 加载字体(直接赋值给全局变量,避免局部覆盖)
    font = TTF_OpenFont("arial.ttf", 24);
    if (font == NULL) {
        printf("字体加载失败! TTF_Error: %s\n", TTF_GetError());
        return false;
    }

    return true;
}

// 资源清理函数
void close() {
    // 释放字体资源
    if (font != NULL) {
        TTF_CloseFont(font);
        font = NULL;
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    TTF_Quit();
    SDL_Quit();
}

// 输入处理
void handleInput() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_QUIT) {
            gameOver = true;
        }
        else if (event.type == SDL_KEYDOWN) {
            switch (event.key.keysym.sym) {
                case SDLK_UP:
                case SDLK_w:
                    if (direction != 2)
                        direction = 0;
                    break;

                case SDLK_DOWN:
                case SDLK_s:
                    if (direction != 0)
                        direction = 2;
                    break;

                case SDLK_LEFT:
                case SDLK_a:
                    if (direction != 1)
                        direction = 3;
                    break;

                case SDLK_RIGHT:
                case SDLK_d:
                    if (direction != 3)
                        direction = 1;
                    break;
            }
        }
    }
}

// 生成水果位置
void generateFruit() {
    fruitX = rand() % (WINDOW_WIDTH / GRID_SIZE);
    fruitY = rand() % (WINDOW_HEIGHT / GRID_SIZE);
}

// 蛇移动逻辑
void moveSnake() {
    for (int i = snakeLength; i > 0; --i) {
        snakeX[i] = snakeX[i - 1];
        snakeY[i] = snakeY[i - 1];
    }

    switch (direction) {
        case 0: // 上
            snakeY[0] -= 1;
            break;
        case 1: // 右
            snakeX[0] += 1;
            break;
        case 2: // 下
            snakeY[0] += 1;
            break;
        case 3: // 左
            snakeX[0] -= 1;
            break;
    }

    // 边界碰撞检测
    if (snakeX[0] < 0 || snakeX[0] >= WINDOW_WIDTH / GRID_SIZE || snakeY[0] < 0 || snakeY[0] >= WINDOW_HEIGHT / GRID_SIZE) {
        gameOver = true;
    }

    // 自碰撞检测
    for (int i = 1; i < snakeLength; ++i) {
        if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
            gameOver = true;
        }
    }

    // 吃水果逻辑
    if (snakeX[0] == fruitX && snakeY[0] == fruitY) {
        score += 1;
        snakeLength++;
        generateFruit();
    }
}

// 渲染分数
void renderScore(){
    SDL_Color textColor = {255,255,255};
    char scoreText[20];
    sprintf(scoreText, "Score: %d", score);

    SDL_Surface* surface = TTF_RenderText_Solid(font, scoreText, textColor);
    if (surface == NULL) {
        printf("渲染分数文本失败! TTF_Error: %s\n", TTF_GetError());
        return;
    }
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);

    int textWidth = surface->w;
    int textHeight = surface->h;

    SDL_Rect textRect = {10, 10, textWidth, textHeight};
    SDL_RenderCopy(renderer, texture, NULL, &textRect);

    SDL_FreeSurface(surface);
    SDL_DestroyTexture(texture);
}

// 渲染游戏画面
void render() {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);

    // 渲染蛇
    SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
    for (int i = 0; i < snakeLength; ++i) {
        SDL_Rect rect = { snakeX[i] * GRID_SIZE, snakeY[i] * GRID_SIZE, GRID_SIZE, GRID_SIZE };
        SDL_RenderFillRect(renderer, &rect);
    }

    // 渲染水果
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_Rect fruitRect = { fruitX * GRID_SIZE, fruitY * GRID_SIZE, GRID_SIZE, GRID_SIZE };
    SDL_RenderFillRect(renderer, &fruitRect);

    // 渲染分数
    renderScore();

    SDL_RenderPresent(renderer);
}

// 游戏结束界面
void showGameOver(){
    bool quit=false;
    SDL_Event event;

    while (!quit){
        // 清空背景
        SDL_SetRenderDrawColor(renderer, 255,255,255,255);
        SDL_RenderClear(renderer);

        // 渲染"Game Over"文本
        SDL_Color textColor = {0, 0, 255};
        SDL_Surface* surface = TTF_RenderText_Solid(font, "Game Over!", textColor);
        if (surface == NULL) {
            printf("渲染游戏结束文本失败! TTF_Error: %s\n", TTF_GetError());
            break;
        }
        SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);

        int textWidth = surface->w;
        int textHeight = surface->h;

        // 修正文本居中计算(原代码计算错误,导致文本位置偏移)
        SDL_Rect textRect = {(WINDOW_WIDTH - textWidth) / 2, (WINDOW_HEIGHT - textHeight) / 2, textWidth, textHeight};
        SDL_RenderCopy(renderer, texture, NULL, &textRect);
        SDL_FreeSurface(surface);
        SDL_DestroyTexture(texture);

        // 渲染操作提示文本
        surface = TTF_RenderText_Solid(font, "Press ESC to quit or R to restart", textColor);
        if (surface == NULL) {
            printf("渲染提示文本失败! TTF_Error: %s\n", TTF_GetError());
            break;
        }
        texture = SDL_CreateTextureFromSurface(renderer, surface);

        textWidth = surface->w;
        textHeight = surface->h;

        SDL_Rect textRect1 = {(WINDOW_WIDTH - textWidth) / 2, (WINDOW_HEIGHT - textHeight) / 2 + 50, textWidth, textHeight};
        SDL_RenderCopy(renderer, texture, NULL, &textRect1);

        SDL_FreeSurface(surface);
        SDL_DestroyTexture(texture);
        SDL_RenderPresent(renderer);

        // 处理输入
        while(SDL_PollEvent(&event)){
            if(event.type == SDL_QUIT){
                quit = true;
                gameOver=true;
            }
            else if(event.type == SDL_KEYDOWN){
                if(event.key.keysym.sym == SDLK_ESCAPE){
                    quit=true;
                    gameOver=true;
                }
                else if (event.key.keysym.sym == SDLK_r){
                    quit=true; 
                    // 此处可添加重启游戏的逻辑
                }
            }
        }
    }
}

// 主函数
int main(int argc, char* argv[]) {
    srand(time(NULL));

    // 初始化
    if (!init()) {
        return 1;
    }

    // 初始画面
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);

    // 初始化游戏状态
    generateFruit();
    snakeX[0] = WINDOW_WIDTH / (2 * GRID_SIZE);
    snakeY[0] = WINDOW_HEIGHT / (2 * GRID_SIZE);
    snakeLength = 1;
    direction = -1;
    gameOver = false;
    score = 0;

    Uint32 lastTime = SDL_GetTicks();

    // 游戏主循环
    while (!gameOver) {
        Uint32 currentTime = SDL_GetTicks();
        Uint32 deltaTime = currentTime - lastTime;

        handleInput();

        if (deltaTime >= GAME_SPEED) {
            moveSnake();
            render();
            lastTime = currentTime;
        } 
    }

    // 显示游戏结束界面
    showGameOver();

    // 清理资源
    close();

    return 0;
}

额外修复说明

  • 修正了游戏结束界面中文本居中的计算错误(原代码用WINDOW_HEIGHT - WINDOW_WIDTH得到负数,导致文本位置偏移)。
  • 添加了文本渲染失败的错误检查,便于排查后续问题。
  • 可选添加了当前工作目录打印,帮助确认字体文件是否在程序运行路径下。

内容的提问来源于stack exchange,提问作者ImaHUman2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 19:22:43