C 语言游戏开发:Pygame、SDL、OpenGL 深度解析
一、前言:为什么游戏开发是 C 语言开发的重要技能?
学习目标
- 理解游戏开发的本质:编写程序实现游戏逻辑、图形渲染、用户交互
- 明确游戏开发的重要性:支撑游戏产业的发展,成为游戏开发者的必备技能
- 掌握本章学习重点:Pygame、SDL、OpenGL 的开发方法、避坑指南、实战案例分析
- 学会使用 C 语言开发游戏,实现游戏逻辑和用户交互
二、模块 1:Pygame 游戏开发基础
2.1 学习目标
- 理解Pygame的本质:基于 SDL 的 Python 游戏库,简化游戏开发
- 掌握Pygame 的核心架构:窗口管理、事件处理、图形渲染、音频播放
- 掌握Pygame 的开发方法:使用 Pygame 库进行游戏开发
- 掌握Pygame 的避坑指南:避免窗口创建失败、避免图形渲染错误、避免事件处理错误
2.2 Pygame 的核心架构
窗口管理:创建和管理窗口
事件处理:处理键盘、鼠标、触摸等输入事件
图形渲染:使用 Surface 进行图形绘制
音频播放:播放音频文件
2.3 Pygame 的开发方法
代码示例 1:Pygame 窗口创建
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 绘制图形
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 的避坑指南:避免窗口创建失败、避免图形渲染错误、避免事件处理错误
3.2 SDL 的核心架构
窗口管理:创建和管理窗口
事件处理:处理键盘、鼠标、触摸等输入事件
图形渲染:使用渲染器绘制图形
音频播放:播放音频文件
3.3 SDL 的开发方法
代码示例 3:SDL 窗口创建
#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) {
(SDL_PollEvent(&event)) {
(event.type == SDL_QUIT) {
quit = ;
}
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
;
}
代码示例 4:SDL 绘制图形
#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, , , , );
SDL_RenderFillRect(renderer, &rect);
x = , y = , r = ;
SDL_SetRenderDrawColor(renderer, , , , );
( i = x - r; i <= x + r; i++) {
( j = y - r; j <= y + r; j++) {
dx = i - x;
dy = j - y;
(dx * dx + dy * dy <= r * r) {
SDL_RenderDrawPoint(renderer, i, j);
}
}
}
SDL_SetRenderDrawColor(renderer, , , , );
SDL_RenderDrawLine(renderer, , , , );
SDL_RenderPresent(renderer);
SDL_Event event;
quit = ;
(!quit) {
(SDL_PollEvent(&event)) {
(event.type == SDL_QUIT) {
quit = ;
}
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
;
}
四、模块 3:OpenGL 游戏开发基础
4.1 学习目标
- 理解OpenGL的本质:跨平台图形库,用于 3D 和 2D 图形渲染
- 掌握OpenGL 的核心架构:上下文管理、着色器、顶点缓冲、纹理
- 掌握OpenGL 的开发方法:使用 OpenGL 3.3+ 核心模式进行游戏开发
- 掌握OpenGL 的避坑指南:避免上下文创建失败、避免着色器编译错误、避免渲染错误
4.2 OpenGL 的核心架构
上下文管理:创建和管理 OpenGL 上下文
着色器:顶点着色器和片段着色器,负责图形渲染
顶点缓冲:存储顶点数据
纹理:存储图像数据
4.3 OpenGL 的开发方法
代码示例 5:OpenGL 三角形绘制
#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 ;
}
glfwMakeContextCurrent(window);
(glewInit() != GLEW_OK) {
(, );
glfwTerminate();
;
}
width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(, , width, height);
GLfloat vertices[] = {, , , , , , , , };
GLuint VBO, VAO;
glGenVertexArrays(, &VAO);
glGenBuffers(, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, (vertices), vertices, GL_STATIC_DRAW);
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, , &vertexSource, );
glCompileShader(vertexShader);
GLint success;
GLchar infoLog[];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
(!success) {
glGetShaderInfoLog(vertexShader, , , infoLog);
(, , infoLog);
}
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, , &fragmentSource, );
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
(!success) {
glGetShaderInfoLog(fragmentShader, , , infoLog);
(, , infoLog);
}
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
(!success) {
glGetProgramInfoLog(shaderProgram, , , infoLog);
(, , infoLog);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, * (GLfloat), (GLvoid *));
glEnableVertexAttribArray();
glBindBuffer(GL_ARRAY_BUFFER, );
glBindVertexArray();
(!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClearColor(, , , );
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, , );
glBindVertexArray();
glfwSwapBuffers(window);
}
glDeleteVertexArrays(, &VAO);
glDeleteBuffers(, &VBO);
glDeleteProgram(shaderProgram);
glfwTerminate();
;
}
五、模块 4:实战案例分析——使用 SDL 实现简单的游戏
5.1 学习目标
- 掌握使用 SDL 实现简单的游戏:通过 SDL 库实现一个简单的贪吃蛇游戏
- 学会使用 SDL 的窗口管理、渲染、事件处理功能
5.2 使用 SDL 实现简单的游戏
代码示例 6:贪吃蛇游戏
#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 - ].x = snake->head.x - i;
snake->body[i - ].y = snake->head.y;
}
}
{
SDL_SetRenderDrawColor(renderer, , , , );
SDL_Rect head_rect = {snake->head.x * GRID_SIZE, snake->head.y * GRID_SIZE, GRID_SIZE, GRID_SIZE};
SDL_RenderFillRect(renderer, &head_rect);
( i = ; i < snake->length - ; 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);
}
}
{
( i = snake->length - ; i >= ; i--) {
snake->body[i + ] = snake->body[i];
}
snake->body[] = snake->head;
(snake->direction) {
:
snake->head.y--;
;
:
snake->head.x++;
;
:
snake->head.y++;
;
:
snake->head.x--;
;
}
(snake->head.x < || snake->head.x >= GRID_WIDTH || snake->head.y < || snake->head.y >= GRID_HEIGHT) {
*game_over = ;
}
( i = ; i < snake->length - ; i++) {
(snake->head.x == snake->body[i].x && snake->head.y == snake->body[i].y) {
*game_over = ;
}
}
}
{
valid_position = ;
(!valid_position) {
food->position.x = rand() % GRID_WIDTH;
food->position.y = rand() % GRID_HEIGHT;
valid_position = ;
(food->position.x == snake->head.x && food->position.y == snake->head.y) {
valid_position = ;
}
( i = ; i < snake->length - ; i++) {
(food->position.x == snake->body[i].x && food->position.y == snake->body[i].y) {
valid_position = ;
}
}
}
food->active = ;
}
{
(food->active) {
SDL_SetRenderDrawColor(renderer, , , , );
SDL_Rect rect = {food->position.x * GRID_SIZE, food->position.y * GRID_SIZE, GRID_SIZE, GRID_SIZE};
SDL_RenderFillRect(renderer, &rect);
}
}
{
(snake->head.x == food->position.x && snake->head.y == food->position.y) {
snake->length++;
food->active = ;
generate_food(food, snake);
}
}
{
SDL_Window *window = ;
SDL_Renderer *renderer = ;
(SDL_Init(SDL_INIT_VIDEO) < ) {
(, , SDL_GetError());
;
}
window = SDL_CreateWindow(, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
(window == ) {
(, , SDL_GetError());
SDL_Quit();
;
}
renderer = SDL_CreateRenderer(window, , SDL_RENDERER_ACCELERATED);
(renderer == ) {
(, , SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
;
}
srand(time());
Snake snake;
init_snake(&snake);
Food food;
food.active = ;
generate_food(&food, &snake);
game_over = ;
last_time = SDL_GetTicks();
frame_time = ;
SDL_Event event;
(!game_over) {
current_time = SDL_GetTicks();
delta_time = current_time - last_time;
last_time = current_time;
frame_time += delta_time;
(SDL_PollEvent(&event)) {
(event.type == SDL_QUIT) {
game_over = ;
} (event.type == SDL_KEYDOWN) {
(event.key.keysym.sym) {
SDLK_UP:
(snake.direction != ) {
snake.direction = ;
}
;
SDLK_RIGHT:
(snake.direction != ) {
snake.direction = ;
}
;
SDLK_DOWN:
(snake.direction != ) {
snake.direction = ;
}
;
SDLK_LEFT:
(snake.direction != ) {
snake.direction = ;
}
;
}
}
}
(frame_time >= SNAKE_SPEED) {
update_snake(&snake, &game_over);
check_collision(&snake, &food);
frame_time = ;
}
SDL_SetRenderDrawColor(renderer, , , , );
SDL_RenderClear(renderer);
draw_snake(renderer, &snake);
draw_food(renderer, &food);
SDL_RenderPresent(renderer);
SDL_Delay();
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
;
}
六、本章总结与课后练习
6.1 总结
✅ Pygame 游戏开发:基于 SDL 的 Python 游戏库,简化游戏开发
✅ SDL 游戏开发:跨平台图形、音频、输入库,适合游戏开发
✅ OpenGL 游戏开发:跨平台图形库,用于 3D 和 2D 图形渲染
✅ 实战案例分析:使用 SDL 实现简单的贪吃蛇游戏
6.2 课后练习
- 编写程序:使用 Pygame 创建窗口并绘制图形
- 编写程序:使用 Pygame 处理键盘和鼠标事件
- 编写程序:使用 Pygame 播放音频
- 编写程序:使用 SDL 创建窗口并绘制图形
- 编写程序:使用 SDL 处理键盘和鼠标事件
- 编写程序:使用 SDL 播放音频
- 编写程序:使用 OpenGL 绘制三角形
- 编写程序:使用 OpenGL 绘制矩形
- 编写程序:使用 OpenGL 加载并显示纹理
- 编写程序:使用 SDL 和 OpenGL 结合开发 3D 游戏


