C语言游戏开发:Pygame、SDL、OpenGL深度解析

C语言游戏开发:Pygame、SDL、OpenGL深度解析

一、前言:为什么游戏开发是C语言开发的重要技能?

学习目标

  • 理解游戏开发 的本质:编写程序实现游戏逻辑、图形渲染、用户交互
  • 明确游戏开发的重要性:支撑游戏产业的发展,成为游戏开发者的必备技能
  • 掌握本章学习重点:Pygame、SDL、OpenGL的开发方法、避坑指南、实战案例分析
  • 学会使用C语言开发游戏,实现游戏逻辑和用户交互

重点提示

💡 游戏开发是C语言开发的重要技能!随着游戏产业的发展,游戏开发的需求越来越大,C语言的高性能和可移植性使其在游戏开发中具有重要地位。


二、模块1:Pygame游戏开发基础

2.1 学习目标

  • 理解Pygame 的本质:基于SDL的Python游戏库,简化游戏开发
  • 掌握Pygame的核心架构:窗口管理、事件处理、图形渲染、音频播放
  • 掌握Pygame的开发方法:使用Pygame库进行游戏开发
  • 掌握Pygame的避坑指南:避免窗口创建失败、避免图形渲染错误、避免事件处理错误
  • 避开Pygame使用的3大常见坑

2.2 Pygame的核心架构

窗口管理 :创建和管理窗口
事件处理 :处理键盘、鼠标、触摸等输入事件
图形渲染 :使用Surface进行图形绘制
音频播放:播放音频文件

2.3 Pygame的开发方法

代码示例1:Pygame窗口创建

python 复制代码
import pygame
import sys

# 初始化Pygame
pygame.init()

# 设置窗口大小和标题
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Pygame窗口创建")

# 设置背景颜色
background_color = (255, 255, 255)

# 游戏循环
running = True
while running:
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 填充背景颜色
    screen.fill(background_color)

    # 更新显示
    pygame.display.flip()

# 退出Pygame
pygame.quit()
sys.exit()

代码示例2:Pygame绘制图形

python 复制代码
import pygame
import sys

# 初始化Pygame
pygame.init()

# 设置窗口大小和标题
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Pygame绘制图形")

# 设置颜色
white = (255, 255, 255)
red = (255, 0, 0)
blue = (0, 0, 255)
green = (0, 255, 0)

# 游戏循环
running = True
while running:
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 填充背景颜色
    screen.fill(white)

    # 绘制矩形
    rect = pygame.Rect(300, 200, 200, 200)
    pygame.draw.rect(screen, red, rect)

    # 绘制圆形
    pygame.draw.circle(screen, blue, (400, 300), 100)

    # 绘制线条
    pygame.draw.line(screen, green, (100, 100), (700, 500), 5)

    # 更新显示
    pygame.display.flip()

# 退出Pygame
pygame.quit()
sys.exit()

三、模块2:SDL游戏开发基础

3.1 学习目标

  • 理解SDL 的本质:Simple DirectMedia Layer,跨平台图形、音频、输入库
  • 掌握SDL的核心架构:窗口管理、事件处理、图形渲染、音频播放
  • 掌握SDL的开发方法:使用SDL 2.0库进行游戏开发
  • 掌握SDL的避坑指南:避免窗口创建失败、避免图形渲染错误、避免事件处理错误
  • 避开SDL使用的3大常见坑

3.2 SDL的核心架构

窗口管理 :创建和管理窗口
事件处理 :处理键盘、鼠标、触摸等输入事件
图形渲染 :使用渲染器绘制图形
音频播放:播放音频文件

3.3 SDL的开发方法

代码示例3:SDL窗口创建

c 复制代码
#include <SDL.h>

int main(int argc, char *argv[]) {
    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;

    // 初始化SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDL初始化失败:%s\n", SDL_GetError());
        return 0;
    }

    // 创建窗口
    window = SDL_CreateWindow(
        "SDL窗口",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        800,
        600,
        SDL_WINDOW_SHOWN
    );
    if (window == NULL) {
        fprintf(stderr, "窗口创建失败:%s\n", SDL_GetError());
        SDL_Quit();
        return 0;
    }

    // 创建渲染器
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (renderer == NULL) {
        fprintf(stderr, "渲染器创建失败:%s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 0;
    }

    // 设置渲染器颜色
    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);

    // 事件循环
    SDL_Event event;
    int quit = 0;
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = 1;
            }
        }
    }

    // 清理资源
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

