IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 14 - SDL - RWops, vlákna a další

V předchozí lekci, SDL - Vlastní události a filtrování, jsme si vytvořili vlastní událost, vložili ji do naší fronty a poté se blíže podívali na filtrování událostí.

V dnešním díle se blíže podíváme na věci, které se mi nepodařilo vložit do předchozích článků. Tento díl bude rozdílný od předchozích v tom, že nebude tak podrobný. Hlavním důvodem je použití informací, které dnes budu zmiňovat. Ve většině programů nebudeme potřebovat joystick ani ovladač a stejně tak nebudeme vytvářet vlastní zdroj dat. Získáte ale základní povědomí o těchto možnostech.

SDL_RWops

SDL_RWops je struktura, která je prostředníkem pro vstupně výstupní operace. Interně všechny funkce, které pracují s daty, vytvářejí SDL_RWops. Standardně nebudeme potřebovat vytvářet vlastní SDL_RWops, SDL má funkce pro jeho vytvoření ze souboru nebo z paměti. Definice struktury je následující.

/* RWops Types */
#define SDL_RWOPS_UNKNOWN
#define SDL_RWOPS_WINFILE
#define SDL_RWOPS_STDFILE
#define SDL_RWOPS_JNIFILE
#define SDL_RWOPS_MEMORY
#define SDL_RWOPS_MEMORY_RO

typedef struct SDL_RWops
{
    Uint32 type;
    Sint64 (SDLCALL * size) (struct SDL_RWops * context);
    Sint64 (SDLCALL * seek) (struct SDL_RWops * context, Sint64 offset, int whence);
    size_t (SDLCALL * read) (struct SDL_RWops * context, void *ptr, size_t size, size_t maxnum);
    size_t (SDLCALL * write) (struct SDL_RWops * context, const void *ptr, size_t size, size_t num);
    int (SDLCALL * close) (struct SDL_RWops * context);
    union
    {
        struct stdio;
        struct mem;
        struct
        {
            void *data1;
            void *data2;
        } unknown;
    } hidden;
} SDL_RWops;

Typ může být jedna z hodnot, které jsou vypsány nad definicí struktury. Jak jsem zmínil, jedná se o různé druhy souborů a paměti. Prvním typem je SDL_RWOPS_UNKNOWN, který bychom měli použít pro všechny námi vytvořené SDL_RWops. S vytvářením SDL_RWops se běžně nesetkáme, zpravidla jen při získávání dat z „exotických“ zdrojů, jako je síť nebo naše třída. Jak vytvořit vlastní SDL_RWops si ukážeme dále.

Dále má struktura definovaných 5 callbacků, které volají rutiny pro získávání dat. Tyto callbacky získávají v parametru samotnou instanci SDL_Rwops, se kterou můžeme pracovat. Funkce by měly být nadefinované tak, že vrací -1 při chybě nebo v případě, kdy daná funkce nedává smysl. Takovým příkladem může být nekonečný zdroj náhodných hodnot (ukázka dále). Pravděpodobně do takového streamu nebudeme chtít zapisovat, tedy nastavení pozice čtení, velikost a zápis nedávají smysl. Tyto callbacky se nevolají přímo, ale pomocí maker, které SDL definuje (SDL_RWseize, SDL_RWseek, SDL_RWread, SDL_RWwrite, SDL_RWclose). Následující dva zápisy jsou totožné.

RW_Read(RWInstance,data,sizeof(MyStruc),10);
RWInstance->read(RWInstance,data,sizeof(MyStruc),10);

Prvním callbackem je size, který vrací velikost streamu. Dalším je seek, který nastaví aktuální pozici, od které se bude číst. Třetím parametrem funkce je whence, který blíže určuje, odkud se bude kurzor přesouvat. Může nabývat hodnot RW_SEEK_SET pro začátek, RW_SEEK_CUR pro aktuální pozici a SDL_SEEK_END pro konec. Funkce vrací aktuální pozici ve streamu. Uvedu pár ukázek pro lepší představu.

SDL_RWseek(instance, 10, SDL_SEEK_SET); //10 bajtů od začátku
SDL_RWseek(instance,10,SDLK_SEEK_CUR); // 10 bajtů od aktuální pozice
SDL_RWseek(instance,-10,SDL_SEEK_END); // 10 bajtů před koncem
SDL_RWseek(instance,0,SDL_SEEK_CUR); //vrátí aktuální pozici
SDL_RWtell(instance); //ekvivalentní k předchozímu případu

write a read jsou téměř totožné. První parametr je instance SDL_RWops, druhým parametrem ukazatel na data, ze kterých zapisovat/číst. Třetím parametrem je velikost jednoho objektu a posledním počet objektů, které čteme. Obě funkce vrací počet objektů, které přečetly/zapsaly. Hodnotu je potřeba ověřit, protože funkce může zapsat/přečíst méně objektů, než jsme požadovali.

