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 963x (9.17 MB)