代码示例4:SDL绘制图形

c 复制代码
#include <SDL.h>

int main(int argc, char *argv[]) {
    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;

    // 初始化SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDL初始化失败:%s\n", SDL_GetError());
        return 0;
    }

    // 创建窗口
    window = SDL_CreateWindow(
        "SDL绘制图形",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        800,
        600,
        SDL_WINDOW_SHOWN
    );
    if (window == NULL) {
        fprintf(stderr, "窗口创建失败:%s\n", SDL_GetError());
        SDL_Quit();
        return 0;
    }

    // 创建渲染器
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (renderer == NULL) {
        fprintf(stderr, "渲染器创建失败:%s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 0;
    }

    // 绘制图形
    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
    SDL_RenderClear(renderer);

    // 绘制矩形
    SDL_Rect rect = {300, 200, 200, 200};
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderFillRect(renderer, &rect);

    // 绘制圆形
    int x = 400, y = 300, r = 100;
    SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
    for (int i = x - r; i <= x + r; i++) {
        for (int j = y - r; j <= y + r; j++) {
            int dx = i - x;
            int dy = j - y;
            if (dx * dx + dy * dy <= r * r) {
                SDL_RenderDrawPoint(renderer, i, j);
            }
        }
    }

    // 绘制线条
    SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
    SDL_RenderDrawLine(renderer, 100, 100, 700, 500);

    SDL_RenderPresent(renderer);

    // 事件循环
    SDL_Event event;
    int quit = 0;
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = 1;
            }
        }
    }

    // 清理资源
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

四、模块3:OpenGL游戏开发基础

4.1 学习目标

  • 理解OpenGL 的本质:跨平台图形库,用于3D和2D图形渲染
  • 掌握OpenGL的核心架构:上下文管理、着色器、顶点缓冲、纹理
  • 掌握OpenGL的开发方法:使用OpenGL 3.3+核心模式进行游戏开发
  • 掌握OpenGL的避坑指南:避免上下文创建失败、避免着色器编译错误、避免渲染错误
  • 避开OpenGL使用的3大常见坑

4.2 OpenGL的核心架构

上下文管理 :创建和管理OpenGL上下文
着色器 :顶点着色器和片段着色器,负责图形渲染
顶点缓冲 :存储顶点数据
纹理:存储图像数据

4.3 OpenGL的开发方法

代码示例5:OpenGL三角形绘制

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

const GLchar *vertexSource =
    "#version 330 core\n"
    "layout (location = 0) in vec3 position;\n"
    "void main() {\n"
    "    gl_Position = vec4(position, 1.0);\n"
    "}\n";

const GLchar *fragmentSource =
    "#version 330 core\n"
    "out vec4 color;\n"
    "void main() {\n"
    "    color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
    "}\n";

int main() {
    // 初始化GLFW
    if (!glfwInit()) {
        fprintf(stderr, "GLFW初始化失败!\n");
        return 0;
    }

    // 配置GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // 创建窗口
    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL三角形绘制", NULL, NULL);
    if (window == NULL) {
        fprintf(stderr, "窗口创建失败!\n");
        glfwTerminate();
        return 0;
    }
    glfwMakeContextCurrent(window);

    // 初始化GLEW
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "GLEW初始化失败!\n");
        glfwTerminate();
        return 0;
    }

    // 设置视口
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);

    // 顶点数据
    GLfloat vertices[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };

    // 创建顶点缓冲对象
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 编译顶点着色器
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);

    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        fprintf(stderr, "顶点着色器编译失败:%s\n", infoLog);
    }

    // 编译片段着色器
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        fprintf(stderr, "片段着色器编译失败:%s\n", infoLog);
    }

    // 链接着色器程序
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        fprintf(stderr, "着色器程序链接失败:%s\n", infoLog);
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // 配置顶点属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    // 渲染循环
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();

        // 清除颜色缓冲
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // 使用着色器程序
        glUseProgram(shaderProgram);

        // 绑定顶点数组对象
        glBindVertexArray(VAO);

        // 绘制三角形
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glBindVertexArray(0);

        // 交换缓冲
        glfwSwapBuffers(window);
    }

    // 清理资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);

    // 终止GLFW
    glfwTerminate();

    return 0;
}