Poslední callback je close. Ten by měl uvolnit všechna data, která SDL_RWops používala, a také samotné SDL_RWops funkcí SDL_FreeRW. Stejně jako ostatní funkce, vrací -1 při neúspěchu. Podle dokumentace přesto nemůžeme považovat strukturu za validní a neměla by být dále používána. Jak se v takové situaci zachovat, už dokumentace neuvádí.

Ve většině případů budeme potřebovat strukturu nebo třídu, která nám uchová informace. Pro vlastní SDL_RWops máme připravené dva ukazatele typu void. Získáme je pomocí hidden.unknown.data1 a hidden.unknown.data2. Zbytek struktur v unionu hidden slouží pro interní účely SDL a kromě prostřednictvím callbacků bychom v s nimi neměli vůbec pracovat.

Vlastní SDL_RWops

V programu navrhneme SDL_RWops, který bude vracet nekonečný proud náhodně generovaných hodnot. V této situaci nedává většina funkcí smysl, proto write, seek, i size budou vracet hodnotu -1. Nebudeme potřebovat žádná data, v callbacky close jen uvolníme SDL_RWops instanci a vrátíme 0. Vše se bude odehrávat v callbacky read. Tělo funkce je následující:

size_t read(SDL_RWops* instance, void* ReadTo, size_t OneObjectSize, size_t ObjectsToRead)
{
    Uint64 BytesToRead = OneObjectSize * ObjectsToRead; //kolik bytů přečíst
    Uint8* Begin = (Uint8*)ReadTo;  //odkud začít číst
    Uint8* End = Begin + BytesToRead; //kde ukončit čtení
    while (Begin != End)
    {
        *Begin = rand(); //vložení náhodné hodnoty
        Begin += sizeof(Uint8);
    }
    return ObjectsToRead;
};

Nyní jen struktuře nastavíme type na SDL_RWOPS_UNKNOWN a máme hotovo. Kompletní řešení a použití je v přiloženém projektu.

Vlákna

SDL poskytuje všechny operace, které pro práci s vlákny potřebujeme. Jak bylo zmíněno už několikrát, práce s více vlákny by potřebovala celý vlastní seriál, proto uvedu jen základní příkazy. V projektech více vláken používat nebudeme.

Pro manipulaci s vlákny obsahuje SDL funkce SDL_CreateThread pro vytvoření, SDL_DetachThread pro označení vlákna běžícího na pozadí a funkcí SDL_WaitThread počkáme na vykonání vlákna.

SDL také obsahuje funkce pro zamykání, práci se semafory a mutexy. Veškeré dostupné funkce nalezneme v dokumentaci. Pro ty, kterým tyto pojmy nic neříkají - jedná se o prostředky k synchronizaci vláken mezi sebou.

Dále SDL obsahuje funkce pro atomické operace. Jejich seznam je opět uveden v dokumentaci. Atomická operace proběhne jako jeden celek. Například při sčítání dvou čísel v jednom vláknu (SDL_AtomicAdd) se nám nestane, že by jiné vlákno změnilo jednu z hodnot uprostřed sčítání a výsledek by byl nevalidní.

Pro bližší porozumění vláknům doporučím články na ITNetwork(Java nebo C#) popřípadě překlad v PDF od Jakuba Kottnauera, který se problémem zabývá hlouběji (i když stále na povrchu). Pro začátek není důležité, ve kterém jazyce s vlákny pracujete. Důležité je pochopit principy, které se vláken týkají.

Ovladač a joystick

S ovladačem se pojí události SDL_Controlle­rAxisEvent (pohyb) a SDL_Controller­ButtonEvent (stisk tlačítek). U joysticku je situace poněkud komplikovanější, obsahuje události SDL_JoyAxisEvent (pohyb), SDL_JoyBallEvent (trackball), SDL_JoyButtonE­vent (stisk tlačítka) a SDL_JoyHatEvent (změna pozice hlavy). Stejně jako u myši a klávesnice, SDL poskytuje i informace o aktuálním stavu ovladače i joysticku. Protože ani jednu z výše uvedených událostí nebudeme v potřebovat, nebudu je ani blíže popisovat.

Závěr

Dnešním dílem jsme dobrali většinu funkcionality, kterou SDL poskytuje. Pomalu se vrhneme na vytváření hry. Poslední subkategorie, která nám v SDL chybí, je zvuk. Na ten se zaměříme od příštího dílu.

V přiloženém projektu je tentokrát ukázáno, jak vytvořit vlastní SDL_RWops a číst z něj data.


 

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 694x (9.17 MB)

 

Předchozí článek
SDL - Vlastní události a filtrování
Všechny články v sekci
SDL
Článek pro vás napsal Patrik Valkovič
Avatar
Uživatelské hodnocení:
Ještě nikdo nehodnotil, buď první!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity