Chipmunk

Při tvorbě interaktivních grafických aplikací nebo her můžeme chtít simulovat pohyb objektů tak, aby dodržoval fyzikální zákony (působení gravitace, tření a kolize objektů, pohyb lana atd.). K tomu můžeme použít nějakou knihovnu na simulaci fyziky. Chipmunk je knihovna pro simulování jednoduchých fyzikálních procesů ve 2D prostoru. Zde se můžete podívat, co všechno se s takovou knihovnou dá udělat.

Možná znáte hry jako Angry Birds nebo Fruit Ninja. Podobné typy her by se bez nějaké knihovny pro simulaci fyziky neobešly.

Instalace

Knihovna Chipmunk nenabízí distribuci již přeložených objektových souborů, musíme tedy její zdrojové soubory přidat k našemu projektu a přeložit je ručně.

Stáhněte si poslední verzi zdrojových kódů knihovny z webu Chipmunku, rozbalte je a výslednou složku (např. Chipmunk-X.Y.Z nebo ChipmunkLatest) přejmenujte na Chipmunk.

Dále můžete knihovnu přidat ke svému CMake projektu pomocí následující CMakeLists.txt souboru:

Ukázkový CMakeLists.txt soubor pro Chipmunk
cmake_minimum_required(VERSION 3.4)

project(physics)

# Parametr -pthread je nutný při použití této knihovny
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")

# Vložení adresáře Chipmunk
add_subdirectory(Chipmunk)

# Vytvoření programu
add_executable(physics main.c)

# Přidání knihovny k našemu programu
target_include_directories(physics PRIVATE Chipmunk/include/chipmunk)
target_link_libraries(physics chipmunk)

Chipmunk hello world

Stejně jako u SDL není v silách tohoto textu poskytnout kompletního průvodce touto knihovnou. Pro to můžete použít manuál nebo podrobnou dokumentaci funkcí.

Zde je okomentovaná ukázka "hello-world" příkladu, který simuluje pád sady kostek a vykresluje je pomocí SDL:

Okomentovaný program využívající knihovny Chipmunk a SDL
#include <chipmunk.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <assert.h>
#include <stdbool.h>

const int WIDTH = 800;
const int HEIGHT = 600;

int main() {
    // Vytvoření SDL okna a kreslítka
    assert(!SDL_Init(SDL_INIT_VIDEO));

    SDL_Window* window = SDL_CreateWindow("Physics", 100, 100, WIDTH, HEIGHT, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    // Načtení obrázku z disku
    SDL_Texture* image = IMG_LoadTexture(renderer, "wood.jpg");
    assert(image);

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

    // Vytvoření prostoru, ve kterém bude probíhat fyzikální simulace
    cpSpace* space = cpSpaceNew();
    // Nastavení síly gravitace
    cpSpaceSetGravity(space, (cpVect) { .x = 0, .y = -100.0f });

    // Vytvoření země
    cpShape* ground = cpSegmentShapeNew(
        cpSpaceGetStaticBody(space),
        (cpVect) { .x = 0, .y = 10},
        (cpVect) { .x = WIDTH, .y = 10},
        0
    );
    cpShapeSetFriction(ground, 1.0f);    // Nastavení tření země
    cpSpaceAddShape(space, ground);      // Přidání země do světa

    const float mass = 10.0f;            // Váha kostky
    const int dimension = 30;            // Rozměr kostky

    cpShape* boxes[10];                  // Pole kostek
    for (int i = 0; i < 10; i++) {
        // Vytvoření těla kostky, které se bude hýbat
        cpBody* body = cpBodyNew(mass, cpMomentForBox(mass, dimension, dimension));
        // Přidání těla do prostoru
        cpSpaceAddBody(space, body);
        // Nastavení pozice kostky
        cpBodySetPosition(body, (cpVect) {
            .x = 100 + 5 * i,
            .y = 40 + i * (dimension + 10)
        });

        // Vytvoření tvaru kostky, který bude použito pro detekci kolizí
        cpShape* shape = cpBoxShapeNew(body, dimension, dimension, 1);
        // Přidání tvaru do prostoru
        cpSpaceAddShape(space, shape);
        // Nastavení tření kostky
        cpShapeSetFriction(shape, 1.0f);

        boxes[i] = shape;
    }

    Uint64 last = SDL_GetPerformanceCounter();   // Počítání času vykreslování
    float physics_counter = 0.0f;                // Počítání času fyziky
    float timestep = 1.0f / 60.0f;               // Časový krok, o který se bude fyzika posouvat

    bool quit = false;
    while (!quit) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = true;
            }
        }

        Uint64 now = SDL_GetPerformanceCounter();

        // Počet vteřin od poslední iterace herní smyčky
        float delta_time_s = ((float)(now - last) / (float)SDL_GetPerformanceFrequency());
        last = now;

        // Odsimulování času fyziky
        physics_counter += delta_time_s;
        while (physics_counter >= timestep) {
            cpSpaceStep(space, timestep);  // Provedení jednoho časového kroku
            physics_counter -= timestep;
        }

        SDL_RenderClear(renderer);

        for (int i = 0; i < 10; i++) {
            cpShape* shape = boxes[i];
            cpBody* body = cpShapeGetBody(shape);

            cpVect position = cpBodyGetPosition(body);  // Získání pozice kostky
            float angle_radians = cpBodyGetAngle(body); // Získání úhlu kostky (v radiánech)
            float angle_deg = angle_radians * (180 / M_PI); // Převod na stupně

            SDL_Rect rect = {
                .x = position.x - dimension / 2,
                .y = HEIGHT - (position.y + dimension / 2),  // V Chipmunku jde Y nahoru, v SDL dolů, musíme jej vyměnit
                .w = dimension,
                .h = dimension
            };

            SDL_RenderCopyEx(renderer, image, NULL, &rect, -angle_deg, NULL, SDL_FLIP_NONE);
        }

        SDL_RenderPresent(renderer);
    }

    // Uvolnění prostředků
    for (int i = 0; i < 10; i++) {
        cpShape* shape = boxes[i];
        cpBody* body = cpShapeGetBody(shape);

        cpSpaceRemoveShape(space, shape);
        cpSpaceRemoveBody(space, body);
        cpShapeFree(shape);
        cpBodyFree(body);
    }
    cpSpaceRemoveShape(space, ground);
    cpShapeFree(ground);
    cpSpaceFree(space);
    SDL_DestroyTexture(image);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

Ukázka fungování programu:

Tento program spolu s CMakeLists.txt souborem a knihovnou Chipmunk si můžete stáhnout zde. Přeložit a spustit ho můžete pomocí následujících příkazů:

$ mkdir build
$ cd build
$ cmake ..
$ make -j
$ cd ..
$ ./build/physics