五、模块4:实战案例分析------使用SDL实现简单的游戏

5.1 学习目标

  • 掌握使用SDL实现简单的游戏:通过SDL库实现一个简单的贪吃蛇游戏
  • 学会使用SDL的窗口管理、渲染、事件处理功能
  • 避开实战案例使用的3大常见坑

5.2 使用SDL实现简单的游戏

代码示例6:贪吃蛇游戏

c 复制代码
#include <SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define GRID_SIZE 20
#define GRID_WIDTH (SCREEN_WIDTH / GRID_SIZE)
#define GRID_HEIGHT (SCREEN_HEIGHT / GRID_SIZE)
#define INITIAL_SNAKE_LENGTH 3
#define SNAKE_SPEED 100

typedef struct {
    int x;
    int y;
} Point;

typedef struct {
    Point head;
    Point body[100];
    int length;
    int direction; // 0: up, 1: right, 2: down, 3: left
} Snake;

typedef struct {
    Point position;
    int active;
} Food;

void init_snake(Snake *snake) {
    snake->length = INITIAL_SNAKE_LENGTH;
    snake->direction = 1; // right
    snake->head.x = GRID_WIDTH / 2;
    snake->head.y = GRID_HEIGHT / 2;
    for (int i = 1; i < snake->length; i++) {
        snake->body[i - 1].x = snake->head.x - i;
        snake->body[i - 1].y = snake->head.y;
    }
}

void draw_snake(SDL_Renderer *renderer, Snake *snake) {
    SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
    SDL_Rect head_rect = {snake->head.x * GRID_SIZE, snake->head.y * GRID_SIZE, GRID_SIZE, GRID_SIZE};
    SDL_RenderFillRect(renderer, &head_rect);

    for (int i = 0; i < snake->length - 1; i++) {
        SDL_Rect body_rect = {snake->body[i].x * GRID_SIZE, snake->body[i].y * GRID_SIZE, GRID_SIZE, GRID_SIZE};
        SDL_RenderFillRect(renderer, &body_rect);
    }
}

void update_snake(Snake *snake, int *game_over) {
    for (int i = snake->length - 2; i >= 0; i--) {
        snake->body[i + 1] = snake->body[i];
    }
    snake->body[0] = snake->head;

    switch (snake->direction) {
        case 0: // up
            snake->head.y--;
            break;
        case 1: // right
            snake->head.x++;
            break;
        case 2: // down
            snake->head.y++;
            break;
        case 3: // left
            snake->head.x--;
            break;
    }

    if (snake->head.x < 0 || snake->head.x >= GRID_WIDTH || snake->head.y < 0 || snake->head.y >= GRID_HEIGHT) {
        *game_over = 1;
    }

    for (int i = 0; i < snake->length - 1; i++) {
        if (snake->head.x == snake->body[i].x && snake->head.y == snake->body[i].y) {
            *game_over = 1;
        }
    }
}

void generate_food(Food *food, Snake *snake) {
    int valid_position = 0;
    while (!valid_position) {
        food->position.x = rand() % GRID_WIDTH;
        food->position.y = rand() % GRID_HEIGHT;
        valid_position = 1;

        if (food->position.x == snake->head.x && food->position.y == snake->head.y) {
            valid_position = 0;
        }

        for (int i = 0; i < snake->length - 1; i++) {
            if (food->position.x == snake->body[i].x && food->position.y == snake->body[i].y) {
                valid_position = 0;
            }
        }
    }

    food->active = 1;
}

void draw_food(SDL_Renderer *renderer, Food *food) {
    if (food->active) {
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        SDL_Rect rect = {food->position.x * GRID_SIZE, food->position.y * GRID_SIZE, GRID_SIZE, GRID_SIZE};
        SDL_RenderFillRect(renderer, &rect);
    }
}

