SDL2(Simple DirectMedia Layer 2)游戏开发概述(二)

260 阅读8分钟

SDL2(Simple DirectMedia Layer 2)游戏开发概述(二)

以下是如何使用C语言和SDL2库结构化你的项目以创建一款简单的横版格斗游戏:

项目结构

main.c:游戏的入口点,负责初始化SDL库,设置游戏循环,并处理用户输入。
game.h:包含游戏相关常量、结构体以及函数声明的头文件。
game.c:包含游戏相关功能实现的源文件,如游戏逻辑、碰撞检测和计分系统等。
player.h:包含玩家相关常量、结构体以及函数声明的头文件。
player.c:包含玩家相关功能实现的源文件,如玩家移动、攻击和防守动作等。
enemy.h:包含敌人相关常量、结构体以及函数声明的头文件。
enemy.c:包含敌人相关功能实现的源文件,如敌人移动、攻击和防守动作等。
level.h:包含关卡相关常量、结构体以及函数声明的头文件。
level.c:包含关卡相关功能实现的源文件,如关卡加载、瓦片地图绘制以及碰撞检测等。
graphics.h:包含图形相关常量、结构体以及函数声明的头文件。
graphics.c:包含图形相关功能实现的源文件,如渲染、精灵管理以及动画播放等。
audio.h:包含音频相关常量、结构体以及函数声明的头文件。
audio.c:包含音频相关功能实现的源文件,如音效播放、背景音乐控制以及语音配音等。
SDL_utils.h:包含SDL相关实用函数的头文件,如错误处理和资源管理等。
SDL_utils.c:包含SDL相关实用函数的具体实现的源文件。

游戏循环

游戏循环将在main.c中定义,它会不断调用各个模块的功能来更新游戏状态、处理事件、渲染画面和播放声音,从而形成一个完整的、连续运行的游戏流程。

int main() {
    // Initialize SDL
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

    // Set up game window and renderer
    SDL_Window* window = SDL_CreateWindow("Side-Scrolling Fighting Game",   
    SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 
    SDL_RENDERER_ACCELERATED);

    // Set up game state
    Game game;
    game_init(&game);

    // Game loop
    while (1) {
        // Handle user input
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                break;
            }
            // Handle player input
            player_handle_input(&game.player, &event);
        }

        // Update game state
        game_update(&game);

        // Render game
        renderer_clear(renderer);
        game_render(&game, renderer);
        SDL_RenderPresent(renderer);

        // Cap framerate
        SDL_Delay(16);
    }

    // Clean up
    game_free(&game);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

游戏状态

游戏状态将会包含关于当前关卡、玩家、敌人以及其他游戏相关数据的信息。
以下是一个Game结构体可能的样子的例子:

typedef struct {
    Level level;
    Player player;
    Enemy enemies[10];
    int score;
    int lives;
} Game;

玩家

玩家会有自己的一套用于移动、攻击和防御的功能方法。以下是一个Player结构体可能的样子示例: (Player结构体不仅包含了表示玩家状态的各种属性,还包含了指向移动、攻击和防御等操作函数的指针,以便在游戏中实现玩家的行为逻辑。)

typedef struct {
    int x, y;
    int velocity_x, velocity_y;
    int health;
    int attack_cooldown;
    SDL_Texture* sprite;
} Player;

敌人

敌人会有自己的一套用于移动、攻击和防御的功能函数。下面是一个Enemy结构体可能的样子的示例:

typedef struct {
    int x, y;
    int velocity_x, velocity_y;
    int health;
    int attack_cooldown;
    SDL_Texture* sprite;
} Enemy;
Level

关卡

关卡将包含当前关卡的相关信息,例如瓦片地图、碰撞检测以及关卡几何结构等。
以下是一个Level结构体可能的样子的示例。

typedef struct {
    int width, height;
    int** tile_map;
    SDL_Texture* background;
} Level;

根据以上架构写出示例代码

代码示例

请注意,这只是个简化的例子,为了创建一个完全功能性的游戏,你需要添加更多的特性、错误处理以及完善工作。

main.c

#include "SDL_utils.h"
#include "game.h"

int main() {
    // Initialize SDL
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

    // Set up game window and renderer
    SDL_Window* window = SDL_CreateWindow("Side-Scrolling Fighting Game", 
    SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    // Set up game state
    Game game;
    game_init(&game);

    // Game loop
    while (1) {
        // Handle user input
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                break;
            }
            // Handle player input
            player_handle_input(&game.player, &event);
        }

        // Update game state
        game_update(&game);

        // Render game
        renderer_clear(renderer);
        game_render(&game, renderer);
        SDL_RenderPresent(renderer);

        // Cap framerate
        SDL_Delay(16);
    }

    // Clean up
    game_free(&game);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

game.h

#ifndef GAME_H
#define GAME_H

#include "player.h"
#include "enemy.h"
#include "level.h"

typedef struct {
    Level level;
    Player player;
    Enemy enemies[10];
    int score;
    int lives;
} Game;

void game_init(Game* game);
void game_update(Game* game);
void game_render(Game* game, SDL_Renderer* renderer);
void game_free(Game* game);

#endif // GAME_H

game.c

#include "game.h"
#include "SDL_utils.h"

void game_init(Game* game) {
    // Initialize level
    level_init(&game->level);

    // Initialize player
    player_init(&game->player);

    // Initialize enemies
    for (int i = 0; i < 10; i++) {
        enemy_init(&game->enemies[i]);
    }

    // Initialize score and lives
    game->score = 0;
    game->lives = 3;
}

void game_update(Game* game) {
    // Update player
    player_update(&game->player);

    // Update enemies
    for (int i = 0; i < 10; i++) {
        enemy_update(&game->enemies[i]);
    }

    // Check for collisions
    for (int i = 0; i < 10; i++) {
        if (check_collision(&game->player, &game->enemies[i])) {
            // Handle collision
            game->lives--;
        }
    }
}

void game_render(Game* game, SDL_Renderer* renderer) {
    // Render level
    level_render(&game->level, renderer);

    // Render player
    player_render(&game->player, renderer);

    // Render enemies
    for (int i = 0; i < 10; i++) {
        enemy_render(&game->enemies[i], renderer);
    }
}

void game_free(Game* game) {
    // Free level resources
    level_free(&game->level);

    // Free player resources
    player_free(&game->player);

    // Free enemy resources
    for (int i = 0; i < 10; i++) {
        enemy_free(&game->enemies[i]);
    }
}

player.h

#ifndef PLAYER_H
#define PLAYER_H

#include "SDL_utils.h"

typedef struct {
    int x, y;
    int velocity_x, velocity_y;
    int health;
    int attack_cooldown;
    SDL_Texture* sprite;
} Player;

void player_init(Player* player);
void player_update(Player* player);
void player_render(Player* player, SDL_Renderer* renderer);
void player_free(Player* player);
void player_handle_input(Player* player, SDL_Event* event);

#endif // PLAYER_H

player.c

#include "player.h"

void player_init(Player* player) {
    player->x = 100;
    player->y = 100;
    player->velocity_x = 0;
    player->velocity_y = 0;
    player->health = 100;
    player->attack_cooldown = 0;
    player->sprite = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("player.bmp"));
}

void player_update(Player* player) {
    // Update player position
    player->x += player->velocity_x;
    player->y += player->velocity_y;

    // Update attack cooldown
    if (player->attack_cooldown > 0) {
        player->attack_cooldown--;
    }
}

void player_render(Player* player, SDL_Renderer* renderer) {
    // Render player sprite
    SDL_RenderCopy(renderer, player->sprite, NULL, &(SDL_Rect){player->x, player->y, 32, 32});
}

void player_free(Player* player) {
    SDL_DestroyTexture(player->sprite);
}

void player_handle_input(Player* player, SDL_Event* event) {
    if (event->type == SDL_KEYDOWN) {
        switch (event->key.keysym.sym) {
            case SDLK_LEFT:
                player->velocity_x = -5;
                break;
            case SDLK_RIGHT:
                player->velocity_x = 5;
                break;
            case SDLK_UP:
                player->velocity_y = -5;
                break;
            case SDLK_DOWN:
                player->velocity_y = 5;
                break;
            case SDLK_SPACE:
                if (player->attack_cooldown == 0) {
                    // Handle attack
                    player->attack_cooldown = 30;
                }
                break;
        }
    } else if (event->type == SDL_KEYUP) {
        switch (event->key.keysym.sym) {
            case SDLK_LEFT:
            case SDLK_RIGHT:
                player->velocity_x = 0;
                break;
            case SDLK_UP:
            case SDLK_DOWN:
                player->velocity_y = 0;
                break;
        }
    }
}

enemy.h

#ifndef ENEMY_H
#define ENEMY_H

#include "SDL_utils.h"

typedef struct {
    int x, y;
    int velocity_x, velocity_y;
    int health;
    int attack_cooldown;
    SDL_Texture* sprite;
} Enemy;

void enemy_init(Enemy* enemy);
void enemy_update(Enemy* enemy);
void enemy_render(Enemy* enemy, SDL_Renderer* renderer);
void enemy_free(Enemy* enemy);

#endif // ENEMY_H

enemy.c

void enemy_init(Enemy* enemy) {
    enemy->x = 300;
    enemy->y = 100;
    enemy->velocity_x = 2;
    enemy->velocity_y = 0;
    enemy->health = 50;
    enemy->attack_cooldown = 0;
    enemy->sprite = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("enemy.bmp"));
}

void enemy_update(Enemy* enemy) {
    // Update enemy position
    enemy->x += enemy->velocity_x;
    enemy->y += enemy->velocity_y;

    // Update attack cooldown
    if (enemy->attack_cooldown > 0) {
        enemy->attack_cooldown--;
    }
}

void enemy_render(Enemy* enemy, SDL_Renderer* renderer) {
    // Render enemy sprite
    SDL_RenderCopy(renderer, enemy->sprite, NULL, &(SDL_Rect){enemy->x, enemy->y, 32, 32});
}

void enemy_free(Enemy* enemy) {
    SDL_DestroyTexture(enemy->sprite);
}

level.h

#ifndef LEVEL_H
#define LEVEL_H

#include "SDL_utils.h"

typedef struct {
    int width, height;
    int** tile_map;
    SDL_Texture* background;
} Level;

void level_init(Level* level);
void level_update(Level* level);
void level_render(Level* level, SDL_Renderer* renderer);
void level_free(Level* level);

#endif // LEVEL_H

level.c

#include "level.h"

void level_init(Level* level) {
    level->width = 640;
    level->height = 480;
    level->tile_map = (int**)malloc(level->width * sizeof(int*));
    for (int i = 0; i < level->width; i++) {
        level->tile_map[i] = (int*)malloc(level->height * sizeof(int));
    }
    // Initialize tile map
    for (int i = 0; i < level->width; i++) {
        for (int j = 0; j < level->height; j++) {
            level->tile_map[i][j] = 0; // 0 = empty, 1 = wall, 2 = platform
        }
    }
    level->background = SDL_CreateTextureFromSurface(renderer,  
    SDL_LoadBMP("background.bmp"));
}

void level_update(Level* level) {
    // Update level state
}

void level_render(Level* level, SDL_Renderer* renderer) {
    // Render background
    SDL_RenderCopy(renderer, level->background, NULL, &(SDL_Rect){0, 0, level->width, level->height});

    // Render tiles
    for (int i = 0; i < level->width; i++) {
        for (int j = 0; j < level->height; j++) {
            if (level->tile_map[i][j] == 1) {
                // Render wall tile
                SDL_RenderCopy(renderer, SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("wall.bmp")), NULL, &(SDL_Rect){i * 32, j * 32, 32, 32});
            } else if (level->tile_map[i][j] == 2) {
                // Render platform tile
                SDL_RenderCopy(renderer, SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("platform.bmp")), NULL, &(SDL_Rect){i * 32, j * 32, 32, 32});
            }
        }
    }
}

void level_free(Level* level) {
    // Free tile map memory
    for (int i = 0; i < level->width; i++) {
        free(level->tile_map[i]);
    }
    free(level->tile_map);

    SDL_DestroyTexture(level->background);
}

SDL_utils.h

#ifndef SDL_UTILS_H
#define SDL_UTILS_H

#include <SDL2/SDL.h>

void renderer_clear(SDL_Renderer* renderer);
void renderer_present(SDL_Renderer* renderer);

#endif // SDL_UTILS_H

SDL_utils.c

#include "SDL_utils.h"

void renderer_clear(SDL_Renderer* renderer) {
    SDL_RenderClear(renderer);
}

void renderer_present(SDL_Renderer* renderer) {
    SDL_RenderPresent(renderer);
}

代码简单讲解

这段代码提供了一个使用SDL2开发的横版格斗游戏的基本框架,它包含了以下几个组成部分:

Game结构体:表示游戏状态,其中包括玩家、敌人、关卡以及得分等内容。
Player结构体:表示玩家,其中包含了位置、速度、生命值以及攻击冷却时间等属性。
Enemy结构体:表示敌人,其中包含了位置、速度、生命值以及攻击冷却时间等属性。
Level结构体:表示关卡,其中包含了宽度、高度、瓦片地图以及背景纹理等属性。
game_init函数:用于初始化游戏状态。
game_update函数:用于更新游戏状态,包括玩家和敌人的移动、碰撞检测以及得分计算。
game_render函数:用于渲染游戏状态,包括玩家、敌人、关卡以及得分的展示。
player_init函数:用于初始化玩家角色。
player_update函数:用于更新玩家状态,包括移动和攻击冷却时间的处理。
player_render函数:用于渲染玩家角色。 enemy_init函数:用于初始化敌人角色。
enemy_update函数:用于更新敌人状态,包括移动和攻击冷却时间的处理。
enemy_render函数:用于渲染敌人角色。
level_init函数:用于初始化关卡。
level_update函数:用于更新关卡状态。
level_render函数:用于渲染关卡画面。
SDL_utils函数集:提供了一系列与SDL2相关的实用工具函数,包括清理和呈现渲染器等功能。

请注意,这只是构建游戏的一个起点,还需要添加更多功能和特性来完成一个完整的游戏,如创建一些必需的资源,图像和音效文件等。