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_ControllerAxisEvent (pohyb) a SDL_ControllerButtonEvent (stisk tlačítek). U joysticku je situace poněkud komplikovanější, obsahuje události SDL_JoyAxisEvent (pohyb), SDL_JoyBallEvent (trackball), SDL_JoyButtonEvent (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ínkamiStaženo 1021x (9.17 MB)