void check_collision(Snake *snake, Food *food) {
    if (snake->head.x == food->position.x && snake->head.y == food->position.y) {
        snake->length++;
        food->active = 0;
        generate_food(food, snake);
    }
}

int main(int argc, char *argv[]) {
    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDL初始化失败:%s\n", SDL_GetError());
        return 0;
    }

    window = SDL_CreateWindow(
        "贪吃蛇游戏",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        SCREEN_WIDTH,
        SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN
    );
    if (window == NULL) {
        fprintf(stderr, "窗口创建失败:%s\n", SDL_GetError());
        SDL_Quit();
        return 0;
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (renderer == NULL) {
        fprintf(stderr, "渲染器创建失败:%s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 0;
    }

    srand(time(NULL));

    Snake snake;
    init_snake(&snake);

    Food food;
    food.active = 0;
    generate_food(&food, &snake);

    int game_over = 0;
    int last_time = SDL_GetTicks();
    int frame_time = 0;

    SDL_Event event;

    while (!game_over) {
        int current_time = SDL_GetTicks();
        int delta_time = current_time - last_time;
        last_time = current_time;

        frame_time += delta_time;

        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                game_over = 1;
            } else if (event.type == SDL_KEYDOWN) {
                switch (event.key.keysym.sym) {
                    case SDLK_UP:
                        if (snake.direction != 2) {
                            snake.direction = 0;
                        }
                        break;
                    case SDLK_RIGHT:
                        if (snake.direction != 3) {
                            snake.direction = 1;
                        }
                        break;
                    case SDLK_DOWN:
                        if (snake.direction != 0) {
                            snake.direction = 2;
                        }
                        break;
                    case SDLK_LEFT:
                        if (snake.direction != 1) {
                            snake.direction = 3;
                        }
                        break;
                }
            }
        }

        if (frame_time >= SNAKE_SPEED) {
            update_snake(&snake, &game_over);
            check_collision(&snake, &food);
            frame_time = 0;
        }

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        draw_snake(renderer, &snake);
        draw_food(renderer, &food);

        SDL_RenderPresent(renderer);

        SDL_Delay(16); // 限制帧率在60FPS
    }

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

    return 0;
}

六、本章总结与课后练习

6.1 总结

Pygame游戏开发 :基于SDL的Python游戏库,简化游戏开发

SDL游戏开发 :跨平台图形、音频、输入库,适合游戏开发

OpenGL游戏开发 :跨平台图形库,用于3D和2D图形渲染

实战案例分析:使用SDL实现简单的贪吃蛇游戏

6.2 课后练习

  1. 编写程序:使用Pygame创建窗口并绘制图形
  2. 编写程序:使用Pygame处理键盘和鼠标事件
  3. 编写程序:使用Pygame播放音频
  4. 编写程序:使用SDL创建窗口并绘制图形
  5. 编写程序:使用SDL处理键盘和鼠标事件
  6. 编写程序:使用SDL播放音频
  7. 编写程序:使用OpenGL绘制三角形
  8. 编写程序:使用OpenGL绘制矩形
  9. 编写程序:使用OpenGL加载并显示纹理
  10. 编写程序:使用SDL和OpenGL结合开发3D游戏
相关推荐
xcLeigh2 小时前
Python入门:Python3基础练习题详解,从入门到熟练的 25 个实例(六)
开发语言·python·教程·python3·练习题
不懒不懒2 小时前
安装python3.9.7和pycharm-community-2022.3.2.exe以及linux
linux·ide·python·pycharm
Jasonakeke2 小时前
我的编程来时路
java·c++·python
Yvonne爱编码2 小时前
Java 中的 hashCode () 与 equals () 核心原理、契约规范、重写实践与面试全解
java·开发语言·数据结构·python·hash
HappyAcmen2 小时前
理解Python中的global与nonlocal
python
测试老哥3 小时前
Web自动化测试:Cypress 测试框架概述
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
曲幽3 小时前
FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞!
python·logging·fastapi·web·monitoring·webserver·health·uptimerobot
Bert.Cai3 小时前
Python模块简介
开发语言·python
2501_924952693 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python