Minetest (Wikipedie), tj. multiplatformní open source voxelový herní engine a hra inspirovaná Minecraftem, se přejmenovává na Luanti.
Minulý týden byl představen (YouTube) Rocky Linux from CIQ (RLC) aneb Rocky Linux s komerční podporou od společnosti CIQ. Cena podpory je 25 000 dolarů ročně bez ohledu na počet jader CPU, serverů nebo virtuálních počítačů.
Byla aktualizována časová osa podpory Manifest V2 v Chrome, tj. verze API rozšíření webových prohlížečů. V průběhu týdnů budou rozšíření Chrome používající tuto verzi deaktivována a uživatelům budou doporučeny alternativy používající Manifest V3. Uživatelé Chrome si mohou pomocí "chrome://extensions/" zjistit, kterých rozšíření se to týká. Například také uBlock Origin. Podporu Manifest V2 v Chrome bude možné dočasně prodloužit nastavením ExtensionManifestV2Availability.
Sada nástrojů Distrobox pro spouštění libovolných linuxových distribucí v terminálu pomocí kontejnerů byla vydána ve verzi 1.8.0.
Byly zpracovány a zveřejněny videozáznamy z konference LinuxDays 2024. Přistupovat k nim lze přímo z programu, kde jsou také odkazy na prezentace, nebo z YouTube.
Byla vydána nová verze 1.4 svobodného multiplatformního vektorového grafického editoru Inkscape. Podrobný přehled novinek i s náhledy a animovanými gify v poznámkách k vydání.
Softwarový KVM Input Leap (dříve Barrier) byl vydán ve verzi 3.0.0 (a následně pár opravných). Přidává podporu Waylandu a Qt6. Jde o první vydání od přesunu z projektu Barrier v roce 2021. Barrier vznikl jako fork Synergy, jehož verze 2 byla částečně proprietární a její bezplatná open-source verze měla umělá omezení.
Na čem aktuálně pracují vývojáři GNOME a KDE? Pravidelný přehled novinek v Týden v GNOME a Týden v KDE.
Přímý přenos (YouTube) z konference LinuxDays 2024, jež probíhá tento víkend v Praze v prostorách Fakulty informačních technologií Českého vysokého učení v Praze (FIT ČVUT). Na programu je spousta zajímavých přednášek.
Elon Musk na akci We, Robot (YouTube, 𝕏) představil Robotaxi, Robovan a vylepšeného Tesla Bota (Optimus).
🇮🇱 Pomůžeš králi Davidovi vyhnat z Jeruzaléma všechny zlé duchy? (tutoriál jak si jako v céčku s pomocí knihovny raylib vyrobit uplně supr akční horrorózní plošinovku (se soudruhem Kolybáčem v hlavní roli 😛 😛 😁 😜)) 🇮🇱
Protože ste určitě všichni nejdřiv sescrollovali dolu k hejbavejm vobrázkům, nóó tak předtim než se pustíme do programovaní asi jako musim vodpovědět čí že je to jakoby xift. Tadydle našeho soudruha Davida Kolybáče hele, se kterým to asi jakoby došlo až tak daleko, že si do patičky připnul vlajku terroristů z hamásu 😁 😁 😁 😁
Původně jsem měla v plánu někdy na dušičky vyrobit horrorózní hru vokolo tamtoho dýňovýho incidentu s otcem Jaroslavem, jenže pak mi soudruh Kolybáč schodil z hlavní strany blogísek vo prohamáskejch libtardech 😮 🙄 Mě to děsně naštvalo aže když vám jakože neva co tady s. Kolybáč vyvádí nóó takže se na vás uplně vykašlu a nevyrobim sem už vůůbec nic. Jenže pak za ňákou dobu mi došlo že von ten Kolybáč je vlastně jenom takovej nemocnej chudák aže to co tady s tou cenzurou předved je veskutečnosti něco na způsob toho, co vyváděj jeho soudruzi, podobně vypatlaný vod reality vodtržený akademický netáhla, když třeba strhávaj plagáty s fotkama pohřešovanejch lidí který sedmýho řijna unesl tamten islámskej stát v2.0 do gazy. Tendle náš salónní komunista už asi v ulicích všecky plagátky strhnul, nóó a když už neměl co strhávat, tak hrábnul svou nemytou prackou po mým blogísku 😅.
gratuluju si stotisicátejmiliontejprvní naštěvnik blogisku!!!! Klikni a *možná* vyhraješ 😁 😁 😁 😜
Lidi jako Kolybáč jsou ale taky jenom voběti libtardismu, si myslim že levicová ideologie a/nebo uživání návykovejch omamnejch látek jim rozežralo mozky a už vnímaj svět jenom prostřednictvím primitivních zjednodušujících modýlků který jim do hlav nakecávaj jiný různý pomatený ultralevicový pošuci a pravdu podle nich asi jako má dycky ten, kdo umí víc nahlas křičet (anebo navopak umlčovat 😅), proto sou schopný uvěřit že terroristický hnutí, pro normálního člověka naprosto nevodlišitelný vod islámskýho státu, sou prejže ňáký chudáčci kterejm celej svět jenom furt ubližuje nebo co. A když už jsem měla ten pahýl Otce Jaroslava, tak mě napadlo že to mužu překopat a vyrobit nějakou hru/tutorial s Kolybáčem, kde se bude bojovat proti islámskýmu zlu, páč si myslim že když mu situaci na blízkým východě dostatečně jednoduše vysvětlíme, gamifikujeme a uděláme ho přímou součástí děje, tak třeba pochopí že islám == zlo. Nóó tak uvidíme, jak to na něj bude fungovat a jestli se nám ho podaří aspoň trošku polepšit 😁 Jestli to na kolybáče ňák zabere by se pak mohla vyrobit třeba ňáká zimní hra ve který by se voxidem uhličitým bojovalo proti době ledový na napravování Krályka, nebo fps/střilečka ve který by se ekologicky likvidovali lyžaři na kurýrování lyžařů (sou zase letos děsně přemnožený), nebo třeba ňákej klon papers please z prostředí současný čechijský cenzury převlečený za tzv. 'boj proti dezinformacím' na napravování zase milovniků totalitních praktik a státní zvůle 🤔 😜
Raylib je jednoduchá svobodná opensourcová multiplatformní 'multimediální' knihovnička určená primárně pro programování her, má minimum externích závislostí a je uplně mrňavá. Není to žádnej herní engine, spíš hodně odlehčenej framework, takže si dost práce budeme muset udělat sami. Ukážu jak v tom jako vyrobit malou jednoduchou plošinovku, nicméně možnosti knihovničky nekončej jenom u dvourozměrnejch her, zvládá to načítání 3D objektů, má to podporu pro VR headset etc. Pod kapotou Raylib používá openGL, vulkan maj myslimže teprve v plánu a vubec je kolem tý knihovny celkem živo, takže si myslim že se to stane supr alternativou k libSDl2 😮 😜
Jako jazyk sem vybrala vobyč cčko páč to je jazyk ve kterým je psaná i ta knihova a vubec je to uplně supr jazyk, zná ho každej takže asi i Kolybáč a bude si moct programovat tu duchobijeckou hru s náma 😁 😜. Ale Raylib má bindingy/wrappery pro víc jak šedesátku jinejch programovacích jazyků hele 😮 😁, takže si mužete vybrat cokoliv jinýho, cčko neni nutná podmínka. Pro Andreje tam je wrapper na C++, pro Krályka rustový raylib-rs nebo novější raylib-fii, pro Bystroušáka hnedka 4 knihovny pro python, pro Dušana dokonce ňáký raylib-php, takže si vyberte co se vám líbí. Ale když to nebude cčko, tak si toho holt budete muset hodně napsat sami 😛 😁 😜 😜
Spustitelný příklady včetně zdrojáků maj tady na stránce hele, návod jak Raylib nainstalovat maj tady hele, nejdřiv ale koukněte jestli ho nááhodou nemáte už v repozitářích svý distribuce, pak by ste mohli raylib nainstalovat normálně jako balíček.
V Debianu jsem nikde v repozitářích Raylib neviděla, tak sem nainstalovala podle návodu určenýho pro Ubuntu takle nějak:
sudo apt install build-essential git cmake libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev git clone https://github.com/raysan5/raylib.git raylib cd raylib mkdir build && cd build cmake -DBUILD_SHARED_LIBS=ON .. make sudo make install
Vyrobíme si takovou děsně horrorózní hru. Bude to plošinovka ve který bude David běhat po dlaždicový/tiled mapě a pistolkou bojovat proti islamistickejm duchům. Mam to zatim rožčleněný na 17 kroků, vysledkem bude pustitelná hratelná věc, nicmeně eště furt ne kompletní hra. To možná až v ňákým pokračování dalším, jestli napišu. Noa jestli napišu to nvm, ale jestli mi kolybáč zase šáhne prackou na blogísek tak to je skoro jistý že napišu celej děsně dlóóóóóóóóóóóóóóuhatatatatatánckej duchovní serial 😁 😁 😏 😜
Celej zdrojáček (i všecky soubory vobrázků, zvuků a hudeb) najdete hele na gitlabu, zdrojáky jednotlivejch kroků hele taky v tom gitlabovým repozitáři. Ze srandy jsem to nazrcadlila i komoušům z kliniky hele sem, by sme si vyzkoušeli jejich pojetí svobody slova a jak to maj v porovnání s tim našim kolybáčem, rači k tomudle druhýmu repozitáři přistupujte jako by byl hostovanej někde v kldr nebo v ňákým bělorusku (legrační bude už jenom to jak je to bude sejřit že to nemužou smazat páč nechtěj bejt za stejný cenzory proti kterejm prejže ty smažky bojujou 😁 😁)
Se mi podařilo nacpat na gitlab, všecko hele najdete ve složšce 'assets'. Který lidi jaký zvuky a muziku vyrobili najdete hele dole v 'credits'
Tvl je to hrozně velkej blogovej zapisek (páč sem sem pro jistotu nakopirovala celý změněný zdrojáčky dycky), tak sem rači udělala pro víc rychlejší vorientaci rejstřík 😅 😜
(asi by bylo dobrý si zapnout javascript jestli ho máte vyplej páč pak sou tady na abclinuxu zdrojáčky rozbalovávací a zbalovávací a nebude to zabirat tolik moc mista)
Začneme tim, že si někde vyrobíme složšku projektu, pomenujem to třeba David_a_duchove, tam si vyrobíme další složšku, 'src', kam budeme psát zdroják. Vyrobíme dva soubory, v kořenový složšce soubor 'CMakeLists.txt' a ve složšce 'src' soubor 'main.c'. 'CMakeLists.txt' bude takovej jakože návod pro cmake, jak vyrobit 'Makefile', kterým pak zkompilujeme samotnou hotovou binárku tý naší hry (neni to vubec tak složitý jak to možná teďko zní 😮 😜), v 'main.c' bude, jak už nám název naznačuje, bydlet 'main' funkce 😁. Tam bude hlavní loop naší hry, načítání vobrázků, muziky, budou se votamtaď volat všecky další funkce etc, prostě to bude uplně hlavní funkce 😁 😁
mkdir David_a_duchove && cd David_a_duchove mkdir src touch CMakeLists.txt ./src/main.c
Vyrobíme si složšky 'build' a 'assets'. Ve složšce 'build' budeme sestavovat hotovou spustitelnou binárku, do složšky 'assets' budeme strkat vobrázky, zvuky, hudbu a případnej další multimediální a jinej vobsah pro naši hru. Sem to napsala tak, by si to načítalo 'assets' ze stejný relativní cesty/stejný složšky, ve který je binárka. (Todle samozdřejmě neni jediný možný řešení, si mužeme i.e. přepsat cestu k načítanejm souborům ve zdrojáku, mužeme si ve složšce 'build' udělat symlink/'zástupce' na 'assets', mužeme binárku dycky někam přesouvat/kopírovat etc).
mkdir assets build
Takže teďko bysme měli mit takovýdleho něco
. └── David_a_duchove ├── assets ├── build ├── CMakeLists.txt └── src └── main.c
Do souboru 'CMakeLists.txt' napíšeme todle:
cmake_minimum_required(VERSION 3.0) project(David_a_duchove LANGUAGES C) add_executable(David_a_duchove ./src/main.c) target_link_libraries(David_a_duchove raylib m) install(TARGETS David_a_duchove RUNTIME DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
Vicemeně tam jenom řikáme jak se nám ta binárka bude menovat, na třetím řádku řikáme kde máme poskovávaný *.c soubory (máme jenom jedinej, 'main.c'), na čtvrtým jaký chceme přilinkovat knihovny (pochopytelně knihovnu 'raylib', to 'm' je matematická knihovna ze stdlib páč se nám bude hodit na různý kejkle), na posledním řádku řikáme kam se to bude jakože instalovat/přesouvat když zadáme příkaz 'make install' ('CMAKE_CURRENT_SOURCE_DIR' je normálně složška se zdrojákem, nicmeně za složšku se zdrojákem to považuje tu, kde je strčenej 'CMakeLists.txt', takže tim vodkazujeme na kořenovej adresář projektu).
Když už máme 'CMakeLists.txt' přichystanej, tak si mužem vyrobit 'Makefile'. Uděláme to tak, že ve složšce 'build' zavoláme cmake, který nám ho na základě vobsahu souboru 'CMakeLists.txt' vygeneruje:
cd build cmake ..
Hru pak budeme kompilovat a přesouvat do kořenový složšky tak, že ve složšce build zavoláme 'make':
cd build make David_a_duchove && make install
a pustíme když v kořenový složšce zavoláme jednoduše './David_a_duchove'. Takže jednořádkovej přikaz na zkompilování a puštění zavoláme z kořenový složšky třeba takle nějak:
make -C build David_a_duchove && make -C build install && ./David_a_duchove
....akorátže když to zavoláme teď, tak nám to akorát vyhubuje, páč v tom našem 'main.c' neni eště vubec nic napsanýho 😮 😁 Tak to poďme změnit 😁 😜
Takže si votevřem ten soubor 'main.c' a napišem/zkopirujem si tam todle
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif //šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // proměná která nám bude držet referenci na texturu mesice Texture2D textura_mesic; int main ( void ) { // nakonfigurujeme si raylib, že budeme chtít vytvořit okno s proměnlivou velikostí a s vertikální synchronizací // poznámka pod čarou, ten fígl s bitovým operátorem 'or' jakože se znakem '|' funguje tak, že každá z těch flagovejch // konstant má hodotu nastavenou tak, by byl v jejich bytu vobsazenej dycky jenom jeden jedinej bit. Noa když uděláme // to bitový or, tak se nám ty proměný zkombinujou do nový unikátní hodnoty kterou ta knihovna umí rozlišit, // respektive čte jednotlivý bity v bajtech :O ;D SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); // inicializujeme vokno vo daný šířce, vejšce a s titulkem InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); // inicializujem audio zařízení, by sme mohli přehrávat zvuky InitAudioDevice(); // nastavíme požadovanou frekvecni vykreslování // takle řikáme, že chceme vykreslovat šedesát snímků za sekundu (framů per sekundu) SetTargetFPS ( 60 ); // načtem si ze složšky assets texturu měsice textura_mesic = LoadTexture ( "assets/moon.png" ); // spustíme 'nekonečnej' while cyklus, ve kterým poběží ta naše hra // přeruší se když se zavře vokno nebo se zmáčkne na klávesnici čudlik 'escape' while ( !WindowShouldClose() ) { // uložíme si do proměný 'dt' kolik času realně uplynulo vod posledního vykreslenýho framu // máme sice nastavený to FPS na 60, nicmeně na to se nedá spolehnout, jednotlivý cykly nám mužou // trvat ruzně dlouho, něco se muže špracnout etc a je víc lepšejší a víc přesnější si tu deltu dycky změřit float dt = GetFrameTime(); // začnem vykreslovat BeginDrawing(); // překreslíme si celou vobrazovku vobdélníkem s takovým mordočerným gradientem // První dva argumenty funkce sou iksová a ypsilonová souřadnice levýho horního rohu toho vykreslovanýho vobdélníka, // když jsme tam napsali 0,0 tak tim řikáme, že ho chceme začít vykreslovat v levým horním rohu toho našeho vokna. // Druhý dva argumenty jsou šířka a vejška našeho vobdelnika, vykreslíme ho přes celou šířku a vejšku našeho vokna // poslední dva jsou barvy našeho gradientu, raylib má některý základní předdefinovaný, jinak tam mužem strkat struturu Color, // ve který jsou definovaný barvy RGBA v rozsahu vod nuly až po 255 DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); //hovorka se asi jako bojí děsně velkejch měsiců když je furt v horrorovejch komixech kreslí, // tak mu uděláme radost a na pozadí vykreslíme supr velkej strašidelnej měsíc :D :D //vezmem si ten víc menšejší rozměr z šiřky a vejšky naší vobrazovky, to použijem jako velikost toho měsice float mensi_rozmer = MIN ( GetRenderWidth(),GetRenderHeight() ); // a vykreslíme texturu měsíce. Raylib má víc podobnejch funkcí pro 2D vykreslování textur, // použijeme 'DrawTexturePro' pokrejvá asi nejvíc nejvěší množinu různejch kombinací argumentů co ty // jednotlivý fce berou, vicemeně si myslim že jich de věčinu nahradit toudle fcí // první argument je textura // druhej argument je 'zdrojovej' vobdelnik, kterým definujem v textuře voblast, kterou chceme vykreslovat // chceme vykreslit celej měsic, takže vybereme uplně všecko, vod horního levýho vokraje (0,0) až po dolní pravej vokraj, // takže šiřku a vejšku zdrojovýho vobdelniku nastavíme na šířku a vejšku textury // třetí argument je 'cílovej' vobdelník, jakože voblast na našem vokně // Měsíc chceme vykreslit přesně uprostřed vobrazovky a nebyl by problém si // dopočitat iksovou a ypsilonovu souřadnici, nicmeně mužeme použít 'fígl s originem/počátkem', takže // jeho ikosvou a ypsilonovou souřadnici nastavíme na střed vobrazovky (půlku vejšky a půlku šířky) // šířku a vejšku cílovýho vobdélníka nastavíme na proměnou 'mensi_rozmer', by nám měl ten menší rozměr vobrazovky // uplně vyplnit // črtvrtej argument je struktura 'Vector2', jakože dvourozměrnej bod, kterej nám řiká kde má náš vobdelnik svuj počátek // (defaultně to je bod (0,0)). My ho nastavíme na půlku vejšky a šířky vobdelnika, tzn. do jeho středu. // Tim dosáhnem toho, že se nám měsic vykreslí uplně přesně doprostřed :O ;D // předposledním argumentem je uhel povotočení textury ve stupních // mužeme si z legrace vyrobit třeba ňákou proměnou do který budeme cpát uplynulej čas, takže se nám měsic bude furt točit :D :D static float uhel = 0.0f; uhel += dt * 50.0f; // noa poslední argument je barva. Funguje tam takovej zálkadní blending, že nám to jakože celou tu vykreslovanou texturu tou barvou pronásobí. // prostě si to jakoby pro každej pixel vezme jednotlivý barevný složšky a vynásbí je to s jednotlivejma barevnejma složškama zvolený barvy, // ty barvy je asi nejlepčí si přectavit jako hodnoty v rozsahu vod nuly až do jedničky. // takže když jako barvu zvolime třeba bílou jakože WHITE, tak texturu vykreslíme pronásobenou jedničkou, takže nezměněnou, Když navopak zvolime // černou BLACK, tak to zase prozměnu vynasobime nulama a budem mit texturu celou černou. Když si nastavíme třeba červenou RED, tak tám to // z puvodních RGBA kanálů nechá zase jenom červenou a vostatní barvy pronásobí nulou etc // Je tam nastavenej ňákej vodstín modrý, mužeme si zkusit s jednotlivejma barvičkama hejbat DrawTexturePro ( textura_mesic, ( Rectangle ) { 0,0,textura_mesic.width,textura_mesic.height }, ( Rectangle ) { GetRenderWidth() /2, GetRenderHeight() /2, mensi_rozmer, mensi_rozmer }, ( Vector2 ) { mensi_rozmer/2,mensi_rozmer/2 },uhel, ( Color ) { 102, 191, 255, 255 } ); // ukončíme vykreslování EndDrawing(); } // jestli se přerušil tamten náš hlavní while cyklus, tak zavřem okno, uklidíme po sobě a skončíme // běh programu vrácením návratový hodnoty CloseWindow(); //uklidíme texturu, to se musí UnloadTexture ( textura_mesic ); // vypnem audio zařízení CloseAudioDevice(); // nakonec vrátíme nulu jakože všecko proběhlo v cajku a hotovo return 0; }
V poctatě si jenom načtem jednu texturu, vytvoříme vokno naší aplikace, překreslíme černomodrým gradientem na kterým vykreslíme točivej měsíc, by jsme si ukázali, jak se jako s tim raylibem a cčkem dělá. V komentářích je všecko duležitý vysvětlený 😁 😜
Točivej měsíc byl asi jako moc pěknej, my ale potřebujem hejbavýho Davida. V 'assets' mužete vidět celej spritesheet hnedka tří aktivit Davida, je tam animace běhu, sednutí si nazadek a animace pérování v kolenou, jen to budem muset ňák rozhejbat. Boužel možnosti animací sou zatim v Raylib vomezený, maj tam ňákou základní funkci 'LoadImageAnim' pro rendrování animací ale předpokládá se asi jako jenom *.gif soubor a to se nám jakože moc nehodí. Naštěstí animace je jenom děsně moc moc kousků nějakýho vobrázku v řadě za sebou, takže si to budem moct napsat celkem snadno sami.
Animaci Davida máme v jednom velikatatatánským obrázku, kde jsou namalovaný jednotlivý kroky tří různejch animací (takovýmu velkýmu vobrázku ve kterým sou namalovaný vedle sebe různý menčí vobrázky se řiká spritesheet nebo texturovej atlas, už sem vo tom dokonce jednou tady povidala hele 😮 😜). Si jednotlivý obrázky animace popíšeme řadou vobdelníků/rectanglů, který když ve správným pořadí naskládáme za sebe a budem po jednom zobrazovat, tak budeme vlastně vykreslovat tu animaci.
To by mohlo bejt takle nějak
// šířka jednotlivejch obrázků/framů animace // (pro necečkaře, slovičko 'DAVID_F_SIRKA' a jiný definovaný nám pak prekompilátor nahradí tou hodnotou) #define DAVID_F_SIRKA 150.0f // vejška jednotlivejch obrázků animace #define DAVID_F_VYSKA 240.0f //jednotlivý animace/pole framů // jsou to vlastně pole strutkur typu 'Rectangle' // rectangle deklarujeme čtyrma proměnejma: // 1. iksová souřadnice levýho horního rohu // 2. ypsilonová souřadnice levýho horního rohu // 3. šiřka vobdelnika // 4. vejška vobdelnika // všechny framy maj stejnou vejšku i šířku takže máme vlastně takovou jakože mřížku // když si ten vobrázek spritesheetu votevřeme v ňákým prohlížeči vobrázků, // tak si mužeme jednoduše prstem na monitoru vodpočítat sloupec (iksová souřadnice) a řádek (ypsilonová) // a tim pronásobit šiřku příp. vejšku framu a tim dostanem pozici v tý textuře // (nvm jestli jakoby mam vysvětlovat i takovýdle trivialni věci ale asi jo když už to dělám) // pole framů animace běhu Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; // pole framů animace skoku // ( sou to vlastně použitý některý framy běhu, takže sme vlastně ziskali // 4 aktivity z puvodnich 3 ) Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; // pole framů animace pérování na místě/flákání se Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; // pole framů sednutí si na zadek Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, };
Tak to by sme měli vymezený ty framy ale to tak ňák eště nestačí. Budem si potřebovat pamatovat pořadí právě zobrazenýho snimku, budem si muset počítat čas, budem potřebovat nějak nastavovat rychlost přehrávání, hlídat konec animace etc. Takže si jako ve složšce 'src' vyrobíme novej soubor 'animace.h', kde si vytvoříme datovou strukturu která nám tydle proměný bude držet. Pak dvě funkce, jednu pro aktualizaci snimku textury časovou deltou (tim 'dt', který se použilo na točení točicím měsicem), druhou pro samotný vykreslování voblasti textury.
#ifndef _DAVID_A_DUCHOVE_ANIMACE_H_ #define _DAVID_A_DUCHOVE_ANIMACE_H_ // ten fígl s ifndef nahoře slouží k tomu by se nám // soubor naimportoval jenom jednou #include <raylib.h> typedef struct Animace { // textura animace Texture2D textura; // jak dlouho bude trvat jeden frame/snímek animace float trvani_framu; // jakože vnitřní čas tý animace (se bude hodit) float relativni_cas; // ukazatel na pole framů (v cčku je pole a ukazatel tak trochu uplně to samý) Rectangle * framy; // kolik má animace framů celkem size_t pocet_framu; // index právě zobrazenýho framu int index; } Animace; // bere dva argumenty, ukazatel na animaci noa pak časovou deltu void aktualizovatAnimaci ( Animace * animace, float dt ) { //přičtem časovou deltu k tamtomu vnitřnímu času animace->relativni_cas += dt; // dokud je relativní čas víc věčí než je doba trvání jednoho framu, // tak vodečtem vod relativního času bodu trvání framu a posunem index vo jedničku while ( animace->relativni_cas > animace->trvani_framu ) { animace->relativni_cas -= animace->trvani_framu; animace->index++; // jestli sme vyskočili z maximální dýlky animce, tak se // přetočíme zpátky na začátek if(animace->index >= animace->pocet_framu) animace->index = 0; } } // bere tři argumenty, ukazatel na animaci, souřadnici kde se bude vykreslovat levej horní roh a barvu void vykreslitAnimaci ( Animace * animace, Vector2 pozice, Color barva ) { DrawTextureRec ( animace->textura, animace->framy[animace->index],pozice, barva ); } #endif
V souboru 'main.c' si ten hlavičkovej soubor nahoře naimportujem, deklarujem si struktury animací a budem je v tom renderovávacím 'while' loopu furt aktualizovat a vykreslovat
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // naimportujem si vlastní hlavičku #include "animace.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif //šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; // šířka jednotlivejch obrázků/framů animace // (pro necečkaře, slovičko 'DAVID_F_SIRKA' a jiný definovaný nám pak prekompilátor nahradí tou hodnotou) #define DAVID_F_SIRKA 150.0f // vejška jednotlivejch obrázků animace #define DAVID_F_VYSKA 240.0f //jednotlivý animace/pole framů // jsou to vlastně pole strutkur typu 'Rectangle' // rectangle deklarujeme čtyrma proměnejma: // 1. iksová souřadnice levýho horního rohu // 2. ypsilonová souřadnice levýho horního rohu // 3. šiřka vobdelnika // 4. vejška vobdelnika // všechny framy maj stejnou vejšku i šířku takže máme vlastně takovou jakože mřížku // když si ten vobrázek spritesheetu votevřeme v ňákým prohlížeči vobrázků, // tak si mužeme jednoduše prstem na monitoru vodpočítat sloupec (iksová souřadnice) a řádek (ypsilonová) // a tim pronásobit šiřku příp. vejšku framu a tim dostanem pozici v tý textuře // (nvm jestli jakoby mam vysvětlovat i takovýdle trivialni věci ale asi jo když už to dělám) // pole framů animace běhu Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; // pole framů animace skoku // ( sou to vlastně použitý některý framy běhu, takže sme vlastně ziskali // 4 aktivity z puvodnich 3 ) Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; // pole framů animace pérování na místě/flákání se Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; // pole framů sednutí si na zadek Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; int main ( void ) { // nakonfigurujeme si raylib, že budeme chtít vytvořit okno s proměnlivou velikostí a s vertikální synchronizací // poznámka pod čarou, ten fígl s bitovým operátorem 'or' jakože se znakem '|' funguje tak, že každá z těch flagovejch // konstant má hodotu nastavenou tak, by byl v jejich bytu vobsazenej dycky jenom jeden jedinej bit. Noa když uděláme // to bitový or, tak se nám ty proměný zkombinujou do nový unikátní hodnoty kterou ta knihovna umí rozlišit, // respektive čte jednotlivý bity v bajtech :O ;D SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); // inicializujeme vokno vo daný šířce, vejšce a s titulkem InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); // inicializujem audio zařízení, by sme mohli přehrávat zvuky InitAudioDevice(); // nastavíme požadovanou frekvecni vykreslování // takle řikáme, že chceme vykreslovat šedesát snímků za sekundu (framů per sekundu) SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); // vyrobíme si animace //animace běhání Animace a_behani = { // jedna patnactina sekundy .trvani_framu = 1.0f/15.0f, //relativní čas a index vynulujem .relativni_cas = 0.0f, .index = 0, // textura bude ten spritesheet .textura = textura_david_spritesheet, // animace běhání bude mit samože framy běhu .framy = david_framy_behu, // počet framů ziskáme podělením velikosti celýho pole velikostí jednoho prvku .pocet_framu = sizeof ( david_framy_behu ) / sizeof ( Rectangle ) }; //animace skákání Animace a_skok = { .trvani_framu = 1.0f/15.0f, .relativni_cas = 0.0f, .index = 0, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) / sizeof ( Rectangle ) }; //animace idle Animace a_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas = 0.0f, .index = 0, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) / sizeof ( Rectangle ) }; //animace sednutí si Animace a_sed = { .trvani_framu = 1.0f/15.0f, .relativni_cas = 0.0f, .index = 0, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) / sizeof ( Rectangle ) }; // spustíme 'nekonečnej' while cyklus, ve kterým poběží ta naše hra // přeruší se když se zavře vokno nebo se zmáčkne na klávesnici čudlik 'escape' while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // aktualizujem animace // ( naše funkce chce po nás ukazatel, takže tam cpem adresu ) aktualizovatAnimaci(&a_behani, dt); aktualizovatAnimaci(&a_skok, dt); aktualizovatAnimaci(&a_idle, dt); aktualizovatAnimaci(&a_sed, dt); // začnem vykreslovat BeginDrawing(); DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); //vykreslíme animace vykreslitAnimaci(&a_behani,(Vector2){0,0},WHITE); vykreslitAnimaci(&a_skok,(Vector2){DAVID_F_SIRKA*1,0},WHITE); vykreslitAnimaci(&a_idle,(Vector2){DAVID_F_SIRKA*2,0},WHITE); vykreslitAnimaci(&a_sed,(Vector2){DAVID_F_SIRKA*3,0},WHITE); // skončímes vykreslováním EndDrawing(); } // jestli se přerušil tamten náš hlavní while cyklus, tak zavřem okno, uklidíme po sobě a skončíme // běh programu vrácením návratový hodnoty CloseWindow(); //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); // vypnem audio zařízení CloseAudioDevice(); // nakonec vrátíme nulu jakože všecko proběhlo v cajku a hotovo return 0; }
To by byla uplně jednoduchá animace ale my asi jakoby budeme potřebovat trošku víc. Se nám asi jako bude hodit mit tam možnost vypínat loopování, by se jakože animace přehrála dycky jenom jednou a zustala na posledním framu (například když si David jednou sedne na zadek by zustal sedět auž se nezvedal). Pak by se nám hodilo moct tam nastavovat 'yoyo' efekt, jakože aby to ďálo že když animace dojede až nakonec, tak se začne přehrávat vod konce zpátky až na začátek (třeba ten běh, vidite že se je vidět jak se tam ta levá noha dycky skokově prohodí za pravou. S yoyo efektem to komíhání hnát bude krásně plynulý).
Takže trošku rozvinutější struktura animace by mohla bejt třeba takle ňák, jako v nasledujicím upraveným zdrojáčku 'animace.h'. Sou tam různý kejkle vokolo ale základní princip je furt stejnej. A vyzkoušíme si tudle vylepšenou animaci rovnou na herní postavičce našeho hlavního hrdiny 😁 😜
#ifndef _DAVID_A_DUCHOVE_ANIMACE_H_ #define _DAVID_A_DUCHOVE_ANIMACE_H_ // ten fígl s ifndef nahoře slouží k tomu by se nám // soubor naimportoval jenom jednou #include <raylib.h> typedef struct Animace { // textura animace Texture2D textura; // jak dlouho bude trvat jeden frame/snímek animace float trvani_framu; // jakože vnitřní čas tý animace (se bude hodit) float relativni_cas; // jestli je animace pozastavená bool pauznuta; // jestli má jojo efekt bool jojo; // jestli běží pozpátku bool reverzne; // jestli běží furt dokolečka nebo jestli se zasekné, když dojede nakonec bool loopovat; // jestli animaci vykreslujeme překlopenou vodorovně/po ose x bool zrcadlit; // ukazatel na pole framů (v cčku je pole a ukazatel tak trochu uplně to samý) Rectangle * framy; // kolik má animace framů celkem size_t pocet_framu; // index právě zobrazenýho framu int index; } Animace; // bere dva argumenty, ukazatel na animaci noa pak časovou deltu void aktualizovatAnimaci ( Animace * animace, float dt ) { if ( animace->pauznuta ) { return; } animace->relativni_cas += dt; while ( animace->relativni_cas > animace->trvani_framu ) { animace->relativni_cas -= animace->trvani_framu; if ( animace->reverzne ) { if ( --animace->index <= 0 ) { if ( animace->loopovat ) { if ( animace->jojo ) { animace->index = 0; animace->reverzne = false; } else { animace->index = animace->pocet_framu - 1; } } else { animace->pauznuta=true; animace->index=0; return; } } } else { if ( ++animace->index == animace->pocet_framu ) { if ( animace->loopovat ) { if ( animace->jojo ) { animace->index = animace->pocet_framu - 1; animace->reverzne = true; } else { animace->index = 0; } } else { animace->pauznuta=true; animace->index =animace->pocet_framu-1; return; } } } } } // bere tři argumenty, ukazatel na animaci, souřadnici kde se bude vykreslovat levej horní roh a barvu void vykreslitAnimaci ( Animace * animace, Vector2 pozice, Color barva ) { // převrácení po některý s os dosháhneme nastavením šiřky a/nebo vejšky // zdrojovýho vobdelnika na zápornou hodnotu // ( víc efektivnější by asi bylo mit dvě sady animací, jednu normální a druhou s // překlopenou šiřkou, než to počitat v každým kroku renderu) if ( animace->zrcadlit ) { Rectangle frame = animace->framy[animace->index]; frame.width = - frame.width; DrawTextureRec ( animace->textura, frame,pozice, barva ); } else { DrawTextureRec ( animace->textura, animace->framy[animace->index],pozice, barva ); } } #endif
Že sme si takle vykreslili animace vedle sebe v řadě je fajn, teďko je potřebujem jakoby spojit do jednoho voběktu, kterej nám bude reprezentovat hlavní herní postavičku, krále Davida. Takže si vyrobíme v 'src' novej soubor 'david.h' kde si nadefinujeme strukturu 'David'. Budem tam mit dvě funkce, jednu pro vykreslování Davida (tam viceměne jenom vykreslíme jednu z těch animací) noa funkci na aktualizovávání toho Davida, kam nastrkáme všecku jeho 'herní' logiku. Tam si nastavíme a budem chytat uživatelskej vstup z klávesnice, jakože když hráč bude mačkat šipku doleva, tak David na obrazovce půjde doleva, když hráč máčkne šipku doprava, tak David pujde doprava noa když máčkne šipku dolů, tak si David sedne nazadek - takže budeme podle toho jaký čudliky se mačkaj přepínat Davidovu aktualně zvolenou animaci a měnit jeho polohu na vobrazovce.
(a taky si ukažeme jak načíst zvuky a jak multimediální vobsah sdílet mezi jednotlivejma souborama zdrojáčku, když David bude chodit, tak to bude dělat zvuk 😮 😜)
Takže soubor 'david.h' by moch bejt takovejdle třeba zatim:
#ifndef _DAVID_A_DUCHOVE_DAVID_H_ #define _DAVID_A_DUCHOVE_DAVID_H_ // naimportujem si animaci // (s ní se nám současně natáhne raylib.h) #include "animace.h" // kouzelným slovíčkem 'extern' https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/ // mužeme 'sdílet' se souborem 'main.c' v něm deklarovaný proměný, teďko na ukázku zatim jenom // zvuk chůze, de ale všecko :O ;D extern Sound zvuk_kroku; // přestěhujem si sem z 'main.c' framy animace by to tam zbytečně nezabiralo misto ve zdrojáčku #define DAVID_F_SIRKA 150.0f #define DAVID_F_VYSKA 240.0f Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; // tady si budeme definovat různý hodnoty a konstatny k davidoj // zatim si tady jenom nadeklarujeme rychlost jeho chůze // Je vzdálenost v 'pixelech' kterou David jakože urazí za jednu vteřinu #define RYCHLOST_CHUZE_DAVIDA 350.0f typedef struct David { // jednotlivý animace davida Animace animace_beh; Animace animace_idle; Animace animace_sed; Animace animace_skok; // ukazatel na animaci, která bude jakože jedniná vybraná, // aktualizovaná a vykreslovaná. Máme čtyry ale vidět chcem přece jako // jenom jednu :D :D Animace * aktualni_animace; // pozice levýho horního vokraje vykreslovaný textury Vector2 pozice; // okraje naší herní postavičky (vykreslíme si, když v 'main.c' nahoře aktivujem 'DEBUG') // bude se nám hodit v budoucnu třeba pro kolize s jinejma herníma věcma/entitamama // (vymezíme si menší voblast než jsou rozměry framu animace) Rectangle okraje; // směr kterým David kouká. Jednička znamená z leva doprava, -1 znamená z z prava doleva int smer; } David; // funcke na aktualizování davida, argumenty jsou ukazatel na strukturu 'David' a hodnota časový delty // Funkce nám bude kromě aktualizovávání Davida hlídat i uživatelský vstupy na klávesnici, tzn. mačkání // čudlíků, kterejma se david bude hejbat void aktualizovatDavida ( David * david, float dt ) { // pokud je máčknutá na klávesnici šipka nahoru tak se kouknem jestli si david sednul na // zem nebo jestli si právě na zem sedá. To poznáme tim, že jako atribut 'aktualni_animace' je nastavená // animace sednutí si na zem, tzn. že ukazatel aktuální animace ukazuje na adresu atributu 'animace_sed' if ( IsKeyDown ( KEY_UP ) ) { if ( david->aktualni_animace == &david->animace_sed ) { // pro případ že je animace pauznutá (neloopujicí animace se nám samy pauznou když dojenou na konec) // ji vodpauzujeme.... david->aktualni_animace->pauznuta = false; // ....a začnem přehrávat pozpátku david->aktualni_animace->reverzne=true; // pokud aktuální animace (což je furt animace sednutí si) dojela zpátky na začátek (index je nula), // tak přepnem aktuální animaci na takový to pérování v kolenou (do ukazatele aktualni_animace narvem adresu // animace idle) if ( david->aktualni_animace->index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } // pokud je na klávesnici máčknutá šipka doleva a aktuální animace není animace sednutí // si na zem (david přece nemuže chodit když sedí, to dá rozum :D ;D), tak budem aktualizovat // davidovu polohu, jakože nám david jde někam doleva if ( IsKeyDown ( KEY_LEFT ) && david->aktualni_animace != &david->animace_sed ) { // nastavíme jeho směr doleva (vicemeně jenom kuli vykreslování animací) david->smer = -1; // a vytvoříme si prepozici jeho polohy, na kterou ho pak šoupnem // (by sme mohli jednoduše posouvat samotný vokraje i pozici, prepozice se nám ale bude hodit v dalších // krocích vyrábění tý naší hry) // zkopírujeme si současnou polohu vokrajů do dočasný proměný.... Rectangle prepozice = david->okraje; // a posunem ji směrem doleva vo rychlost chůze krát časová delta // (aťuž si nastavíme jaký chceme FPS, když poščítáme dohromady všecky časový delty v proměný 'dt' za nějakej čas 't', // tak velikost toho celkovýho součtu bude +- rovna tomu času 't'. Napřiklad, my sme si nastavili fps na šedesát, // takže by sme měli mit (idelálně, nic neni 100% přesný) proměnou 'dt' rovnou pokaždý jedný šedesatině, páč rendrujeme // těch šedesát snimků za sekundu. Noa kdyby sme si poščitali hodnoty proměný 'dt' za dobu jedný vteřiny, během který // vyrendrujem těch 60 snímků, nóó tak budeme mit děsně překvapivě šedesát šedesátin a sme zase na jedničce, resp. na // jedný vteřině :O ;D. Takže když tou deltou budeme násobit rychlost, máme skoro něco jako 'posun za jednu jednotku času' // (fajnšmekři si mužou vygooglit věci jako sou 'derivace' nebo třeba 'diferenciál' :D ;D)) prepozice.x -= RYCHLOST_CHUZE_DAVIDA * dt; // noa provedeme posun Davida na tu spočitanou prepozici. // Takže nastavíme iksovou souřadnici vokrajů na nově spočitanou hodnotu.... david->okraje.x = prepozice.x; // ....a z vokrajů si dopočítáme iksovou souřadnici levýho horního vobdelnika framu textury david->pozice.x = david->okraje.x - 45; // nakonec eště musíme přepnout aktuální animaci na animaci běhu david->aktualni_animace = &david->animace_beh; } // pro máčknutí čudliku pravý šipky je +- uplně stejnej blok kódu jako pro máčknutí šipky doleva, // jenom máme ten iksovej posun kladnej else if ( IsKeyDown ( KEY_RIGHT ) && david->aktualni_animace != &david->animace_sed ) { david->smer = 1; Rectangle prepozice = david->okraje; prepozice.x += RYCHLOST_CHUZE_DAVIDA * dt; david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->aktualni_animace = & david->animace_beh; } // jestli je máčkutej na klávesnici čudlik šipky dolu, tak začnem přehrávat animaci sednutí // si na zadek else if ( IsKeyDown ( KEY_DOWN ) ) { // přepnem aktualní animaci.... david->aktualni_animace = & david->animace_sed; // ....nastavíme atribut 'reverzně' na 'false' by se nám animace přehrávala normálně, // jakože směrem vod začátku ke konci.... david->aktualni_animace->reverzne = false; // ....a vodpauzujem ji, pro případ kdyby byla pauznutá david->aktualni_animace->pauznuta = false; } // mačkání tědlech čudliků sme si jakoby zřetězili takovou jakože nudlí if()else if() else...... takže jestli // sme se teďko v běhu programu dostali k tomudlectomu kousku kódu, tak neni máčknutej žádnej knoflik. // Koukneme se, jestli si David náhodou nesednul na zem nebo jestli si zrovna teďko nesedá nebo z tý země nevstává // (to poznáme tim, jestli má jako aktualní animaci nastavenou animaci sedání) noa jestli ne, tak mu přepnem // animaci na to akční pérování v kolenou else if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; } // nastavíme vertikální překlopení vykreslování textury podle Davidova atributu 'směr', jakože podle toho, jakým // směrem se kouká david->aktualni_animace->zrcadlit = david->smer<0; // pokud je aktuální animace animace sedání a pokud ta animace dojela už na konec (animace sednutí se neloopuje, // takže když dojede na konec, tak se sama pauzne) tak se kouknem, na index zobrazovanýho snimku. Pokud to je první // snímek animace (index rovnej nule), tak víme, že David už nesedí, ani si nesedá, ani nevstává, ale že už vstal. // Takže přepnem animaci na 'idle' if ( david->aktualni_animace == &david->animace_sed ) { if ( david->animace_sed.pauznuta ) { if ( david->animace_sed.index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } // pokud je aktualní animace běhání, tak se kouknem jestli hraje zvuk kroků noa jestli ne, // ho začnem přehrávat else if ( david->aktualni_animace == &david->animace_beh ) { if ( !IsSoundPlaying ( zvuk_kroku ) ) { PlaySound ( zvuk_kroku ); } } // nakonec aktualizujem aktuální animaci :D ;D aktualizovatAnimaci ( david->aktualni_animace, dt ); } // funkce na vykreslování Davida void vykreslitDavida ( David * david ) { vykreslitAnimaci ( david->aktualni_animace, david->pozice, WHITE ); // jestli je definovanej 'DEBUG', vykreslíme vokraje třeba zelenou barvičkou #ifdef DEBUG DrawRectangleLines ( david->okraje.x, david->okraje.y, david->okraje.width, david->okraje.height, GREEN ); #endif } #endif
Ještě si musíme pochopytelně zase překopat ten soubor 'main.c' a Davida si tam přidat 😮 😜
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif //šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; // zvuky Sound zvuk_kroku; int main ( void ) { // nakonfigurujeme si raylib, že budeme chtít vytvořit okno s proměnlivou velikostí a s vertikální synchronizací // poznámka pod čarou, ten fígl s bitovým operátorem 'or' jakože se znakem '|' funguje tak, že každá z těch flagovejch // konstant má hodotu nastavenou tak, by byl v jejich bytu vobsazenej dycky jenom jeden jedinej bit. Noa když uděláme // to bitový or, tak se nám ty proměný zkombinujou do nový unikátní hodnoty kterou ta knihovna umí rozlišit, // respektive čte jednotlivý bity v bajtech :O ;D SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); // inicializujeme vokno vo daný šířce, vejšce a s titulkem InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); // inicializujem audio zařízení, by sme mohli přehrávat zvuky InitAudioDevice(); // nastavíme požadovanou frekvecni vykreslování // takle řikáme, že chceme vykreslovat šedesát snímků za sekundu (framů per sekundu) SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); // vyrobíme si instance jednotlivejch animací možnejch davidovejch aktivit // animace běhu // jeden snímek animace nám bude trvat jednu třicetinu sekundy, relativní čas a index vynulujem, zapnem atribut 'jojo' by // se nám pak animace po normálním přehrání pouštěla pozpátku, nastavíme atributy 'reverzně' a 'zrcadlit' na false, // zapnem loopování, nastavíme texturu a pole jednotlivejch framů ve spritesheetu, počet framů/dylku pole spočitáme podělením velikosti // celýho pole velikostí jednoho prvku Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; // animace sednutí si, puvodně to měla bejt animace chcípnutí jakože si ztoho sedne na zadek :D ale pak mě napadlo že ta hra // by mohla bejt víc zajimavější když by se david moch přikrčovat před muslimskou střelbou k zemi // pozn. si všimněte že má animace vypnutej loop Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { //nakopírujeme do davida jednotlivý animace .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, // aktuální animaci nastavíme zatim na nulovou adresu, ten ukazatel vyplníme až // adresou animace z instance Davida v tý právě vyráběný proměný 'david' .aktualni_animace = NULL, //souřadnice davida // (je to pozice levýho horního okraje 'obrázku') .pozice = {GetRenderWidth()/2 - DAVID_F_SIRKA/2,200}, //směr kterým kouká (jednička je zleva doprava, -1 je zprava doleva) .smer = 1, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu // (jsou vo trošku menčí než vokraje voblasti framu animace) david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // aktualizujem Davida aktualizovatDavida(&david, dt); BeginDrawing(); DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); //vykreslíme davida vykreslitDavida(&david); EndDrawing(); } // jestli se přerušil tamten náš hlavní while cyklus, tak zavřem okno, uklidíme po sobě a skončíme // běh programu vrácením návratový hodnoty CloseWindow(); //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); return 0; }
Nj ale David nám jentak běhá vevzduchu. Musíme mu vyrobit ňákej svět ve kterým by se moch ňák normálně pohybovat, musel řešit řešit a vobcházet různý překážky a tak 😁 😜
Herní svět Davidoj vyrobíme z takovejch jakože bloků, pravidelnejch čtverečků s texturou kamenů (použitá textura sou vopravdický šutry ze Zdi nářků hele třeba 😮 😜), který budeme rovnat do pravidelný dvourozměrný mřížky, takový jakože improvizovaný tiled mapy/ dlaždicový mapy. Takže si ve složšce 'src' vyrobíme novej hlavičkovej soubor 'mapa.h' a tam napišeme todle:
#ifndef _DAVID_A_DUCHOVE_MAPA_H_ #define _DAVID_A_DUCHOVE_MAPA_H_ #include <raylib.h> // vejška a šířka kostiček, resp. 'dlaždic', ze kterejch budem skládat tu naši herní mapu #define BLOK_SIRKA 80 #define BLOK_VYSKA 80 // makra na zišťování maximální/minimální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b) ? (a) : (b)) #define MIN(a, b) ((a)<(b) ? (a) : (b)) #endif // struktura tý dlaždicový mapy typedef struct Mapa { // v cčku muže bejt ukazatel pole, noa když máme ukazatel na ukazatele takže tam vlastně mužeme schovat // 'pole polí' a tak ziskat dvourozměrný pole. Todlecto 2d pole bude vlastně taková jakože mřížka a jednotlivý // chlívečky v mřížce mužou bejt buďto volný misto, noa nebo šutr.Jednotlivý kostičky herní mapy budem popisovat číslem, // nula bude prázndej prostor, kladný číslo bude znamenat žeje tednlecten 'chlíveček v mřížce' vobsazenej pevným blokem. // Noa konkrétní hodnota toho čísla bude určovat konkrétní texturu v spritesheetu kamenů, kterou vykreslit (máme víc variant) int ** bloky; //vejška a šířka toho našeho 2d pole int sirka, vyska; // textura, kterou budem používat na vykreslování těch kostek Texture2D textura; }Mapa; // funkce na vykreslování mapy // bere tři argumenty, první je samozdřejmě ukazatel na mapu, noa pak minimální a maximální hodnota vykreslovaný iksový // souřadnice, ty nám vymezujou kterou voblast mapy budem malovat. Dokavaď máme tu mapu relativně malou, tak takovýho něco asi // jako nemusíme moc řešit. Kdybysme ale měli mapu děsně dlouhatatatatánckou bambilion kostiček tak by nám vykreslování celý mřížky // dlaždicový mapy žralo zbytečně celkem poctatnej kus počítacího víkonu. Proto si stanovujem vokraje vykreslovaný mapy void vykreslitMapu(Mapa * mapa, float min_x, float max_x) { // vypočítáme si z hodnoty iksový polohy souřadnici kostky v dlaždicový mapě // (prostě to číslo podělíme dýlkou bloku/dlaždice a zavokrouhlíme. Menčí hodnotu zavokrouhlíme floorem // dolu, večí čislo ceilem nahoru, by sme fakt jako pokryly i ty nejvíc nejvokrajovějšejší hodnoty) int _min_x = (int)floor((min_x) / (float)BLOK_SIRKA); int _max_x = (int)ceil((max_x) / (float)BLOK_SIRKA); // si pohlídáme si by sme mezema nevyskočili z mapy a nelezli mimo voblast pole // takže dolní mez muže bejt minimálně nula.... _min_x = MAX(0, _min_x); // ....a horní mez maximálně velká jako šířka celý mapy _max_x = MIN(mapa->sirka, _max_x); // projdeme si mapu vod min_x až po max_x sloupec po sloupci.... for(int x = _min_x; x < _max_x; x++) { // každej sloupeček budem číst vodzhora až dolu for(int y = 0; y < mapa->vyska; y++) { // a pro každej chlíveček v tý 2d mřížce se kouknem jaká je tam skovaná hodnota int blok = mapa->bloky[y][x]; //noa jestli to neni nula (nebo zaporný čislo), tak vykreslíme vodpovidajicí kostku if(blok) // jeruzálémský kameny sou ve spritesheetu srovnaný v jednom řádku za sebou takže vodpovidajicí pořadí ziskame pronásobením // dýlky kostky hodnotou vobsaženou v proměný 'blok'. Šutry maj ve spritesheetu kolem sebe trošku volnýho místa (by se předešlo // tile bleedingu), tendle vokraj musíme zohlednit (sou to ty 2.0f, co tam strašej) DrawTextureRec(mapa->textura, (Rectangle){2.0f +84.0f * (float)(blok-1),2.0f,80.0f,80.0f},(Vector2){(float)x*BLOK_SIRKA,(float)y*BLOK_VYSKA},DARKBLUE); } } // noa teďko mě napadnul uplně supr zlepšovák pro vykreslování tý naší mapy, by nám jentak nevysela vevzuduchu jako taková tenká vrstva // tlustá jenom jednu kostku na vejšku. Projdeme si zase jako celou mapu a kouknem jestli souřadnice v tom nejvíc nejspodnějším řádku mapy // neni ďoura, noa jestli neni resp. je tam pevnej blok, tak eště tři kostičky vykreslíme // ( jestli nastavíme vejšku mapy na deset kostiček, tak to bude dohromady nějakejch 800 pixelů. Vejšku vokna máme definovanou v main.c jako // 960 pixelů, takže když budeme vykreslovat naši dlaždicovou mapu na pozici (0,0) jakože v levým a hlavně horním rohu vobrazovky, tak nám // na vejšku na pokrytí celý vobrazovky zbejvá eště 160 pixelů, to sou dvě kostky, přestřelíme na tři (vono se to s // tou oblastí vobrazovky pak bude trošku komplikovat až začnem dělat s kamerou v některým z dalších kroků)) for(int x = _min_x; x < _max_x; x++) { if(mapa->bloky[mapa->vyska-1][x] != 0) for(int y = mapa->vyska; y < mapa->vyska + 3; y++) { int index = mapa->bloky[mapa->vyska - 1][x]; index = 7; DrawTextureRec(mapa->textura, (Rectangle){2 +84*(index-1),2,80,80},(Vector2){x*BLOK_SIRKA,y*BLOK_VYSKA},DARKBLUE); } } } // funkce na vytažení hodnoty bloku mapy na souřadnicích 'x' a 'y' // jenom to hlídá by sme nevyskočili z mapy a neptali se na hodnotu ležicí mimo rozsah toho 2d pole // (hežčí by to asi jako bylo hlídat ty meze v těch druhejch funkcích páč by to zredukovalo množšství // volání ifů) int getBlokMapy(int x, int y, Mapa * mapa) { if(x >= 0 && x < mapa->sirka && y>=0 && y < mapa->vyska) { return mapa->bloky[y][x]; } // jestli sme vyskočili z mapy, vrátíme -1 return -1; } // funkce na zišťování, jestli bod leží ve voblasti některý z kostek mapy bool kolizeSeBlokemMapy_bod(Vector2 bod, Mapa * mapa) { // převedem si souřadnici na polohu v mapě int x = (int)floor(bod.x / (float)BLOK_SIRKA); int y = (int)floor(bod.y / (float)BLOK_VYSKA); // a kouknem jestli je to kladná hodnota // (v cčku by sme to mohli napsat i bez toho '> 0' na konci :O ;D) return getBlokMapy(x,y, mapa) > 0; } // funkce na zišťování, jestli vobdelnik leží na některý kostce mapy bool kolizeRectSeBlokemMapy(Rectangle box, Mapa * mapa) { // převedem si souřadnice vobdelnika do souřadnic mapy int x = (int)floor((box.x) / (float)BLOK_SIRKA); int y = (int)floor(box.y / (float)BLOK_VYSKA); int y_max = (int)ceil((box.y + box.height) / (float)BLOK_VYSKA); int x_max = (int)ceil((box.width + box.x) / (float)BLOK_VYSKA); // a projdem všecky chlívečky mřížky, do kterejch ten náš vobdelnik zasahuje // pokavaď aspoň jeden chlíveček vobsahuje bevnej blok, tak máme kolizi for (int j = x; j < x_max; j++) for (int i = y; i < y_max; i++) { if(getBlokMapy(j,i, mapa) > 0) return true; } return false; } #endif
Teďkonc upravíme kód Davida, by si jeho struktura v sobě držela referenci na dlaždicovou mapu ve který se David jakože pohybuje, pak upravíme kód aktualizace Davida by sme tam hlídali narážení do bloků herní mapy (jinak by nám David vlez do kostky noa to by sme mohli považovat za bug :explode: 😮)
#ifndef _DAVID_A_DUCHOVE_DAVID_H_ #define _DAVID_A_DUCHOVE_DAVID_H_ // naimportujem si animaci // (s ní se nám současně natáhne raylib.h) #include "animace.h" #include "mapa.h" // kouzelným slovíčkem 'extern' https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/ // mužeme 'sdílet' se souborem 'main.c' v něm deklarovaný proměný, teďko na ukázku zatim jenom // zvuk chůze, de ale všecko :O ;D extern Sound zvuk_kroku; // přestěhujem si sem z 'main.c' framy animace by to tam zbytečně nezabiralo misto ve zdrojáčku #define DAVID_F_SIRKA 150.0f #define DAVID_F_VYSKA 240.0f Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; // tady si budeme definovat různý hodnoty a konstatny k davidoj // zatim si tady jenom nadeklarujeme rychlost jeho chůze // Je vzdálenost kterou david urazí za jednu vteřinu #define RYCHLOST_CHUZE_DAVIDA 350.0f typedef struct David { // jednotlivý animace davida Animace animace_beh; Animace animace_idle; Animace animace_sed; Animace animace_skok; // ukazatel na animaci, která bude jakože jedniná vybraná, // aktualizovaná a vykreslovaná. Máme čtyry ale vidět chcem přece jako // jenom jednu :D :D Animace * aktualni_animace; // pozice levýho horního vokraje vykreslovaný textury Vector2 pozice; // okraje naší herní postavičky (vykreslíme si, když v 'main.c' nahoře aktivujem 'DEBUG') // bude se nám hodit v budoucnu třeba pro kolize s jinejma herníma věcma/entitamama // (vymezíme si menší voblast než jsou rozměry framu animace) Rectangle okraje; // směr kterým David kouká. Jednička znamená z leva doprava, -1 znamená z z prava doleva int smer; // dlaždicová mapa, ve který se david pohybuje Mapa * mapa; } David; // funcke na aktualizování davida, argumenty jsou ukazatel na strukturu 'David' a hodnota časový delty // Funkce nám bude kromě aktualizovávání Davida hlídat i uživatelský vstupy na klávesnici, tzn. mačkání // čudlíků, kterejma se david bude hejbat void aktualizovatDavida ( David * david, float dt ) { // pokud je máčknutá na klávesnici šipka nahoru tak se kouknem jestli si david sednul na // zem nebo jestli si právě na zem sedá. To poznáme tim, že jako atribut 'aktualni_animace' je nastavená // animace sednutí si na zem, tzn. že ukazatel aktuální animace ukazuje na adresu atributu 'animace_sed' if ( IsKeyDown ( KEY_UP ) ) { if ( david->aktualni_animace == &david->animace_sed ) { // pro případ že je animace pauznutá (neloopujicí animace se nám samy pauznou když dojenou na konec) // ji vodpauzujeme.... david->aktualni_animace->pauznuta = false; // ....a začnem přehrávat pozpátku david->aktualni_animace->reverzne=true; // pokud aktuální animace (což je furt animace sednutí si) dojela zpátky na začátek (index je nula), // tak přepnem aktuální animaci na takový to pérování v kolenou (do ukazatele aktualni_animace narvem adresu // animace idle) if ( david->aktualni_animace->index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } // voproti minulýmu kódu sme si sem přidali podmínku, že davida bude možný posouvat doleva jenom když je jeho pozice // věčí než nula (by nám neutek z mapy) if ( IsKeyDown ( KEY_LEFT ) && david->pozice.x > 0 && david->aktualni_animace != &david->animace_sed ) { // nastavíme jeho směr doleva (vicemeně jenom kuli vykreslování animací) david->smer = -1; Rectangle prepozice = david->okraje; prepozice.x -= RYCHLOST_CHUZE_DAVIDA * dt; // kouknem se, jestli nemá prepozice kolizi s některým ze bloků mapy // Jestli jo, tak se nebudem posouvat a přepnem animaci na 'idle' if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { //jestli ale kolizi nemáme, tak Davida normálně šoupnem na prepozici jako sme to ďáli předtim david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->aktualni_animace = &david->animace_beh; } } // Voproti minule sme si sem přidali podmínku by david nemoch utýct z herní mapy, doprava teďko bude chodit navíc jenom když bude jeho // iksová souřadnice menčí než šiřka mapy (- minus šiřka velikosti framu animace, davidova pozice vodpovídá levý horní souřadnici jeho // atributu 'okraje', nás zajímá ale pravej vokraj tak to celý musíme šoupnout vo jeho šířku) else if ( IsKeyDown ( KEY_RIGHT ) && david->pozice.x < david->mapa->sirka*BLOK_SIRKA - DAVID_F_SIRKA && david->aktualni_animace != &david->animace_sed ) { david->smer = 1; Rectangle prepozice = david->okraje; prepozice.x += RYCHLOST_CHUZE_DAVIDA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->aktualni_animace = & david->animace_beh; } } // jestli je máčkutej na klávesnici čudlik šipky dolu, tak začnem přehrávat animaci sednutí // si na zadek else if ( IsKeyDown ( KEY_DOWN ) ) { // přepnem aktualní animaci.... david->aktualni_animace = & david->animace_sed; // ....nastavíme atribut 'reverzně' na 'false' by se nám animace přehrávala normálně, // jakože směrem vod začátku ke konci.... david->aktualni_animace->reverzne = false; // ....a vodpauzujem ji, pro případ kdyby byla pauznutá david->aktualni_animace->pauznuta = false; } // mačkání tědlech čudliků sme si jakoby zřetězili takovou jakože nudlí if()else if() else...... takže jestli // sme se teďko v běhu programu dostali k tomudlectomu kousku kódu, tak neni máčknutej žádnej knoflik. // Koukneme se, jestli si David náhodou nesednul na zem nebo jestli si zrovna teďko nesedá nebo z tý země nevstává // (to poznáme tim, jestli má jako aktualní animaci nastavenou animaci sedání) noa jestli ne, tak mu přepnem // animaci na to akční pérování v kolenou else if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; } // nastavíme vertikální překlopení vykreslování textury podle Davidova atributu 'směr', jakože podle toho, jakým // směrem se kouká david->aktualni_animace->zrcadlit = david->smer<0; // pokud je aktuální animace animace sedání a pokud ta animace dojela už na konec (animace sednutí se neloopuje, // takže když dojede na konec, tak se sama pauzne) tak se kouknem, na index zobrazovanýho snimku. Pokud to je první // snímek animace (index rovnej nule), tak víme, že David už nesedí, ani si nesedá, ani nevstává, ale že už vstal. // Takže přepnem animaci na 'idle' if ( david->aktualni_animace == &david->animace_sed ) { if ( david->animace_sed.pauznuta ) { if ( david->animace_sed.index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } // pokud je aktualní animace běhání, tak se kouknem jestli hraje zvuk kroků noa jestli ne, // ho začnem přehrávat else if ( david->aktualni_animace == &david->animace_beh ) { if ( !IsSoundPlaying ( zvuk_kroku ) ) { PlaySound ( zvuk_kroku ); } } // nakonec aktualizujem aktuální animaci :D ;D aktualizovatAnimaci ( david->aktualni_animace, dt ); } // funkce na vykreslování Davida void vykreslitDavida ( David * david ) { vykreslitAnimaci ( david->aktualni_animace, david->pozice, WHITE ); // jestli je definovanej 'DEBUG', vykreslíme vokraje třeba zelenou barvičkou #ifdef DEBUG DrawRectangleLines ( david->okraje.x, david->okraje.y, david->okraje.width, david->okraje.height, GREEN ); #endif } #endif
Noa eště si musíme někde v 'main.c' tu mapu vyrobit a strčit do Davida:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif //šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; // zvuky Sound zvuk_kroku; int main ( void ) { // nakonfigurujeme si raylib, že budeme chtít vytvořit okno s proměnlivou velikostí a s vertikální synchronizací // poznámka pod čarou, ten fígl s bitovým operátorem 'or' jakože se znakem '|' funguje tak, že každá z těch flagovejch // konstant má hodotu nastavenou tak, by byl v jejich bytu vobsazenej dycky jenom jeden jedinej bit. Noa když uděláme // to bitový or, tak se nám ty proměný zkombinujou do nový unikátní hodnoty kterou ta knihovna umí rozlišit, // respektive čte jednotlivý bity v bajtech :O ;D SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); // inicializujeme vokno vo daný šířce, vejšce a s titulkem InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); // inicializujem audio zařízení, by sme mohli přehrávat zvuky InitAudioDevice(); // nastavíme požadovanou frekvecni vykreslování // takle řikáme, že chceme vykreslovat šedesát snímků za sekundu (framů per sekundu) SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); // vyrobíme si instance jednotlivejch animací možnejch davidovejch aktivit // animace běhu // jeden snímek animace nám bude trvat jednu třicetinu sekundy, relativní čas a index vynulujem, zapnem atribut 'jojo' by // se nám pak animace po normálním přehrání pouštěla pozpátku, nastavíme atributy 'reverzně' a 'zrcadlit' na false, // zapnem loopování, nastavíme texturu a pole jednotlivejch framů ve spritesheetu, počet framů/dylku pole spočitáme podělením velikosti // celýho pole velikostí jednoho prvku Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; // animace sednutí si, puvodně to měla bejt animace chcípnutí jakože si ztoho sedne na zadek :D ale pak mě napadlo že ta hra // by mohla bejt víc zajimavější když by se david moch přikrčovat před muslimskou střelbou k zemi // pozn. si všimněte že má animace vypnutej loop Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // alokujeme si dynamický dourozměrný pole který jakože bude mřížka tý naší herní mapy // použijem misto mallocu calloc, kterej má tu vyhodu že alokovanou paměť vyplní nulama // (alokovanou paměť zase musíme uvolnit) //šiřk a vejška toho pole (bude schodný s vejškou a šiřkou mapy) const int vejska_mapy = 10; const int sirka_mapy = 20; // nejdřiv si alokujeme ukazatele na ukazatele, jakože pole polí (pro nás pole řádků mapy) int ** bloky = calloc ( vejska_mapy, sizeof ( int * ) * vejska_mapy ); // pak jednotlivý řádky mapy for ( size_t i=0; i<vejska_mapy; i++ ) { bloky[i] = calloc ( sirka_mapy,sizeof ( int ) ); } // a spodní vrstvu mapy vyplníme plošinou for ( size_t i=0; i<sirka_mapy; i++ ) bloky[vejska_mapy - 1][i] = 1; // přidáme si do cesty pár bloků, by sme viděli jak se David zasekává vo překážky bloky[vejska_mapy - 2][7] = 1; bloky[vejska_mapy - 2][10] = 1; bloky[vejska_mapy - 3][10] = 1; // vyrobíme si herní mapu Mapa mapa = { .textura = textura_kameny, .sirka = sirka_mapy, .vyska = vejska_mapy, .bloky = bloky }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // davida postavíme nohama na ty kostky // (spočitáme si souřadnici spodní vrstvy a vodečtem vejšku davida // (pozice je levej horní roh, my nastavujem spodní stranu)) .pozice = {0,(vejska_mapy - 1) * 80 - DAVID_F_VYSKA}, .smer = 1, // strčíme mapu do Davida .mapa = &mapa, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu // (jsou vo trošku menčí než vokraje voblasti framu animace) david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // aktualizujem Davida aktualizovatDavida(&david, dt); BeginDrawing(); DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); //vykreslíme mapu vykreslitMapu(&mapa,0.0f,9999.0f); //vykreslíme davida vykreslitDavida(&david); EndDrawing(); } CloseWindow(); // musíme uklidit to alokovaný pole 'bloky' for ( size_t i=0; i<vejska_mapy; i++ ) { free ( bloky[i] ); } free ( bloky ); //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); return 0; }
Když zkompilujeme a pustíme by sme měli vidět takovýdleho něco
Dali sme si do cesty kameny vo který se David zasekává a nedá se jít dál 😮 🙄 Nóó takže Davida musíme naučit skákat 😮 😜 A taky mu dát ňákou aspoň primitivní fyziku by z kostek moch padat dolu a choval se v tom dlaždicovým svěťě ňák víc přirozenějc 😁 😜
Upravíme soubor 'david.h' do týdle podoby:
#ifndef _DAVID_A_DUCHOVE_DAVID_H_ #define _DAVID_A_DUCHOVE_DAVID_H_ // naimportujem si animaci // (s ní se nám současně natáhne raylib.h) #include "animace.h" #include "mapa.h" // kouzelným slovíčkem 'extern' https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/ // mužeme 'sdílet' se souborem 'main.c' v něm deklarovaný proměný, teďko na ukázku zatim jenom // zvuk chůze, de ale všecko :O ;D extern Sound zvuk_kroku; extern Sound zvuk_skoku; // přestěhujem si sem z 'main.c' framy animace by to tam zbytečně nezabiralo misto ve zdrojáčku #define DAVID_F_SIRKA 150.0f #define DAVID_F_VYSKA 240.0f Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; #define RYCHLOST_CHUZE_DAVIDA 350.0f //máme tady dvě další hodnoty, rychlost skoku a gravitaci #define RYCHLOST_SKOKU_DAVIDA -15.0f #define GRAVITACE_DAVIDA 40.0f typedef struct David { // jednotlivý animace davida Animace animace_beh; Animace animace_idle; Animace animace_sed; Animace animace_skok; Animace * aktualni_animace; Vector2 pozice; Rectangle okraje; int smer; Mapa * mapa; // david bude mit dva nový atributy: // boolean jestli skáče a vertikální rychlost kterou se jakoby pohybuje bool zdaSkace; float vertikalni_rychlost; } David; void aktualizovatDavida ( David * david, float dt ) { // čudlikem šipky nahroru jsme zatim jenom zvedali Davida ze země, teďko mu přidáme eště jednu funkci a to // že jim david bude skákat (pokud nesedí a nesedá si nebo nevstává) if ( IsKeyDown ( KEY_UP ) ) { if ( david->aktualni_animace == &david->animace_sed ) { david->aktualni_animace->pauznuta = false; david->aktualni_animace->reverzne=true; if ( david->aktualni_animace->index == 0 ) { david->aktualni_animace = & david->animace_idle; } // pokud je zmáčknutá šipka nahoru a animace není animace sedání, // tak se kouknem jestli David už neskáče, a jestli ne, tak zapnem zvuk skoku, // nastavíme atrimut skákání, resetujem skákací animaci na index 0 a nastavíme vertikální rychlost na max // (aktuální animaci přepnem kousek dál v upraveným kódu) } else if ( !david->zdaSkace ) { PlaySound ( zvuk_skoku ); david->zdaSkace = true; david->animace_skok.index = 0; david->vertikalni_rychlost = RYCHLOST_SKOKU_DAVIDA; } } if ( IsKeyDown ( KEY_LEFT ) && david->pozice.x > 0 && david->aktualni_animace != &david->animace_sed ) { david->smer = -1; Rectangle prepozice = david->okraje; prepozice.x -= RYCHLOST_CHUZE_DAVIDA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->aktualni_animace = &david->animace_beh; } } else if ( IsKeyDown ( KEY_RIGHT ) && david->pozice.x < david->mapa->sirka*BLOK_SIRKA - DAVID_F_SIRKA && david->aktualni_animace != &david->animace_sed ) { david->smer = 1; Rectangle prepozice = david->okraje; prepozice.x += RYCHLOST_CHUZE_DAVIDA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->aktualni_animace = & david->animace_beh; } } // jestli je máčkutej na klávesnici čudlik šipky dolu, tak začnem přehrávat animaci sednutí // si na zadek else if ( IsKeyDown ( KEY_DOWN ) ) { david->aktualni_animace = & david->animace_sed; david->aktualni_animace->reverzne = false; david->aktualni_animace->pauznuta = false; } else if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; } // kouknem jestli má david pod nohama pevnou zem // vyrobíme si kopii okrajů/boundingboxu a postrčíme ji o 5 pixelů dolu // pokud okraje/boundingbox nebude mit kolizi s pevnejma kostkama herní mapy, // tak davidoj zapnem skákací atribut, ale nedáme mu žádnou rychlost, // tzn. necháme ho padat volným pádem dolu s počateční vertikální rychlostí nula // (rychlost nastavujem na nulu dycky když se David nohama dotkne země takže by už rychlost // na nulu měla bejt nastavená z jinýho kousku kodu) Rectangle prepozice = david->okraje; prepozice.y += 5; if ( ! kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->zdaSkace = true; } // kouknem jestli David má nastavenej atribut skákání na true. Pokud jakože jo, // tak mu budem aktualizovat polohu podle tý jeho vertikální rychlosti. if ( david->zdaSkace ) { // aktualizujem si Davidovu vertikální rychlost // (pomalinku ji táhnem gravitací směrem dolu k zemi) david->vertikalni_rychlost += dt * GRAVITACE_DAVIDA; // uplně stejně jako při chození do stran si spočitáme jeho prepozici, jakože jeho // vokraje posunutý vo vertikální rychlost směrem dolu Rectangle prepozice = david->okraje; prepozice.y += david->vertikalni_rychlost; // noa kouknem, jestli by posunem na tu prepozici došlo ke kontaktu s mapou // (posouváme jenom po ose ypsilon, bočniho narazu do kostek mapy se jakoby nemusíme bát) if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { // pokud by mělo příštím krokem dojit k tomu kontaktu Davida s mapou, tak si to budem počitat jako že // nám David už spadnul na zem. Takže mu vynulujem vertikální rychlost a nastavíme atribut skákání 'zdaSkace' na false david->vertikalni_rychlost = 0.0f; david->zdaSkace = false; // jenže David nám furt visí ve vzduchu, a když ho posunem vo vetikální rychlost, tak ho zase zanoříme do dlaždicový mapy :O :O // takže si spočitáme vertikální polohu spodního vokraje Davida (okraje.y + vejška), přepočitáme ho na souřadnici dlažidový mapy // ( ceil(y/BLOK_VYSKA) a tu zpátky pronásobíme vejškou bloku, takže sme získali souřadnici zarovnanou na kostku mapy // teďko vod toho eště musíme vodečíst vejšku Davida, páč Davidova poloha je jeho levej horní roh ( A eště vodečtem ňákou mrňavou // hodnotu, páč by byl jinak těsně zanořenej ve hraně mapy) david->okraje.y = ceil ( ( david->okraje.y + david->okraje.height ) / BLOK_VYSKA ) * BLOK_VYSKA - david->okraje.height - 1; // pokud je díky mačkutí šipky doprava nebo doleva náhodou nastavená jako aktuální animace běhání, // tak využijem toho že sme animaci skoku vyrobili z podmnožiny framů animace běhu a nastavíme běhací animaci jako // právě teď zobrazenej snímek (index) vodpovídající snimek animace skoku, současně nastavíme stejnej směr přehrávání, jakože // jestli přehráváme reverzně nebo ne. Tim si myslim že dosáhneme trošičku lepší navaznosti animací na sebe a budou jakoby mezi sebou // krásně plynule přecházet. Uvidime :D if ( david->aktualni_animace == &david->animace_beh ) { david->aktualni_animace->index = david->animace_skok.index +5; david->aktualni_animace->reverzne = david->animace_skok.reverzne; } } else { // pokud se padací/skákací prepozice neprotne s dlaždicovou mapou, tzn. David eště nedopadne na zem, // tak mu nastavíme aktualní animaci na animaci skákání (pro případ kdyby ji hráč mačkáním šipek přepnul na běch) david->aktualni_animace = & david->animace_skok; // a posunem okraje na spočitanou prepozici david->okraje.y += david->vertikalni_rychlost; } // aktualizujem davidovu pozici podle aktuální pozice vokrajů // (trošku sem tu polohu upravila, vektor 'pozice' určuje polohu vykreslování textury Davida, // šoupla sem ji vo pár čísel dolu, by David neplandal nohama vevzduchu nad mapou. Vypočty kolizí a relálný polohy stejně // věčinou ve zdrojáčku děláme pomocí toho rectanglu 'okraje', tady de spíš jakože vo tu estetiku nebo co :D) david->pozice.y = david->okraje.y - 3; } david->aktualni_animace->zrcadlit = david->smer<0; if ( david->aktualni_animace == &david->animace_sed ) { if ( david->animace_sed.pauznuta ) { if ( david->animace_sed.index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } // pokud je aktualní animace běhání, tak se kouknem jestli hraje zvuk kroků noa jestli ne, // ho začnem přehrávat else if ( david->aktualni_animace == &david->animace_beh ) { if ( !IsSoundPlaying ( zvuk_kroku ) ) { PlaySound ( zvuk_kroku ); } } // pokud je aktualní animace skákání a pokud hraje zvuk kroků, tak ten zvuk vypnem if ( david->aktualni_animace == &david->animace_skok && IsSoundPlaying ( zvuk_kroku ) ) { StopSound ( zvuk_kroku ); } // nakonec aktualizujem aktuální animaci :D ;D aktualizovatAnimaci ( david->aktualni_animace, dt ); } // funkce na vykreslování Davida void vykreslitDavida ( David * david ) { vykreslitAnimaci ( david->aktualni_animace, david->pozice, WHITE ); // jestli je definovanej 'DEBUG', vykreslíme vokraje třeba zelenou barvičkou #ifdef DEBUG DrawRectangleLines ( david->okraje.x, david->okraje.y, david->okraje.width, david->okraje.height, GREEN ); #endif } #endif
A upravíme si i 'main.c', by sme si tam načetli zvuk skákání a nastavili Davidoj nový atributy:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif //šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; int main ( void ) { // nakonfigurujeme si raylib, že budeme chtít vytvořit okno s proměnlivou velikostí a s vertikální synchronizací // poznámka pod čarou, ten fígl s bitovým operátorem 'or' jakože se znakem '|' funguje tak, že každá z těch flagovejch // konstant má hodotu nastavenou tak, by byl v jejich bytu vobsazenej dycky jenom jeden jedinej bit. Noa když uděláme // to bitový or, tak se nám ty proměný zkombinujou do nový unikátní hodnoty kterou ta knihovna umí rozlišit, // respektive čte jednotlivý bity v bajtech :O ;D SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); // inicializujeme vokno vo daný šířce, vejšce a s titulkem InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); // inicializujem audio zařízení, by sme mohli přehrávat zvuky InitAudioDevice(); // nastavíme požadovanou frekvecni vykreslování // takle řikáme, že chceme vykreslovat šedesát snímků za sekundu (framů per sekundu) SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); // vyrobíme si instance jednotlivejch animací možnejch davidovejch aktivit // animace běhu // jeden snímek animace nám bude trvat jednu třicetinu sekundy, relativní čas a index vynulujem, zapnem atribut 'jojo' by // se nám pak animace po normálním přehrání pouštěla pozpátku, nastavíme atributy 'reverzně' a 'zrcadlit' na false, // zapnem loopování, nastavíme texturu a pole jednotlivejch framů ve spritesheetu, počet framů/dylku pole spočitáme podělením velikosti // celýho pole velikostí jednoho prvku Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; // animace sednutí si, puvodně to měla bejt animace chcípnutí jakože si ztoho sedne na zadek :D ale pak mě napadlo že ta hra // by mohla bejt víc zajimavější když by se david moch přikrčovat před muslimskou střelbou k zemi // pozn. si všimněte že má animace vypnutej loop Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // alokujeme si dynamický dourozměrný pole který jakože bude mřížka tý naší herní mapy // použijem mito mallocu calloc, kterej má tu vyhodu že alokovanou paměť vyplní nulama // (alokovanou paměť zase musíme uvolnit) const int vejska_mapy = 10; const int sirka_mapy = 20; int ** bloky = calloc ( vejska_mapy, sizeof ( int * ) * vejska_mapy ); for ( size_t i=0; i<vejska_mapy; i++ ) { bloky[i] = calloc ( sirka_mapy,sizeof ( int ) ); } // a spodní vrstvu mapy vyplníme plošinou for ( size_t i=0; i<sirka_mapy; i++ ) bloky[vejska_mapy - 1][i] = 1; // přidáme si do cesty pár bloků, by sme viděli jak se David zasekává vo překážky bloky[vejska_mapy - 2][7] = 1; bloky[vejska_mapy - 2][10] = 1; bloky[vejska_mapy - 3][10] = 1; // vyrobíme si herní mapu Mapa mapa = { .textura = textura_kameny, .sirka = sirka_mapy, .vyska = vejska_mapy, .bloky = bloky }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // strčíme mapu do Davida .mapa = &mapa, // nastavíme vertikální rychost na nulu a to zda skáče na true // (ikdyž to zda skáče by se asi jako stejně přeplo samo hnedka :D) .vertikalni_rychlost = 0.0f, .zdaSkace = true, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu // (jsou vo trošku menčí než vokraje voblasti framu animace) david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // aktualizujem Davida aktualizovatDavida(&david, dt); BeginDrawing(); DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); //vykreslíme mapu vykreslitMapu(&mapa,0.0f,9999.0f); //vykreslíme davida vykreslitDavida(&david); EndDrawing(); } CloseWindow(); // musíme uklidit to alokovaný pole 'bloky' for ( size_t i=0; i<vejska_mapy; i++ ) { free ( bloky[i] ); } free ( bloky ); //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); return 0; }
Když si ale jakoby zkusíme dojít s Davidem na pravej konec mapy tak se stane vošklivá věc, David nám uplně normálně uteče z vobrazovky někam pryč 😮 😮
Naštěstí Raylib nám umožňuje rendrování pomocí kamery. Představte si, že ten náš herní svět máme namalovanej na takový velikatatatáncký čtvrtce papíru nalepený někde na zdi, noa po tý čtvrtce nám běhá tamta 2D postavička krále Davida. A teďko si eště přectavte, že před tou čtvrtkou stojí fotograf David Ježek (pozor, nebezpečí zaměny Davidů 😁 😁) s ňákým tim svým foťákem na fotomodulky (jakože s kamerou) nebo cože to bylo a snaží se pohyb tý 2d figurky na ten svuj foťák nějak jakože nahrávat na videjko. Noa jak to jakoby ten Ježek bude dělat: nejdřiv si nastaví na foťáku nějaký přiblížení/zoom, by byla na tom jeho videjku postavička vidět v ňáký rozumný velikosti. A když se bude postavička po čtvrtce pohybovat, tak se Ježek bude snažit by byl jeho foťák furt kolmo namířenej na tu 2d figurku, takže poběží i s tim svým foťákem podél tý čtvrtky a bude furt mířit na střed tý běhajicí figurky 😮 😜
Uplně to samý uděláme my, deklarujeme si nějakou kameru, budem ji nastavovat nějaký rozumný zazoomování/přiblížení a budem ji furt zaměřovat někam na střed krále Davida. Noa co jakoby tou kamerou uvidíme se prostě bude zobrazovat na vobrazovce 😮 😜 (pod kapotou má raylib na 100% ty starý známý fígle s maticovovým počitáním, je to před náma tou kamerou skovaný takže to vubec nemusíme řešit. Fajnšmekři si ale jako mužou vygooglit tisic ruznejch povidání a tutoriálů vokolo wolrd/view/model/etc matic hele třeba)
Takže si takle upravíme 'main.c', by sme si tam tu kameru přidali:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif //šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 //kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 50 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; int main ( void ) { SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); // inicializujeme si strukturu kamery // (vlastně kromě rotace nemusíme vyplňovat atributy // vono si je to bude aktuallizovat každým loopem) Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; const int vejska_mapy = 10; const int sirka_mapy = DELKA_LEVELU_BLOKU; int ** bloky = calloc ( vejska_mapy, sizeof ( int * ) * vejska_mapy ); for ( size_t i=0; i<vejska_mapy; i++ ) { bloky[i] = calloc ( sirka_mapy,sizeof ( int ) ); } // a spodní vrstvu mapy vyplníme plošinou for ( size_t i=0; i<sirka_mapy; i++ ) bloky[vejska_mapy - 1][i] = 1; // přidáme si do cesty pár bloků, by sme viděli jak se David zasekává vo překážky bloky[vejska_mapy - 2][7] = 1; bloky[vejska_mapy - 2][10] = 1; bloky[vejska_mapy - 3][10] = 1; // vyrobíme si herní mapu Mapa mapa = { .textura = textura_kameny, .sirka = sirka_mapy, .vyska = vejska_mapy, .bloky = bloky }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // strčíme mapu do Davida .mapa = &mapa, // nastavíme vertikální rychost na nulu a to zda skáče na true // (ikdyž to zda skáče by se asi jako stejně přeplo samo hnedka :D) .vertikalni_rychlost = 0.0f, .zdaSkace = true, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu // (jsou vo trošku menčí než vokraje voblasti framu animace) david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; // aktualizujem Davida aktualizovatDavida(&david, dt); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; //určíme si meze, ve kterejch se kamera muže pohybovat // minimální iksová souřadnice je polovina šířky vobrazovky (takže když se david bude přibližovat // levýmu vokraji mapy, přestanem ho kamerou sledovat by sme nevykoukli mimo voblast tý dlaždicový mapy), // maximální dýlka levelu v počtu bloků minus zase půlka vobrazovky (ze stejnejch duvodů jen pro druhej vokraj) // maximální ypsilon (roste nám směrem zezhora dolu, horní vokraj obrazovky má nulu) je // půlka vejšky vobrazovky + půlka vejšky davida + vejška pár bloků navíc, by hráč nemusel mit voči moc sklopený k dolnímu vokraji // vobrazovky (zpomináte si jak sme si vykreslovali v souboru 'mapa.h' těch pár kostek navíc?? teďko se šiknou. Posun kamery mi // přišel nejhežčí dokonce vo vejšku pěti bloků, nejenom tří jak to tam máme v hlavičce 'mapa.h' napsaný. Necháme soubor mapa.h jak je, // už mam vymyšlený jak to celý zavonačíme by spodek tý mapy byl pěknej a nemuseli sme si rendrovat zbytečně moc kostek :O ;D) // (hodnoty transformujem do rozměrů kamerózního světa podělením ňákýho toho rozměru nebo // souřadnice spočitanou hodnotou proměný 'přiblížení') const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme mapu vykreslitMapu(&mapa,0.0f,9999.0f); // vykreslíme davida vykreslitDavida(&david); // a teďko příde to slíbený zavonačení :D :D // nakreslíme na spodku mapy čtverec s černým gradientem, takže to bude vypadat že se nám spodek mapy // jakože noří do ňákýho stínu nebo čeho DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // musíme uklidit to alokovaný pole 'bloky' for ( size_t i=0; i<vejska_mapy; i++ ) { free ( bloky[i] ); } free ( bloky ); //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); return 0; }
Sme si řekli že to má bejt jakože akční hra, navíc má David v ruce ňákou takovou pistolku takže asi jako musíme dodržet Čechovovovovo pravidlo hele takže Davidova pistolka musí střílet 😁 😜
Že projektily budou takový herní voběkty který maj ňákou rychlost a budou se posouvat po mapě a když narazej do ňáký pevný překážky tak se zničej, to je jasný. My ale abysme nemuseli furt alokovat a uvolňovat paměť, kdyby sme si třeba struktury projektilů furt dynamicky vytvářeli a při zničení z paměti mazali, tak si alokujeme celej zásobník Davidovejch střel a jednou použitý projektily budem znova recyklovat. Hráč si ničeho nevšimne páč to bude vypadat uplně stejně jako by furt znikaly ňáký nový projektily, ale to bude jenom takovej trik, veskutečnosti budem furt točit dokolečka ty samý struktury 😮 😜
takže si ve složšce 'src' vyrobíme novej soubor 'projektily.h' a dáme tam takovejdle vobsach:
#ifndef _DAVID_A_DUCHOVE_PROJEKTILY_H_ #define _DAVID_A_DUCHOVE_PROJEKTILY_H_ #include <raylib.h> #include "mapa.h" // druh střely // stejnou strukturu budem využívat pozdějc i u duchů, zatim máme jenom jedinej možnej druch enum druh_strely {strela_davida}; typedef struct Strela { // rychlost střely float rychlost; // relativní čas střely float relativni_cas; // nastavená maximální doba života střely // když relativní čas překročí tudle hodnotu tak střelu deaktivujem a budem považovat za zničenou float doba_zivota; enum druh_strely druh; // pozice střely Vector2 pozice; // zda je střela aktivní // pokud aktivní neni považujem ji za zničenou a/nebo nevystřelenou, takovou střelu nebudeme // vykreslovat ani aktualizovat bool aktivni; } Strela; void aktualizovatStrelu ( Strela * strela, Mapa * mapa, float dt ) { strela->relativni_cas += dt; if ( strela->relativni_cas > strela->doba_zivota ) { strela->aktivni = false; } else { strela->pozice.x += dt * strela->rychlost; // pokud dojde ke srážce střely s blokem mapy, tak střelu deaktivujem if ( kolizeSeBlokemMapy_bod ( strela->pozice, mapa ) ) { strela->aktivni = false; } } } void vykreslitStrelu ( Strela * strela ) { if ( !strela->aktivni ) { return; } // zatim budem vykreslovat jenom Davidovy střely, // budou to takový jakoby softwérový kuličky s bílošedivým gradientem switch ( strela->druh ) { case strela_davida: DrawCircleGradient ( strela->pozice.x,strela->pozice.y,5.0f,WHITE,DARKGRAY ); break; default: break; } } #endif
Upravíme si kód Davida v hlavičkovým souboru 'david.h', přidáme mu tam střílení mezernikem a aktualizaci a vykreslování střel:
#ifndef _DAVID_A_DUCHOVE_DAVID_H_ #define _DAVID_A_DUCHOVE_DAVID_H_ // naimportujem si animaci // (s ní se nám současně natáhne raylib.h) #include "animace.h" #include "mapa.h" #include "projektily.h" extern Sound zvuk_kroku; extern Sound zvuk_skoku; extern Sound zvuk_vystrel; // přestěhujem si sem z 'main.c' framy animace by to tam zbytečně nezabiralo misto ve zdrojáčku #define DAVID_F_SIRKA 150.0f #define DAVID_F_VYSKA 240.0f Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; #define RYCHLOST_CHUZE_DAVIDA 350.0f #define RYCHLOST_SKOKU_DAVIDA -15.0f #define GRAVITACE_DAVIDA 40.0f //přidáme si další tři hodnoty // 1. rychlost davidovejch střel #define RYCHLOST_STREL_DAVIDA 800.0f // 2. dobu čekání mezi jednotlivejma výstřelama (čtvrt sekundy) #define STRILECI_COOLDOWN_DAVIDA 0.25f // 3. maximální počet střel, jakože max velikost zasobniku #define POCET_STREL_DAVIDA_MAX 32 typedef struct David { // jednotlivý animace davida Animace animace_beh; Animace animace_idle; Animace animace_sed; Animace animace_skok; Animace * aktualni_animace; Vector2 pozice; Rectangle okraje; int smer; Mapa * mapa; bool zdaSkace; float vertikalni_rychlost; // zásobník střel, bude to ukazatel na 'pole' střel Strela * strely; // zbejvajicí čas do dalšího výstřelu float strileci_cooldown; } David; void aktualizovatDavida ( David * david, float dt ) { if ( IsKeyDown ( KEY_UP ) ) { if ( david->aktualni_animace == &david->animace_sed ) { david->aktualni_animace->pauznuta = false; david->aktualni_animace->reverzne=true; if ( david->aktualni_animace->index == 0 ) { david->aktualni_animace = & david->animace_idle; } } else if ( !david->zdaSkace ) { PlaySound ( zvuk_skoku ); david->zdaSkace = true; david->animace_skok.index = 0; david->vertikalni_rychlost = RYCHLOST_SKOKU_DAVIDA; } } // když hráč zmáčkne mezernik, tak se David pokusí vystřelit z tý svý pistolky if ( IsKeyDown ( KEY_SPACE ) ) { // vystřelit mužeme jenom když uplynula čekací doba mezi výstřelama resp. střílecí cooldown je menší nebo rovnej nule if ( david->strileci_cooldown <= 0.0f ) { // projdeme si celej davidův zásobník a pokusíme se v něm najít střelu, kterou budeme moct použít // to poznáme tak, že bude mit atribut aktivní nastavenej na nulu for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { if ( !david->strely[i].aktivni ) { // pokud sme takovou střelu našli, tak si vybereme pro upravování atributů Strela * s = david->strely + i; // nejdřiv ji nastavíme novou polohu, //to znamená přibližně někam na konec hlavně tý pistolky co má david v rukou //nj jenže david muže stát, sedět na zemi, muže koukat z prava doleva, nebo muže dokonce právě vstávat ze země // všecky tydlecty eventualitky jakoby musíme pokrejt if ( david->aktualni_animace != &david->animace_sed ) s->pozice = ( Vector2 ) { // střelu umisťujem podle toho jakým směrem david kouká david->smer==1? david->pozice.x+DAVID_F_SIRKA - 25: david->pozice.x+25, david->pozice.y + DAVID_F_VYSKA/2 - 36 }; else { // pokud david má jako aktuální animaci sedání, tak si zistíme index a vo ten budeme posouvat iksovou a ypsilonovou // souřadnici střely. Neni to uplně přesný ale na to nikdo koukat nebude :D int index = david->animace_sed.index; s->pozice = ( Vector2 ) { david->smer==1? david->pozice.x+DAVID_F_SIRKA - 25 - index*5: david->pozice.x+25 + index*5,david->pozice.y + DAVID_F_VYSKA/2 - 26 - 8 + index*14 }; } //polohu máme, teďko nastavíme další atributy střely // nastavíme střele rychlost, zohledníme i směr kterým poletí, ten vodpovídá // směru kterým David právě teďko kouká s->rychlost = RYCHLOST_STREL_DAVIDA * ( float ) david->smer; // vynulujem relativní čas s->relativni_cas = 0.0f; // nastavíme dobu života třeba na čtyry vteřiny s->doba_zivota = 4.0f; // a aktivujem s->aktivni=true; // střelu máme upravenou, ukazatel už nepotřebujem s=0; // ..a když sme aktivovali střelu, tak sme vlastně vystřelili, takže nastavíme střílecí čekací dobu // na maximální hodnotu david->strileci_cooldown = STRILECI_COOLDOWN_DAVIDA; // zahrajem zvuk výstřelu PlaySound ( zvuk_vystrel ); // a přerušíme hledací for cyklus break; } } } // eště upravíme aktuální animaci, pokud to neni animace sedání, tak přepnem animaci na idle na první snímek, // páč při tý animaci david hejbe pistolkou a vypadalo by to divně kdyby z ní vylítla vodorovně střela. Pokud david skáče, // běží nebo padá, tak se nám ta animace stejně přepne někde dál tady ve zdrojáčku týdle funkce // (při animaci běhu/skoků todle nepřectavuje problém, pistolka je na všech vobrázcích spritesheetu ve stejný vejšce, // kromě tý animace 'idle', tam je ve stejný vejšce právě jenom na prvním snimku) if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; david->aktualni_animace->index = 0; david->aktualni_animace->relativni_cas = 0.0f; } } if ( IsKeyDown ( KEY_LEFT ) && david->pozice.x > 0 && david->aktualni_animace != &david->animace_sed ) { david->smer = -1; Rectangle prepozice = david->okraje; prepozice.x -= RYCHLOST_CHUZE_DAVIDA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->aktualni_animace = &david->animace_beh; } } else if ( IsKeyDown ( KEY_RIGHT ) && david->pozice.x < david->mapa->sirka*BLOK_SIRKA - DAVID_F_SIRKA && david->aktualni_animace != &david->animace_sed ) { david->smer = 1; Rectangle prepozice = david->okraje; prepozice.x += RYCHLOST_CHUZE_DAVIDA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->aktualni_animace = & david->animace_beh; } } // jestli je máčkutej na klávesnici čudlik šipky dolu, tak začnem přehrávat animaci sednutí // si na zadek else if ( IsKeyDown ( KEY_DOWN ) ) { david->aktualni_animace = & david->animace_sed; david->aktualni_animace->reverzne = false; david->aktualni_animace->pauznuta = false; } else if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; } Rectangle prepozice = david->okraje; prepozice.y += 5; if ( ! kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->zdaSkace = true; } if ( david->zdaSkace ) { david->vertikalni_rychlost += dt * GRAVITACE_DAVIDA; Rectangle prepozice = david->okraje; prepozice.y += david->vertikalni_rychlost; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->vertikalni_rychlost = 0.0f; david->zdaSkace = false; david->okraje.y = ceil ( ( david->okraje.y + david->okraje.height ) /BLOK_VYSKA ) * BLOK_VYSKA - david->okraje.height - 1; if ( david->aktualni_animace == &david->animace_beh ) { david->aktualni_animace->index = david->animace_skok.index +5; david->aktualni_animace->reverzne = david->animace_skok.reverzne; } } else { david->aktualni_animace = & david->animace_skok; david->okraje.y += david->vertikalni_rychlost; } david->pozice.y = david->okraje.y - 3; } david->aktualni_animace->zrcadlit = david->smer<0; if ( david->aktualni_animace == &david->animace_sed ) { if ( david->animace_sed.pauznuta ) { if ( david->animace_sed.index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } else if ( david->aktualni_animace == &david->animace_beh ) { if ( !IsSoundPlaying ( zvuk_kroku ) ) { PlaySound ( zvuk_kroku ); } } if ( david->aktualni_animace == &david->animace_skok && IsSoundPlaying ( zvuk_kroku ) ) { StopSound ( zvuk_kroku ); } // odečteme časovou deltu vod zbejvajicí čekací doby mezi výstřelama if ( david->strileci_cooldown > 0.0f ) { david->strileci_cooldown -= dt; } // a aktualizujem si celej davidův zasobnik for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { aktualizovatStrelu ( david->strely+i, david->mapa,dt ); } // nakonec aktualizujem aktuální animaci :D ;D aktualizovatAnimaci ( david->aktualni_animace, dt ); } // funkce na vykreslování Davida void vykreslitDavida ( David * david ) { vykreslitAnimaci ( david->aktualni_animace, david->pozice, WHITE ); // vykreslíme střely for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { vykreslitStrelu ( david->strely+i ); } // jestli je definovanej 'DEBUG', vykreslíme vokraje třeba zelenou barvičkou #ifdef DEBUG DrawRectangleLines ( david->okraje.x, david->okraje.y, david->okraje.width, david->okraje.height, GREEN ); #endif } #endif
Noa eště musíme upravit 'main.c', by sme si tam vyrobili ten zásobnik Davidovejch střel a přidali zvuk střílení
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 50 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; int main ( void ) { SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; const int vejska_mapy = 10; const int sirka_mapy = DELKA_LEVELU_BLOKU; int ** bloky = calloc ( vejska_mapy, sizeof ( int * ) * vejska_mapy ); for ( size_t i=0; i<vejska_mapy; i++ ) { bloky[i] = calloc ( sirka_mapy,sizeof ( int ) ); } // a spodní vrstvu mapy vyplníme plošinou for ( size_t i=0; i<sirka_mapy; i++ ) bloky[vejska_mapy - 1][i] = 1; // přidáme si do cesty pár bloků, by sme viděli jak se David zasekává vo překážky bloky[vejska_mapy - 2][7] = 1; bloky[vejska_mapy - 2][10] = 1; bloky[vejska_mapy - 3][10] = 1; // vyrobíme si herní mapu Mapa mapa = { .textura = textura_kameny, .sirka = sirka_mapy, .vyska = vejska_mapy, .bloky = bloky }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, .mapa = &mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidův vnitřní časovač střílecího cooldownu .strileci_cooldown = 0.0f, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu // (jsou vo trošku menčí než vokraje voblasti framu animace) david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; // aktualizujem Davida aktualizovatDavida(&david, dt); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; //určíme si meze, ve kterejch se kamera muže pohybovat // minimální iksová souřadnice je polovina šířky vobrazovky (takže když se david bude přibližovat // levýmu vokraji mapy, přestanem ho kamerou sledovat by sme nevykoukli mimo voblast tý dlaždicový mapy), // maximální dýlka levelu v počtu bloků minus zase půlka vobrazovky (ze stejnejch duvodů jen pro druhej vokraj) // maximální ypsilon (roste nám směrem zezhora dolu, horní vokraj obrazovky má nulu) je // půlka vejšky vobrazovky + půlka vejšky davida + vejška pár bloků navíc, by hráč nemusel mit voči moc sklopený k dolnímu vokraji // vobrazovky (zpomináte si jak sme si vykreslovali v souboru 'mapa.h' těch pár kostek navíc?? teďko se šiknou. Posun kamery mi // přišel nejhežčí dokonce vo vejšku pěti bloků, nejenom tří jak to tam máme v hlavičce 'mapa.h' napsaný. Necháme soubor mapa.h jak je, // už mam vymyšlený jak to celý zavonačíme by spodek tý mapy byl pěknej a nemuseli sme si rendrovat zbytečně moc kostek :O ;D) // (hodnoty transformujem do rozměrů kamerózního světa podělením ňákýho toho rozměru nebo // souřadnice spočitanou hodnotou proměný 'přiblížení') const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme mapu vykreslitMapu(&mapa,0.0f,9999.0f); // vykreslíme davida vykreslitDavida(&david); // a teďko příde to slíbený zavonačení :D :D // nakreslíme na spodku mapy čtverec s černým gradientem, takže to bude vypadat že se nám spodek mapy // jakože noří do ňákýho stínu nebo čeho DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // musíme uklidit to alokovaný pole 'bloky' for ( size_t i=0; i<vejska_mapy; i++ ) { free ( bloky[i] ); } free ( bloky ); // uvolníme pole střel if ( david_strely ) { free ( david_strely ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); return 0; }
Vytvoříme si strukturu herního levelu, která bude řešit celej herní svět a Davidovu interakci s nim. Zatim tam budeme mít jenom to generování pseudonáhodný mapy ale v příštích krocích si tam budeme přidávat duchy, bonusy a možná ňáký další jiný herní voběkty (to generování mapy je spíš dočasný řešení by sme si tu hru vyzkoušeli, jestli se vám tendlecten duchobijeckej blogísek bude líbit tak si spíš v ňákým možným pokračování ukažeme supr editor dlaždicovejch světů tiled)
Takže si vyrobíme v tý zdrojáčkový složce 'src' novej soubor 'level.h' a nacpem tam todle:
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" // maximální vejška mapy (hodnota je v počtu kostek dlaždicový mapy) #define MAPA_MAX_VYSKA 10 // struktura levelu // zatim tam máme jenom tu mapu typedef struct Level { Mapa * dlazdicova_mapa; } Level; // pomocná struktura pro generování mapy // pude popisovat dílčí kousek ze kterejch budeme skládat tu svou náhodnou mapu // bude to vlastně takový jakoby dvourozměrný pole // (je to nadefinovaný jako jednorozměrný, ukazatelovou aritmetikou s nim budem pracovat jako s dvourozměrným ) typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { // alokujem si tu strukturu kterou chcem jakože vracet Level * lvl = (Level * )malloc ( sizeof ( Level ) ); // a mapu kterou do ní nacpem // (pozor, nehlídám selhávání alokace :O :O správně by sme měli uklízet alokovanou paměť když třeba selže až ten druhej malloc ) Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); // enum který nám bude popisovat jednotlivý prvky mapy, zsatim tám máme jenom 'N' jakože nic a 'B' jakože blok enum herniVec {N,B}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // (sem to napsala takle 'dvourozměrně' na řádky by to bylo líp čitelný) // placka int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; // taková malinkatá pyramidka int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; // díra v zemi // na ni si vyzkoušíme jak to vypadá když David spadne do ďoury a jak se při tom chová kamera int seg3 [] = { 0,0,0,0,0, B,0,0,0,B, }; // vyrobíme si ty pomocný struktury (vicemeně jenom by sme měli někde poznamennanou vejšku a šiřku těch segmentů, // páč nemusej bejt dycky stejně velký, jak to máme zrovna teďko) SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {5,2,seg3}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); } // prvních a posledních deset sloupečků herní mapy bude placka, // na začátku potřebujem dát hráčoj trošku volnýho místa by pak nespadnul rovnou někam do boje třeba, // na konci si pak uděláme ňákou specialitku která bude voznačovat konec mapy for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } // budeme postupně do mapy kopírovat náhodně vybraný segmenty pěkně v řadě za sebou, // na to si potřebujem měřit zbejvající místo v mapě int zbyva_delka = sirka - 10; // dokud je zbejvajicí dýlka věčí než 15 // k tomudle čislu sme došli tim, že chceme vynechat posledních 10 sloupců mapy a pětka je // nejmenčí možná dýlka náhodnýho segmentu. Takže dokavaď je dýlka rovna věčí patnácti, furt de // vygenerovat náhodnej segment aniž by sme vlezli do tý vyhrazený zóny na konci mapy while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment // (raylibová fuknce 'GetRandomValue' vrací celý čislo v rozsahu svýho prvního a druhýho argumentu(inkluzivně)) int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; // pokud sme náhodou trefili moc dlouhej segment kterej se nám už na mapu nevejde (aniž by sme vlezli // do tý zóny na konci), tak si zkusíme vybrat jinej náhodnej segment if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat // zatim tam máme jenom ten blok mapy switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; default: break; }; } } // vod zbejvajicí dýlky vodečtem dýlku segmentu kterej sme tam nacpali zbyva_delka-=sirka_segmentu; } // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; return lvl; } // až si s levelem přestanem hrát, tak musíme alokovanou paměť po sobě uklidit // (a alokovanou pamět se musí dycky pak volat free()) void freeLevel ( Level * lvl ) { for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); free ( lvl ); } void aktualizovatLevel ( Level * lvl, David * david, float dt ) { // zatim eště nemáme co aktualizovat } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { // minimální a maximální vykreslovanou vzdalenost dlaždicový mapy spočitáme z kamery // kamera kouká někam doprostředka vobrazovky, takže když k tomu přičtem půlku šířky vobrazovky // na vobě strany, tak si krásně vymezíme v dlaždicový mapě voblast vobrazovky // (vodtransformováváme hodnotu z kamerovýho světa, proto to eště dělíme hodnotou zoomu) float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); } #endif
Pochopytelně si zase jakoby musíme upravit 'main.c', musíme si tam ten level vyrobit, mimojiný si taky musíme inicializovat generator náhodnejch čisel (raylib má svou vlastní funkci na generování pseudonáhodnejch čisel):
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // nastavíme mapu na tu skovanou ve struktuře levelu .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidův vnitřní časovač střílecího cooldownu .strileci_cooldown = 0.0f, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu // (jsou vo trošku menčí než vokraje voblasti framu animace) david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; // aktualizujem Davida aktualizovatDavida(&david, dt); // aktualizujem level aktualizovatLevel ( level, &david, dt ); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); return 0; }
Aby Davidoj nebylo na tý mapě smutno, tak mu tam přidáme duchy.
Ve spritesheetu máme hnedka několik různejch duchů (nejsou jim vidět nohy páč to sou duchové hele a maj červeně sviticí voči protože sou zlí). Z tohodlectoho duchovního spritesheetu vybereme náhodnej vobrázek a to bude jakože ten náš duch. Duch bude jezdit po dlaždicový mapě a když narazí na překážku (což muže bejt jak blok v cestě nebo ďoura v zemi) tak se votočí a pojede na vopačnou stranu. Trochu zajimavější to eště možná bude když duchům přidáme i ňákou malou šanci na děsně překvapivou změnu směru.
Duchové budou mit nějakej počet životů a dycky když ňákýho ducha král David trefí, tak duchoj jeden život vodečtem. Noa když duchoj životy dojdou, tak asi zahrajeme nějakej chcípací zvuk a uděláme nějakej jednoduchej chcípací efekt, celkem srandovní mi přišlo že se duch roztočí na mistě a pomaličku zmizne 😁 😁.
Že David ducha trefil poznáme tak, že Davidův projektil bude v ňáký vobdelnikový voblasti voběktu ducha. Mohli by sme použít vobdelnik 'okraje', jenže to by sme detekovali zásach ducha vo hodně dřív, než by se Davidova kulka vubec ducha dotkla, takže si nadefinujeme kromě vokrajů ještě jeden vobdelnik, 'hitbox'. Bude to jakoby taková jakože podvoblast vobrázku ducha, kterou když trefíme tak až todlecto budem považovat za zásach ducha (hitboxy vykreslujem červeně když si nazačátku souboru main.c vodkomentujete to '#define DEBUG' tak je uvidite)
Duchové taky budou po davidoj střílet vočima, takže každej duch bude mit podobně jako David nějakej zásobnik střel. Budou to takový malý červený softwérový kuličky který budem z duchů vypouštět někde na urovni jejich xiftů, podobně jako sme vypouštěli softwérový kuličky z tý Davidovy pistolky.
Takže si vyrobíme ve zdrojákový složšce 'src' soubor 'duch.h' a napišem tam všecko todlenc co sme si řekli:
#ifndef _DAVID_A_DUCHOVE_DUCH_H_ #define _DAVID_A_DUCHOVE_DUCH_H_ #include <stdlib.h> #include <raylib.h> #include "mapa.h" #include "projektily.h" // vezmem si z 'main.c' texturu spritesheetu duchů a zvuky extern Texture2D textura_duchove_spritesheet; extern Sound zvuk_duch_chcip; extern Sound zvuk_duch_strela; // maximalni počet životů ducha #define HITPOINTU_DUCHA 3 // čas v sekundách jak dlouho duch chcípe #define CHIPACI_CAS_DUCHA 1.5f // kolik má duch střel #define ZASOBNIK_STREL_DUCHA 3 // střílecí cooldown ducha, vlastně to střílení je uplně identicky řešený jako v davidoj :O ;D #define STRILECI_COOLDOWN_DUCHA 1.0f // rychlost vypálenejch projektilů ducha #define RYCHLOST_STRELY_DUCHA 600.0f //rychlost chůze ducha #define RYCHLOST_CHUZE_DUCHA 120.0f // šířka a vejška jednoho 'podvobrázku' v spritesheetu #define DUCH_F_VYSKA 240.0f #define DUCH_F_SIRKA 85.0f typedef struct Duch { // směr kterým duch kouká, taky identicky řešený jako v davidoj int smer; // hp jakože hitpointy jakože počet životů int hp; // oblast textury, jakože kterej kousek z toho spritesheetu s duchama jsme vybrali Rectangle oblast_textury; // okraje ducha, podobně jako u davida // (duchoj vodpovídaj vokraje velikosti textury, to u davida neplatí) Rectangle okraje; // hitbox, voblast která definuje 'zranitelnou' část vobrázku ducha // bude trochu tenčí než 'okraje', by sme měli zásah až když bude kulka fakt někde nad // viditelnou částí vobrázku // a pak taky trošku nad zemí (ta poloprusvitná část vobrázků duchů bude jakoby nezranitelná) Rectangle hitbox; // jestli je duch aktivní // pokud neni, tak ho nebudem vykreslovat ani aktualizovat, prostě bude upně vyplej bool aktivni; // jestli duch chícpe, jakože mu už došly hitpointy a máme za bool chcipe; // vnitřní čas ducha float relativni_cas; // uhel ducha pro animaci efektu chcípání float uhel; // střílecí cooldown ducha float strileci_cooldown; // zásobnik střel ducha Strela * strely; // zvuky ducha // pozor budou to 'aliasy' těch zvuků,povim v 'konstruktoru' Sound zvuk_chcipnuti; Sound zvuk_strela; } Duch; // takovej jakože pseudokonstruktor instace ducha // bere jedinej argument, pozici bodu kde ducha jakoby vyrobíme //pozor, používá malloc, takže se pak musí udělat na každým duchoj freeDucha() Duch * vygenerovatDucha ( Vector2 kde ) { // alokujem si ducha, kterýho pak budeme v připadě uspěšnýho vyrobení z konstrukoru vracet // (když se něco pokazí, vracíme nulovou adresu, prostě nulu) Duch * duch = calloc ( 1, sizeof ( Duch ) ); if(!duch)return NULL; // počáteční směr ducha bude náhodnej duch->smer = GetRandomValue( 0,1 ) ? 1 : -1; // vokraje určíme z počáteční polohy a rozměrů podvobrázku spritesheetu duch->okraje = ( Rectangle ) { kde.x,kde.y,DUCH_F_SIRKA,DUCH_F_VYSKA }; // podobně si určíme hitbox, bude ale votrošku menčí než okraje a bude i trošku kratčí na vejšku // (kuli těm nezranitelnejm nohoum duchů) duch->hitbox = ( Rectangle ) { kde.x+15,kde.y+15,55,155 }; // oblast textury určíme náhodně, vybereme uplně náhodnej podvobrázek duch->oblast_textury = ( Rectangle ) { DUCH_F_SIRKA * GetRandomValue( 0,5 ),0 + DUCH_F_VYSKA * GetRandomValue( 0,1 ),DUCH_F_SIRKA * duch->smer,DUCH_F_VYSKA }; // aktivujem ducha a nastavíme mu hitpointy na vychozí hodnotu duch->aktivni = true; duch->hp = HITPOINTU_DUCHA; // a nakopírujem si zvuky tim, že si vyrobíme jejich alias // Potíž s normálníma zvukama v raylib je v tom, že dycky máme jakoby jenom jednu přehratelnou instanci. U voběktu/struktury davida // todle neni moc vekej problém, páč Davida máme na mapě jenom jednoho a ten jeho kod vubec moc nepřipouští situace žeby David měl // vydávat ňákej stenej zvuk rychle posobě. Duchů ale budem mit na mapě hodně a šance na tudle kolizi přehrávání je vyrazně věčí, // třeba zvuk chcípání ducha je dost dlouhej a je možný že ho budem chtít začít přehrávat ve stejným vokamžiku, // kdy ho už ňákej jinej chcipajicí duch hraje. Stalo by se nejspiš to že by to stoplo předchazejicí přehrávání a začalo by to hrát // stejnej zvuk vodzačátku. // Na todlecto chytrý lidi vod raylibu naštěstí mysleli a přidali možnost vyrábění aliasu zvuků, prostě si jakoby vyrobíme kopii toho zvuku // (pod kapotou nejspíš ty kopie a voriginál furt sdílej stejný zdrojový data zvuku, jen si k tomu každá kopie asi jakoby pamatuje jinej // balast vokolo, jakože čas přehrávání, nastavení hlasitosti etc) // takže si vyrobíme ty aliasy zvuků duch->zvuk_chcipnuti = LoadSoundAlias ( zvuk_duch_chcip ); duch->zvuk_strela = LoadSoundAlias ( zvuk_duch_strela ); // nakonec alokujeme střely // (děláme to stejně jako se střelama pro Davida) duch->strely = calloc ( ZASOBNIK_STREL_DUCHA, sizeof ( Strela ) ); if(!duch->strely) { free(duch); return NULL; } for ( size_t i =0; i<ZASOBNIK_STREL_DUCHA; i++ ) { duch->strely[i].druh = strela_ducha; } return duch; } // funkce na vykreslování ducha void vykreslitDucha ( Duch * duch ) { // ducha budem vykreslovat jenom když je aktivní if ( !duch->aktivni ) { return; } // jestli duch chcípe, tak budem vykreslovat ten efekt umiraní if ( duch->chcipe ) { // vyrobíme si kopii vokrajů, // převedem si progress chcípání do rozsahu vod nuly do jedničky a invertujem to // noa tim budem scvrkávat velikost tý kopie vobdélnika vokrajů Rectangle _okraje = duch->okraje; const float chcipani = 1.0f - duch->relativni_cas/CHIPACI_CAS_DUCHA; _okraje.x += _okraje.width/2; _okraje.y += _okraje.height/2; _okraje.width *= chcipani; _okraje.height *= chcipani; // budeme texturu vykreslovat do tý scvrklý kopie vokrajů a střed textury posunem z defaultního // levýho horního rohu doprostředka. To děláme proto, že nastavujeme i úhel náklonu vykreslovaný textury noa // chcem, by střed rotace nebyl někde v tom rohu vobrázku, ale pěkně uprostřed // barvu textury budem nastavovat pomocí raylibový funkce 'Fade', ta nám umožňuje měnit alfa složšku barvy (pro nás // teďko alfa znamená 'průsvitnost' resp. 'neviditelnost' tý barvy) ňákou hodnotou v rosahu nula až jedna // (duch se bude jakoby postupně zneviditelňovat jak bude chcípat) DrawTexturePro ( textura_duchove_spritesheet,duch->oblast_textury,_okraje, ( Vector2 ) { _okraje.width/2.0f,_okraje.height/2.0f}, duch->uhel, Fade ( WHITE, 1.0f - duch->relativni_cas/CHIPACI_CAS_DUCHA ) ); } else { // když duch nechcípe, vykreslíme ho uplně normálně DrawTexturePro ( textura_duchove_spritesheet,duch->oblast_textury,duch->okraje, ( Vector2 ) {0,0},0.0f, WHITE ); } #ifdef DEBUG // v připadě debugovávání vykreslíme okraje a hitbox DrawRectangleLines ( duch->okraje.x,duch->okraje.y, duch->okraje.width, duch->okraje.height,GREEN ); DrawRectangleLines ( duch->hitbox.x,duch->hitbox.y, duch->hitbox.width, duch->hitbox.height,RED ); #endif } // funkce na aktualizovávání ducha // jako argumenty bere pochopytelně ducha, pak mapu ve který se duch jakože pohybuje noa pak vobligátní časovou deltu void aktualizovatDucha ( Duch * duch, Mapa * mapa, float dt ) { // když neni aktivní, tak ho nebudem aktualizovat if ( !duch->aktivni ) { return; } // jestli eště duch nechcípe ale má hitpointy na nule, // tak mu zapnem chcípání a začnem přehrávat zvuk chcípání if ( !duch->chcipe && duch->hp <= 0 ) { duch->chcipe = true; PlaySound ( duch->zvuk_chcipnuti ); } // jestli duch chcípe, tak mu začnem měřit relativní čas, by sme věděli jestli už uplynula doba // potřebná na uplný chpípnutí ducha. Až ta doba uplyne, tak ducha uplně vypnem (nastavíme mu atribut 'aktivní' na false) if ( duch->chcipe ) { duch->relativni_cas += dt; // eště musíme při tom chcípání furt upravovat tu rotaci vykreslovaný textury // myslimže to bude takový zajimavější když se rychlost rotace bude s uplynulým časem furt zvěčovat duch->uhel+= dt * ( 500 + 250 * duch->relativni_cas ); if ( duch->relativni_cas > CHIPACI_CAS_DUCHA ) { duch->aktivni = false; } //noa jestli duch chcípe, tak už nás nezajímá žádný další dělání v tý jeho aktualizční funkci, už nikam nebude chodit ani střílet, // prostě z týdle funkce vyskočíme returnem return; } // malá pravděpodobnost že duch nečekaně změní směr if ( GetRandomValue ( 0,1000 ) == 1 ) { duch->smer*=-1; // překlopíme šiřku zdrojový textury, by se nám vykreslovala zrcadlově vobráceně // (podobně sme ďáli už v 'animace.h') duch->oblast_textury.width *= -1.0f; } // uplně stejně jako u davida budem počitat prepozici Rectangle prepozice = duch->okraje; // když de z leva doprava if ( duch->smer == 1 ) { // posunem prepozici vo rychlost ducha krát časová delta prepozice.x += RYCHLOST_CHUZE_DUCHA * dt; // a eště si spočitáme roh pravýho dolního vokraje prepozice a posunem ho // vo nějakejch pět pixelů dolu. Timdle bodem se budeme koukat, jestli má prepozice // pod sebou pevnou kostku mapy, noa jestli ne duch přišel na vokraj plošiny :O ;D Vector2 bod = ( Vector2 ) { prepozice.x + prepozice.width, prepozice.y + prepozice.height +5 }; // kouknem jestli nenastává kolize prepozice s blokem mapy, nebo jestli prepozice nekončí nad ďourou // v tom připadě votočíme ducha a příští aktualizací pude duch vopačným směrem if ( kolizeRectSeBlokemMapy ( prepozice, mapa ) || ! kolizeSeBlokemMapy_bod ( bod,mapa ) ) { duch->smer *= -1; duch->oblast_textury.width *= -1.0f; } else { // jinak posunem ducha na prepozici duch->okraje.x += RYCHLOST_CHUZE_DUCHA * dt; duch->hitbox.x += RYCHLOST_CHUZE_DUCHA * dt; } // když de z prava doleva // vlastně to je zase uplně to samý jako pro chození z leva doprava, jenom na vopačnou stranu :D ;D } else if ( duch->smer == -1 ) { prepozice.x -= RYCHLOST_CHUZE_DUCHA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, mapa ) || ! kolizeSeBlokemMapy_bod ( ( Vector2 ) { prepozice.x, prepozice.y + prepozice.height +5 },mapa ) ) { duch->smer *= -1; duch->oblast_textury.width *= -1.0f; } else { duch->okraje.x -= RYCHLOST_CHUZE_DUCHA * dt; duch->hitbox.x -= RYCHLOST_CHUZE_DUCHA * dt; } } // střílení je vlastně taky uplně stejný jako u davida, taky se počitá střilecí colldown, taky se vybírá // volná neaktivní střela, taky se recykluje, taky se ji nastavuje ňáká rychlost etc if ( duch->strileci_cooldown > 0.0f ) { duch->strileci_cooldown-=dt; } else { // jenom tady nemáme to chování nijak hráčem vovládaný // prostě budem střílet náhodně když uplyne cooldown tak budeme skoušet trefovat náhodný čislo tak dlouho // až nám padne naše vybraný noa v ten vokamžik duch zkusí vystřelit if ( GetRandomValue ( 0,200 ) == 1 ) { for ( size_t i =0; i<ZASOBNIK_STREL_DUCHA; i++ ) { if ( !duch->strely[i].aktivni ) { // vyrobíme si střelu a nakopírujem jeji hodnoty na vodpovidajicí misto v zasobniku Strela s = { .druh = strela_ducha, .rychlost = RYCHLOST_STRELY_DUCHA * ( float ) duch->smer, .pozice = ( Vector2 ) { duch->smer==1? duch->okraje.x+DUCH_F_SIRKA/2.0f: duch->okraje.x,duch->okraje.y + 25}, .relativni_cas = 0.0f, .doba_zivota = 2.0f, .aktivni=true, }; duch->strely[i] = s; // zahrajeme zvuk výstřelu, nastavíme střílecí čekací čas a řerušíme hledací for cyklus PlaySound ( duch->zvuk_strela ); duch->strileci_cooldown = STRILECI_COOLDOWN_DUCHA; break; } } } } } // něco jako destruktor ducha // musíme samozdřejmě uvolnit alokovaný struktury // a taky aliasy zvuků void freeDucha ( Duch * duch ) { UnloadSoundAlias ( duch->zvuk_chcipnuti ); UnloadSoundAlias ( duch->zvuk_strela ); free( duch->strely ); free ( duch ); } #endif
Duchové uměj střílet, takže si upravíme soubor 'projektily.h' a přidáme si tam novej druh střely a jeho vykreslování:
#ifndef _DAVID_A_DUCHOVE_PROJEKTILY_H_ #define _DAVID_A_DUCHOVE_PROJEKTILY_H_ #include <raylib.h> #include "mapa.h" // přidáme si sem střelu duchů enum druh_strely {strela_davida,strela_ducha}; typedef struct Strela { // rychlost střely float rychlost; // relativní čas střely float relativni_cas; // nastavená maximální doba života střely // když relativní čas překročí tudle hodnotu tak střelu deaktivujem a budem považovat za zničenou float doba_zivota; enum druh_strely druh; // pozice střely Vector2 pozice; // zda je střela aktivní // pokud aktivní neni považujem ji za zničenou a/nebo nevystřelenou, takovou střelu nebudeme // vykreslovat ani aktualizovat bool aktivni; } Strela; void aktualizovatStrelu ( Strela * strela, Mapa * mapa, float dt ) { strela->relativni_cas += dt; if ( strela->relativni_cas > strela->doba_zivota ) { strela->aktivni = false; } else { strela->pozice.x += dt * strela->rychlost; // pokud dojde ke srážce střely s blokem mapy, tak střelu deaktivujem if ( kolizeSeBlokemMapy_bod ( strela->pozice, mapa ) ) { strela->aktivni = false; } } } void vykreslitStrelu ( Strela * strela ) { if ( !strela->aktivni ) { return; } switch ( strela->druh ) { case strela_davida: DrawCircleGradient ( strela->pozice.x,strela->pozice.y,5.0f,WHITE,DARKGRAY ); break; // přidáme si sem střelu duchů // bude to taky taková červená softwérová kulička case strela_ducha: DrawCircleGradient ( strela->pozice.x,strela->pozice.y,5.0f,RED,Fade(RED,0.8f) ); break; default: break; } } #endif
Uravíme soubor 'level.h' a přidáme si tam generování a aktualizaci duchů, vodteďka tam bude bydlet vzajemná interakce herních věcí s Davidem:
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" #include "duch.h" // zvuk zásahu // (je hrozně moc krátkej tak si myslim že asi jako nemusíme mit alias) extern Sound zvuk_zasah; // maximální vejška mapy (hodnota je v počtu kostek dlaždicový mapy) #define MAPA_MAX_VYSKA 10 // minimální nutná vzdálenost herních entit vod polohy Davida abysme je vykreslovali a aktualizovali // (herní svět muže bejt děsně velkej a bylo by zbytečný aktualizovat a vykreslovat věci který sou vod hráče moc daleko, // takže je vlastně ani eště nemůže vidět ani s nima iteragovat ňák) #define NUTNA_VZDALENOST (BLOK_SIRKA * 20) // struktura levelu typedef struct Level { // pole duchů // (budem to použivat jako jednorozměrný pole ukazatelů na jednotlivý instance duchů) Duch ** duchove; // kolik je v poli duchů size_t pocet_duchu; Mapa * dlazdicova_mapa; } Level; // pomocná struktura pro generování mapy // pude popisovat dílčí kousek ze kterejch budeme skládat tu svou náhodnou mapu // bude to vlastně takový jakoby dvourozměrný pole // (je to nadefinovaný jako jednorozměrný, ukazatelovou aritmetikou s nim budem pracovat jako s dvourozměrným ) typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { Level * lvl = (Level * )malloc ( sizeof ( Level ) ); Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); // alokujeme si pole duchů // připravíme si prostor pro 256 duchů, páč ještě nevíme kolik jich budem vyrábět // zbytečný místo pak ucvaknem // (určitě by šlo vodhadnout worst case scenario z dýlky levelu ale na to kadí dalmatýn) Duch ** duchove = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); // enum který nám bude popisovat jednotlivý prvky mapy, // 'N' jakože nic, 'B' jakože blok, 'D' jakože duch enum herniVec {N,D,B}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // přidáme si tam ňáký segmenty s duchama int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg3 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,B,0,D,0, 0,D,0,B,0, B,B,B,B,B, B,B,B,B,B, }; int seg4 [] = { 0,0,0,0,0, B,0,0,0,B, }; int seg5 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,D,D, D,D,0,0,0, 0,0,B,B,B, B,B,B,0,0, B,B,B,B,B, B,B,B,B,B, }; SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {10,5,seg3}, ( SegmentMapy ) {5,2,seg4}, ( SegmentMapy ) {10,5,seg5}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); } // prvních a posledních deset sloupečků herní mapy bude placka, // na začátku potřebujem dát hráčoj trošku volnýho místa by pak nespadnul rovnou někam do boje třeba, // na konci si pak uděláme ňákou specialitku která bude voznačovat konec mapy for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } // budeme postupně do mapy kopírovat náhodně vybraný segmenty pěkně v řadě za sebou, // na to si potřebujem měřit zbejvající místo v mapě int zbyva_delka = sirka - 10; size_t duchu = 0; // dokud je zbejvajicí dýlka věčí než 15 // k tomudle čislu sme došli tim, že chceme vynechat posledních 10 sloupců mapy a pětka je // nejmenčí možná dýlka náhodnýho segmentu. Takže dokavaď je dýlka rovna věčí patnácti, furt de // vygenerovat náhodnej segment aniž by sme vlezli do tý vyhrazený zóny na konci mapy while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment // (raylibová fuknce 'GetRandomValue' vrací celý čislo v rozsahu svýho prvního a druhýho argumentu(inkluzivně)) int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; // pokud sme náhodou trefili moc dlouhej segment kterej se nám už na mapu nevejde (aniž by sme vlezli // do tý zóny na konci), tak si zkusíme vybrat jinej náhodnej segment if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; case D: //vyrobíme na tý pozici ducha { // pozice bude souřadnice bloku minus vejška dvou bloků a chlup (chlup by se nám duch neprolínal s blokama mapy) Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA - 161, }; Duch * duch = vygenerovatDucha ( pozice ); // strčíme ducha do toho našeho zasobniku a počítadlo duchů pak zvednem vo jedničku duchove[duchu++] = duch; } break; default: break; }; } } // vod zbejvajicí dýlky vodečtem dýlku segmentu kterej sme tam nacpali zbyva_delka-=sirka_segmentu; } // realokujem pole duchů, máme tam zabranýho víc místa než kolik sme relalně duchů alokovali, // určitě sme jich nevyrobili 256 :D ;D duchove = realloc ( duchove, sizeof ( Duch * ) * duchu ); //nacpem duchy do tý struktury levelu lvl->pocet_duchu = duchu; lvl->duchove = duchove; // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; return lvl; } void freeLevel ( Level * lvl ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { freeDucha ( lvl->duchove[i] ); } free ( lvl->duchove ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); free ( lvl ); } void aktualizovatLevel ( Level * lvl, David * david, float dt ) { // teďko už máme co aktualizovat :D ;D // ukazatel na davidovy střely Strela * strely = david->strely; // každou aktualizací projdem pole duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { // a kouknem, jestli je duch dostatečně blízko k hráčoj, abysme s duchem ztráceli // čas a aktualizovali ho. Pokud je iksová vzdálenost Davida a Ducha věčí než dýlka dvaceti kostek, // tak ducha přeskočíme a pokusíme se aktualizovat dalšího // (tamto 'fabsf' je absolutní hodnota která bere i vrací float, použiváme ji na to měření vzdáleností) if(fabsf ( david->pozice.x - lvl->duchove[i]->okraje.x ) > NUTNA_VZDALENOST) continue; // pokud je duch dostatečnš blízko tak se kouknem jestli nechcípe.... if ( ! lvl->duchove[i]->chcipe ) // noa jestli eště nechcípe, tak postupně projdem všecky Davidovy projektily a kouken jestli nááhodou některá // z nich neleží ve voblasti hitboxu ducha (zistíme funkcí 'CheckCollisionPointRec' z raylibu) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->duchove[i]->hitbox ) ) { // noa jestli v tom hitboxu některá z aktivních střel skutečně je, tak ji 'zničíme' tim // že ji deaktivujem s->aktivni = false; // noa pak duchoj vodečtem hitpoint a zahrajem zvuk zásahu lvl->duchove[i]->hp--; PlaySound ( zvuk_zasah ); } } } // aktualizujem každýho ducha kterej je dostatečně blízko.... aktualizovatDucha ( lvl->duchove[i], lvl->dlazdicova_mapa, dt ); // ....a každýmu duchoj eště aktualizujem všecky střely for ( size_t j=0; j<ZASOBNIK_STREL_DUCHA; j++ ) { Strela * strela_ducha = &lvl->duchove[i]->strely[j]; if ( strela_ducha->aktivni ) { aktualizovatStrelu ( strela_ducha,lvl->dlazdicova_mapa, dt ); } } } } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); // vykreslíme všecky viditelný duchy a všecky střely duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if ( fabsf ( kamera->target.x - lvl->duchove[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->duchove[i] ); } for ( size_t j =0; j<ZASOBNIK_STREL_DUCHA; j++ ) { vykreslitStrelu ( lvl->duchove[i]->strely + j ); } } } #endif
Noa nakonec si v 'main.c' načtem nový zvuky a spritesheet duchů:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // nastavíme mapu na tu skovanou ve struktuře levelu .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidův vnitřní časovač střílecího cooldownu .strileci_cooldown = 0.0f, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu // (jsou vo trošku menčí než vokraje voblasti framu animace) david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; // aktualizujem Davida aktualizovatDavida(&david, dt); // aktualizujem level aktualizovatLevel ( level, &david, dt ); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); return 0; }
Máme duchy a mužeme je postřílet, jenom to asi jakoby nebude moc velká zábava když je David nezranitelnej 🙄 😜 Uděláme to s Davidem jako s duchem, nastavíme mu nějakej počet životu a přidáme mu hitbox. A když se duchové do Davida trefjej (střela ducha zasáhne hitbox) nebo se Davida dotknou (hitbox ducha má kolizi s hitboxem Davida), tak mu vodečtem život (David zatim eště nebude nijak umírat, budem zatim jenom vodečítat ty životy).
Aby zásah nepřítelem Davida hnedka nezabil a nesebral mu všecky životy, tak mu dycky nastavíme krátkej čas nezranitelnosti. V různejch hrách co sem viděla se přitom postavička rozbliká a dokavaď je postavička nezranitelná tak furt bliká. Tendle koncept vykradem a uděláme by nám David při zranění blikal.
Nóó takže si upravíme kód Davida v 'david.h' apřidáme mu tam tu nezranitelnost a blikání:
#ifndef _DAVID_A_DUCHOVE_DAVID_H_ #define _DAVID_A_DUCHOVE_DAVID_H_ #include "animace.h" #include "mapa.h" #include "projektily.h" extern Sound zvuk_kroku; extern Sound zvuk_skoku; extern Sound zvuk_vystrel; // načtem si zvuk pádu kterej budem hrát dycky když nám David zahučí někam do ďoury v zemi extern Sound zvuk_padu; #define DAVID_F_SIRKA 150.0f #define DAVID_F_VYSKA 240.0f Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; #define RYCHLOST_CHUZE_DAVIDA 350.0f #define RYCHLOST_SKOKU_DAVIDA -15.0f #define GRAVITACE_DAVIDA 40.0f #define RYCHLOST_STREL_DAVIDA 800.0f #define STRILECI_COOLDOWN_DAVIDA 0.25f #define POCET_STREL_DAVIDA_MAX 32 // přidáme si sem maximální počet životů/hitpointů davida #define POCET_ZIVOTU_DAVIDA_MAX 10 // a blikací čas (v sekundách) #define BLIKACI_CAS_DAVIDA 0.5f typedef struct David { Animace animace_beh; Animace animace_idle; Animace animace_sed; Animace animace_skok; Animace * aktualni_animace; Vector2 pozice; Rectangle okraje; // david bude mit vodteď hitbox vymezujicí jeho zranitelnou voblast, podobně jako duchové Rectangle hitbox; int smer; Mapa * mapa; bool zdaSkace; float vertikalni_rychlost; Strela * strely; float strileci_cooldown; // david bude mit eště další nový tři atributy // aktuální počet životů int zivoty; // zbejvajicí čas blikání (dočasný nezranitelnosti po zásahu) float blikaci_cas; // a boolean jestli je právě teďko zranitelnej nebo ne bool zranitelny; } David; void aktualizovatDavida ( David * david, float dt ) { if ( IsKeyDown ( KEY_UP ) ) { if ( david->aktualni_animace == &david->animace_sed ) { david->aktualni_animace->pauznuta = false; david->aktualni_animace->reverzne=true; if ( david->aktualni_animace->index == 0 ) { david->aktualni_animace = & david->animace_idle; } } else if ( !david->zdaSkace ) { PlaySound ( zvuk_skoku ); david->zdaSkace = true; david->animace_skok.index = 0; david->vertikalni_rychlost = RYCHLOST_SKOKU_DAVIDA; } } // když hráč zmáčkne mezernik, tak se David pokusí vystřelit z tý svý pistolky if ( IsKeyDown ( KEY_SPACE ) ) { // vystřelit mužeme jenom když uplynula čekací doba mezi výstřelama resp. střílecí cooldown je menší nebo rovnej nule if ( david->strileci_cooldown <= 0.0f ) { // projdeme si celej davidův zásobník a pokusíme se v něm najít střelu, kterou budeme moct použít // to poznáme tak, že bude mit atribut aktivní nastavenej na nulu for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { if ( !david->strely[i].aktivni ) { // pokud sme takovou střelu našli, tak si vybereme pro upravování atributů Strela * s = david->strely + i; // nejdřiv ji nastavíme novou polohu, //to znamená přibližně někam na konec hlavně tý pistolky co má david v rukou //nj jenže david muže stát, sedět na zemi, muže koukat z prava doleva, nebo muže dokonce právě vstávat ze země // všecky tydlecty eventualitky jakoby musíme pokrejt if ( david->aktualni_animace != &david->animace_sed ) s->pozice = ( Vector2 ) { // střelu umisťujem podle toho jakým směrem david kouká david->smer==1? david->pozice.x+DAVID_F_SIRKA - 25: david->pozice.x+25, david->pozice.y + DAVID_F_VYSKA/2 - 36 }; else { // pokud david má jako aktuální animaci sedání, tak si zistíme index a vo ten budeme posouvat iksovou a ypsilonovou // souřadnici střely. Neni to uplně přesný ale na to nikdo koukat nebude :D int index = david->animace_sed.index; s->pozice = ( Vector2 ) { david->smer==1? david->pozice.x+DAVID_F_SIRKA - 25 - index*5: david->pozice.x+25 + index*5,david->pozice.y + DAVID_F_VYSKA/2 - 26 - 8 + index*14 }; } //polohu máme, teďko nastavíme další atributy střely // nastavíme střele rychlost, zohledníme i směr kterým poletí, ten vodpovídá // směru kterým David právě teďko kouká s->rychlost = RYCHLOST_STREL_DAVIDA * ( float ) david->smer; // vynulujem relativní čas s->relativni_cas = 0.0f; // nastavíme dobu života třeba na čtyry vteřiny s->doba_zivota = 4.0f; // a aktivujem s->aktivni=true; // střelu máme upravenou, ukazatel už nepotřebujem s=0; // ..a když sme aktivovali střelu, tak sme vlastně vystřelili, takže nastavíme střílecí čekací dobu // na maximální hodnotu david->strileci_cooldown = STRILECI_COOLDOWN_DAVIDA; // zahrajem zvuk výstřelu PlaySound ( zvuk_vystrel ); // a přerušíme hledací for cyklus break; } } } // eště upravíme aktuální animaci, pokud to neni animace sedání, tak přepnem animaci na idle na první snímek, // páč při tý animaci david hejbe pistolkou a vypadalo by to divně kdyby z ní vylítla vodorovně střela. Pokud david skáče, // běží nebo padá, tak se nám ta animace stejně přepne někde dál tady ve zdrojáčku týdle funkce // (při animaci běhu/skoků todle nepřectavuje problém, pistolka je na všech vobrázcích spritesheetu ve stejný vejšce, // kromě tý animace 'idle', tam je ve stejný vejšce právě jenom na prvním snimku) if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; david->aktualni_animace->index = 0; david->aktualni_animace->relativni_cas = 0.0f; } } if ( IsKeyDown ( KEY_LEFT ) && david->pozice.x > 0 && david->aktualni_animace != &david->animace_sed ) { david->smer = -1; Rectangle prepozice = david->okraje; prepozice.x -= RYCHLOST_CHUZE_DAVIDA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; // aktualizujem pozici hitboxu david->hitbox.x = david->okraje.x + 10; david->aktualni_animace = &david->animace_beh; } } else if ( IsKeyDown ( KEY_RIGHT ) && david->pozice.x < david->mapa->sirka*BLOK_SIRKA - DAVID_F_SIRKA && david->aktualni_animace != &david->animace_sed ) { david->smer = 1; Rectangle prepozice = david->okraje; prepozice.x += RYCHLOST_CHUZE_DAVIDA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; // aktualizujem pozici hitboxu david->hitbox.x = david->okraje.x + 10; david->aktualni_animace = & david->animace_beh; } } // jestli je máčkutej na klávesnici čudlik šipky dolu, tak začnem přehrávat animaci sednutí // si na zadek else if ( IsKeyDown ( KEY_DOWN ) ) { david->aktualni_animace = & david->animace_sed; david->aktualni_animace->reverzne = false; david->aktualni_animace->pauznuta = false; } else if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; } Rectangle prepozice = david->okraje; prepozice.y += 5; if ( ! kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->zdaSkace = true; } if ( david->zdaSkace ) { david->vertikalni_rychlost += dt * GRAVITACE_DAVIDA; Rectangle prepozice = david->okraje; prepozice.y += david->vertikalni_rychlost; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->vertikalni_rychlost = 0.0f; david->zdaSkace = false; david->okraje.y = ceil ( ( david->okraje.y + david->okraje.height ) /BLOK_VYSKA ) * BLOK_VYSKA - david->okraje.height - 1; if ( david->aktualni_animace == &david->animace_beh ) { david->aktualni_animace->index = david->animace_skok.index +5; david->aktualni_animace->reverzne = david->animace_skok.reverzne; } } else { david->aktualni_animace = & david->animace_skok; david->okraje.y += david->vertikalni_rychlost; } // aktualizujem po dopadu taky hitbox david->hitbox.y = david->okraje.y + 12; david->pozice.y = david->okraje.y - 3; } david->aktualni_animace->zrcadlit = david->smer<0; if ( david->aktualni_animace == &david->animace_sed ) { // pokud má David jako aktualní animaci sedání, tak mu musíme nastavit hitbox podle jednotlivejch framů tý animace // asi to takle nebude uplně přesný, nicmeně pro náš učel to je dostatečně přesný, schvalně si zapněte v main.c DEBUG :D ;D david->hitbox= ( Rectangle ) { david->pozice.x + 55 + david->smer* ( -5*david->animace_sed.index ), david->pozice.y + 10 + 13*david->animace_sed.index, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30-13*david->animace_sed.index }; if ( david->animace_sed.pauznuta ) { if ( david->animace_sed.index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } else if ( david->aktualni_animace == &david->animace_beh ) { if ( !IsSoundPlaying ( zvuk_kroku ) ) { PlaySound ( zvuk_kroku ); } } // jestli má David moc podezřele velkou ypsilonovou souřadnici tak asi jako nejspíš spadnul do ďoury // zahrajeme zvuk pádu (jestli už nehraje) a budem mu postupně každým voláním týdle aktualizvávací funkce // vodečítat jeden život za druhým až uplně umře :O ;D if ( david->pozice.y > BLOK_VYSKA*13 ) { if ( !IsSoundPlaying ( zvuk_padu ) ) { PlaySound ( zvuk_padu ); } david->zivoty--; } if ( david->aktualni_animace == &david->animace_skok && IsSoundPlaying ( zvuk_kroku ) ) { StopSound ( zvuk_kroku ); } if ( david->strileci_cooldown > 0.0f ) { david->strileci_cooldown -= dt; } // pokud je davidův blikací čas věčí než nula, tak vod něj vodečtem časovou deltu // a uděláme Davida nezranitelnýho, páč furt eště bliká if ( david->blikaci_cas > 0.0f ) { david->blikaci_cas -= dt; david->zranitelny = false; } else { //ale jestli už doblikal, tak už zase zranitelnej bude david->zranitelny = true; } for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { aktualizovatStrelu ( david->strely+i, david->mapa,dt ); } aktualizovatAnimaci ( david->aktualni_animace, dt ); } // funkce na vykreslování Davida void vykreslitDavida ( David * david ) { // pokud je David nezranitelnej, tak bude červeně blikat // podělíme si blikací čas ňákým kouskem kterej nám bude určovat periodu blikání // tou desetinou bliknem nějak 10x za vteřinu if ( !david->zranitelny ) { Color barva = ( int ) ( david->blikaci_cas/0.1f ) %2 ? WHITE : RED; vykreslitAnimaci ( david->aktualni_animace, david->pozice,barva ); } else { vykreslitAnimaci ( david->aktualni_animace, david->pozice,WHITE ); } for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { vykreslitStrelu ( david->strely+i ); } #ifdef DEBUG DrawRectangleLines ( david->okraje.x, david->okraje.y, david->okraje.width, david->okraje.height, GREEN ); // nově teďko vykreslujem červeně i ten hitbox DrawRectangleLines ( david->hitbox.x, david->hitbox.y, david->hitbox.width, david->hitbox.height, RED ); #endif } #endif
Musíme upravit soubor 'level.h' a přidat tam novou interakci duchů a Davida, vodteď tam budem v aktualizaci hlídat kolizi hitboxů duchů s Davidovým hitboxem:
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" #include "duch.h" // zvuky extern Sound zvuk_zasah; extern Sound zvuk_kontakt; #define MAPA_MAX_VYSKA 10 #define NUTNA_VZDALENOST (BLOK_SIRKA * 20) // struktura levelu typedef struct Level { Duch ** duchove; size_t pocet_duchu; Mapa * dlazdicova_mapa; } Level; typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { Level * lvl = (Level * )malloc ( sizeof ( Level ) ); Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); // alokujeme si pole duchů // připravíme si prostor pro 256 duchů, páč ještě nevíme kolik jich budem vyrábět // zbytečný místo pak ucvaknem // (určitě by šlo vodhadnout worst case scenario z dýlky levelu ale na to kadí dalmatýn) Duch ** duchove = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); // enum který nám bude popisovat jednotlivý prvky mapy, // 'N' jakože nic, 'B' jakože blok, 'D' jakože duch enum herniVec {N,D,B}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // přidáme si tam ňáký segmenty s duchama int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg3 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,B,0,D,0, 0,D,0,B,0, B,B,B,B,B, B,B,B,B,B, }; int seg4 [] = { 0,0,0,0,0, B,0,0,0,B, }; int seg5 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,D,D, D,D,0,0,0, 0,0,B,B,B, B,B,B,0,0, B,B,B,B,B, B,B,B,B,B, }; SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {10,5,seg3}, ( SegmentMapy ) {5,2,seg4}, ( SegmentMapy ) {10,5,seg5}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); } // prvních a posledních deset sloupečků herní mapy bude placka, // na začátku potřebujem dát hráčoj trošku volnýho místa by pak nespadnul rovnou někam do boje třeba, // na konci si pak uděláme ňákou specialitku která bude voznačovat konec mapy for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } // budeme postupně do mapy kopírovat náhodně vybraný segmenty pěkně v řadě za sebou, // na to si potřebujem měřit zbejvající místo v mapě int zbyva_delka = sirka - 10; size_t duchu = 0; // dokud je zbejvajicí dýlka věčí než 15 // k tomudle čislu sme došli tim, že chceme vynechat posledních 10 sloupců mapy a pětka je // nejmenčí možná dýlka náhodnýho segmentu. Takže dokavaď je dýlka rovna věčí patnácti, furt de // vygenerovat náhodnej segment aniž by sme vlezli do tý vyhrazený zóny na konci mapy while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment // (raylibová fuknce 'GetRandomValue' vrací celý čislo v rozsahu svýho prvního a druhýho argumentu(inkluzivně)) int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; // pokud sme náhodou trefili moc dlouhej segment kterej se nám už na mapu nevejde (aniž by sme vlezli // do tý zóny na konci), tak si zkusíme vybrat jinej náhodnej segment if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; case D: //vyrobíme na tý pozici ducha { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA - 161, }; Duch * duch = vygenerovatDucha ( pozice ); duchove[duchu++] = duch; } break; default: break; }; } } zbyva_delka-=sirka_segmentu; } // realokujem pole duchů, máme tam zabranýho víc místa než kolik sme relalně duchů alokovali, // určitě sme jich nevyrobili 256 :D ;D duchove = realloc ( duchove, sizeof ( Duch * ) * duchu ); //nacpem duchy do tý struktury levelu lvl->pocet_duchu = duchu; lvl->duchove = duchove; // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; return lvl; } void freeLevel ( Level * lvl ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { freeDucha ( lvl->duchove[i] ); } free ( lvl->duchove ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); free ( lvl ); } void aktualizovatLevel ( Level * lvl, David * david, float dt ) { Strela * strely = david->strely; for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if(fabsf ( david->pozice.x - lvl->duchove[i]->okraje.x ) > NUTNA_VZDALENOST) continue; if ( ! lvl->duchove[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->duchove[i]->hitbox ) ) { s->aktivni = false; lvl->duchove[i]->hp--; PlaySound ( zvuk_zasah ); } } } aktualizovatDucha ( lvl->duchove[i], lvl->dlazdicova_mapa, dt ); // pohlídáme si kolizi Davida s duchem, pokud se srazej tak duch Davida zraní // kolizi zistíme raylibí funkcí 'CheckCollisionRecs' do který nacpem hitboxy vobou herních entit if ( !lvl->duchove[i]->chcipe && CheckCollisionRecs ( lvl->duchove[i]->hitbox, david->hitbox ) ) { if ( david->zranitelny ) { // zahrajem zvuk kontaktu // (vlastně nvm jestli to má bejt jakože zvuk co vydává duch nebo david :D ) PlaySound ( zvuk_kontakt ); // vodečtem davidoj život david->zivoty--; // nastavíme davidoj blikací čas dočasný nezranitelnosti.. david->blikaci_cas = BLIKACI_CAS_DAVIDA; // ..a zapnem mu tu nezranitelnost david->zranitelny = false; // pokud má ňákou vertikální rychlost směrem nahoru k hornímu vokraji vobrazkovky, // tak mu ji snižime na nulu. Vono to vytváří takovej psychochologickej efekt jakože // hráče ty duchové chytaj a bráněj mu v pohybu :O ;D if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } } for ( size_t j=0; j<ZASOBNIK_STREL_DUCHA; j++ ) { Strela * strela_ducha = &lvl->duchove[i]->strely[j]; if ( strela_ducha->aktivni ) { aktualizovatStrelu ( strela_ducha,lvl->dlazdicova_mapa, dt ); // podobně jako sme hlídali zásah hitboxu ducha davidovou střelou, // tak budeme klídat zásah davida střelou ducha if ( CheckCollisionPointRec ( strela_ducha->pozice, david->hitbox ) ) { strela_ducha->aktivni = false; if ( david->zranitelny ) { // v poctatě to samý jako při kontaktu PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; david->zranitelny = false; } } } } } } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); // vykreslíme všecky viditelný duchy a všecky střely duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if ( fabsf ( kamera->target.x - lvl->duchove[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->duchove[i] ); } for ( size_t j =0; j<ZASOBNIK_STREL_DUCHA; j++ ) { vykreslitStrelu ( lvl->duchove[i]->strely + j ); } } } #endif
V souboru 'main.c' musíme načíst nový zvuky a jednu texturu. V tý textuře je mimojiný namalový srdíčko, to použijem na vykreslování počtu Davidovejch životů v HUDu (češtinsky se to řekne 'průhledový display' hele 😁) vlevo dole na vobrazovce:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; Texture2D textura_ruzne; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; Sound zvuk_kontakt; Sound zvuk_padu; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); textura_ruzne = LoadTexture ( "assets/misc.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); zvuk_kontakt = LoadSound ( "assets/kontakt.wav" ); zvuk_padu = LoadSound ( "assets/pad.wav" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // nastavíme mapu na tu skovanou ve struktuře levelu .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidovy vnitřní časovače střílecího cooldownu a času blikání resp. dočasný // nezranitelnosti po nepřátelským zásahu ie. islamistickým duchem nebo rudou kulkou .strileci_cooldown = 0.0f, .blikaci_cas = 0.0f, // nastavíme počet životů na max a atribut zranitelnosti na true .zivoty = POCET_ZIVOTU_DAVIDA_MAX, .zranitelny = true, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu a teďko nově i hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; // aktualizujem Davida aktualizovatDavida(&david, dt); // aktualizujem level aktualizovatLevel ( level, &david, dt ); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // nakreslíme HUD - takový to herní menu co ukazuje počet životů, skóre a tak // (nám to vykresluje jenom počet životů) // po tom co sme vypli kameru, malujeme bez tý kamerový transformace, takže nebudem // mit problem trefit levej spodní vokraj vobrazovky kde si budeme malovat // takovou řadu srdíček který budou jakože ukazovat kolik Davidoj zbejvá životů // zrojová voblast srdička ve spritesheetu 'různé' const Rectangle srdicko_rect = {2,503,96,83}; // srdíček budem vykreslovat v řadě za sebou furt stejnej maximální počet, // akorát jenom když budem vykreslovat ty který by přesahovali aktualní počet Davidovejch // životů, tak je budeme malovat černý for ( int i = 0; i < POCET_ZIVOTU_DAVIDA_MAX; i++ ) { Rectangle cil = { .x = i*srdicko_rect.width/2, .y = GetScreenHeight() - srdicko_rect.height/2, .width = srdicko_rect.width/2, .height = srdicko_rect.height/2, }; DrawTexturePro ( textura_ruzne,srdicko_rect,cil, ( Vector2 ) { 0,0 },0.0f,i<david.zivoty? WHITE:BLACK ); } // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); UnloadTexture ( textura_ruzne ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); UnloadSound ( zvuk_kontakt ); UnloadSound ( zvuk_padu ); return 0; }
Vyrobíme si eště jeden druh nepřátelskýho ducha, burkinátora 😮 😁. Burkinátor bude na Davida číhat v ďourách a dycky když se k němu David děsně blízko přiblíží, tak na něj burkinátor z tý díry vyletí, začně dělat takový to výhružný vejskání který hele duchové vobvykle vydávaj když třeba voslavujou terroristický utoky proti bezbranejm civilistům a poletí až někam nahoru nad vobrazovku, pak se vrátí zpátky do ďoury v zemi, kam patří 😏 😜. (Žádný další druhy duchů v tomdlectom návodu dělat nebudem, sem nechtěla dělat ani toho burkinátora původně, ale nápad se mi děsně líbil 😁 😁 Jestli ale budete hodný tak napišu ňáký pokračování a tam si vyrobíme ňáký nový druhy bubáků 😮 😜)
Na burkinátora použijem uplně tu samou strukturu kterou už používaj duchové, dokonce použijem i stejnou vykreslovací funkci. Burkinátor se bude vod normálních duchů lišit svým 'konstruktorem' a funkcí kterou budem burkinátora aktualizovat. Je to malá změna takže si na to nebudem zakládat žádnej novej soubor a upravíme si zdrojáček souboru 'duch.h':
#ifndef _DAVID_A_DUCHOVE_DUCH_H_ #define _DAVID_A_DUCHOVE_DUCH_H_ #include <stdlib.h> #include <raylib.h> #include "mapa.h" #include "projektily.h" extern Texture2D textura_duchove_spritesheet; extern Sound zvuk_duch_chcip; extern Sound zvuk_duch_strela; //přidáme si sem zvuk toho vejskání extern Sound zvuk_zaghrouta; #define HITPOINTU_DUCHA 3 #define CHIPACI_CAS_DUCHA 1.5f #define ZASOBNIK_STREL_DUCHA 3 #define STRILECI_COOLDOWN_DUCHA 1.0f #define RYCHLOST_STRELY_DUCHA 600.0f #define RYCHLOST_CHUZE_DUCHA 120.0f // definujem si počateční vertikální rychlost burkinátora #define RYCHLOST_BURKINATORA 200.0f #define DUCH_F_VYSKA 240.0f #define DUCH_F_SIRKA 85.0f typedef struct Duch { // směr kterým duch kouká, taky identicky řešený jako v davidoj int smer; // hp jakože hitpointy jakože počet životů int hp; Rectangle oblast_textury; Rectangle okraje; Rectangle hitbox; // jestli je duch aktivní // pokud neni, tak ho nebudem vykreslovat ani aktualizovat, prostě bude upně vyplej bool aktivni; // jestli duch chícpe, jakože mu už došly hitpointy a máme za bool chcipe; // vnitřní čas ducha float relativni_cas; // uhel ducha pro animaci efektu chcípání float uhel; // střílecí cooldown ducha float strileci_cooldown; // zásobnik střel ducha Strela * strely; // zvuky ducha Sound zvuk_chcipnuti; Sound zvuk_strela; } Duch; Duch * vygenerovatDucha ( Vector2 kde ) { Duch * duch = calloc ( 1, sizeof ( Duch ) ); if(!duch)return NULL; duch->smer = GetRandomValue( 0,1 ) ? 1 : -1; duch->okraje = ( Rectangle ) { kde.x,kde.y,DUCH_F_SIRKA,DUCH_F_VYSKA }; duch->hitbox = ( Rectangle ) { kde.x+15,kde.y+15,55,155 }; // oblast textury určíme náhodně, vybereme uplně náhodnej podvobrázek duch->oblast_textury = ( Rectangle ) { DUCH_F_SIRKA * GetRandomValue( 0,5 ),0 + DUCH_F_VYSKA * GetRandomValue( 0,1 ),DUCH_F_SIRKA * duch->smer,DUCH_F_VYSKA }; duch->aktivni = true; duch->hp = HITPOINTU_DUCHA; duch->zvuk_chcipnuti = LoadSoundAlias ( zvuk_duch_chcip ); duch->zvuk_strela = LoadSoundAlias ( zvuk_duch_strela ); duch->strely = calloc ( ZASOBNIK_STREL_DUCHA, sizeof ( Strela ) ); if(!duch->strely) { free(duch); return NULL; } for ( size_t i =0; i<ZASOBNIK_STREL_DUCHA; i++ ) { duch->strely[i].druh = strela_ducha; } return duch; } // funkce na vykreslování ducha void vykreslitDucha ( Duch * duch ) { // ducha budem vykreslovat jenom když je aktivní if ( !duch->aktivni ) { return; } // jestli duch chcípe, tak budem vykreslovat ten efekt umiraní if ( duch->chcipe ) { Rectangle _okraje = duch->okraje; const float chcipani = 1.0f - duch->relativni_cas/CHIPACI_CAS_DUCHA; _okraje.x += _okraje.width/2; _okraje.y += _okraje.height/2; _okraje.width *= chcipani; _okraje.height *= chcipani; DrawTexturePro ( textura_duchove_spritesheet,duch->oblast_textury,_okraje, ( Vector2 ) { _okraje.width/2.0f,_okraje.height/2.0f}, duch->uhel, Fade ( WHITE, 1.0f - duch->relativni_cas/CHIPACI_CAS_DUCHA ) ); } else { // když duch nechcípe, vykreslíme ho uplně normálně DrawTexturePro ( textura_duchove_spritesheet,duch->oblast_textury,duch->okraje, ( Vector2 ) {0,0},0.0f, WHITE ); } #ifdef DEBUG // v připadě debugovávání vykreslíme okraje a hitbox DrawRectangleLines ( duch->okraje.x,duch->okraje.y, duch->okraje.width, duch->okraje.height,GREEN ); DrawRectangleLines ( duch->hitbox.x,duch->hitbox.y, duch->hitbox.width, duch->hitbox.height,RED ); #endif } // funkce na aktualizovávání ducha // jako argumenty bere pochopytelně ducha, pak mapu ve který se duch jakože pohybuje noa pak vobligátní časovou deltu void aktualizovatDucha ( Duch * duch, Mapa * mapa, float dt ) { if ( !duch->aktivni ) { return; } if ( !duch->chcipe && duch->hp <= 0 ) { duch->chcipe = true; PlaySound ( duch->zvuk_chcipnuti ); } if ( duch->chcipe ) { duch->relativni_cas += dt; duch->uhel+= dt * ( 500 + 250 * duch->relativni_cas ); if ( duch->relativni_cas > CHIPACI_CAS_DUCHA ) { duch->aktivni = false; } return; } if ( GetRandomValue ( 0,1000 ) == 1 ) { duch->smer*=-1; duch->oblast_textury.width *= -1.0f; } Rectangle prepozice = duch->okraje; if ( duch->smer == 1 ) { prepozice.x += RYCHLOST_CHUZE_DUCHA * dt; Vector2 bod = ( Vector2 ) { prepozice.x + prepozice.width, prepozice.y + prepozice.height +5 }; if ( kolizeRectSeBlokemMapy ( prepozice, mapa ) || ! kolizeSeBlokemMapy_bod ( bod,mapa ) ) { duch->smer *= -1; duch->oblast_textury.width *= -1.0f; } else { duch->okraje.x += RYCHLOST_CHUZE_DUCHA * dt; duch->hitbox.x += RYCHLOST_CHUZE_DUCHA * dt; } } else if ( duch->smer == -1 ) { prepozice.x -= RYCHLOST_CHUZE_DUCHA * dt; if ( kolizeRectSeBlokemMapy ( prepozice, mapa ) || ! kolizeSeBlokemMapy_bod ( ( Vector2 ) { prepozice.x, prepozice.y + prepozice.height +5 },mapa ) ) { duch->smer *= -1; duch->oblast_textury.width *= -1.0f; } else { duch->okraje.x -= RYCHLOST_CHUZE_DUCHA * dt; duch->hitbox.x -= RYCHLOST_CHUZE_DUCHA * dt; } } if ( duch->strileci_cooldown > 0.0f ) { duch->strileci_cooldown-=dt; } else { if ( GetRandomValue ( 0,200 ) == 1 ) { for ( size_t i =0; i<ZASOBNIK_STREL_DUCHA; i++ ) { if ( !duch->strely[i].aktivni ) { Strela s = { .druh = strela_ducha, .rychlost = RYCHLOST_STRELY_DUCHA * ( float ) duch->smer, .pozice = ( Vector2 ) { duch->smer==1 ? duch->okraje.x+DUCH_F_SIRKA/2.0f : duch->okraje.x,duch->okraje.y + 25}, .relativni_cas = 0.0f, .doba_zivota = 2.0f, .aktivni=true, }; duch->strely[i] = s; PlaySound ( duch->zvuk_strela ); duch->strileci_cooldown = STRILECI_COOLDOWN_DUCHA; break; } } } } } // 'konstruktor' burkinátora Duch * vygenerovatBurkinatora ( Vector2 kde ) { Duch * duch = ( Duch * ) calloc ( 1, sizeof ( Duch ) ); if(!duch) return NULL; // směr nám řídí jenom vykreslování textury // (burkinátor bude lítat zezdol nahoru, tak jakej jako směr do stran) duch->smer = GetRandomValue ( 0,1 ) ? 1 : -1; duch->okraje = ( Rectangle ) { kde.x,kde.y,DUCH_F_SIRKA,DUCH_F_VYSKA }; duch->hitbox = ( Rectangle ) { kde.x+15,kde.y+15,55,160 }; // by sme burkinátora vod vostatních duchů vodlišili, tak bude mit texturu jenom ducha v burce // sou tam na víběr dva různý, jeden v černým hadru, druhej v bílým // z tědlech dou textur si pokaždý nahodně vyberem const Rectangle moznosti[2]= { ( Rectangle ) {DUCH_F_SIRKA*3,0,DUCH_F_SIRKA,DUCH_F_VYSKA}, ( Rectangle ) {DUCH_F_SIRKA*2,DUCH_F_VYSKA,DUCH_F_SIRKA,DUCH_F_VYSKA}, }; duch->oblast_textury = moznosti[GetRandomValue ( 0,1 )]; duch->aktivni = true; duch->hp = HITPOINTU_DUCHA; duch->zvuk_chcipnuti = LoadSoundAlias ( zvuk_duch_chcip ); // mistu zvuku střely nakopírujem alias zvuku tamtoho terroristickýho vejskání duch->zvuk_strela = LoadSoundAlias ( zvuk_zaghrouta ); // burkinátor nebude mít žádný střely duch->strely = NULL; return duch; } // burkinátor nebude řešit žádný kolize s ďourama a blokama, takže voproti vobyčejnýmu duchoj nepotřebuje znát mapu // bude ale potřebovat znát iksovou pozici Davida, by burkinátor věděl kdy na Davida vybafnout void aktualizovatBurkinatora ( Duch * duch, float x_pozice_cile, float dt ) { if ( !duch->aktivni ) { return; } // tudle chcípací část kódu budem mit uplně stejnou jako u vobyč ducha if ( !duch->chcipe && duch->hp <= 0 ) { duch->chcipe = true; PlaySound ( duch->zvuk_chcipnuti ); duch->relativni_cas = 0.0f; } if ( duch->chcipe ) { duch->relativni_cas += dt; duch->uhel+= dt * ( 500 + 250 * duch->relativni_cas ); if ( duch->relativni_cas > CHIPACI_CAS_DUCHA ) { duch->aktivni = false; } return; } // pokud je pozice cíle blíž jak dýlka jeden a půl herní kostky, tak burkinátor začne letět nahoru // (stav, že má letět budem hlídat tim, že se kouknem jestli je burkinátorova vypsilonová souřadnice menčí než výchozí, // proto ten operátor 'or' s tou vejškou menčí než 960 (to ňákejch dvanáct kostek, takže spodek mapy)) if ( fabsf ( x_pozice_cile - duch->okraje.x + duch->okraje.width/2 ) < BLOK_SIRKA*1.5 || duch->okraje.y < 959.9999f ) { // přičtem desetinu dt k relativnímu času (desetinu páč to rostlo moc rychle :D :D) duch->relativni_cas += dt/10; // a tim relativním časem vynasobeným rychlostí posuneme ducha nahoru // (děláme to takle, by burkinátor zrychloval ) duch->okraje.y-= RYCHLOST_BURKINATORA*duch->relativni_cas; duch->hitbox.y = duch->okraje.y+15; if ( !IsSoundPlaying ( duch->zvuk_strela ) ) { PlaySound ( duch->zvuk_strela ); } } // pokud duch vylít dostatečně vysoko, tak ho 'teleportujeme' zpátkydolu do ďoury // vynulujeme mu realtivní čas, vypneme zvuk vejskání noa vlastně ho necháme znova číhat na Davida if ( duch->okraje.y + DUCH_F_VYSKA <= -800 ) { duch->okraje.y = 960; duch->hitbox.y = duch->okraje.y+15; duch->relativni_cas = 0.0f; StopSound ( duch->zvuk_strela ); } } // funguje i na burkinátory void freeDucha ( Duch * duch ) { UnloadSoundAlias ( duch->zvuk_chcipnuti ); UnloadSoundAlias ( duch->zvuk_strela ); free( duch->strely ); free ( duch ); } #endif
Aby se nám burkinátoři voběvovali na mapě, tak si je musíme ňák připsat do 'level.h', přidáme je tam uplně stejně jako sme přidávali duchy. Rozdíl voproti vobyč duchům bude jenom v aktualizaci (páč burkinátoři nestřílej) a ve spawnování burkinátorů při generování levelu (vobyč duchy generujem tak by stáli na mapě, burkinátory vygenerujem u spodku vobrazovky, necháme je trošku z ďour vykukovat by si jich hráč měl šanci všimnout) :
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" #include "duch.h" // zvuky extern Sound zvuk_zasah; extern Sound zvuk_kontakt; #define MAPA_MAX_VYSKA 10 #define NUTNA_VZDALENOST (BLOK_SIRKA * 20) // struktura levelu typedef struct Level { Duch ** duchove; size_t pocet_duchu; // level teďko bude mit burkinátory, // alokujem si je stejným stylem jako předtim duchy Duch ** burkinatori; size_t pocet_burkinatoru; Mapa * dlazdicova_mapa; } Level; typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { Level * lvl = (Level * )malloc ( sizeof ( Level ) ); Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); Duch ** duchove = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); // alokujem si burkinátory Duch ** burkinatori = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); // enum který nám bude popisovat jednotlivý prvky mapy, // 'N' jakože nic, 'B' jakože blok, 'D' jakože duch // 'Q' jako burkinátor (vono se to prej správně piše ňák s kvé jakože 'burqa' nebo jak) enum herniVec {N,D,B,Q}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // přidáme si tam ňáký burkinátory int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg3 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,B,0,D,0, 0,D,0,B,0, B,B,B,B,B, B,B,B,B,B, }; int seg4 [] = { 0,0,0,0,0, B,0,0,0,B, }; int seg5 [] = { 0,0,0,0,0, B,0,Q,0,B, }; int seg6 [] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,B,0,B,0, 0,0,B,0,B,0,0,0,B,0,0,B,0,B,0, B,Q,B,Q,B,Q,0,Q,B,Q,0,B,Q,B,B, }; int seg7 [] = { 0,0,0,0,0,0,0,0,0,B,0,0,0,0,0, 0,0,0,0,0,0,0,0,B,B,0,0,0,0,0, 0,0,0,0,0,0,0,B,B,B,0,0,0,0,0, 0,0,0,0,0,0,B,B,B,B,0,0,0,0,0, 0,0,0,0,0,B,B,B,B,B,0,0,0,0,0, 0,0,0,0,B,B,B,B,B,B,0,0,0,0,0, 0,0,0,B,B,B,B,B,B,B,0,0,0,0,0, 0,0,B,B,B,B,B,B,B,B,0,0,0,0,0, 0,B,B,B,B,B,B,B,B,B,0,0,0,0,0, B,B,B,B,B,B,B,B,B,B,Q,Q,Q,Q,B, }; int seg8 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,D,D, D,D,0,0,0, 0,0,B,B,B, B,B,B,0,0, B,B,B,B,B, B,B,B,B,B, }; int seg9 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,D,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg10 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,B,0, 0,B,0,B,0, B,B,B,B,B }; int seg11 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,0,0,0,0, 0,B,B,D,0, D,0,D,0,B, B,B,B,B,B, B,B,B,B,B, }; SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {10,5,seg3}, ( SegmentMapy ) {5,2,seg4}, ( SegmentMapy ) {5,2,seg5}, ( SegmentMapy ) {15,5,seg6}, ( SegmentMapy ) {15,10,seg7}, ( SegmentMapy ) {10,5,seg8}, ( SegmentMapy ) {5,5,seg9}, ( SegmentMapy ) {5,5,seg10}, ( SegmentMapy ) {10,5,seg11}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); } // prvních a posledních deset sloupečků herní mapy bude placka, for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } int zbyva_delka = sirka - 10; size_t duchu = 0; // podobně jako sme si počitali normální duchy, si budem počitat burkinátory size_t burkinatoru = 0; while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; case D: //vyrobíme na tý pozici ducha { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA - 161, }; Duch * duch = vygenerovatDucha ( pozice ); duchove[duchu++] = duch; } break; case Q: //vyrobíme burkinátora { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA + BLOK_VYSKA*3, }; Duch * duch = vygenerovatBurkinatora ( pozice ); burkinatori[burkinatoru++] = duch; } break; default: break; }; } } zbyva_delka-=sirka_segmentu; } duchove = realloc ( duchove, sizeof ( Duch * ) * duchu ); // realokujem burkinátory burkinatori = realloc ( burkinatori, sizeof ( Duch * ) * burkinatoru ); // nacpem duchy do tý struktury levelu lvl->pocet_duchu = duchu; lvl->duchove = duchove; // napcem tam i burkinátory lvl->pocet_burkinatoru = burkinatoru; lvl->burkinatori = burkinatori; // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; return lvl; } void freeLevel ( Level * lvl ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { freeDucha ( lvl->duchove[i] ); } free ( lvl->duchove ); // musíme uvolnit i burkinátory for ( size_t i=0; i<lvl->pocet_burkinatoru; i++ ) { freeDucha ( lvl->burkinatori[i] ); } free ( lvl->burkinatori ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); free ( lvl ); } void aktualizovatLevel ( Level * lvl, David * david, float dt ) { Strela * strely = david->strely; for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if(fabsf ( david->pozice.x - lvl->duchove[i]->okraje.x ) > NUTNA_VZDALENOST) continue; if ( ! lvl->duchove[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->duchove[i]->hitbox ) ) { s->aktivni = false; lvl->duchove[i]->hp--; PlaySound ( zvuk_zasah ); } } } aktualizovatDucha ( lvl->duchove[i], lvl->dlazdicova_mapa, dt ); // pohlídáme si kolizi Davida s duchem, pokud se srazej tak duch Davida zraní // kolizi zistíme raylibí funkcí 'CheckCollisionRecs' do který nacpem hitboxy vobou herních entit if ( !lvl->duchove[i]->chcipe && CheckCollisionRecs ( lvl->duchove[i]->hitbox, david->hitbox ) ) { if ( david->zranitelny ) { // zahrajem zvuk kontaktu // (vlastně nvm jestli to má bejt jakože zvuk co vydává duch nebo david :D ) PlaySound ( zvuk_kontakt ); // vodečtem davidoj život david->zivoty--; // nastavíme davidoj blikací čas dočasný nezranitelnosti.. david->blikaci_cas = BLIKACI_CAS_DAVIDA; // ..a zapnem mu tu nezranitelnost david->zranitelny = false; // pokud má ňákou vertikální rychlost směrem nahoru k hornímu vokraji vobrazkovky, // tak mu ji snižime na nulu. Vono to vytváří takovej psychochologickej efekt jakože // hráče ty duchové chytaj a bráněj mu v pohybu :O ;D if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } } for ( size_t j=0; j<ZASOBNIK_STREL_DUCHA; j++ ) { Strela * strela_ducha = &lvl->duchove[i]->strely[j]; if ( strela_ducha->aktivni ) { aktualizovatStrelu ( strela_ducha,lvl->dlazdicova_mapa, dt ); // podobně jako sme hlídali zásah hitboxu ducha davidovou střelou, // tak budeme klídat zásah davida střelou ducha if ( CheckCollisionPointRec ( strela_ducha->pozice, david->hitbox ) ) { strela_ducha->aktivni = false; if ( david->zranitelny ) { // v poctatě to samý jako při kontaktu PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; david->zranitelny = false; } } } } } // vicemeně skoro uplně stejně si sem přidáme aktualizaci burkinátorů, jako sme napsali aktualizaci vobyč duchů for ( size_t i =0; i<lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( david->pozice.x - lvl->burkinatori[i]->okraje.x ) > NUTNA_VZDALENOST ) continue; if ( ! lvl->burkinatori[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->burkinatori[i]->hitbox ) ) { s->aktivni = false; lvl->burkinatori[i]->hp--; PlaySound ( zvuk_zasah ); } } } // zkusíme kolizi s davidem if ( david->zranitelny && CheckCollisionRecs ( lvl->burkinatori[i]->hitbox, david->hitbox ) ) { PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } aktualizovatBurkinatora ( lvl->burkinatori[i], david->pozice.x, dt ); } } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); // vykreslíme všecky viditelný duchy a všecky střely duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if ( fabsf ( kamera->target.x - lvl->duchove[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->duchove[i] ); } for ( size_t j =0; j<ZASOBNIK_STREL_DUCHA; j++ ) { vykreslitStrelu ( lvl->duchove[i]->strely + j ); } } // vykreslíme burkinátory for ( size_t i = 0; i < lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( kamera->target.x - lvl->burkinatori[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->burkinatori[i] ); } } } #endif
V 'main.c' si akorát načteme zvuk toho strašidelnýho vejskání:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; Texture2D textura_ruzne; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; Sound zvuk_kontakt; Sound zvuk_padu; Sound zvuk_zaghrouta; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); textura_ruzne = LoadTexture ( "assets/misc.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); zvuk_kontakt = LoadSound ( "assets/kontakt.wav" ); zvuk_padu = LoadSound ( "assets/pad.wav" ); zvuk_zaghrouta = LoadSound ( "assets/zaghrouta.ogg" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // nastavíme mapu na tu skovanou ve struktuře levelu .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidovy vnitřní časovače střílecího cooldownu a času blikání resp. dočasný // nezranitelnosti po nepřátelským zásahu ie. islamistickým duchem nebo rudou kulkou .strileci_cooldown = 0.0f, .blikaci_cas = 0.0f, // nastavíme počet životů na max a atribut zranitelnosti na true .zivoty = POCET_ZIVOTU_DAVIDA_MAX, .zranitelny = true, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu a teďko nově i hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; // aktualizujem Davida aktualizovatDavida(&david, dt); // aktualizujem level aktualizovatLevel ( level, &david, dt ); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // nakreslíme HUD - takový to herní menu co ukazuje počet životů, skóre a tak // (nám to vykresluje jenom počet životů) // po tom co sme vypli kameru, malujeme bez tý kamerový transformace, takže nebudem // mit problem trefit levej spodní vokraj vobrazovky kde si budeme malovat // takovou řadu srdíček který budou jakože ukazovat kolik Davidoj zbejvá životů // zrojová voblast srdička ve spritesheetu 'různé' const Rectangle srdicko_rect = {2,503,96,83}; // srdíček budem vykreslovat v řadě za sebou furt stejnej maximální počet, // akorát jenom když budem vykreslovat ty který by přesahovali aktualní počet Davidovejch // životů, tak je budeme malovat černý for ( int i = 0; i < POCET_ZIVOTU_DAVIDA_MAX; i++ ) { Rectangle cil = { .x = i*srdicko_rect.width/2, .y = GetScreenHeight() - srdicko_rect.height/2, .width = srdicko_rect.width/2, .height = srdicko_rect.height/2, }; DrawTexturePro ( textura_ruzne,srdicko_rect,cil, ( Vector2 ) { 0,0 },0.0f,i<david.zivoty? WHITE:BLACK ); } // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); UnloadTexture ( textura_ruzne ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); UnloadSound ( zvuk_kontakt ); UnloadSound ( zvuk_padu ); UnloadSound ( zvuk_zaghrouta ); return 0; }
Přidáme si na mapu sebratelný bonusy, léčivý srdíčko a Davidovu hvězdu. Srdíčko bude léčit Davida vo jeden život, hvězda dá Davidoj dočasnou nezranitelnost a bude moct instantně potlouct duchy tim že se jich dotkne ( sem si pouštěla videjka ruznejch starodávnejch plošinovek pro inspiraci a todlecto s tou hvězdou sem tam viděla v Márijoj 😁 eště jednu věc sem tam vokoukla, uvidite asi až uplně nakonec 😁 😁)
Ve vobligátní složšce 'src' si vytvoříme novej hlavičkovej soubor 'sebratelne.h', kde bude struktura reprezentujicí ty dva druhy sebratelnejch věcí:
#ifndef _DAVID_A_DUCHOVE_SEBRATELNE_H_ #define _DAVID_A_DUCHOVE_SEBRATELNE_H_ #include <raylib.h> #include <math.h> #include <stdlib.h> extern Texture2D textura_ruzne; // uděláme si tam taovej jednoduchej efekt tý sebratelný věci, rotaci kolem svý vosy // todle je doba toho točení :O ;D #define CAS_ROTACE_SEBRATELNE_VECI_MAX 3.0f // budem mit dvě sebratelný věci, lečivý srdičko a magickou hvězdu // pro voba dva druhy věcí budem používat stejnej voběkt/strukturu. // By sme ale poznali, kterejže bonus z tědlech dvou má ta sebratelná věc reprezentovat, // tak ji to nastavíme jako jako proměnou typu enum // (podobně jako už nastavujem třeba druh střely) enum DruhSebratelneVeci {SEBRATELNE_SRDICKO, SEBRATELNA_HVEZDA}; // voblasti podtextur srdička a magický hvězdy ve spritesheetu 'různé' const Rectangle SRDICKO_TXTOBLAST_RECT = {2,503,96,83}, HVEZDA_TXTOBLAST_RECT = {2,758,74,80}; // struktura tý naší sebratelný věci typedef struct Sebratelne { // sebratelnej bonus musí mit ňáký vokraje, do kterejch David muže svým hitboxem vlízt a // a jakoby ten bonus sebrat. Vykreslovací vobdelnik budem použivat na vykreslování efektu tý rotace Rectangle okraje, vykreslovaci_rect; // druh tý sebratelný věci (muže bejt srdičko nebo hvězda) enum DruhSebratelneVeci druh; // relativní čas // budem jim řídit tu rotaci vykreslovaný textury float relativni_cas; // voboba proměny aktivní u duchů nebo střel, // když bude bonus sebranej tak už ho nebudem vykreslovat ani aktualizovat bool sebrano; } Sebratelne; void aktualizovatSebratelnouVec(Sebratelne * vec, float dt) { // když už je věc Davidem sebraná tak ji nebudem aktualizovat if(vec->sebrano) return; // přičteme si deltu k relativnímu času a uděláme na něm modulo maximálním časem rotace, // takle zařídíme by byl náš čas furt v rosahu nula až maximální čas rotace, navíc se vyhnem // možnýmu přehoupnutí floatu zpátky někam na začátek až by nám třeba (nějak za strašně dlouho) // přetekla proměná // ( fmodf je funkce na dělání modula s floatama, z knihovny math.h ) vec->relativni_cas += dt; vec->relativni_cas = fmodf(vec->relativni_cas, CAS_ROTACE_SEBRATELNE_VECI_MAX); // noa budem dělat rotaci toho vykreslovacího vobdelnika // chcem dosáhnout efektu rotace tý věci kolem svý vlastní vosy (podobně jako se točej třeba vypadlý věci v minecraftu // když je vyhodíme z inventáře). Toho dosáhnem tak že budem tomu vobdelniku podle uplynulýho postupně zmenčovat šiřku až // nebude mit vubec žádnou. Aby se nám ten vobelnik neposouval jakoby ke svýmu levýmu vokraji, tak musíme to zmenčování kompenzovat // vodpovidajicím posunem iksový souřadnice směrem doprava, by se jakože ten vobdelnik smrskával z vobou stran současně. // Noa jakmile se smrskne uplně někam na nulu, tak ho zase začnem zvěčovat. // Jenže chcem by se nám ta věc jakože točila kolem tý svý vosy, takže budem chtít by při tom zvěčování měl veskutečnosti zápornou // šířku, by se nám textura vykreslila převráceně. // Takže teďko sme si tu věc překlopili vo 180 stupňů, chcem ale 360 jakože uplně dokola, takže zase začnem zmenčovat ten překlopenej // vobdelnik až na nulu a pak zase začnem zvěčovat vobdelnik z nuly na normální šiřku (a při tom furt kompenzovat ten iksovej posun) float pomer; // bude v rosahu -1 až 1, by sme s nim mohli překlápět šiřku vobdelnika // v první půlce času rotace budem překlápět z maximalní šiřky do minimální // (poměr pude pomaličku z jedničky na minus jedna) if(vec->relativni_cas <= CAS_ROTACE_SEBRATELNE_VECI_MAX/2) { pomer = 1.0f - (vec->relativni_cas / (CAS_ROTACE_SEBRATELNE_VECI_MAX/2))*2.0f; } // v druhý půlce času pudem navopak vod minus jedničky k jedničce else { pomer = 1.0f - ((vec->relativni_cas - (CAS_ROTACE_SEBRATELNE_VECI_MAX/2)) / (CAS_ROTACE_SEBRATELNE_VECI_MAX/2))*2.0f; pomer*=-1.0f; } // (sem to takle vyifovala ale de to řešit i čistě matematikózně) vec->vykreslovaci_rect = (Rectangle){ // absolutní hotnotou dosahnem toho že to měnění iksovýho posunu bude mit jakoby 2x víc věčí 'frekvenci' než // překlápětí šiřky ( za dobu jednoho uplnýho překlopení textury uděláme 2x tam a zpátky tim iksovým posunem) .x = vec->okraje.x + vec->okraje.width/2.0f - vec->okraje.width/2.0f * fabsf(pomer), .y = vec->okraje.y, .width = vec->okraje.width * pomer, .height = vec->okraje.height, }; } void vykreslitSebratelnouVec(Sebratelne * vec) { if(vec->sebrano) return; // texturu sebratelný věci vyberem podle atrubutu 'druh' switch(vec->druh) { case SEBRATELNE_SRDICKO: { Rectangle src = SRDICKO_TXTOBLAST_RECT; // (raylib nechce texturu ve funkci DrawTexturePro překlápět když neni záporná šířka ve zdrojovým rectanglu 'src', // na překlopenou šiřku v rectanglu 'dest' neregauje, myslimže to je bug. pokud se ale pletu a ukaže se žeto je feature // nóó tak se ke zdrojačku vrátim a upravim ho by v aktualizovávací funkci využival src vobdelnik) if(vec->vykreslovaci_rect.width < 0.0f) { vec->vykreslovaci_rect.width*=-1.0f; src.width *= -1.0f; } DrawTexturePro(textura_ruzne,src,vec->vykreslovaci_rect,(Vector2){0,0},0.0f,WHITE); break; } case SEBRATELNA_HVEZDA: { Rectangle src = HVEZDA_TXTOBLAST_RECT; if(vec->vykreslovaci_rect.width < 0.0f) { vec->vykreslovaci_rect.width*=-1.0f; src.width *= -1.0f; } DrawTexturePro(textura_ruzne,src,vec->vykreslovaci_rect,(Vector2){0,0},0.0f,WHITE); break; } default: break; } #ifdef DEBUG DrawRectangleLines(vec->okraje.x,vec->okraje.y,vec->okraje.width,vec->okraje.height,GREEN); #endif } Sebratelne * vygenerovatSebratelnouVec(Vector2 kde, enum DruhSebratelneVeci druh) { Sebratelne * s = calloc(1,sizeof(Sebratelne)); if(!s) return NULL; // vejšku a šiřku sebratelný věci nastavíme na velikost jednoho bloku dlaždicový mapy // textury sou +- čtvercový a ňáký malý nepřesnosti se snad stratěj :O ;D s->okraje = (Rectangle){kde.x,kde.y,BLOK_SIRKA,BLOK_VYSKA}; s->vykreslovaci_rect = (Rectangle){kde.x,kde.y,BLOK_SIRKA,BLOK_VYSKA}; s->druh = druh; return s; } #endif
Upravíme si Davida v souboru 'david.h', přidáme mu atributy pro ten hvězdnej bonus a upravíme aktualizovávací a vykreslovací funkci:
#ifndef _DAVID_A_DUCHOVE_DAVID_H_ #define _DAVID_A_DUCHOVE_DAVID_H_ #include "animace.h" #include "mapa.h" #include "projektily.h" extern Sound zvuk_kroku; extern Sound zvuk_skoku; extern Sound zvuk_vystrel; // načtem si zvuk pádu kterej budem hrát dycky když nám David zahučí někam do ďoury v zemi extern Sound zvuk_padu; #define DAVID_F_SIRKA 150.0f #define DAVID_F_VYSKA 240.0f Rectangle david_framy_behu[] = { {DAVID_F_SIRKA*0,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_skoku[] = { {DAVID_F_SIRKA*5,DAVID_F_VYSKA*0,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA} }; Rectangle david_framy_idle[] = { {DAVID_F_SIRKA*4,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*3,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; Rectangle david_framy_sed[] = { {DAVID_F_SIRKA*3,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*4,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*5,DAVID_F_VYSKA*1,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*0,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*1,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*2,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, {DAVID_F_SIRKA*3,DAVID_F_VYSKA*2,DAVID_F_SIRKA,DAVID_F_VYSKA}, }; #define RYCHLOST_CHUZE_DAVIDA 350.0f #define RYCHLOST_SKOKU_DAVIDA -15.0f #define GRAVITACE_DAVIDA 40.0f #define RYCHLOST_STREL_DAVIDA 800.0f #define STRILECI_COOLDOWN_DAVIDA 0.25f #define POCET_STREL_DAVIDA_MAX 32 #define POCET_ZIVOTU_DAVIDA_MAX 10 #define BLIKACI_CAS_DAVIDA 0.5f // maximální doba trvání bonusu #define HVEZDNY_CAS_DAVIDA 14.5f // bonus rychlosti chůze davida jestli sebral hvězdu #define HVEZDNA_RYCHLOST_CHUZE_DAVIDA 200.0f typedef struct David { Animace animace_beh; Animace animace_idle; Animace animace_sed; Animace animace_skok; Animace * aktualni_animace; Vector2 pozice; Rectangle okraje; Rectangle hitbox; int smer; Mapa * mapa; bool zdaSkace; float vertikalni_rychlost; Strela * strely; float strileci_cooldown; int zivoty; float blikaci_cas; bool zranitelny; // kolik času trvání bonusu eště zbejvá float hvezdny_bonus_cas; // jestli má bonus bool ma_bonus; } David; void aktualizovatDavida ( David * david, float dt ) { if ( IsKeyDown ( KEY_UP ) ) { if ( david->aktualni_animace == &david->animace_sed ) { david->aktualni_animace->pauznuta = false; david->aktualni_animace->reverzne=true; if ( david->aktualni_animace->index == 0 ) { david->aktualni_animace = & david->animace_idle; } } else if ( !david->zdaSkace ) { PlaySound ( zvuk_skoku ); david->zdaSkace = true; david->animace_skok.index = 0; // jestli má david bonus tak mu zvednem rychlost skákání na 150% david->vertikalni_rychlost = david->ma_bonus ? RYCHLOST_SKOKU_DAVIDA*1.5f : RYCHLOST_SKOKU_DAVIDA; } } // když hráč zmáčkne mezernik, tak se David pokusí vystřelit z tý svý pistolky if ( IsKeyDown ( KEY_SPACE ) ) { // vystřelit mužeme jenom když uplynula čekací doba mezi výstřelama resp. střílecí cooldown je menší nebo rovnej nule if ( david->strileci_cooldown <= 0.0f ) { // projdeme si celej davidův zásobník a pokusíme se v něm najít střelu, kterou budeme moct použít // to poznáme tak, že bude mit atribut aktivní nastavenej na nulu for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { if ( !david->strely[i].aktivni ) { // pokud sme takovou střelu našli, tak si vybereme pro upravování atributů Strela * s = david->strely + i; // nejdřiv ji nastavíme novou polohu, //to znamená přibližně někam na konec hlavně tý pistolky co má david v rukou //nj jenže david muže stát, sedět na zemi, muže koukat z prava doleva, nebo muže dokonce právě vstávat ze země // všecky tydlecty eventualitky jakoby musíme pokrejt if ( david->aktualni_animace != &david->animace_sed ) s->pozice = ( Vector2 ) { // střelu umisťujem podle toho jakým směrem david kouká david->smer==1? david->pozice.x+DAVID_F_SIRKA - 25: david->pozice.x+25, david->pozice.y + DAVID_F_VYSKA/2 - 36 }; else { // pokud david má jako aktuální animaci sedání, tak si zistíme index a vo ten budeme posouvat iksovou a ypsilonovou // souřadnici střely. Neni to uplně přesný ale na to nikdo koukat nebude :D int index = david->animace_sed.index; s->pozice = ( Vector2 ) { david->smer==1? david->pozice.x+DAVID_F_SIRKA - 25 - index*5: david->pozice.x+25 + index*5,david->pozice.y + DAVID_F_VYSKA/2 - 26 - 8 + index*14 }; } //polohu máme, teďko nastavíme další atributy střely // nastavíme střele rychlost, zohledníme i směr kterým poletí, ten vodpovídá // směru kterým David právě teďko kouká s->rychlost = RYCHLOST_STREL_DAVIDA * ( float ) david->smer; // vynulujem relativní čas s->relativni_cas = 0.0f; // nastavíme dobu života třeba na čtyry vteřiny s->doba_zivota = 4.0f; // a aktivujem s->aktivni=true; // střelu máme upravenou, ukazatel už nepotřebujem s=0; // ..a když sme aktivovali střelu, tak sme vlastně vystřelili, takže nastavíme střílecí čekací dobu // na maximální hodnotu david->strileci_cooldown = STRILECI_COOLDOWN_DAVIDA; // zahrajem zvuk výstřelu PlaySound ( zvuk_vystrel ); // a přerušíme hledací for cyklus break; } } } if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; david->aktualni_animace->index = 0; david->aktualni_animace->relativni_cas = 0.0f; } } if ( IsKeyDown ( KEY_LEFT ) && david->pozice.x > 0 && david->aktualni_animace != &david->animace_sed ) { david->smer = -1; Rectangle prepozice = david->okraje; // zohledníme možnej hvězdnej bonus rychosti prepozice.x -= (RYCHLOST_CHUZE_DAVIDA + ( david->ma_bonus* HVEZDNA_RYCHLOST_CHUZE_DAVIDA )) * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->hitbox.x = david->okraje.x + 10; david->aktualni_animace = &david->animace_beh; } } else if ( IsKeyDown ( KEY_RIGHT ) && david->pozice.x < david->mapa->sirka*BLOK_SIRKA - DAVID_F_SIRKA && david->aktualni_animace != &david->animace_sed ) { david->smer = 1; Rectangle prepozice = david->okraje; // zohledníme možnej hvězdnej bonus rychosti prepozice.x += (RYCHLOST_CHUZE_DAVIDA + ( david->ma_bonus* HVEZDNA_RYCHLOST_CHUZE_DAVIDA )) * dt; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->aktualni_animace = & david->animace_idle; } else { david->okraje.x = prepozice.x; david->pozice.x = david->okraje.x - 45; david->hitbox.x = david->okraje.x + 10; david->aktualni_animace = & david->animace_beh; } } // jestli je máčkutej na klávesnici čudlik šipky dolu, tak začnem přehrávat animaci sednutí // si na zadek else if ( IsKeyDown ( KEY_DOWN ) ) { david->aktualni_animace = & david->animace_sed; david->aktualni_animace->reverzne = false; david->aktualni_animace->pauznuta = false; } else if ( david->aktualni_animace != &david->animace_sed ) { david->aktualni_animace = & david->animace_idle; } Rectangle prepozice = david->okraje; prepozice.y += 5; if ( ! kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->zdaSkace = true; } if ( david->zdaSkace ) { david->vertikalni_rychlost += dt * GRAVITACE_DAVIDA; Rectangle prepozice = david->okraje; prepozice.y += david->vertikalni_rychlost; if ( kolizeRectSeBlokemMapy ( prepozice, david->mapa ) ) { david->vertikalni_rychlost = 0.0f; david->zdaSkace = false; david->okraje.y = ceil ( ( david->okraje.y + david->okraje.height ) /BLOK_VYSKA ) * BLOK_VYSKA - david->okraje.height - 1; if ( david->aktualni_animace == &david->animace_beh ) { david->aktualni_animace->index = david->animace_skok.index +5; david->aktualni_animace->reverzne = david->animace_skok.reverzne; } } else { david->aktualni_animace = & david->animace_skok; david->okraje.y += david->vertikalni_rychlost; } // aktualizujem po dopadu taky hitbox david->hitbox.y = david->okraje.y + 12; david->pozice.y = david->okraje.y - 3; } david->aktualni_animace->zrcadlit = david->smer<0; if ( david->aktualni_animace == &david->animace_sed ) { // pokud má David jako aktualní animaci sedání, tak mu musíme nastavit hitbox podle jednotlivejch framů tý animace // asi to takle nebude uplně přesný, nicmeně pro náš učel to je dostatečně přesný, schvalně si zapněte v main.c DEBUG :D ;D david->hitbox= ( Rectangle ) { david->pozice.x + 55 + david->smer* ( -5*david->animace_sed.index ), david->pozice.y + 10 + 13*david->animace_sed.index, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30-13*david->animace_sed.index }; if ( david->animace_sed.pauznuta ) { if ( david->animace_sed.index == 0 ) { david->aktualni_animace = & david->animace_idle; } } } else if ( david->aktualni_animace == &david->animace_beh ) { if ( !IsSoundPlaying ( zvuk_kroku ) ) { PlaySound ( zvuk_kroku ); } } if ( david->pozice.y > BLOK_VYSKA*13 ) { if ( !IsSoundPlaying ( zvuk_padu ) ) { PlaySound ( zvuk_padu ); } david->zivoty--; } if ( david->aktualni_animace == &david->animace_skok && IsSoundPlaying ( zvuk_kroku ) ) { StopSound ( zvuk_kroku ); } if ( david->strileci_cooldown > 0.0f ) { david->strileci_cooldown -= dt; } // pokud je davidův blikací čas věčí než nula, tak vod něj vodečtem časovou deltu // a uděláme Davida nezranitelnýho, páč furt eště bliká if ( david->blikaci_cas > 0.0f ) { david->blikaci_cas -= dt; david->zranitelny = false; } else { //ale jestli už doblikal, tak už zase zranitelnej bude david->zranitelny = true; } // jestli má david bonus tak má nejspiš i nenulovej hvězdnej čas // budem ho každým krokem vodečitat noa dokavaď davidoj ňákej bude eště zbejvat, // tak mu budem zapínat nezranitelnost (resp. vypínat zranitelnost :D) // noa až čas dojde tak mu vypnem ten atribut 'ma_bonus' pochopytelně :D ;D if ( david->ma_bonus ) { david->hvezdny_bonus_cas -=dt; if ( david->hvezdny_bonus_cas>0.0f ) { david->zranitelny = false; } else { david->ma_bonus = false; } } for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { aktualizovatStrelu ( david->strely+i, david->mapa,dt ); } aktualizovatAnimaci ( david->aktualni_animace, dt ); } // funkce na vykreslování Davida void vykreslitDavida ( David * david ) { // pokud je David nezranitelnej, tak bude červeně blikat // podělíme si blikací čas ňákým kouskem kterej nám bude určovat periodu blikání // tou desetinou bliknem nějak 10x za vteřinu // přidáme sem i efekt hvězdnýho bonusu, když bude mit david sebranou hvězdu, tak ho budem // vykreslovat žlutě. Aby ale hráč poznal že bonus brzy muže skončit, tak až zbejvajicí hvězdnej čas // bude třeba pětina max hodnoty, tak začne žlutě blikat (podobně jako když je raněnej, akorátže žlutě) if ( !david->zranitelny ) { Color barva; if ( david->ma_bonus ) { if ( david->hvezdny_bonus_cas > HVEZDNY_CAS_DAVIDA/5.0f ) { barva = YELLOW; } else { barva = ( int ) ( david->hvezdny_bonus_cas/0.1f ) %2 ? WHITE : YELLOW; } } else { barva = ( int ) ( david->blikaci_cas/0.1f ) %2 ? WHITE : RED; } vykreslitAnimaci ( david->aktualni_animace, david->pozice, barva ); } else { vykreslitAnimaci ( david->aktualni_animace, david->pozice, WHITE ); } for ( size_t i=0; i<POCET_STREL_DAVIDA_MAX; i++ ) { vykreslitStrelu ( david->strely+i ); } #ifdef DEBUG DrawRectangleLines ( david->okraje.x, david->okraje.y, david->okraje.width, david->okraje.height, GREEN ); // nově teďko vykreslujem červeně i ten hitbox DrawRectangleLines ( david->hitbox.x, david->hitbox.y, david->hitbox.width, david->hitbox.height, RED ); #endif } #endif
Uplně stejně jako sme si do souboru levelu 'level.h' předtim přidali duchy, si tam teďko přidáme i bonusy, eště musíme u duchů zohlednit tu kolizi se zabijáckým blikacím Davidem:
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" #include "duch.h" // naimportujem si ty sebratelný věci #include "sebratelne.h" // zvuky extern Sound zvuk_zasah; extern Sound zvuk_kontakt; // zvuk sebrání některýho toho bonusu extern Sound zvuk_powerup; #define MAPA_MAX_VYSKA 10 #define NUTNA_VZDALENOST (BLOK_SIRKA * 20) // struktura levelu typedef struct Level { Duch ** duchove; size_t pocet_duchu; Duch ** burkinatori; size_t pocet_burkinatoru; // podobně jako duchy si vyrobíme i ty sebratelný věci Sebratelne ** sebratelne_veci; size_t pocet_sebratelnych_veci; Mapa * dlazdicova_mapa; } Level; typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { Level * lvl = (Level * )malloc ( sizeof ( Level ) ); Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); Duch ** duchove = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Duch ** burkinatori = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Sebratelne ** sebratelne_veci = (Sebratelne **)malloc ( sizeof ( Sebratelne * ) * 256 ); // enum který nám bude popisovat jednotlivý prvky mapy, // 'N' jakože nic, 'B' jakože blok, 'D' jakože duch // 'Q' jako burkinátor (vono se to prej správně piše ňák s kvé jakože 'burqa' nebo jak) // 'S' jao srdičko, 'H' jako hvězda enum herniVec {N,D,B,Q,S,H}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // musíme si tam dát ňáký ty bonusy // (zatim to nemáme vybalancovaný, hvězda by asi jako měla bejt víc zácnej bonus) int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg3 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,B,0,D,0, 0,D,0,B,0, B,B,B,B,B, B,B,B,B,B, }; int seg4 [] = { 0,0,0,0,0, B,0,0,0,B, }; int seg5 [] = { 0,0,0,0,0, B,0,Q,0,B, }; int seg6 [] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,B,0,B,0, 0,0,B,0,B,0,0,0,B,0,0,B,0,B,0, B,Q,B,Q,B,Q,0,Q,B,Q,0,B,Q,B,B, }; int seg7 [] = { 0,0,0,0,0,0,0,0,0,B,0,0,0,0,0, 0,0,0,0,0,0,0,0,B,B,0,0,0,0,0, 0,0,0,0,0,0,0,B,B,B,0,0,0,0,0, 0,0,0,0,0,0,B,B,B,B,0,0,0,0,0, 0,0,0,0,0,B,B,B,B,B,0,0,0,0,0, 0,0,0,0,B,B,B,B,B,B,0,0,0,0,0, 0,0,0,B,B,B,B,B,B,B,0,0,0,0,0, 0,0,B,B,B,B,B,B,B,B,0,0,0,0,0, 0,B,B,B,B,B,B,B,B,B,0,0,0,0,0, B,B,B,B,B,B,B,B,B,B,Q,Q,Q,Q,B, }; int seg8 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,D,D, D,D,0,0,0, 0,0,B,B,B, B,B,B,0,0, B,B,B,B,B, B,B,B,B,B, }; int seg9 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,H,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg10 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,S,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg11 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,D,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg12 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,B,0, 0,B,0,B,0, B,B,B,B,B }; int seg13 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,0,0,0,0, 0,B,B,D,0, D,0,D,0,B, B,B,B,B,B, B,B,B,B,B, }; SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {10,5,seg3}, ( SegmentMapy ) {5,2,seg4}, ( SegmentMapy ) {5,2,seg5}, ( SegmentMapy ) {15,5,seg6}, ( SegmentMapy ) {15,10,seg7}, ( SegmentMapy ) {10,5,seg8}, ( SegmentMapy ) {5,5,seg9}, ( SegmentMapy ) {5,5,seg10}, ( SegmentMapy ) {5,5,seg11}, ( SegmentMapy ) {5,5,seg12}, ( SegmentMapy ) {10,5,seg13}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); } // prvních a posledních deset sloupečků herní mapy bude placka, for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } int zbyva_delka = sirka - 10; size_t duchu = 0; size_t burkinatoru = 0; // počitat si budem i sebratelný věci pochopytelně :D ;D size_t sebratelnych_veci = 0; while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; case D: //vyrobíme na tý pozici ducha { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA - 161, }; Duch * duch = vygenerovatDucha ( pozice ); duchove[duchu++] = duch; } break; case Q: //vyrobíme burkinátora { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA + BLOK_VYSKA*3, }; Duch * duch = vygenerovatBurkinatora ( pozice ); burkinatori[burkinatoru++] = duch; } break; case S: //vyrobíme srdíčko { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNE_SRDICKO ); sebratelne_veci[sebratelnych_veci++] = vec; } break; case H: //vyrobíme bonusovou hvězdu { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNA_HVEZDA ); sebratelne_veci[sebratelnych_veci++] = vec; } break; default: break; }; } } zbyva_delka-=sirka_segmentu; } duchove = realloc ( duchove, sizeof ( Duch * ) * duchu ); burkinatori = realloc ( burkinatori, sizeof ( Duch * ) * burkinatoru ); // realokujem i ty sebratelný věci sebratelne_veci = realloc ( sebratelne_veci, sizeof ( Sebratelne * ) * sebratelnych_veci ); // nacpem duchy do tý struktury levelu lvl->pocet_duchu = duchu; lvl->duchove = duchove; // napcem tam i burkinátory lvl->pocet_burkinatoru = burkinatoru; lvl->burkinatori = burkinatori; // a nacpem tam taky bonusy lvl->pocet_sebratelnych_veci = sebratelnych_veci; lvl->sebratelne_veci = sebratelne_veci; // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; return lvl; } void freeLevel ( Level * lvl ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { freeDucha ( lvl->duchove[i] ); } free ( lvl->duchove ); for ( size_t i=0; i<lvl->pocet_burkinatoru; i++ ) { freeDucha ( lvl->burkinatori[i] ); } free ( lvl->burkinatori ); // musíme uvolnit taky sebratelný věci for ( size_t i=0; i<lvl->pocet_sebratelnych_veci; i++ ) { free ( lvl->sebratelne_veci[i] ); } free ( lvl->sebratelne_veci ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); free ( lvl ); } void aktualizovatLevel ( Level * lvl, David * david, float dt ) { Strela * strely = david->strely; for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if(fabsf ( david->pozice.x - lvl->duchove[i]->okraje.x ) > NUTNA_VZDALENOST) continue; if ( ! lvl->duchove[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->duchove[i]->hitbox ) ) { s->aktivni = false; lvl->duchove[i]->hp--; PlaySound ( zvuk_zasah ); } } } aktualizovatDucha ( lvl->duchove[i], lvl->dlazdicova_mapa, dt ); // pohlídáme si kolizi Davida s duchem, pokud se srazej tak duch Davida zraní // kolizi zistíme raylibí funkcí 'CheckCollisionRecs' do který nacpem hitboxy vobou herních entit if ( !lvl->duchove[i]->chcipe && CheckCollisionRecs ( lvl->duchove[i]->hitbox, david->hitbox ) ) { if ( david->zranitelny ) { // zahrajem zvuk kontaktu // (vlastně nvm jestli to má bejt jakože zvuk co vydává duch nebo david :D ) PlaySound ( zvuk_kontakt ); // vodečtem davidoj život david->zivoty--; // nastavíme davidoj blikací čas dočasný nezranitelnosti.. david->blikaci_cas = BLIKACI_CAS_DAVIDA; // ..a zapnem mu tu nezranitelnost david->zranitelny = false; // pokud má ňákou vertikální rychlost směrem nahoru k hornímu vokraji vobrazkovky, // tak mu ji snižime na nulu. Vono to vytváří takovej psychochologickej efekt jakože // hráče ty duchové chytaj a bráněj mu v pohybu :O ;D if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // přidáme si sem to zabíjení duchů tim hvězdičkovým bonusem else if ( david->ma_bonus ) { lvl->duchove[i]->hp = 0; } } for ( size_t j=0; j<ZASOBNIK_STREL_DUCHA; j++ ) { Strela * strela_ducha = &lvl->duchove[i]->strely[j]; if ( strela_ducha->aktivni ) { aktualizovatStrelu ( strela_ducha,lvl->dlazdicova_mapa, dt ); // podobně jako sme hlídali zásah hitboxu ducha davidovou střelou, // tak budeme klídat zásah davida střelou ducha if ( CheckCollisionPointRec ( strela_ducha->pozice, david->hitbox ) ) { strela_ducha->aktivni = false; if ( david->zranitelny ) { // v poctatě to samý jako při kontaktu PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; david->zranitelny = false; } } } } } // vicemeně skoro uplně stejně si sem přidáme aktualizaci burkinátorů, jako sme napsali aktualizaci vobyč duchů for ( size_t i =0; i<lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( david->pozice.x - lvl->burkinatori[i]->okraje.x ) > NUTNA_VZDALENOST ) continue; if ( ! lvl->burkinatori[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->burkinatori[i]->hitbox ) ) { s->aktivni = false; lvl->burkinatori[i]->hp--; PlaySound ( zvuk_zasah ); } } } // zkusíme kolizi s davidem if( ! lvl->burkinatori[i]->chcipe && CheckCollisionRecs ( lvl->burkinatori[i]->hitbox, david->hitbox )) { if ( david->zranitelny ) { PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // sem si taky přidáme to zabíjení burkinátorů magickou hvězdou else if ( david->ma_bonus ) { lvl->burkinatori[i]->hp = 0; } } aktualizovatBurkinatora ( lvl->burkinatori[i], david->pozice.x, dt ); } // a budeme aktualizovat sebratelný věci // kouknem jesli má věc minimální nutnou vzdálenost, jestli jo tak se kouknem jestli ji de sebrat a jestli má // kolizi s Davidovým hitboxem, jestli jo, tak ji David jakože sebere for ( size_t i = 0; i< lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( david->pozice.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { if ( !lvl->sebratelne_veci[i]->sebrano && CheckCollisionRecs ( lvl->sebratelne_veci[i]->okraje, david->hitbox ) ) { lvl->sebratelne_veci[i]->sebrano = true; PlaySound ( zvuk_powerup ); switch ( lvl->sebratelne_veci[i]->druh ) { case SEBRATELNA_HVEZDA: //hvězda aktivuje bonus a nastaví hvězdnej čas { david->ma_bonus = true; david->hvezdny_bonus_cas = HVEZDNY_CAS_DAVIDA; } break; case SEBRATELNE_SRDICKO: //srdičko přidá davidoj jeden život { david->zivoty++; // davidovy životy by asi jako možná neměli překročit maximum if ( david->zivoty > POCET_ZIVOTU_DAVIDA_MAX ) { david->zivoty = POCET_ZIVOTU_DAVIDA_MAX; } } break; default: break; } } aktualizovatSebratelnouVec ( lvl->sebratelne_veci[i],dt ); } } } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); // vykreslíme všecky viditelný duchy a všecky střely duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if ( fabsf ( kamera->target.x - lvl->duchove[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->duchove[i] ); } for ( size_t j =0; j<ZASOBNIK_STREL_DUCHA; j++ ) { vykreslitStrelu ( lvl->duchove[i]->strely + j ); } } // vykreslíme burkinátory for ( size_t i = 0; i < lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( kamera->target.x - lvl->burkinatori[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->burkinatori[i] ); } } // vykreslíme sebratelný věci for ( size_t i = 0; i < lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( kamera->target.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitSebratelnouVec ( lvl->sebratelne_veci[i] ); } } } #endif
V 'main.c' si přidáme zvuk sebrání bonusu a inicializujem Davidoj ty nový jeho atributy:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; Texture2D textura_ruzne; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; Sound zvuk_kontakt; Sound zvuk_padu; Sound zvuk_zaghrouta; Sound zvuk_powerup; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); textura_ruzne = LoadTexture ( "assets/misc.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); zvuk_kontakt = LoadSound ( "assets/kontakt.wav" ); zvuk_padu = LoadSound ( "assets/pad.wav" ); zvuk_zaghrouta = LoadSound ( "assets/zaghrouta.ogg" ); zvuk_powerup = LoadSound ( "assets/powerup.wav" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // nastavíme mapu na tu skovanou ve struktuře levelu .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidovy vnitřní časovače střílecího cooldownu a času blikání resp. dočasný // nezranitelnosti po nepřátelským zásahu ie. islamistickým duchem nebo rudou kulkou .strileci_cooldown = 0.0f, .blikaci_cas = 0.0f, // nastavíme počet životů na max a atribut zranitelnosti na true .zivoty = POCET_ZIVOTU_DAVIDA_MAX, .zranitelny = true, // nastavíme atributy vokolo bonusu .ma_bonus = false, .hvezdny_bonus_cas = 0.0f, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu a teďko nově i hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; while ( !WindowShouldClose() ) { float dt = GetFrameTime(); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; //aktualizujem Davida aktualizovatDavida(&david, dt); //aktualizujem level aktualizovatLevel ( level, &david, dt ); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // nakreslíme HUD - takový to herní menu co ukazuje počet životů, skóre a tak // (nám to vykresluje jenom počet životů) // po tom co sme vypli kameru, malujeme bez tý kamerový transformace, takže nebudem // mit problem trefit levej spodní vokraj vobrazovky kde si budeme malovat // takovou řadu srdíček který budou jakože ukazovat kolik Davidoj zbejvá životů // zrojová voblast srdička ve spritesheetu 'různé' const Rectangle srdicko_rect = {2,503,96,83}; // srdíček budem vykreslovat v řadě za sebou furt stejnej maximální počet, // akorát jenom když budem vykreslovat ty který by přesahovali aktualní počet Davidovejch // životů, tak je budeme malovat černý for ( int i = 0; i < POCET_ZIVOTU_DAVIDA_MAX; i++ ) { Rectangle cil = { .x = i*srdicko_rect.width/2, .y = GetScreenHeight() - srdicko_rect.height/2, .width = srdicko_rect.width/2, .height = srdicko_rect.height/2, }; DrawTexturePro ( textura_ruzne,srdicko_rect,cil, ( Vector2 ) { 0,0 },0.0f,i<david.zivoty? WHITE:BLACK ); } // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); UnloadTexture ( textura_ruzne ); // vypnem audio zařízení CloseAudioDevice(); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); UnloadSound ( zvuk_kontakt ); UnloadSound ( zvuk_padu ); UnloadSound ( zvuk_zaghrouta ); UnloadSound ( zvuk_powerup ); return 0; }
Chtělo by to ňákou hudbu asi 😁 😜 Zatim si tam přidáme dvě, jednu normální herní, druhou kterou pustíme dycky když bude David pod vlivem hvězdnýho bonusu (v tom márijoj to taky takle maj, když mario sebere hvězdu tak se taky dočasně přepne hudba).
V 'main.c' si načtem hudební streamy (musej se načíst a pak uvolnit uplně stejně jako zvuky nebo textury), vytvoříme si ukazatel jakože na aktuální zvolenej hudební stream a v herním loopu ho budem aktualizovat (se předpokládá že hudemní stream je víc věčí kus paměti takže ho asi jako nebudem načítat celej soubor najednou jako třeba vobyč zvuky, ale jak už jako napovídá to slovičko 'stream' v názvu, budem ho číst z disku po malejch kouskách v řadě za sebou. Noa to načitání malejch kousků nám zařizuje v raylibu tamta funkce 'UpdateMusicStream')
Takže si teda jako upravíme tamten 'main.c' soubor:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; Texture2D textura_ruzne; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; Sound zvuk_kontakt; Sound zvuk_padu; Sound zvuk_zaghrouta; Sound zvuk_powerup; // hudby // máme zatim dvě, hudba_lvl jakože levelu, která bude normálně hrát na pozadí, // druhá je hudba která bude hrát když David sebere bonus Music hudba_lvl; Music hudba_bonus; //ukazatel na právě hranou hudbu Music * hudba_aktualni; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); textura_ruzne = LoadTexture ( "assets/misc.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); zvuk_kontakt = LoadSound ( "assets/kontakt.wav" ); zvuk_padu = LoadSound ( "assets/pad.wav" ); zvuk_zaghrouta = LoadSound ( "assets/zaghrouta.ogg" ); zvuk_powerup = LoadSound ( "assets/powerup.wav" ); //načtem hudby hudba_lvl = LoadMusicStream ( "assets/waltz_of_the_ghosts.ogg" ); hudba_bonus = LoadMusicStream ( "assets/for_a_few_shekels_more_band.ogg" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // nastavíme mapu na tu skovanou ve struktuře levelu .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidovy vnitřní časovače střílecího cooldownu a času blikání resp. dočasný // nezranitelnosti po nepřátelským zásahu ie. islamistickým duchem nebo rudou kulkou .strileci_cooldown = 0.0f, .blikaci_cas = 0.0f, // nastavíme počet životů na max a atribut zranitelnosti na true .zivoty = POCET_ZIVOTU_DAVIDA_MAX, .zranitelny = true, // nastavíme atributy vokolo bonusu .ma_bonus = false, .hvezdny_bonus_cas = 0.0f, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu a teďko nově i hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; // nastavíme aktuální hudbu hudba_aktualni = &hudba_lvl; // zapnem normální hudbu levelu PlayMusicStream( hudba_lvl ); while ( !WindowShouldClose() ) { float dt = GetFrameTime(); //aktualizujeme hudbu, na kterou ukazuje ukazatel 'hudba_aktualni' UpdateMusicStream ( *hudba_aktualni ); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; //aktualizujem Davida aktualizovatDavida(&david, dt); //aktualizujem level aktualizovatLevel ( level, &david, dt ); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // nakreslíme HUD - takový to herní menu co ukazuje počet životů, skóre a tak // (nám to vykresluje jenom počet životů) // po tom co sme vypli kameru, malujeme bez tý kamerový transformace, takže nebudem // mit problem trefit levej spodní vokraj vobrazovky kde si budeme malovat // takovou řadu srdíček který budou jakože ukazovat kolik Davidoj zbejvá životů // zrojová voblast srdička ve spritesheetu 'různé' const Rectangle srdicko_rect = {2,503,96,83}; // srdíček budem vykreslovat v řadě za sebou furt stejnej maximální počet, // akorát jenom když budem vykreslovat ty který by přesahovali aktualní počet Davidovejch // životů, tak je budeme malovat černý for ( int i = 0; i < POCET_ZIVOTU_DAVIDA_MAX; i++ ) { Rectangle cil = { .x = i*srdicko_rect.width/2, .y = GetScreenHeight() - srdicko_rect.height/2, .width = srdicko_rect.width/2, .height = srdicko_rect.height/2, }; DrawTexturePro ( textura_ruzne,srdicko_rect,cil, ( Vector2 ) { 0,0 },0.0f,i<david.zivoty? WHITE:BLACK ); } // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); UnloadTexture ( textura_ruzne ); // vypnem audio zařízení CloseAudioDevice(); // musíme uvolnit i muziku UnloadMusicStream ( hudba_lvl ); UnloadMusicStream ( hudba_bonus ); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); UnloadSound ( zvuk_kontakt ); UnloadSound ( zvuk_padu ); UnloadSound ( zvuk_zaghrouta ); UnloadSound ( zvuk_powerup ); return 0; }
Do souboru 'level.h' přidáme do aktualizovávací funkce levelu přepínání aktualní zvolený hudby, přepínat budem na hudbu bonusu podle toho, jestli David sebral hvězdu a vypínat podle toho jestli už nemá aktivní atribut bonusu:
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" #include "duch.h" #include "sebratelne.h" // zvuky extern Sound zvuk_zasah; extern Sound zvuk_kontakt; extern Sound zvuk_powerup; // zpřístupníme si sem z 'main.c' hudby a ukazatel na aktuální muziku extern Music hudba_lvl; extern Music hudba_bonus; extern Music * hudba_aktualni; #define MAPA_MAX_VYSKA 10 #define NUTNA_VZDALENOST (BLOK_SIRKA * 20) // struktura levelu typedef struct Level { Duch ** duchove; size_t pocet_duchu; Duch ** burkinatori; size_t pocet_burkinatoru; // podobně jako duchy si vyrobíme i ty sebratelný věci Sebratelne ** sebratelne_veci; size_t pocet_sebratelnych_veci; Mapa * dlazdicova_mapa; } Level; typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { Level * lvl = (Level * )malloc ( sizeof ( Level ) ); Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); Duch ** duchove = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Duch ** burkinatori = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Sebratelne ** sebratelne_veci = (Sebratelne **)malloc ( sizeof ( Sebratelne * ) * 256 ); // enum který nám bude popisovat jednotlivý prvky mapy, // 'N' jakože nic, 'B' jakože blok, 'D' jakože duch // 'Q' jako burkinátor (vono se to prej správně piše ňák s kvé jakože 'burqa' nebo jak) // 'S' jao srdičko, 'H' jako hvězda enum herniVec {N,D,B,Q,S,H}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // musíme si tam dát ňáký ty bonusy // (zatim to nemáme vybalancovaný, hvězda by asi jako měla bejt víc zácnej bonus) int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg3 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,B,0,D,0, 0,D,0,B,0, B,B,B,B,B, B,B,B,B,B, }; int seg4 [] = { 0,0,0,0,0, B,0,0,0,B, }; int seg5 [] = { 0,0,0,0,0, B,0,Q,0,B, }; int seg6 [] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,B,0,B,0, 0,0,B,0,B,0,0,0,B,0,0,B,0,B,0, B,Q,B,Q,B,Q,0,Q,B,Q,0,B,Q,B,B, }; int seg7 [] = { 0,0,0,0,0,0,0,0,0,B,0,0,0,0,0, 0,0,0,0,0,0,0,0,B,B,0,0,0,0,0, 0,0,0,0,0,0,0,B,B,B,0,0,0,0,0, 0,0,0,0,0,0,B,B,B,B,0,0,0,0,0, 0,0,0,0,0,B,B,B,B,B,0,0,0,0,0, 0,0,0,0,B,B,B,B,B,B,0,0,0,0,0, 0,0,0,B,B,B,B,B,B,B,0,0,0,0,0, 0,0,B,B,B,B,B,B,B,B,0,0,0,0,0, 0,B,B,B,B,B,B,B,B,B,0,0,0,0,0, B,B,B,B,B,B,B,B,B,B,Q,Q,Q,Q,B, }; int seg8 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,D,D, D,D,0,0,0, 0,0,B,B,B, B,B,B,0,0, B,B,B,B,B, B,B,B,B,B, }; int seg9 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,H,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg10 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,S,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg11 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,D,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg12 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,B,0, 0,B,0,B,0, B,B,B,B,B }; int seg13 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,0,0,0,0, 0,B,B,D,0, D,0,D,0,B, B,B,B,B,B, B,B,B,B,B, }; SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {10,5,seg3}, ( SegmentMapy ) {5,2,seg4}, ( SegmentMapy ) {5,2,seg5}, ( SegmentMapy ) {15,5,seg6}, ( SegmentMapy ) {15,10,seg7}, ( SegmentMapy ) {10,5,seg8}, ( SegmentMapy ) {5,5,seg9}, ( SegmentMapy ) {5,5,seg10}, ( SegmentMapy ) {5,5,seg11}, ( SegmentMapy ) {5,5,seg12}, ( SegmentMapy ) {10,5,seg13}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); } // prvních a posledních deset sloupečků herní mapy bude placka, for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } int zbyva_delka = sirka - 10; size_t duchu = 0; size_t burkinatoru = 0; // počitat si budem i sebratelný věci pochopytelně :D ;D size_t sebratelnych_veci = 0; while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; case D: //vyrobíme na tý pozici ducha { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA - 161, }; Duch * duch = vygenerovatDucha ( pozice ); duchove[duchu++] = duch; } break; case Q: //vyrobíme burkinátora { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA + BLOK_VYSKA*3, }; Duch * duch = vygenerovatBurkinatora ( pozice ); burkinatori[burkinatoru++] = duch; } break; case S: //vyrobíme srdíčko { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNE_SRDICKO ); sebratelne_veci[sebratelnych_veci++] = vec; } break; case H: //vyrobíme bonusovou hvězdu { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNA_HVEZDA ); sebratelne_veci[sebratelnych_veci++] = vec; } break; default: break; }; } } zbyva_delka-=sirka_segmentu; } duchove = realloc ( duchove, sizeof ( Duch * ) * duchu ); burkinatori = realloc ( burkinatori, sizeof ( Duch * ) * burkinatoru ); // realokujem i ty sebratelný věci sebratelne_veci = realloc ( sebratelne_veci, sizeof ( Sebratelne * ) * sebratelnych_veci ); // nacpem duchy do tý struktury levelu lvl->pocet_duchu = duchu; lvl->duchove = duchove; // napcem tam i burkinátory lvl->pocet_burkinatoru = burkinatoru; lvl->burkinatori = burkinatori; // a nacpem tam taky bonusy lvl->pocet_sebratelnych_veci = sebratelnych_veci; lvl->sebratelne_veci = sebratelne_veci; // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; return lvl; } void freeLevel ( Level * lvl ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { freeDucha ( lvl->duchove[i] ); } free ( lvl->duchove ); for ( size_t i=0; i<lvl->pocet_burkinatoru; i++ ) { freeDucha ( lvl->burkinatori[i] ); } free ( lvl->burkinatori ); // musíme uvolnit taky sebratelný věci for ( size_t i=0; i<lvl->pocet_sebratelnych_veci; i++ ) { free ( lvl->sebratelne_veci[i] ); } free ( lvl->sebratelne_veci ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); free ( lvl ); } void aktualizovatLevel ( Level * lvl, David * david, float dt ) { Strela * strely = david->strely; for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if(fabsf ( david->pozice.x - lvl->duchove[i]->okraje.x ) > NUTNA_VZDALENOST) continue; if ( ! lvl->duchove[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->duchove[i]->hitbox ) ) { s->aktivni = false; lvl->duchove[i]->hp--; PlaySound ( zvuk_zasah ); } } } aktualizovatDucha ( lvl->duchove[i], lvl->dlazdicova_mapa, dt ); // pohlídáme si kolizi Davida s duchem, pokud se srazej tak duch Davida zraní // kolizi zistíme raylibí funkcí 'CheckCollisionRecs' do který nacpem hitboxy vobou herních entit if ( !lvl->duchove[i]->chcipe && CheckCollisionRecs ( lvl->duchove[i]->hitbox, david->hitbox ) ) { if ( david->zranitelny ) { // zahrajem zvuk kontaktu // (vlastně nvm jestli to má bejt jakože zvuk co vydává duch nebo david :D ) PlaySound ( zvuk_kontakt ); // vodečtem davidoj život david->zivoty--; // nastavíme davidoj blikací čas dočasný nezranitelnosti.. david->blikaci_cas = BLIKACI_CAS_DAVIDA; // ..a zapnem mu tu nezranitelnost david->zranitelny = false; // pokud má ňákou vertikální rychlost směrem nahoru k hornímu vokraji vobrazkovky, // tak mu ji snižime na nulu. Vono to vytváří takovej psychochologickej efekt jakože // hráče ty duchové chytaj a bráněj mu v pohybu :O ;D if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // přidáme si sem to zabíjení duchů tim hvězdičkovým bonusem else if ( david->ma_bonus ) { lvl->duchove[i]->hp = 0; } } for ( size_t j=0; j<ZASOBNIK_STREL_DUCHA; j++ ) { Strela * strela_ducha = &lvl->duchove[i]->strely[j]; if ( strela_ducha->aktivni ) { aktualizovatStrelu ( strela_ducha,lvl->dlazdicova_mapa, dt ); // podobně jako sme hlídali zásah hitboxu ducha davidovou střelou, // tak budeme klídat zásah davida střelou ducha if ( CheckCollisionPointRec ( strela_ducha->pozice, david->hitbox ) ) { strela_ducha->aktivni = false; if ( david->zranitelny ) { // v poctatě to samý jako při kontaktu PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; david->zranitelny = false; } } } } } // vicemeně skoro uplně stejně si sem přidáme aktualizaci burkinátorů, jako sme napsali aktualizaci vobyč duchů for ( size_t i =0; i<lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( david->pozice.x - lvl->burkinatori[i]->okraje.x ) > NUTNA_VZDALENOST ) continue; if ( ! lvl->burkinatori[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->burkinatori[i]->hitbox ) ) { s->aktivni = false; lvl->burkinatori[i]->hp--; PlaySound ( zvuk_zasah ); } } } // zkusíme kolizi s davidem if( ! lvl->burkinatori[i]->chcipe && CheckCollisionRecs ( lvl->burkinatori[i]->hitbox, david->hitbox )) { if ( david->zranitelny ) { PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // sem si taky přidáme to zabíjení burkinátorů magickou hvězdou else if ( david->ma_bonus ) { lvl->burkinatori[i]->hp = 0; } } aktualizovatBurkinatora ( lvl->burkinatori[i], david->pozice.x, dt ); } // jestli David už nemá bonus ale furt je aktivní hudba bonusu, // tak zapnem normální hudbu levelu a nastavíme ji jako aktualní hudbu // (hudbu bonusu nemusíme vypínat, tim že přepnem ukazatel ji stejně nebudem aktualizovat) if ( !david->ma_bonus && hudba_aktualni == &hudba_bonus ) { PlayMusicStream ( hudba_lvl ); hudba_aktualni = &hudba_lvl; } // a budeme aktualizovat sebratelný věci // kouknem jesli má věc minimální nutnou vzdálenost, jestli jo tak se kouknem jestli ji de sebrat a jestli má // kolizi s Davidovým hitboxem, jestli jo, tak ji David jakože sebere for ( size_t i = 0; i< lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( david->pozice.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { if ( !lvl->sebratelne_veci[i]->sebrano && CheckCollisionRecs ( lvl->sebratelne_veci[i]->okraje, david->hitbox ) ) { lvl->sebratelne_veci[i]->sebrano = true; PlaySound ( zvuk_powerup ); switch ( lvl->sebratelne_veci[i]->druh ) { case SEBRATELNA_HVEZDA: //hvězda aktivuje bonus a nastaví hvězdnej čas { david->ma_bonus = true; david->hvezdny_bonus_cas = HVEZDNY_CAS_DAVIDA; // pauznem hudbu levelu // (pozor, sou tam dvě funkce, pause stream a stop stream. Když pustíme pauznutou hudbu tak přehrávání // pokračuje vod místa kde přestalo, stopnutá hraje vod začátku) PauseMusicStream ( hudba_lvl ); // přetočíme hudbu bonusu na čas vodpovidajicí 24.4 sekund // ( v david.h jestli si jakoby pamatujete sme nastavovali takovej divnej čas trvání hvězdnýho bonusu. // Je to kuli trvání useku hudby, kterej trvá právě vod těch 24.4 s až 24.4 + nějakejch těch čtrnáct seknud) SeekMusicStream ( hudba_bonus, 24.4f ); //začnem hrát hudbu bonusu a nastavíme na ni ukazatel aktualní hudby PlayMusicStream ( hudba_bonus ); hudba_aktualni = &hudba_bonus; } break; case SEBRATELNE_SRDICKO: //srdičko přidá davidoj jeden život { david->zivoty++; // davidovy životy by asi jako možná neměli překročit maximum if ( david->zivoty > POCET_ZIVOTU_DAVIDA_MAX ) { david->zivoty = POCET_ZIVOTU_DAVIDA_MAX; } } break; default: break; } } aktualizovatSebratelnouVec ( lvl->sebratelne_veci[i],dt ); } } } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); // vykreslíme všecky viditelný duchy a všecky střely duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if ( fabsf ( kamera->target.x - lvl->duchove[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->duchove[i] ); } for ( size_t j =0; j<ZASOBNIK_STREL_DUCHA; j++ ) { vykreslitStrelu ( lvl->duchove[i]->strely + j ); } } // vykreslíme burkinátory for ( size_t i = 0; i < lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( kamera->target.x - lvl->burkinatori[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->burkinatori[i] ); } } // vykreslíme sebratelný věci for ( size_t i = 0; i < lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( kamera->target.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitSebratelnouVec ( lvl->sebratelne_veci[i] ); } } } #endif
Takže zkompilujte a našponujte uši 🎶 👂 😁 😜
Ten svět ve kterým se David pohybuje je takovej děsně prázdnej 😮 🙄 Přidáme na pozadí ten velkej horrorověhovorkovej velkej měsic z prvního kroku, před nim budem vykreslovat takovou texturu siluety jeruzalemskýho hradu. Texturu hradu vykreslíme trošku věčí než bude naše herní vobrazovka a budem texturou hejbat vopačným směrem, než se bude pohybovat David - když David pude doprava, budem texturou hejbat doleva, když david poleze nahoru, budem texturu posouvat dolu, Tomudlectomu triku se řiká hele 'paralaxní scrolling' prej 😮 😜.
Tim dosáhnem iluze takovýho jakože mirně trojrozměrnýho světa, páč v normálním světě to taky takle funguje 😮 😜 Si přectavte že uhanite někam svým 6-ti válcem s uhlikovou stopou jakou má dohromady celý mladý piráctvo a protože se vam do cesty připlete nějakej vožralej strejček na kole, nevosvětlenej koloběžkář nebo tvl ňákej pitoma kolečkovejch bruslích dokonce a protože se to nedá předjet a začnete se za volantem strašně nudit a misto věnování se řízení budete z nudy koukat bokem z vokýnka (tvl ty cyklisti a jiný si neuvědomujou jak děsně moc riskujou tim svým věčným vybržďováním, jednou ňákýho zplacatim a vlastně zato ani nebudu moct 😮 😅) jak venku ubihá krajinka. Nejvíc nejrychlejc se budou pohybovat stromy někde hnedka u silnice, trošku pomalejc se budou pohybovat vod silnice trošku zdálenější věci jako třeba domečky, víc pomalej se bude hejbat hodně zdálenej les někde na kopci noa uplně pomaličku se budou hejbat hory děsně dáááááááááleko na horizontu, ty budou skoro nehejbavý. Uděláme to podobně, nejvíc nejrychlejc se bude hejbat David po herní mapě, za nim se pomalejc bude jebat ta silueta hradu noa měsic necháme statickej, páč je jakoby táák moc daleko a táák moc velikej, že se Davidovo hejbání po tý mapě na poloze měsice vubec nemuže projevit 😮 😜
Máme jenom takovej jednoduchej parallax, nacpem ho rovnou jenom do souboru 'main.c':
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; Texture2D textura_ruzne; Texture2D textura_hrad; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; Sound zvuk_kontakt; Sound zvuk_padu; Sound zvuk_zaghrouta; Sound zvuk_powerup; // hudby // máme zatim dvě, hudba_lvl jakože levelu, která bude normálně hrát na pozadí, // druhá je hudba která bude hrát když David sebere bonus Music hudba_lvl; Music hudba_bonus; //ukazatel na právě hranou hudbu Music * hudba_aktualni; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); textura_ruzne = LoadTexture ( "assets/misc.png" ); textura_hrad = LoadTexture ( "assets/hrad2.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); zvuk_kontakt = LoadSound ( "assets/kontakt.wav" ); zvuk_padu = LoadSound ( "assets/pad.wav" ); zvuk_zaghrouta = LoadSound ( "assets/zaghrouta.ogg" ); zvuk_powerup = LoadSound ( "assets/powerup.wav" ); //načtem hudby hudba_lvl = LoadMusicStream ( "assets/waltz_of_the_ghosts.ogg" ); hudba_bonus = LoadMusicStream ( "assets/for_a_few_shekels_more_band.ogg" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); //alokujeme si 'pole' střel pomocí funkce calloc (se vod malloc liší tim, že nám alokovanou paměť vynuluje, // první argument je počet alokovanejch struktur, druhej velikost jedný tý struktury) david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; // kdyžuž máme vyrobený animace, tak si mužeme vyrobit Davida David david = { .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, // necháme ho na herní mapu spadnou z vejšky .pozice = {0,0}, .smer = 1, // nastavíme mapu na tu skovanou ve struktuře levelu .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, // nastavíme ukazatel střel na ty naše callocem vygenerovaný střely .strely = david_strely, //vynulujeme davidovy vnitřní časovače střílecího cooldownu a času blikání resp. dočasný // nezranitelnosti po nepřátelským zásahu ie. islamistickým duchem nebo rudou kulkou .strileci_cooldown = 0.0f, .blikaci_cas = 0.0f, // nastavíme počet životů na max a atribut zranitelnosti na true .zivoty = POCET_ZIVOTU_DAVIDA_MAX, .zranitelny = true, // nastavíme atributy vokolo bonusu .ma_bonus = false, .hvezdny_bonus_cas = 0.0f, }; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu a teďko nově i hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; // nastavíme aktuální hudbu hudba_aktualni = &hudba_lvl; // zapnem normální hudbu levelu PlayMusicStream( hudba_lvl ); while ( !WindowShouldClose() ) { float dt = GetFrameTime(); //aktualizujeme hudbu, na kterou ukazuje ukazatel 'hudba_aktualni' UpdateMusicStream ( *hudba_aktualni ); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; //aktualizujem Davida aktualizovatDavida(&david, dt); //aktualizujem level aktualizovatLevel ( level, &david, dt ); // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // první kus našeho parallaxu // hovorka se asi jako bojí děsně velkejch měsiců když je furt v horrorovejch komixech kreslí, // tak mu uděláme radost na na pozadí vykreslíme supr strašidelnej měsíc :D :D float mensi_rozmer = MIN ( GetRenderWidth(),GetRenderHeight() ); DrawTexturePro ( textura_mesic, ( Rectangle ) { 0,0,textura_mesic.width,textura_mesic.height }, ( Rectangle ) { GetRenderWidth() /2,GetRenderHeight() /2,mensi_rozmer*1.5, mensi_rozmer*1.5 }, ( Vector2 ) { mensi_rozmer*1.5/2,mensi_rozmer*1.5/2 },0, ( Color ) { 102, 191, 255, 128 } ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); //vykreslíme parallax // to je jakože takový pozadí, který se různě pomaličku posouvá, když se hráč pohybuje na mapě a vytváří to // víc lepší iluzi že se hráč jakože někam pohybuje // todle je zatim dělaný jentak na hrubo, vykresluje to takovou siluletu jeruzalémskýho hradu která se děsně pomaličku posouvá // když David poskakuje nebo de dopředu // velikost tý textury hradu Rectangle hrad_zdroj = {0,0,1024,512}; // cílovej vobdelnik hradu // uděláme ho 2x delší než je šířka vobrazovky a budem // (asi to nebude moc dobře fungovat na uzkejch vobrazovkách, tam by sme asi jako museli zvolit // ňákej uplně jinej koncept, nvm) Rectangle hrad_cil = { .x = kamera.target.x - GetRenderWidth() /priblizeni, .y = kamera.target.y - GetRenderWidth() /2/priblizeni, // vodelnik taky bude muset bejt 2x širší než vyšší, by se nám moc nezdeformovala textura .width = GetRenderWidth() /priblizeni*2, .height = GetRenderWidth() /2/priblizeni*2, }; // počátek/origin toho velkýho rectanglu budem určovat z mezí vobrazovky // musíme si pohlídat když hráč stojí u kraje mapy by nám tam nevylezla někam doprostředka vobrazovky hrana textury :D float hrad_y_posun_paralax = -300.0f + 300.0f * kamera.target.y/kamera_target_max_y; float hrad_x_posun_paralax = -GetRenderWidth() /priblizeni/2 + GetRenderWidth() /priblizeni * ( kamera.target.x - kamera_target_min_x ) /kamera_target_max_x; // noa vykreslíme ten náš parallax DrawTexturePro ( textura_hrad, hrad_zdroj, hrad_cil, ( Vector2 ) { hrad_x_posun_paralax,hrad_y_posun_paralax },0,WHITE ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); // nakreslíme HUD - takový to herní menu co ukazuje počet životů, skóre a tak // (nám to vykresluje jenom počet životů) // po tom co sme vypli kameru, malujeme bez tý kamerový transformace, takže nebudem // mit problem trefit levej spodní vokraj vobrazovky kde si budeme malovat // takovou řadu srdíček který budou jakože ukazovat kolik Davidoj zbejvá životů // zrojová voblast srdička ve spritesheetu 'různé' const Rectangle srdicko_rect = {2,503,96,83}; // srdíček budem vykreslovat v řadě za sebou furt stejnej maximální počet, // akorát jenom když budem vykreslovat ty který by přesahovali aktualní počet Davidovejch // životů, tak je budeme malovat černý for ( int i = 0; i < POCET_ZIVOTU_DAVIDA_MAX; i++ ) { Rectangle cil = { .x = i*srdicko_rect.width/2, .y = GetScreenHeight() - srdicko_rect.height/2, .width = srdicko_rect.width/2, .height = srdicko_rect.height/2, }; DrawTexturePro ( textura_ruzne,srdicko_rect,cil, ( Vector2 ) { 0,0 },0.0f,i<david.zivoty? WHITE:BLACK ); } // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); UnloadTexture ( textura_ruzne ); UnloadTexture ( textura_hrad ); // vypnem audio zařízení CloseAudioDevice(); // musíme uvolnit i muziku UnloadMusicStream ( hudba_lvl ); UnloadMusicStream ( hudba_bonus ); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); UnloadSound ( zvuk_kontakt ); UnloadSound ( zvuk_padu ); UnloadSound ( zvuk_zaghrouta ); UnloadSound ( zvuk_powerup ); return 0; }
Vyrobíme si pro hru takový jednoduchý hlavní menu, vlastně to ani žádný menu nebude páč tam jenom budem čekat na máčknutí čudliku 'enter' na klávesnici. Celý to bude jakože na židovským hřbitově, vepředu se bude za náhrobkem skovávat David s flintou, na pozadí budou levitovat duchové, noa nad tim všim bude nápis s názvem tý hry a pod tim blikavej text s nápisem že má jakože hráč máčknout enter. Až hráč enter máčkne, tak se přepnem normálně do hry, respektive menu nám při máčknutí entru vygeneruje level a v tom hlavním while loopu nás pustí dál.
Pak si taky vyrobíme ňákou uplně jednoduchou vobrazovku s textem 'game over' kterou budem zobrazovat při prohře, jakože když Davidoj dojdou všecky životy. Uděláme ji tak že budem furt vykresovat level ale už ho nebudem aktualizovat, překrejem ho ňákým černým polopruhledným smutečním vobdelnikem a přes něj vykreslíme ten text (z textury, raylib má sice možnosti rendorvat text z fontu, ale nevidim tam žádnou jednoduchou možnost ziskat budoucí rozměry vyrendrovanýho textu. Nastavit origin redrovanýmu textu de, ale neznáme ty jeho rozměry by sme ho kecli někam na správný misto, asi si to máme sami ňák vypočitat z vlastností fontu a na to sou lidi moc líný, dyď to vidite na mě, taky jako rači hnedka použiju texturu 😁 😁) a vyzvem hráče znova k máčknutí enteru. Když to udělá tak se vrátíme zpátky do toho hlavního menu.
(raylib má prej ňákou svou knihovničku a nástroj na vyrábění gui, to bysme mohli vyzkoušet někdy příště jestli se vam tednlecten protiduchovní antiterroristickej blogisek bude libit)
Nejdřiv si vyrobíme to menu, nóó takže zase vyrobíme novej soubor 'menu.h' v tý složšce 'src' a nacpem do něj ňákej takovejdle zdroják, kterej bude primárně sloužit k vykreslování menu (zatim takovej jakože nástřel jenom pro defaultní rozlišení, eště s tou podobou nejsem uplně spokojená a asi to celý překopu):
#ifndef _DAVID_A_DUCHOVE_MENU_H_ #define _DAVID_A_DUCHOVE_MENU_H_ #include <raylib.h> #include <stdlib.h> // budeme potřebovat hodně vobrázků extern Texture2D textura_duchove_spritesheet; extern Texture2D textura_menu_titulek; extern Texture2D textura_menu_pozadi; extern Texture2D textura_menu_pozadi2; extern Texture2D textura_menu_david; extern Texture2D textura_menu_zmackni_enter; // počet duchů vykreslenejch v menu // duchové maj jakoby levitovat po řbitově #define MENU_DUCHU 6 // pro každýho ducha si budem pamatovat jeho texturu.... Rectangle menu_duchove [MENU_DUCHU]; // ....ypsilonovej posun.... float menu_y_duchu [MENU_DUCHU]; // ....a ypsilonovou rychlost float menu_rychlost_duchu [MENU_DUCHU]; // pro efekt blikání textu (a vlastně i houpání Davida) si budem počítat const float menu_fade_max = 100.0f; float menu_fade = menu_fade_max; // jestli k blikací proměný deltu přičitame nebo vodčitáme int menu_fade_smer=-1; void vygenerovatMenu() { // nastavíme duchům nahodnou texturu, ypsilonovou polohu i rychlost for ( size_t i=0; i<MENU_DUCHU; i++ ) { menu_duchove[i]= ( Rectangle ) { 0 + 85 * ( rand() %6 ),0 + 240 * ( rand() %2 ),85,240 }; menu_y_duchu[i] = ( float ) GetRandomValue ( 250,350 ); menu_rychlost_duchu[i] = ( float ) GetRandomValue ( 100,150 ); } } void aktualizovatVykreslitMenu ( float dt ) { // větší rozměr z šiřky a vyšky vobrazovky float vetsi_rozmer = MAX ( GetRenderWidth(),GetRenderHeight() ); // vykreslíme ten fialovej řbitov na pozadí DrawTexturePro ( textura_menu_pozadi, ( Rectangle ) { 0,0,textura_menu_pozadi.width,textura_menu_pozadi.height }, ( Rectangle ) { GetRenderWidth() /2,GetRenderHeight() /2,vetsi_rozmer, vetsi_rozmer }, ( Vector2 ) { vetsi_rozmer/2,vetsi_rozmer/2 },0,WHITE ); // minimálnáí a maximální vyspilonová vejška ducha const float min_vejska = 250.0f; const float max_vejska = 350.0f; // střed ypsilonový trajektorie ducha const float stred_y = ( max_vejska + min_vejska ) /2.0f; // maximální možná zdálenost kterou duch muže mit vod tamtoho ypsilonovýho středu tý trajektorie const float max_vzdalenost = max_vejska - stred_y; // šiřka a vejška ducha const float duch_sirka = GetRenderWidth() / ( MENU_DUCHU ); const float duch_vyska = duch_sirka / ( 85.0f/240.0f ); for ( size_t i=0; i<MENU_DUCHU; i++ ) { // aktualizace ypsilonovejch poloch a ryhclostí duchů // (je tam přidaný brždění podle zdálenosti vod středu tý jejich ypsilonový trajektorie by se jakože houpali plynule) menu_y_duchu[i] += ( menu_rychlost_duchu[i]/4 + menu_rychlost_duchu[i] * ( 1.0f - fabsf ( menu_y_duchu[i] - stred_y ) /max_vzdalenost ) ) *dt; if ( menu_y_duchu[i]>max_vejska ) { menu_y_duchu[i] = max_vejska; menu_rychlost_duchu[i]=-fabsf ( menu_rychlost_duchu[i] ); } else if ( menu_y_duchu[i]<min_vejska ) { menu_y_duchu[i] = min_vejska; menu_rychlost_duchu[i]=fabsf ( menu_rychlost_duchu[i] ); } DrawTexturePro ( textura_duchove_spritesheet, menu_duchove[i], ( Rectangle ) {GetRenderWidth() / ( MENU_DUCHU ) * i,menu_y_duchu[i],duch_sirka, duch_vyska}, ( Vector2 ) { 0,0 },0.0f, WHITE ); } // vykreslíme hrobečky DrawTexturePro ( textura_menu_pozadi2, ( Rectangle ) { 0,0,textura_menu_pozadi2.width,textura_menu_pozadi2.height }, ( Rectangle ) { GetRenderWidth() /2,GetRenderHeight() /2,vetsi_rozmer, vetsi_rozmer }, ( Vector2 ) { vetsi_rozmer/2,vetsi_rozmer/2 },0,WHITE ); // vykreslíme stín Davida // (jak stín tak i David se budou trošku houpat (měnit svuj uhel) podle tý fade proměný) DrawTexturePro ( textura_menu_david, ( Rectangle ) { 0,0,textura_menu_david.width,textura_menu_david.height }, ( Rectangle ) { vetsi_rozmer/4- 40 + ( 10* ( menu_fade/menu_fade_max ) ),GetRenderHeight()+ vetsi_rozmer/8,vetsi_rozmer/2, vetsi_rozmer/2 }, ( Vector2 ) { vetsi_rozmer/4,vetsi_rozmer/2 },menu_fade/menu_fade_max * 3 - 1.5f,Fade ( BLACK,0.25f ) ); // vykreslíme normálního Davida DrawTexturePro ( textura_menu_david, ( Rectangle ) { 0,0,textura_menu_david.width,textura_menu_david.height }, ( Rectangle ) { vetsi_rozmer/4,GetRenderHeight()+ vetsi_rozmer/8,vetsi_rozmer/2, vetsi_rozmer/2 }, ( Vector2 ) { vetsi_rozmer/4,vetsi_rozmer/2 },menu_fade/menu_fade_max * 3 - 1.5f,WHITE ); // vykreslíme titulek DrawTexture ( textura_menu_titulek,GetRenderWidth() /2 - textura_menu_titulek.width/2,0,WHITE ); // aktualizujeme tu fadeovací proměnou // (asi by sme mohli nahradit sinusovkou nebo něčim a mit to hladčejší a bez tý 'směrový' proměný) menu_fade+=menu_fade_smer * dt * 250.0f; if ( menu_fade<=0.0f ) { menu_fade_smer = 1; } else if ( menu_fade>=menu_fade_max ) { menu_fade_smer = -1; } // vykreslíme nápis s tim mačkáním enteru DrawTexture ( textura_menu_zmackni_enter,GetRenderWidth() /2 - textura_menu_zmackni_enter.width/2,GetRenderHeight() - textura_menu_zmackni_enter.height,Fade ( WHITE,0.5f + 0.5f * menu_fade/menu_fade_max ) ); } #endif
Do 'level.h' si přidáme enum s možnejma návratovejma hodnotama funkce aktualizace levelu noa upravíme tu funkci by nám tydlecty hodnoty vracela a dávala takle vědět hlavnímu hernímu loopu co se jakože děje a jak má přepinat hudby a ty vobrazovky. Budem mit tři návratový různý hodnoty, jednu pro výhru, druhou pro prohru noa třetí pro pokračování hry. Zatim nebudem zohledňovat výhru, jenom budem vracet kód prohry když Davidoj dojdou životy, ve vostatních připadech kód pokračování hry. Takže si teda jako upravíme ten soubor 'level.h':
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" #include "duch.h" #include "sebratelne.h" // zvuky extern Sound zvuk_zasah; extern Sound zvuk_kontakt; extern Sound zvuk_powerup; extern Music hudba_lvl; extern Music hudba_bonus; extern Music * hudba_aktualni; #define MAPA_MAX_VYSKA 10 #define NUTNA_VZDALENOST (BLOK_SIRKA * 20) // návratový stavy aktululizace levelu // LVL_POKRACUJE znamená že level neskončil a hraje se dál, // LVL_VYHRA znamená že david vyhrál, // LVL_PROHRA znamená že david umřel, v 'main.c' na to budem reagovat aktivováním // tý game over vobrazovky enum LevelStatus {LVL_POKRACUJE, LVL_VYHRA, LVL_PROHRA}; // struktura levelu typedef struct Level { Duch ** duchove; size_t pocet_duchu; Duch ** burkinatori; size_t pocet_burkinatoru; // podobně jako duchy si vyrobíme i ty sebratelný věci Sebratelne ** sebratelne_veci; size_t pocet_sebratelnych_veci; Mapa * dlazdicova_mapa; } Level; typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { Level * lvl = (Level * )malloc ( sizeof ( Level ) ); Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); Duch ** duchove = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Duch ** burkinatori = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Sebratelne ** sebratelne_veci = (Sebratelne **)malloc ( sizeof ( Sebratelne * ) * 256 ); // enum který nám bude popisovat jednotlivý prvky mapy, // 'N' jakože nic, 'B' jakože blok, 'D' jakože duch // 'Q' jako burkinátor (vono se to prej správně piše ňák s kvé jakože 'burqa' nebo jak) // 'S' jao srdičko, 'H' jako hvězda enum herniVec {N,D,B,Q,S,H}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // musíme si tam dát ňáký ty bonusy // (zatim to nemáme vybalancovaný, hvězda by asi jako měla bejt víc zácnej bonus) int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg3 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,B,0,D,0, 0,D,0,B,0, B,B,B,B,B, B,B,B,B,B, }; int seg4 [] = { 0,0,0,0,0, B,0,0,0,B, }; int seg5 [] = { 0,0,0,0,0, B,0,Q,0,B, }; int seg6 [] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,B,0,B,0, 0,0,B,0,B,0,0,0,B,0,0,B,0,B,0, B,Q,B,Q,B,Q,0,Q,B,Q,0,B,Q,B,B, }; int seg7 [] = { 0,0,0,0,0,0,0,0,0,B,0,0,0,0,0, 0,0,0,0,0,0,0,0,B,B,0,0,0,0,0, 0,0,0,0,0,0,0,B,B,B,0,0,0,0,0, 0,0,0,0,0,0,B,B,B,B,0,0,0,0,0, 0,0,0,0,0,B,B,B,B,B,0,0,0,0,0, 0,0,0,0,B,B,B,B,B,B,0,0,0,0,0, 0,0,0,B,B,B,B,B,B,B,0,0,0,0,0, 0,0,B,B,B,B,B,B,B,B,0,0,0,0,0, 0,B,B,B,B,B,B,B,B,B,0,0,0,0,0, B,B,B,B,B,B,B,B,B,B,Q,Q,Q,Q,B, }; int seg8 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,D,D, D,D,0,0,0, 0,0,B,B,B, B,B,B,0,0, B,B,B,B,B, B,B,B,B,B, }; int seg9 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,H,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg10 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,S,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg11 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,D,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg12 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,B,0, 0,B,0,B,0, B,B,B,B,B }; int seg13 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,0,0,0,0, 0,B,B,D,0, D,0,D,0,B, B,B,B,B,B, B,B,B,B,B, }; SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {10,5,seg3}, ( SegmentMapy ) {5,2,seg4}, ( SegmentMapy ) {5,2,seg5}, ( SegmentMapy ) {15,5,seg6}, ( SegmentMapy ) {15,10,seg7}, ( SegmentMapy ) {10,5,seg8}, ( SegmentMapy ) {5,5,seg9}, ( SegmentMapy ) {5,5,seg10}, ( SegmentMapy ) {5,5,seg11}, ( SegmentMapy ) {5,5,seg12}, ( SegmentMapy ) {10,5,seg13}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); } // prvních a posledních deset sloupečků herní mapy bude placka, for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } int zbyva_delka = sirka - 10; size_t duchu = 0; size_t burkinatoru = 0; // počitat si budem i sebratelný věci pochopytelně :D ;D size_t sebratelnych_veci = 0; while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; case D: //vyrobíme na tý pozici ducha { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA - 161, }; Duch * duch = vygenerovatDucha ( pozice ); duchove[duchu++] = duch; } break; case Q: //vyrobíme burkinátora { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA + BLOK_VYSKA*3, }; Duch * duch = vygenerovatBurkinatora ( pozice ); burkinatori[burkinatoru++] = duch; } break; case S: //vyrobíme srdíčko { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNE_SRDICKO ); sebratelne_veci[sebratelnych_veci++] = vec; } break; case H: //vyrobíme bonusovou hvězdu { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNA_HVEZDA ); sebratelne_veci[sebratelnych_veci++] = vec; } break; default: break; }; } } zbyva_delka-=sirka_segmentu; } duchove = realloc ( duchove, sizeof ( Duch * ) * duchu ); burkinatori = realloc ( burkinatori, sizeof ( Duch * ) * burkinatoru ); // realokujem i ty sebratelný věci sebratelne_veci = realloc ( sebratelne_veci, sizeof ( Sebratelne * ) * sebratelnych_veci ); // nacpem duchy do tý struktury levelu lvl->pocet_duchu = duchu; lvl->duchove = duchove; // napcem tam i burkinátory lvl->pocet_burkinatoru = burkinatoru; lvl->burkinatori = burkinatori; // a nacpem tam taky bonusy lvl->pocet_sebratelnych_veci = sebratelnych_veci; lvl->sebratelne_veci = sebratelne_veci; // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; return lvl; } void freeLevel ( Level * lvl ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { freeDucha ( lvl->duchove[i] ); } free ( lvl->duchove ); for ( size_t i=0; i<lvl->pocet_burkinatoru; i++ ) { freeDucha ( lvl->burkinatori[i] ); } free ( lvl->burkinatori ); // musíme uvolnit taky sebratelný věci for ( size_t i=0; i<lvl->pocet_sebratelnych_veci; i++ ) { free ( lvl->sebratelne_veci[i] ); } free ( lvl->sebratelne_veci ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); free ( lvl ); } // aktualizovatLevel teďko vrací navratovou hodnotu enum LevelStatus aktualizovatLevel ( Level * lvl, David * david, float dt ) { Strela * strely = david->strely; for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if(fabsf ( david->pozice.x - lvl->duchove[i]->okraje.x ) > NUTNA_VZDALENOST) continue; if ( ! lvl->duchove[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->duchove[i]->hitbox ) ) { s->aktivni = false; lvl->duchove[i]->hp--; PlaySound ( zvuk_zasah ); } } } aktualizovatDucha ( lvl->duchove[i], lvl->dlazdicova_mapa, dt ); // pohlídáme si kolizi Davida s duchem, pokud se srazej tak duch Davida zraní // kolizi zistíme raylibí funkcí 'CheckCollisionRecs' do který nacpem hitboxy vobou herních entit if ( !lvl->duchove[i]->chcipe && CheckCollisionRecs ( lvl->duchove[i]->hitbox, david->hitbox ) ) { if ( david->zranitelny ) { // zahrajem zvuk kontaktu // (vlastně nvm jestli to má bejt jakože zvuk co vydává duch nebo david :D ) PlaySound ( zvuk_kontakt ); // vodečtem davidoj život david->zivoty--; // nastavíme davidoj blikací čas dočasný nezranitelnosti.. david->blikaci_cas = BLIKACI_CAS_DAVIDA; // ..a zapnem mu tu nezranitelnost david->zranitelny = false; // pokud má ňákou vertikální rychlost směrem nahoru k hornímu vokraji vobrazkovky, // tak mu ji snižime na nulu. Vono to vytváří takovej psychochologickej efekt jakože // hráče ty duchové chytaj a bráněj mu v pohybu :O ;D if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // přidáme si sem to zabíjení duchů tim hvězdičkovým bonusem else if ( david->ma_bonus ) { lvl->duchove[i]->hp = 0; } } for ( size_t j=0; j<ZASOBNIK_STREL_DUCHA; j++ ) { Strela * strela_ducha = &lvl->duchove[i]->strely[j]; if ( strela_ducha->aktivni ) { aktualizovatStrelu ( strela_ducha,lvl->dlazdicova_mapa, dt ); // podobně jako sme hlídali zásah hitboxu ducha davidovou střelou, // tak budeme klídat zásah davida střelou ducha if ( CheckCollisionPointRec ( strela_ducha->pozice, david->hitbox ) ) { strela_ducha->aktivni = false; if ( david->zranitelny ) { // v poctatě to samý jako při kontaktu PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; david->zranitelny = false; } } } } } // vicemeně skoro uplně stejně si sem přidáme aktualizaci burkinátorů, jako sme napsali aktualizaci vobyč duchů for ( size_t i =0; i<lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( david->pozice.x - lvl->burkinatori[i]->okraje.x ) > NUTNA_VZDALENOST ) continue; if ( ! lvl->burkinatori[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->burkinatori[i]->hitbox ) ) { s->aktivni = false; lvl->burkinatori[i]->hp--; PlaySound ( zvuk_zasah ); } } } // zkusíme kolizi s davidem if( ! lvl->burkinatori[i]->chcipe && CheckCollisionRecs ( lvl->burkinatori[i]->hitbox, david->hitbox )) { if ( david->zranitelny ) { PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // sem si taky přidáme to zabíjení burkinátorů magickou hvězdou else if ( david->ma_bonus ) { lvl->burkinatori[i]->hp = 0; } } aktualizovatBurkinatora ( lvl->burkinatori[i], david->pozice.x, dt ); } // jestli David už nemá bonus ale furt je aktivní hudba bonusu, // tak zapnem normální hudbu levelu a nastavíme ji jako aktualní hudbu // (hudbu bonusu nemusíme vypínat, tim že přepnem ukazatel ji stejně nebudem aktualizovat) if ( !david->ma_bonus && hudba_aktualni == &hudba_bonus ) { PlayMusicStream ( hudba_lvl ); hudba_aktualni = &hudba_lvl; } // a budeme aktualizovat sebratelný věci // kouknem jesli má věc minimální nutnou vzdálenost, jestli jo tak se kouknem jestli ji de sebrat a jestli má // kolizi s Davidovým hitboxem, jestli jo, tak ji David jakože sebere for ( size_t i = 0; i< lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( david->pozice.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { if ( !lvl->sebratelne_veci[i]->sebrano && CheckCollisionRecs ( lvl->sebratelne_veci[i]->okraje, david->hitbox ) ) { lvl->sebratelne_veci[i]->sebrano = true; PlaySound ( zvuk_powerup ); switch ( lvl->sebratelne_veci[i]->druh ) { case SEBRATELNA_HVEZDA: //hvězda aktivuje bonus a nastaví hvězdnej čas { david->ma_bonus = true; david->hvezdny_bonus_cas = HVEZDNY_CAS_DAVIDA; // pauznem hudbu levelu // (pozor, sou tam dvě funkce, pause stream a stop stream. Když pustíme pauznutou hudbu tak přehrávání // pokračuje vod místa kde přestalo, stopnutá hraje vod začátku) PauseMusicStream ( hudba_lvl ); // přetočíme hudbu bonusu na čas vodpovidajicí 24.4 sekund // ( v david.h jestli si jakoby pamatujete sme nastavovali takovej divnej čas trvání hvězdnýho bonusu. // Je to kuli trvání useku hudby, kterej trvá právě vod těch 24.4 s až 24.4 + nějakejch těch čtrnáct seknud) SeekMusicStream ( hudba_bonus, 24.4f ); //začnem hrát hudbu bonusu a nastavíme na ni ukazatel aktualní hudby PlayMusicStream ( hudba_bonus ); hudba_aktualni = &hudba_bonus; } break; case SEBRATELNE_SRDICKO: //srdičko přidá davidoj jeden život { david->zivoty++; // davidovy životy by asi jako možná neměli překročit maximum if ( david->zivoty > POCET_ZIVOTU_DAVIDA_MAX ) { david->zivoty = POCET_ZIVOTU_DAVIDA_MAX; } } break; default: break; } } aktualizovatSebratelnouVec ( lvl->sebratelne_veci[i],dt ); } } // jestli David nemá žádný životy tak prohrál, // jinak pokračujem ve hře if ( david->zivoty <= 0 ) { return LVL_PROHRA; } return LVL_POKRACUJE; } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); // vykreslíme všecky viditelný duchy a všecky střely duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if ( fabsf ( kamera->target.x - lvl->duchove[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->duchove[i] ); } for ( size_t j =0; j<ZASOBNIK_STREL_DUCHA; j++ ) { vykreslitStrelu ( lvl->duchove[i]->strely + j ); } } // vykreslíme burkinátory for ( size_t i = 0; i < lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( kamera->target.x - lvl->burkinatori[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->burkinatori[i] ); } } // vykreslíme sebratelný věci for ( size_t i = 0; i < lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( kamera->target.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitSebratelnouVec ( lvl->sebratelne_veci[i] ); } } } #endif
V 'main.c' si načtem novou hudbu pro gameover (je to budoucí hymna uzemí v současnosti dočasně kotrolovanýho samozvaným separatistickým barbarským terroristickým pseudostátem palestina 😛 😛) a zohledníme ty návratový hodnoty z funkce na aktualizovávání levelu. Přidáme si tam přepínání a vykreslování tří takovejch jakože pomyslnejch vobrazovek, jedna bude to hlavní menu, druhá hra a třetí gameover, držet to budem taky v ňákým enum a přepínat vobrazovku budem právě tou návratovou hodnotou aktualizace levelu. Screen management je uplně primitivní, sem přidala ňáký tři ify do hlavního loopu, takle naprasit to je možný že máme ty vobrazovky jednoduchoučký aže jich je málo. Jestli máme jakože v týdlectý hře ňák pokračovat tak asi jako dobudoucna budem potřebovat ňákej jakože víc lepčejší system vobrazovek a jejich přepinání.
Takže teda jako nová podoba 'main.c' je takovádlenc:
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" #include "menu.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // přidáme si enum popisující na který vobrazovce jakože sme, // jestli tý s menu, tý s herním levelem nebo tý s game over enum KteraObrazovka {OBRAZOVKA_HLAVNI_MENU, OBRAZOVKA_HRA, OBRAZOVKA_GAME_OVER}; enum KteraObrazovka obrazovka = OBRAZOVKA_HLAVNI_MENU; // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; Texture2D textura_ruzne; Texture2D textura_hrad; //textury menu a 'gui' prvků Texture2D textura_menu_titulek; Texture2D textura_menu_pozadi; Texture2D textura_menu_pozadi2; Texture2D textura_menu_game_over; Texture2D textura_menu_zmackni_enter; Texture2D textura_menu_david; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; Sound zvuk_kontakt; Sound zvuk_padu; Sound zvuk_zaghrouta; Sound zvuk_powerup; // hudby Music hudba_lvl; Music hudba_bonus; // přidáme si hudby pro menu a game over Music hudba_menu; Music hudba_game_over; //ukazatel na právě hranou hudbu Music * hudba_aktualni; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); textura_ruzne = LoadTexture ( "assets/misc.png" ); textura_hrad = LoadTexture ( "assets/hrad2.png" ); textura_menu_titulek = LoadTexture ( "assets/title.png" ); textura_menu_pozadi = LoadTexture ( "assets/gy.png" ); textura_menu_pozadi2 = LoadTexture ( "assets/gy_hroby.png" ); textura_menu_game_over = LoadTexture ( "assets/game_over.png" ); textura_menu_zmackni_enter = LoadTexture ( "assets/zmackni_enter.png" ); textura_menu_david = LoadTexture ( "assets/koli.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); zvuk_kontakt = LoadSound ( "assets/kontakt.wav" ); zvuk_padu = LoadSound ( "assets/pad.wav" ); zvuk_zaghrouta = LoadSound ( "assets/zaghrouta.ogg" ); zvuk_powerup = LoadSound ( "assets/powerup.wav" ); //načtem hudby hudba_lvl = LoadMusicStream ( "assets/waltz_of_the_ghosts.ogg" ); hudba_bonus = LoadMusicStream ( "assets/for_a_few_shekels_more_band.ogg" ); hudba_menu = LoadMusicStream ( "assets/ghost_trip.ogg" ); hudba_game_over = LoadMusicStream ( "assets/hatikva.ogg" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; David david; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu a teďko nově i hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; // nastavíme aktuální hudbu na hudbu menu hudba_aktualni = &hudba_menu; //vygenerujem si hlavní menu a zapnem vodpovidajicí muziku vygenerovatMenu(); PlayMusicStream ( hudba_menu ); while ( !WindowShouldClose() ) { float dt = GetFrameTime(); //aktualizujeme hudbu, na kterou ukazuje ukazatel 'hudba_aktualni' UpdateMusicStream ( *hudba_aktualni ); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; if ( obrazovka == OBRAZOVKA_HLAVNI_MENU ) { BeginDrawing(); // GetRenderHeight a GetRenderWidth je skoro to samý co GetScreenHeight a GetScreenWidth jenom s tim rozdílem // že to započitává DPI hele https://cs.wikipedia.org/wiki/DPI, jakože počet pixelů na jednotku délky // (na vobyčejným počitači nás to asi nemusí moc trápit, na mobilním zařizení by to ale bylo zásadní) DrawRectangleGradientV ( 0,0,GetRenderWidth(),GetRenderHeight(),DARKBLUE,BLACK ); //aktualizujeme a vykreslíme menu aktualizovatVykreslitMenu ( dt ); EndDrawing(); if ( IsKeyPressed ( KEY_ENTER ) ) { // pokud ukazatel na strukturu level neni vynulovanej, tak to pravděpodobně znamená že se už jednou hrálo a // že musíme starej level uklidit by sme mohli vygenerovat novej if ( level != NULL ) { freeLevel ( level ); // uklidíme i střely, idkyž by asi jako bylo víc lepčejší každý prostě nastavit atribut 'aktivni' na false :D free ( david_strely ); } //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); //inicializujeme si davida david = (David){ .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, .pozice = {0,0}, .smer = 1, .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, .strely = david_strely, .strileci_cooldown = 0.0f, .blikaci_cas = 0.0f, .zivoty = POCET_ZIVOTU_DAVIDA_MAX, .zranitelny = true, .ma_bonus = false, .hvezdny_bonus_cas = 0.0f, }; //nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; //podle aktuální pozice nastavíme okraje oběktu a hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; //vypnem hudbu menu StopMusicStream ( hudba_menu ); // vypnem hudbu levelu (by sme ji restartovali aby nám nehrála třeba někde vodprostředka) StopMusicStream ( hudba_lvl ); // zapnem hudbu levelu PlayMusicStream ( hudba_lvl ); // a nastavíme jeji adresu do ukazatele na aktuální hudbu, by se nám muzika taky aktualizovala, bez toho by nehrála :D hudba_aktualni = &hudba_lvl; //nakonec přepnem vobrazovku obrazovka = OBRAZOVKA_HRA; } // vykreslujem menu, takže nás další věci nezajímaj a přeskočíme je tim, že skočíme na začátek dalšího while cyklusu continue; } if ( obrazovka == OBRAZOVKA_HRA ) { // aktualizujem davida // funkce žere ukazatel, takže davida tam nacpem tak, že tam strčíme jeho adresu aktualizovatDavida ( &david, dt ); // aktualizujem level enum LevelStatus status = aktualizovatLevel ( level, &david, dt ); // podle návratový hodnoty určíme, jesttli se hraje dál nebo jestli nám David umřel a musíme // přepnout na 'game over' nebo jestli vyhrál a musíme se vrátit zpátky do hlavního menu // (ve finální verzi tý hry by sme asi měli mit ňáký hi-score nebo tak něco) // jenom tady přepínáme muziku a aktualní 'vobrazovku' if ( status == LVL_PROHRA ) { obrazovka = OBRAZOVKA_GAME_OVER; StopMusicStream ( hudba_lvl ); StopMusicStream ( hudba_bonus ); PlayMusicStream ( hudba_game_over ); hudba_aktualni = &hudba_game_over; } else if ( status == LVL_VYHRA ) { StopMusicStream ( hudba_lvl ); StopMusicStream ( hudba_bonus ); PlayMusicStream ( hudba_menu ); hudba_aktualni = &hudba_menu; obrazovka = OBRAZOVKA_HLAVNI_MENU; } } // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // první kus našeho parallaxu // hovorka se asi jako bojí děsně velkejch měsiců když je furt v horrorovejch komixech kreslí, // tak mu uděláme radost na na pozadí vykreslíme supr strašidelnej měsíc :D :D float mensi_rozmer = MIN ( GetRenderWidth(),GetRenderHeight() ); DrawTexturePro ( textura_mesic, ( Rectangle ) { 0,0,textura_mesic.width,textura_mesic.height }, ( Rectangle ) { GetRenderWidth() /2,GetRenderHeight() /2,mensi_rozmer*1.5, mensi_rozmer*1.5 }, ( Vector2 ) { mensi_rozmer*1.5/2,mensi_rozmer*1.5/2 },0, ( Color ) { 102, 191, 255, 128 } ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); //vykreslíme parallax // to je jakože takový pozadí, který se různě pomaličku posouvá, když se hráč pohybuje na mapě a vytváří to // víc lepší iluzi že se hráč jakože někam pohybuje // todle je zatim dělaný jentak na hrubo, vykresluje to takovou siluletu jeruzalémskýho hradu která se děsně pomaličku posouvá // když David poskakuje nebo de dopředu // velikost tý textury hradu Rectangle hrad_zdroj = {0,0,1024,512}; // cílovej vobdelnik hradu // uděláme ho 2x delší než je šířka vobrazovky a budem // (asi to nebude moc dobře fungovat na uzkejch vobrazovkách, tam by sme asi jako museli zvolit // ňákej uplně jinej koncept, nvm) Rectangle hrad_cil = { .x = kamera.target.x - GetRenderWidth() /priblizeni, .y = kamera.target.y - GetRenderWidth() /2/priblizeni, // vodelnik taky bude muset bejt 2x širší než vyšší, by se nám moc nezdeformovala textura .width = GetRenderWidth() /priblizeni*2, .height = GetRenderWidth() /2/priblizeni*2, }; // počátek/origin toho velkýho rectanglu budem určovat z mezí vobrazovky // musíme si pohlídat když hráč stojí u kraje mapy by nám tam nevylezla někam doprostředka vobrazovky hrana textury :D float hrad_y_posun_paralax = -300.0f + 300.0f * kamera.target.y/kamera_target_max_y; float hrad_x_posun_paralax = -GetRenderWidth() /priblizeni/2 + GetRenderWidth() /priblizeni * ( kamera.target.x - kamera_target_min_x ) /kamera_target_max_x; // noa vykreslíme ten náš parallax DrawTexturePro ( textura_hrad, hrad_zdroj, hrad_cil, ( Vector2 ) { hrad_x_posun_paralax,hrad_y_posun_paralax },0,WHITE ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); const Rectangle srdicko_rect = {2,503,96,83}; for ( int i = 0; i < POCET_ZIVOTU_DAVIDA_MAX; i++ ) { Rectangle cil = { .x = i*srdicko_rect.width/2, .y = GetScreenHeight() - srdicko_rect.height/2, .width = srdicko_rect.width/2, .height = srdicko_rect.height/2, }; DrawTexturePro ( textura_ruzne,srdicko_rect,cil, ( Vector2 ) { 0,0 },0.0f,i<david.zivoty? WHITE:BLACK ); } // 'obrazovka' game over if ( obrazovka == OBRAZOVKA_GAME_OVER ) { // pokud david umře, tak už nebudeme herní věci aktualizovat ale budem je furt vykreslovat // všecko překryjeme smutečním černým poloprusvitným vobdelnikem.... DrawRectangle ( 0,0,GetRenderWidth(),GetRenderHeight(), ( Color ) { 0,0,0,128 } ); // .... přes kterej namalujeme velkej zelenej text s nápisem 'game over'.... DrawTexture ( textura_menu_game_over,GetRenderWidth() /2 - textura_menu_game_over.width/2,GetRenderHeight() /3 - textura_menu_game_over.height/2,WHITE ); // ....a textem, že má hráč zmáčknout na klávesnici čudlik 'enter' DrawTexture ( textura_menu_zmackni_enter,GetRenderWidth() /2 - textura_menu_zmackni_enter.width/2,GetRenderHeight() - textura_menu_zmackni_enter.height,WHITE ); if ( IsKeyPressed ( KEY_ENTER ) ) { // noa když ten čudlik hráč zmáčkne, tak přepnem muziku, a přepnem vobrazovku StopMusicStream ( hudba_game_over ); PlayMusicStream ( hudba_menu ); hudba_aktualni = &hudba_menu; obrazovka = OBRAZOVKA_HLAVNI_MENU; } } // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); UnloadTexture ( textura_ruzne ); UnloadTexture ( textura_hrad ); UnloadTexture ( textura_menu_titulek ); UnloadTexture ( textura_menu_pozadi ); UnloadTexture ( textura_menu_pozadi2 ); UnloadTexture ( textura_menu_david ); UnloadTexture ( textura_ruzne ); UnloadTexture ( textura_menu_zmackni_enter ); UnloadTexture ( textura_menu_game_over ); // vypnem audio zařízení CloseAudioDevice(); // musíme uvolnit i muziku UnloadMusicStream ( hudba_lvl ); UnloadMusicStream ( hudba_bonus ); UnloadMusicStream ( hudba_menu ); UnloadMusicStream ( hudba_game_over ); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); UnloadSound ( zvuk_kontakt ); UnloadSound ( zvuk_padu ); UnloadSound ( zvuk_zaghrouta ); UnloadSound ( zvuk_powerup ); return 0; }
Jako prozatim poslední krok si přidáme ňáký Davidovo možný vítězství nad duchama. V tom márijoj sem viděla uplně supr věc co se nám hodí přesně nakonec herního levelu: totiž že mário dělá když doběhne na konec herní mapy tak tam je dycky na stožáru vlajka asi ňákýho hlavníh zlýho, noa mário vyskočí a tu vlajku strhne a nahradí ji praporkem se svým vlastním xiftem 😮 😁 My uděláme taky vyměnu praporku, na konec mapy si přidáme stožár na kterým bude pověšená terroristická vlajka noa když se David toho vlajkovýho stojanu dotkne svým hitboxem, tak handru nahradíme poctivou vlajkou státu Izrael (v ňákým možným budoucím kroku by sme si pak mohli udělat aby ta terroristická handra eště hořela až pojede z toho stojanu dolu)
Vyrobíme si ve složšce 'src' novej soubor 'stojan.h' a nacpem tam tendlecten kód:
#ifndef _DAVID_A_DUCHOVE_STOJAN_H_ #define _DAVID_A_DUCHOVE_STOJAN_H_ #include <raylib.h> #include <stdlib.h> extern Texture2D textura_ruzne; // jak dlouho bude na stojanu trvat vyměna vlajky #define PREVLAJKOVAVACI_CAS_STOJANU 2.0f // voblasti textur stojanu a vlajek v textuře 'různé' const Rectangle ISRA_VLAJKA_RECT = (Rectangle){2,590,160,80}; const Rectangle BUBU_VLAJKA_RECT = (Rectangle){2,674,160,80}; const Rectangle STOJAN_RECT = (Rectangle){2,2,18,497}; typedef struct Stojan { Rectangle okraje; // vobdelniky popsiujicí polohu a voblast vobou vlajek Rectangle vlajka_puvodni, vlajka_nova; float relativni_cas; // stojan bude mit vlastně jakoby tři stavy, defaultní, kdy na něm // bude plandat bubu vlajka, pak ňákej přechodnej kdy se dole voběví // nová vlajka a začnou se vyměňovat, // noa pak ňákej třetí stav, kdy už ta změna jakoby dojede do konce // ty tři stavy popišem dvouma boolama (pokud sou voba false tak platí jakoby ten třetí stav) bool prevlajkovava_se, uz_se_prevlajkoval_uplne; } Stojan; Stojan * vygenerovatStojan(Vector2 kde) { Stojan * s = calloc(1,sizeof(Stojan)); if(!s) return NULL; s->okraje = (Rectangle){kde.x,kde.y - STOJAN_RECT.height,STOJAN_RECT.width,STOJAN_RECT.height}; s->vlajka_puvodni = (Rectangle){kde.x + STOJAN_RECT.width/2.0f, kde.y - STOJAN_RECT.height,BUBU_VLAJKA_RECT.width,BUBU_VLAJKA_RECT.height}; s->vlajka_nova = (Rectangle){kde.x + STOJAN_RECT.width/2.0f, kde.y - STOJAN_RECT.height,ISRA_VLAJKA_RECT.width,ISRA_VLAJKA_RECT.height}; return s; } void aktualizovatStojan(Stojan * stojan, float dt) { // stojan budem aktualizovat jenom když se mění vlajka, až se vymění, // tak už aktualizovat nebudem if(!stojan->prevlajkovava_se || stojan->uz_se_prevlajkoval_uplne) return; // jestli už uplynul převlajkovávací čas tak se stojan určitě převlajkoval float progress = stojan->relativni_cas/PREVLAJKOVAVACI_CAS_STOJANU; if(progress>1.0f) { progress = 1.0f; stojan->prevlajkovava_se = false; stojan->uz_se_prevlajkoval_uplne = true; } // vlajky budem posouvat tou pomocnou normalizovanou hodnotou progress, prostě tim čislem // vynásobíme vejšku ve který tu vlajku chcem, jednu vlajku budem pronásobovat rovnou, drhuou převrácenou hodnotou stojan->vlajka_puvodni.y = (STOJAN_RECT.height - BUBU_VLAJKA_RECT.height) * (progress) + stojan->okraje.y; stojan->vlajka_nova.y = (STOJAN_RECT.height - ISRA_VLAJKA_RECT.height) * (1.0f - progress) + stojan->okraje.y; stojan->relativni_cas += dt; } void vykreslitStojan(Stojan * stojan) { DrawTexturePro(textura_ruzne,STOJAN_RECT,stojan->okraje,(Vector2){0,0},0.0f,DARKBLUE); // stará vlajka muže bejt vidět jenom dokavaď se stojan uplně nepřevlajkoval if(!stojan->uz_se_prevlajkoval_uplne) { DrawTexturePro(textura_ruzne,BUBU_VLAJKA_RECT,stojan->vlajka_puvodni,(Vector2){0,0},0.0f,SKYBLUE); } // nová vlajka muže bejt logicky vidět jenom když se stojan převlajkovává nebo už celej převlajkoval if(stojan->prevlajkovava_se || stojan->uz_se_prevlajkoval_uplne) { DrawTexturePro(textura_ruzne,ISRA_VLAJKA_RECT,stojan->vlajka_nova,(Vector2){0,0},0.0f,SKYBLUE); } } #endif
V 'level.h' si vyrobíme ten stojan někde u konce herní mapy a v aktualizaci levelu budem hlídat to převlajkovávání, když se s nim začne tak sopnem muziku, zahrajem vitěznou znělku a až převlakovávání skončí tak se vrátíme zpátky do hlavního menu (eště tam zapošiju možnej leak při selhání konstruktorů. Sem to chtěla puvodně nechat bejt a zavtipkovat že to necháme na zdejších autistech který se nato asi jako nevydržej koukat noa nakonec to ani sama nevydržim a musim to ňák zapošít :'D 😁 😁 😜) :
#ifndef _DAVID_A_DUCHOVE_LEVEL_H_ #define _DAVID_A_DUCHOVE_LEVEL_H_ #include <raylib.h> #include <stdlib.h> #include <math.h> #include "mapa.h" #include "david.h" #include "duch.h" #include "sebratelne.h" // přidáme si stojan #include "stojan.h" // zvuky extern Sound zvuk_zasah; extern Sound zvuk_kontakt; extern Sound zvuk_powerup; // zvuk pro výhru extern Sound zvuk_vyhra; extern Music hudba_lvl; extern Music hudba_bonus; extern Music * hudba_aktualni; #define MAPA_MAX_VYSKA 10 #define NUTNA_VZDALENOST (BLOK_SIRKA * 20) // návratový stavy aktululizace levelu enum LevelStatus {LVL_POKRACUJE, LVL_VYHRA, LVL_PROHRA}; // struktura levelu typedef struct Level { Duch ** duchove; size_t pocet_duchu; Duch ** burkinatori; size_t pocet_burkinatoru; Sebratelne ** sebratelne_veci; size_t pocet_sebratelnych_veci; // level bude vobsahovat jednu instanci stojanu Stojan * stojan; Mapa * dlazdicova_mapa; } Level; typedef struct SegmentMapy { int sirka; int vyska; int * pole; } SegmentMapy; // funkce na vygenerování náhodnýho levelu, vlastně něco jako konstruktor // první argument 'šiřka' je počet kostek jak má bejt level dlouhej (předpokládá se čislo věčí dvacíti a dělitelný pěti) // druhej textura tý dlaždicový mapy Level * vygenerovatLevel ( int sirka, Texture2D texturaTiledMapy ) { Level * lvl = (Level * )malloc ( sizeof ( Level ) ); Mapa * mapa = (Mapa * )malloc ( sizeof ( Mapa ) ); Duch ** duchove = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Duch ** burkinatori = (Duch **)malloc ( sizeof ( Duch * ) * 256 ); Sebratelne ** sebratelne_veci = (Sebratelne **)malloc ( sizeof ( Sebratelne * ) * 256 ); if ( !lvl || !mapa || !duchove || !burkinatori || !sebratelne_veci ) { goto neni_pamet; } // enum který nám bude popisovat jednotlivý prvky mapy, // 'N' jakože nic, 'B' jakože blok, 'D' jakože duch // 'Q' jako burkinátor (vono se to prej správně piše ňák s kvé jakože 'burqa' nebo jak) // 'S' jao srdičko, 'H' jako hvězda enum herniVec {N,D,B,Q,S,H}; // pole jednotlivejch segmentů, ze kterejch budeme skládat tu mapu // musíme si tam dát ňáký ty bonusy // (zatim to nemáme vybalancovaný, hvězda by asi jako měla bejt víc zácnej bonus) int seg1 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, B,B,B,B,B, }; int seg2 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg3 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,B,0,D,0, 0,D,0,B,0, B,B,B,B,B, B,B,B,B,B, }; int seg4 [] = { 0,0,0,0,0, B,0,0,0,B, }; int seg5 [] = { 0,0,0,0,0, B,0,Q,0,B, }; int seg6 [] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,0,0,B,0, 0,0,0,0,B,0,0,0,B,0,0,B,0,B,0, 0,0,B,0,B,0,0,0,B,0,0,B,0,B,0, B,Q,B,Q,B,Q,0,Q,B,Q,0,B,Q,B,B, }; int seg7 [] = { 0,0,0,0,0,0,0,0,0,B,0,0,0,0,0, 0,0,0,0,0,0,0,0,B,B,0,0,0,0,0, 0,0,0,0,0,0,0,B,B,B,0,0,0,0,0, 0,0,0,0,0,0,B,B,B,B,0,0,0,0,0, 0,0,0,0,0,B,B,B,B,B,0,0,0,0,0, 0,0,0,0,B,B,B,B,B,B,0,0,0,0,0, 0,0,0,B,B,B,B,B,B,B,0,0,0,0,0, 0,0,B,B,B,B,B,B,B,B,0,0,0,0,0, 0,B,B,B,B,B,B,B,B,B,0,0,0,0,0, B,B,B,B,B,B,B,B,B,B,Q,Q,Q,Q,B, }; int seg8 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,D,D, D,D,0,0,0, 0,0,B,B,B, B,B,B,0,0, B,B,B,B,B, B,B,B,B,B, }; int seg9 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,H,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg10 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,S,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg11 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,D,0,0, 0,B,B,B,0, B,B,B,B,B }; int seg12 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,B,0, 0,B,0,B,0, B,B,B,B,B }; int seg13 [] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,B,0,0, 0,0,0,0,0, 0,B,B,D,0, D,0,D,0,B, B,B,B,B,B, B,B,B,B,B, }; SegmentMapy segmenty [] = { ( SegmentMapy ) {5,5,seg1}, ( SegmentMapy ) {5,5,seg2}, ( SegmentMapy ) {10,5,seg3}, ( SegmentMapy ) {5,2,seg4}, ( SegmentMapy ) {5,2,seg5}, ( SegmentMapy ) {15,5,seg6}, ( SegmentMapy ) {15,10,seg7}, ( SegmentMapy ) {10,5,seg8}, ( SegmentMapy ) {5,5,seg9}, ( SegmentMapy ) {5,5,seg10}, ( SegmentMapy ) {5,5,seg11}, ( SegmentMapy ) {5,5,seg12}, ( SegmentMapy ) {10,5,seg13}, }; // počet těch segmentů ze kterejch budem vybírat const size_t segmentu = sizeof ( segmenty ) /sizeof ( SegmentMapy ); // alokujem si bloky dlaždicový mapy int ** bloky = calloc ( MAPA_MAX_VYSKA, sizeof ( int * ) * MAPA_MAX_VYSKA ); if ( !bloky ) { goto neni_pamet; } for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { bloky[i] = calloc ( sirka,sizeof ( int ) ); if ( !bloky[i] ) { goto neni_pamet; } } // prvních a posledních deset sloupečků herní mapy bude placka, for ( size_t i=0; i<10; i++ ) { bloky[MAPA_MAX_VYSKA-1][i]=GetRandomValue ( 0,6 ) + 1; //vybíráme náhodnou texturu bloky[MAPA_MAX_VYSKA-1][ sirka - i - 1 ]=GetRandomValue ( 0,6 ) + 1; } int zbyva_delka = sirka - 10; size_t duchu = 0; size_t burkinatoru = 0; size_t sebratelnych_veci = 0; while ( zbyva_delka >= 15 ) { // vyberem si náhodnej segment int index = GetRandomValue ( 0,segmentu-1 ); int vyska_segmentu = segmenty[index].vyska; int sirka_segmentu = segmenty[index].sirka; if ( sirka_segmentu > zbyva_delka -10 ) { continue; } // noa teďko si projdem celý pole toho náhodně vybranýho segmentu.... for ( size_t segment_y = 0; segment_y < vyska_segmentu; segment_y++ ) { for ( size_t segment_x = 0; segment_x < sirka_segmentu; segment_x++ ) { int hodnota = segmenty[index].pole[segment_x + segment_y * sirka_segmentu]; // ....a podle toho na jakou hodnotu sme tam narazili se budem chovat switch ( hodnota ) { case B: // vyrobíme náhodnej blok mapy // zarovnáváme to k dolnímu vokraji mapy bloky[segment_y + MAPA_MAX_VYSKA - vyska_segmentu][segment_x + ( sirka - zbyva_delka )] = GetRandomValue ( 0,6 ) + 1; break; case D: //vyrobíme na tý pozici ducha { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA - 161, }; Duch * duch = vygenerovatDucha ( pozice ); if(!duch) goto neni_pamet; duchove[duchu++] = duch; } break; case Q: //vyrobíme burkinátora { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA + BLOK_VYSKA*3, }; Duch * duch = vygenerovatBurkinatora ( pozice ); if(!duch) goto neni_pamet; burkinatori[burkinatoru++] = duch; } break; case S: //vyrobíme srdíčko { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNE_SRDICKO ); if(!vec) goto neni_pamet; sebratelne_veci[sebratelnych_veci++] = vec; } break; case H: //vyrobíme bonusovou hvězdu { Vector2 pozice = { .x = ( segment_x + ( sirka - zbyva_delka ) ) * BLOK_SIRKA, .y = ( segment_y + MAPA_MAX_VYSKA - vyska_segmentu ) * BLOK_VYSKA, }; Sebratelne * vec = vygenerovatSebratelnouVec ( pozice,SEBRATELNA_HVEZDA ); if(!vec) goto neni_pamet; sebratelne_veci[sebratelnych_veci++] = vec; } break; default: break; }; } } zbyva_delka-=sirka_segmentu; } duchove = realloc ( duchove, sizeof ( Duch * ) * duchu ); burkinatori = realloc ( burkinatori, sizeof ( Duch * ) * burkinatoru ); sebratelne_veci = realloc ( sebratelne_veci, sizeof ( Sebratelne * ) * sebratelnych_veci ); // nvm jestli se muže reallokace pokazit když jenom zmenčujem :D // rači budem předpokládat že jo if ( duchu && !duchove ) { goto neni_pamet; } if ( burkinatoru && !burkinatori ) { goto neni_pamet; } if ( sebratelnych_veci && !sebratelne_veci ) { goto neni_pamet; } // nacpem duchy do tý struktury levelu lvl->pocet_duchu = duchu; lvl->duchove = duchove; // napcem tam i burkinátory lvl->pocet_burkinatoru = burkinatoru; lvl->burkinatori = burkinatori; // a nacpem tam taky bonusy lvl->pocet_sebratelnych_veci = sebratelnych_veci; lvl->sebratelne_veci = sebratelne_veci; // strčíme bloky do mapy mapa->bloky = bloky; mapa->sirka = sirka; mapa->vyska = MAPA_MAX_VYSKA; mapa->textura = texturaTiledMapy; // a mapu strčíme do levelu lvl->dlazdicova_mapa = mapa; // u konce mapy vygenerujem stojan Stojan * stojan = vygenerovatStojan ( ( Vector2 ) {( sirka-5 ) *BLOK_SIRKA,720} ); lvl->stojan = stojan; return lvl; neni_pamet: if ( lvl->duchove ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { if ( lvl->duchove[i] ) { freeDucha ( lvl->duchove[i] ); } } free ( lvl->duchove ); } if ( lvl->burkinatori ) { for ( size_t i=0; i<lvl->pocet_burkinatoru; i++ ) { if ( lvl->burkinatori[i] ) { freeDucha ( lvl->burkinatori[i] ); } } free ( lvl->burkinatori ); } if ( lvl->sebratelne_veci ) { for ( size_t i=0; i<lvl->pocet_sebratelnych_veci; i++ ) { if ( lvl->sebratelne_veci[i] ) { free ( lvl->sebratelne_veci[i] ); } } free ( lvl->sebratelne_veci ); } if ( lvl->dlazdicova_mapa ) { if ( lvl->dlazdicova_mapa->bloky ) { for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { if ( lvl->dlazdicova_mapa->bloky[i] ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } } free ( lvl->dlazdicova_mapa->bloky ); } free ( lvl->dlazdicova_mapa ); } if ( bloky ) { for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { if ( bloky[i] ) { free ( bloky[i] ); } } free ( bloky ); } if ( lvl ) { free ( lvl ); } return NULL; } void freeLevel ( Level * lvl ) { for ( size_t i=0; i<lvl->pocet_duchu; i++ ) { freeDucha ( lvl->duchove[i] ); } free ( lvl->duchove ); for ( size_t i=0; i<lvl->pocet_burkinatoru; i++ ) { freeDucha ( lvl->burkinatori[i] ); } free ( lvl->burkinatori ); for ( size_t i=0; i<lvl->pocet_sebratelnych_veci; i++ ) { free ( lvl->sebratelne_veci[i] ); } free ( lvl->sebratelne_veci ); for ( size_t i=0; i<MAPA_MAX_VYSKA; i++ ) { free ( lvl->dlazdicova_mapa->bloky[i] ); } free ( lvl->dlazdicova_mapa->bloky ); free ( lvl->dlazdicova_mapa ); // uklidíme stojan free ( lvl->stojan ); free ( lvl ); } // aktualizovatLevel teďko vrací navratovou hodnotu enum LevelStatus aktualizovatLevel ( Level * lvl, David * david, float dt ) { Strela * strely = david->strely; for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if(fabsf ( david->pozice.x - lvl->duchove[i]->okraje.x ) > NUTNA_VZDALENOST) continue; if ( ! lvl->duchove[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->duchove[i]->hitbox ) ) { s->aktivni = false; lvl->duchove[i]->hp--; PlaySound ( zvuk_zasah ); } } } aktualizovatDucha ( lvl->duchove[i], lvl->dlazdicova_mapa, dt ); // pohlídáme si kolizi Davida s duchem, pokud se srazej tak duch Davida zraní // kolizi zistíme raylibí funkcí 'CheckCollisionRecs' do který nacpem hitboxy vobou herních entit if ( !lvl->duchove[i]->chcipe && CheckCollisionRecs ( lvl->duchove[i]->hitbox, david->hitbox ) ) { if ( david->zranitelny ) { // zahrajem zvuk kontaktu // (vlastně nvm jestli to má bejt jakože zvuk co vydává duch nebo david :D ) PlaySound ( zvuk_kontakt ); // vodečtem davidoj život david->zivoty--; // nastavíme davidoj blikací čas dočasný nezranitelnosti.. david->blikaci_cas = BLIKACI_CAS_DAVIDA; // ..a zapnem mu tu nezranitelnost david->zranitelny = false; // pokud má ňákou vertikální rychlost směrem nahoru k hornímu vokraji vobrazkovky, // tak mu ji snižime na nulu. Vono to vytváří takovej psychochologickej efekt jakože // hráče ty duchové chytaj a bráněj mu v pohybu :O ;D if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // přidáme si sem to zabíjení duchů tim hvězdičkovým bonusem else if ( david->ma_bonus ) { lvl->duchove[i]->hp = 0; } } for ( size_t j=0; j<ZASOBNIK_STREL_DUCHA; j++ ) { Strela * strela_ducha = &lvl->duchove[i]->strely[j]; if ( strela_ducha->aktivni ) { aktualizovatStrelu ( strela_ducha,lvl->dlazdicova_mapa, dt ); // podobně jako sme hlídali zásah hitboxu ducha davidovou střelou, // tak budeme klídat zásah davida střelou ducha if ( CheckCollisionPointRec ( strela_ducha->pozice, david->hitbox ) ) { strela_ducha->aktivni = false; if ( david->zranitelny ) { // v poctatě to samý jako při kontaktu PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; david->zranitelny = false; } } } } } // vicemeně skoro uplně stejně si sem přidáme aktualizaci burkinátorů, jako sme napsali aktualizaci vobyč duchů for ( size_t i =0; i<lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( david->pozice.x - lvl->burkinatori[i]->okraje.x ) > NUTNA_VZDALENOST ) continue; if ( ! lvl->burkinatori[i]->chcipe ) for ( size_t j = 0; j < POCET_STREL_DAVIDA_MAX; j++ ) { Strela * s = strely + j; if ( s->aktivni ) { if ( CheckCollisionPointRec ( s->pozice, lvl->burkinatori[i]->hitbox ) ) { s->aktivni = false; lvl->burkinatori[i]->hp--; PlaySound ( zvuk_zasah ); } } } // zkusíme kolizi s davidem if( ! lvl->burkinatori[i]->chcipe && CheckCollisionRecs ( lvl->burkinatori[i]->hitbox, david->hitbox )) { if ( david->zranitelny ) { PlaySound ( zvuk_kontakt ); david->zivoty--; david->blikaci_cas = BLIKACI_CAS_DAVIDA; if ( david->vertikalni_rychlost < 0.0f ) { david->vertikalni_rychlost = 0.0f; } } // sem si taky přidáme to zabíjení burkinátorů magickou hvězdou else if ( david->ma_bonus ) { lvl->burkinatori[i]->hp = 0; } } aktualizovatBurkinatora ( lvl->burkinatori[i], david->pozice.x, dt ); } // přidáme si sem stojan // pokud neni převlajkovanej a dojde kolizi s davidem if ( !lvl->stojan->uz_se_prevlajkoval_uplne && CheckCollisionRecs ( lvl->stojan->okraje, david->hitbox ) ) { // tak si pohlídáme by se nám přepnuly ty převlakovávací booly // ( by sme měli asi 'zapouzdřit' do tý struktury stojanu) if ( !lvl->stojan->prevlajkovava_se && !lvl->stojan->uz_se_prevlajkoval_uplne ) { lvl->stojan->prevlajkovava_se=true; } // vypnem hudby a zapnem znělku vítězství (pokud eště nehraje) if ( !IsSoundPlaying ( zvuk_vyhra ) ) { StopMusicStream ( hudba_lvl ); StopMusicStream ( hudba_bonus ); PlaySound ( zvuk_vyhra ); } } aktualizovatStojan ( lvl->stojan,dt ); // jestli se už převlajkoval a už dohrál zvuk výhry tak se vrátíme zpátky do hlavního menu if ( lvl->stojan->relativni_cas > PREVLAJKOVAVACI_CAS_STOJANU && !IsSoundPlaying ( zvuk_vyhra ) ) { return LVL_VYHRA; } // přidali sme si sem podminku hlidajicí převlajkovávání stojanu, by nám nedošlo ke kolizi přepínání hudeb, // páč se muže stát že David strhne bubu vlajku a ve stejný době mu třeba skončí hvězdej bonus. To by nám // vlezlo do tý vítězný znělky hraný jakoby tim stojanem if ( !david->ma_bonus && hudba_aktualni == &hudba_bonus && lvl->stojan->relativni_cas < 0.01f ) { PlayMusicStream ( hudba_lvl ); hudba_aktualni = &hudba_lvl; } // a budeme aktualizovat sebratelný věci // kouknem jesli má věc minimální nutnou vzdálenost, jestli jo tak se kouknem jestli ji de sebrat a jestli má // kolizi s Davidovým hitboxem, jestli jo, tak ji David jakože sebere for ( size_t i = 0; i< lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( david->pozice.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { if ( !lvl->sebratelne_veci[i]->sebrano && CheckCollisionRecs ( lvl->sebratelne_veci[i]->okraje, david->hitbox ) ) { lvl->sebratelne_veci[i]->sebrano = true; PlaySound ( zvuk_powerup ); switch ( lvl->sebratelne_veci[i]->druh ) { case SEBRATELNA_HVEZDA: //hvězda aktivuje bonus a nastaví hvězdnej čas { david->ma_bonus = true; david->hvezdny_bonus_cas = HVEZDNY_CAS_DAVIDA; // pauznem hudbu levelu // (pozor, sou tam dvě funkce, pause stream a stop stream. Když pustíme pauznutou hudbu tak přehrávání // pokračuje vod místa kde přestalo, stopnutá hraje vod začátku) PauseMusicStream ( hudba_lvl ); // přetočíme hudbu bonusu na čas vodpovidajicí 24.4 sekund // ( v david.h jestli si jakoby pamatujete sme nastavovali takovej divnej čas trvání hvězdnýho bonusu. // Je to kuli trvání useku hudby, kterej trvá právě vod těch 24.4 s až 24.4 + nějakejch těch čtrnáct seknud) SeekMusicStream ( hudba_bonus, 24.4f ); //začnem hrát hudbu bonusu a nastavíme na ni ukazatel aktualní hudby PlayMusicStream ( hudba_bonus ); hudba_aktualni = &hudba_bonus; } break; case SEBRATELNE_SRDICKO: //srdičko přidá davidoj jeden život { david->zivoty++; // davidovy životy by asi jako možná neměli překročit maximum if ( david->zivoty > POCET_ZIVOTU_DAVIDA_MAX ) { david->zivoty = POCET_ZIVOTU_DAVIDA_MAX; } } break; default: break; } } aktualizovatSebratelnouVec ( lvl->sebratelne_veci[i],dt ); } } // jestli David nemá žádný životy tak prohrál, // jinak pokračujem ve hře if ( david->zivoty <= 0 ) { return LVL_PROHRA; } return LVL_POKRACUJE; } // vykreslíme level void vykreslitLevel ( Level * lvl, Camera2D * kamera ) { float min_x = kamera->target.x - GetRenderWidth() / 2.0f / kamera->zoom; float max_x = kamera->target.x + GetRenderWidth() / 2.0f / kamera->zoom; vykreslitMapu ( lvl->dlazdicova_mapa,min_x,max_x ); // vykreslíme všecky viditelný duchy a všecky střely duchů for ( size_t i = 0; i < lvl->pocet_duchu; i++ ) { if ( fabsf ( kamera->target.x - lvl->duchove[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->duchove[i] ); } for ( size_t j =0; j<ZASOBNIK_STREL_DUCHA; j++ ) { vykreslitStrelu ( lvl->duchove[i]->strely + j ); } } // vykreslíme burkinátory for ( size_t i = 0; i < lvl->pocet_burkinatoru; i++ ) { if ( fabsf ( kamera->target.x - lvl->burkinatori[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitDucha ( lvl->burkinatori[i] ); } } // vykreslíme sebratelný věci for ( size_t i = 0; i < lvl->pocet_sebratelnych_veci; i++ ) { if ( fabsf ( kamera->target.x - lvl->sebratelne_veci[i]->okraje.x ) < NUTNA_VZDALENOST ) { vykreslitSebratelnouVec ( lvl->sebratelne_veci[i] ); } } // musíme vykreslit i stojan vykreslitStojan ( lvl->stojan ); } #endif
V souboru 'main.c' si načtem tu znělku pro vítězství, je poměrně krátká tak ji načtem jako zvuk (ikdyž to je teda jako asi už celkem nahraně, nvm)
// naimportujem si knihovny #include <math.h> #include <stdlib.h> #include <raylib.h> #include <time.h> // Pokuď vodkomentujeme, tak to zkompiluje preprocesorovou podmínkou vypnutý věci // napřiklad se kolem některejch herních voběktů budou vykreslovat okraje // #define DEBUG // naimportujem si vlastní hlavičky #include "animace.h" #include "david.h" #include "projektily.h" #include "level.h" #include "menu.h" //makra na zišťování minimální a maximální hodnoty #ifndef MAX #define MAX(a, b) ((a)>(b)? (a) : (b)) #define MIN(a, b) ((a)<(b)? (a) : (b)) #endif // šířka a výška vokna #define HERNI_SIRKA 1280 #define HERNI_VYSKA 960 // kolik kostek bude dlouhá naše herní mapa #define DELKA_LEVELU_BLOKU 100 // přidáme si enum popisující na který vobrazovce jakože sme, // jestli tý s menu, tý s herním levelem nebo tý s game over enum KteraObrazovka {OBRAZOVKA_HLAVNI_MENU, OBRAZOVKA_HRA, OBRAZOVKA_GAME_OVER}; enum KteraObrazovka obrazovka = OBRAZOVKA_HLAVNI_MENU; // textury Texture2D textura_mesic; Texture2D textura_david_spritesheet; Texture2D textura_kameny; Texture2D textura_duchove_spritesheet; Texture2D textura_ruzne; Texture2D textura_hrad; //textury menu a 'gui' prvků Texture2D textura_menu_titulek; Texture2D textura_menu_pozadi; Texture2D textura_menu_pozadi2; Texture2D textura_menu_game_over; Texture2D textura_menu_zmackni_enter; Texture2D textura_menu_david; // zvuky Sound zvuk_kroku; Sound zvuk_skoku; Sound zvuk_vystrel; Sound zvuk_duch_chcip; Sound zvuk_duch_strela; Sound zvuk_zasah; Sound zvuk_kontakt; Sound zvuk_padu; Sound zvuk_zaghrouta; Sound zvuk_powerup; // přidáme si znělku výhry Sound zvuk_vyhra; // hudby Music hudba_lvl; Music hudba_bonus; Music hudba_menu; Music hudba_game_over; //ukazatel na právě hranou hudbu Music * hudba_aktualni; int main ( void ) { // nastavíme generátor nahodnejch čisel nějakým seedem // (vobvykle se tam strká aktualní čas ale mužeme si tam dát // třeba ňákou konstantu by sme to měli vopakovatelný a mohli reprodukovat stejnej level) SetRandomSeed ( time ( 0 ) ); SetConfigFlags ( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow ( HERNI_SIRKA, HERNI_VYSKA, "David a duchove" ); InitAudioDevice(); SetTargetFPS ( 60 ); // načtem soubory textur textura_david_spritesheet = LoadTexture ( "assets/david.png" ); textura_mesic = LoadTexture ( "assets/moon.png" ); textura_kameny = LoadTexture ( "assets/kameny.png" ); textura_duchove_spritesheet = LoadTexture ( "assets/duchove.png" ); textura_ruzne = LoadTexture ( "assets/misc.png" ); textura_hrad = LoadTexture ( "assets/hrad2.png" ); textura_menu_titulek = LoadTexture ( "assets/title.png" ); textura_menu_pozadi = LoadTexture ( "assets/gy.png" ); textura_menu_pozadi2 = LoadTexture ( "assets/gy_hroby.png" ); textura_menu_game_over = LoadTexture ( "assets/game_over.png" ); textura_menu_zmackni_enter = LoadTexture ( "assets/zmackni_enter.png" ); textura_menu_david = LoadTexture ( "assets/koli.png" ); // načtem zvuky zvuk_kroku = LoadSound ( "assets/kroky.wav" ); zvuk_skoku = LoadSound ( "assets/skok.wav" ); zvuk_vystrel = LoadSound ( "assets/bum.wav" ); zvuk_duch_chcip = LoadSound ( "assets/duch_chcip.wav" ); zvuk_duch_strela = LoadSound ( "assets/duch_strela.wav" ); zvuk_zasah = LoadSound ( "assets/zasah.wav" ); zvuk_kontakt = LoadSound ( "assets/kontakt.wav" ); zvuk_padu = LoadSound ( "assets/pad.wav" ); zvuk_zaghrouta = LoadSound ( "assets/zaghrouta.ogg" ); zvuk_powerup = LoadSound ( "assets/powerup.wav" ); zvuk_vyhra = LoadSound ( "assets/Victory!.wav" ); //načtem hudby hudba_lvl = LoadMusicStream ( "assets/waltz_of_the_ghosts.ogg" ); hudba_bonus = LoadMusicStream ( "assets/for_a_few_shekels_more_band.ogg" ); hudba_menu = LoadMusicStream ( "assets/ghost_trip.ogg" ); hudba_game_over = LoadMusicStream ( "assets/hatikva.ogg" ); Camera2D kamera = { //posun 'středu' kamery, posunem na střed vobrazovky .offset = ( Vector2 ) { GetRenderWidth() / 2.0f, GetRenderHeight() / 2.0f }, // souřadnice cíle, na co jakože kamera kouká .target = ( Vector2 ) {0,0}, // uhel náklonu kamery ve stupních .rotation = 0.0f, //přiblížení .zoom = 1.0f }; //ukazatel, kde si budeme držet vygenerovanej level Level * level = NULL; // ukazatel, kterej bude držet 'pole' davidovejch střel Strela * david_strely = NULL; // animace běhu Animace david_beh = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_behu, .pocet_framu = sizeof ( david_framy_behu ) /sizeof ( Rectangle ) }; // animace idle, jakože když se fláká a nic nedělá. Je to takový pérování nohama na místě Animace david_idle = { .trvani_framu = 1.0f/15.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_idle, .pocet_framu = sizeof ( david_framy_idle ) /sizeof ( Rectangle ) }; Animace david_sed = { .trvani_framu = 1.0f/30.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = false, .textura = textura_david_spritesheet, .framy = david_framy_sed, .pocet_framu = sizeof ( david_framy_sed ) /sizeof ( Rectangle ) }; // animace skoku, david tam vicemeně jenom máchá nožičkama ve vzduchu Animace david_skok = { .trvani_framu = 1.0f/10.0f, .relativni_cas=0.0f, .index = 0, .jojo = true, .reverzne = false, .zrcadlit = false, .loopovat = true, .textura = textura_david_spritesheet, .framy = david_framy_skoku, .pocet_framu = sizeof ( david_framy_skoku ) /sizeof ( Rectangle ) }; David david; // nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; // podle aktuální pozice nastavíme okraje oběktu a teďko nově i hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; // nastavíme aktuální hudbu na hudbu menu hudba_aktualni = &hudba_menu; //vygenerujem si hlavní menu a zapnem vodpovidajicí muziku vygenerovatMenu(); PlayMusicStream ( hudba_menu ); while ( !WindowShouldClose() ) { float dt = GetFrameTime(); //aktualizujeme hudbu, na kterou ukazuje ukazatel 'hudba_aktualni' UpdateMusicStream ( *hudba_aktualni ); // spočitáme si přiblížení naší kamery // uděláme to tak, že si spočitáme poměr skutečný šířky obrazovky s naší 'virtuální' požadovanou, // to samý uděláme se skutečnou a požadovanou vejškou, noa vybereme tu menší hodnotu // (jak se to chová si mužeme vyzkoušet behem hry, když budeme ruzně měnit velikost vokna) const float priblizeni = MIN ( ( float ) GetRenderWidth() / HERNI_SIRKA, ( float ) GetRenderHeight() / HERNI_VYSKA ); // nastavíme atribut 'zoom' tou naší spočitanou hodnotou kamera.zoom = priblizeni; //nastavíme posun kamery na velikost půlky vobrazovky kamera.offset = ( Vector2 ) { GetScreenWidth() /2, GetScreenHeight() /2 }; if ( obrazovka == OBRAZOVKA_HLAVNI_MENU ) { BeginDrawing(); // GetRenderHeight a GetRenderWidth je skoro to samý co GetScreenHeight a GetScreenWidth jenom s tim rozdílem // že to započitává DPI hele https://cs.wikipedia.org/wiki/DPI, jakože počet pixelů na jednotku délky // (na vobyčejným počitači nás to asi nemusí moc trápit, na mobilním zařizení by to ale bylo zásadní) DrawRectangleGradientV ( 0,0,GetRenderWidth(),GetRenderHeight(),DARKBLUE,BLACK ); //aktualizujeme a vykreslíme menu aktualizovatVykreslitMenu ( dt ); EndDrawing(); if ( IsKeyPressed ( KEY_ENTER ) ) { // pokud ukazatel na strukturu level neni vynulovanej, tak to pravděpodobně znamená že se už jednou hrálo a // že musíme starej level uklidit by sme mohli vygenerovat novej if ( level != NULL ) { freeLevel ( level ); // uklidíme i střely, idkyž by asi jako bylo víc lepčejší každý prostě nastavit atribut 'aktivni' na false :D free ( david_strely ); } //vygenerujeme si herní level level = vygenerovatLevel ( DELKA_LEVELU_BLOKU,textura_kameny ); // správně by jsme asi jako měli ňák líp informovat uživatele že se něco pokazilo třeba ňákým logem // takle by na to koukal jako péro z gauče co se jako pokazilo že hra hnedka spadla :D :D // to až příště jestli budete hodný :D :D :D ;D if(! level) return 1; david_strely = calloc ( POCET_STREL_DAVIDA_MAX,sizeof ( Strela ) ); if(!david_strely) { freeLevel(level); return 1; } //inicializujeme si davida david = (David){ .animace_beh = david_beh, .animace_idle = david_idle, .animace_sed = david_sed, .animace_skok = david_skok, .aktualni_animace = NULL, .pozice = {0,0}, .smer = 1, .mapa = level->dlazdicova_mapa, .vertikalni_rychlost = 0.0f, .zdaSkace = true, .strely = david_strely, .strileci_cooldown = 0.0f, .blikaci_cas = 0.0f, .zivoty = POCET_ZIVOTU_DAVIDA_MAX, .zranitelny = true, .ma_bonus = false, .hvezdny_bonus_cas = 0.0f, }; //nastavíme ukazatel aktuální animace na animaci 'idle' david.aktualni_animace = &david.animace_idle; //podle aktuální pozice nastavíme okraje oběktu a hitbox david.okraje = ( Rectangle ) { david.pozice.x + 45, david.pozice.y +8, DAVID_F_SIRKA -90, DAVID_F_VYSKA - 10 }; david.hitbox = ( Rectangle ) { david.pozice.x + 55, david.pozice.y +20, DAVID_F_SIRKA -110, DAVID_F_VYSKA-30 }; //vypnem hudbu menu StopMusicStream ( hudba_menu ); // vypnem hudbu levelu (by sme ji restartovali aby nám nehrála třeba někde vodprostředka) StopMusicStream ( hudba_lvl ); // zapnem hudbu levelu PlayMusicStream ( hudba_lvl ); // a nastavíme jeji adresu do ukazatele na aktuální hudbu, by se nám muzika taky aktualizovala, bez toho by nehrála :D hudba_aktualni = &hudba_lvl; //nakonec přepnem vobrazovku obrazovka = OBRAZOVKA_HRA; } // vykreslujem menu, takže nás další věci nezajímaj a přeskočíme je tim, že skočíme na začátek dalšího while cyklusu continue; } if ( obrazovka == OBRAZOVKA_HRA ) { // aktualizujem davida // funkce žere ukazatel, takže davida tam nacpem tak, že tam strčíme jeho adresu aktualizovatDavida ( &david, dt ); // aktualizujem level enum LevelStatus status = aktualizovatLevel ( level, &david, dt ); // podle návratový hodnoty určíme, jesttli se hraje dál nebo jestli nám David umřel a musíme // přepnout na 'game over' nebo jestli vyhrál a musíme se vrátit zpátky do hlavního menu // (ve finální verzi tý hry by sme asi měli mit ňáký hi-score nebo tak něco) // jenom tady přepínáme muziku a aktualní 'vobrazovku' if ( status == LVL_PROHRA ) { obrazovka = OBRAZOVKA_GAME_OVER; StopMusicStream ( hudba_lvl ); StopMusicStream ( hudba_bonus ); PlayMusicStream ( hudba_game_over ); hudba_aktualni = &hudba_game_over; } else if ( status == LVL_VYHRA ) { StopMusicStream ( hudba_lvl ); StopMusicStream ( hudba_bonus ); PlayMusicStream ( hudba_menu ); hudba_aktualni = &hudba_menu; obrazovka = OBRAZOVKA_HLAVNI_MENU; } } // nastavíme cíl kamery na střed davida kamera.target = ( Vector2 ) { david.okraje.x + david.okraje.width / 2.0f, david.okraje.y + david.okraje.height / 2.0f }; const float kamera_target_min_x = GetRenderWidth() / 2.0f / priblizeni; const float kamera_target_max_x = DELKA_LEVELU_BLOKU * BLOK_SIRKA - GetRenderWidth() /2/priblizeni; const float kamera_target_max_y = GetRenderHeight() / 2.0f / priblizeni - DAVID_F_VYSKA + BLOK_VYSKA*5; // pohlídáme si ty minimální a maximální možný hodnoty kamera.target.x = MAX ( kamera.target.x, kamera_target_min_x ); kamera.target.x = MIN ( kamera.target.x, kamera_target_max_x ); kamera.target.y = MIN ( kamera.target.y, kamera_target_max_y ); // zapnem vykreslování BeginDrawing(); // vykreslíme ten gradient DrawRectangleGradientV ( 0,0,GetScreenWidth(),GetScreenHeight(),DARKBLUE,BLACK ); // první kus našeho parallaxu // hovorka se asi jako bojí děsně velkejch měsiců když je furt v horrorovejch komixech kreslí, // tak mu uděláme radost na na pozadí vykreslíme supr strašidelnej měsíc :D :D float mensi_rozmer = MIN ( GetRenderWidth(),GetRenderHeight() ); DrawTexturePro ( textura_mesic, ( Rectangle ) { 0,0,textura_mesic.width,textura_mesic.height }, ( Rectangle ) { GetRenderWidth() /2,GetRenderHeight() /2,mensi_rozmer*1.5, mensi_rozmer*1.5 }, ( Vector2 ) { mensi_rozmer*1.5/2,mensi_rozmer*1.5/2 },0, ( Color ) { 102, 191, 255, 128 } ); // aktivujem transformování tou naší kamerou // takže jakoby vykreslujem to, co kamera vidí BeginMode2D ( kamera ); //vykreslíme parallax // to je jakože takový pozadí, který se různě pomaličku posouvá, když se hráč pohybuje na mapě a vytváří to // víc lepší iluzi že se hráč jakože někam pohybuje // todle je zatim dělaný jentak na hrubo, vykresluje to takovou siluletu jeruzalémskýho hradu která se děsně pomaličku posouvá // když David poskakuje nebo de dopředu // velikost tý textury hradu Rectangle hrad_zdroj = {0,0,1024,512}; // cílovej vobdelnik hradu // uděláme ho 2x delší než je šířka vobrazovky a budem // (asi to nebude moc dobře fungovat na uzkejch vobrazovkách, tam by sme asi jako museli zvolit // ňákej uplně jinej koncept, nvm) Rectangle hrad_cil = { .x = kamera.target.x - GetRenderWidth() /priblizeni, .y = kamera.target.y - GetRenderWidth() /2/priblizeni, // vodelnik taky bude muset bejt 2x širší než vyšší, by se nám moc nezdeformovala textura .width = GetRenderWidth() /priblizeni*2, .height = GetRenderWidth() /2/priblizeni*2, }; // počátek/origin toho velkýho rectanglu budem určovat z mezí vobrazovky // musíme si pohlídat když hráč stojí u kraje mapy by nám tam nevylezla někam doprostředka vobrazovky hrana textury :D float hrad_y_posun_paralax = -300.0f + 300.0f * kamera.target.y/kamera_target_max_y; float hrad_x_posun_paralax = -GetRenderWidth() /priblizeni/2 + GetRenderWidth() /priblizeni * ( kamera.target.x - kamera_target_min_x ) /kamera_target_max_x; // noa vykreslíme ten náš parallax DrawTexturePro ( textura_hrad, hrad_zdroj, hrad_cil, ( Vector2 ) { hrad_x_posun_paralax,hrad_y_posun_paralax },0,WHITE ); // vykreslíme level vykreslitLevel( level, &kamera); // vykreslíme davida vykreslitDavida(&david); DrawRectangleGradientV ( kamera.target.x - GetRenderWidth() / 2 / priblizeni,BLOK_VYSKA*10, GetRenderWidth()/priblizeni,BLOK_VYSKA*3,BLANK, BLACK ); // a pod tim všecko vyčerníme černým vodelnikem, kterej hezky navazuje na ten náš černej gradient DrawRectangle ( kamera.target.x - GetRenderWidth() /2/priblizeni,BLOK_VYSKA*13,GetRenderWidth() /priblizeni,GetRenderHeight()/priblizeni,BLACK ); // vypneme kameru EndMode2D(); const Rectangle srdicko_rect = {2,503,96,83}; for ( int i = 0; i < POCET_ZIVOTU_DAVIDA_MAX; i++ ) { Rectangle cil = { .x = i*srdicko_rect.width/2, .y = GetScreenHeight() - srdicko_rect.height/2, .width = srdicko_rect.width/2, .height = srdicko_rect.height/2, }; DrawTexturePro ( textura_ruzne,srdicko_rect,cil, ( Vector2 ) { 0,0 },0.0f,i<david.zivoty? WHITE:BLACK ); } // 'obrazovka' game over if ( obrazovka == OBRAZOVKA_GAME_OVER ) { // pokud david umře, tak už nebudeme herní věci aktualizovat ale budem je furt vykreslovat // všecko překryjeme smutečním černým poloprusvitným vobdelnikem.... DrawRectangle ( 0,0,GetRenderWidth(),GetRenderHeight(), ( Color ) { 0,0,0,128 } ); // .... přes kterej namalujeme velkej zelenej text s nápisem 'game over'.... DrawTexture ( textura_menu_game_over,GetRenderWidth() /2 - textura_menu_game_over.width/2,GetRenderHeight() /3 - textura_menu_game_over.height/2,WHITE ); // ....a textem, že má hráč zmáčknout na klávesnici čudlik 'enter' DrawTexture ( textura_menu_zmackni_enter,GetRenderWidth() /2 - textura_menu_zmackni_enter.width/2,GetRenderHeight() - textura_menu_zmackni_enter.height,WHITE ); if ( IsKeyPressed ( KEY_ENTER ) ) { // noa když ten čudlik hráč zmáčkne, tak přepnem muziku, a přepnem vobrazovku StopMusicStream ( hudba_game_over ); PlayMusicStream ( hudba_menu ); hudba_aktualni = &hudba_menu; obrazovka = OBRAZOVKA_HLAVNI_MENU; } } // a skončíme s vykreslováním by se naše scéna poslala na monitor EndDrawing(); } CloseWindow(); // uvolníme naše vlastní struktury if ( david_strely ) { free ( david_strely ); } if ( level ) { freeLevel ( level ); } //uklidíme textury UnloadTexture ( textura_mesic ); UnloadTexture ( textura_david_spritesheet ); UnloadTexture ( textura_kameny ); UnloadTexture ( textura_duchove_spritesheet ); UnloadTexture ( textura_ruzne ); UnloadTexture ( textura_hrad ); UnloadTexture ( textura_menu_titulek ); UnloadTexture ( textura_menu_pozadi ); UnloadTexture ( textura_menu_pozadi2 ); UnloadTexture ( textura_menu_david ); UnloadTexture ( textura_ruzne ); UnloadTexture ( textura_menu_zmackni_enter ); UnloadTexture ( textura_menu_game_over ); // vypnem audio zařízení CloseAudioDevice(); // musíme uvolnit i muziku UnloadMusicStream ( hudba_lvl ); UnloadMusicStream ( hudba_bonus ); UnloadMusicStream ( hudba_menu ); UnloadMusicStream ( hudba_game_over ); // a taky uvolníme zvuky UnloadSound ( zvuk_kroku ); UnloadSound ( zvuk_skoku ); UnloadSound ( zvuk_vystrel ); UnloadSound ( zvuk_duch_chcip ); UnloadSound ( zvuk_duch_strela ); UnloadSound ( zvuk_zasah ); UnloadSound ( zvuk_kontakt ); UnloadSound ( zvuk_padu ); UnloadSound ( zvuk_zaghrouta ); UnloadSound ( zvuk_powerup ); UnloadSound ( zvuk_vyhra ); return 0; }
Noa tramtadadá, když David doběhne až na konec mapy tak strhne praporek a vítězství!!
Rozhodně to neni ani zdaleka žádná hotová hra, eště nám toho jakoby děsně moc chybí udělat, si přectavte že by ste u takovýdle věci měli třeba hodinu denně sedět a pařit to, nóó byste se jako brzy začali nudit 😁 😁
Určitě to jako chce víc pestřejší škálu nepřátel různejch, chce to víc zbraní páč ta pistolka je děsně nudná (vo tom anketa dole), chce to víc složitějšejší a variabilnějšejší herní svět noa ňáký víc lepčejší efekty, třeba softwérový kuličky co duchové a David střílej by asi jako měli mit ňákej efekt když něco zasahnou, takle prostě mizej, taky nám možná chybí ve spritesheetu ňáká Davidova chcípací animace kterou by sme mohli dycky zahrát když mu dojdou životy, to jak se ta hra skokově šprajce při gameover je takový vošklivý. A taky by to asi jako chtělo ňákej příběch s ňákou zápletkou.
jóóóóóó jestli v týdlectý duchařině budem pokračovat tak toho jako máme před sebou eště dost 😁 😜
Vobrázky sem vyraběla sama z různejch věcí který sem našla na netu, hudba a zvuky věčinou pocházej z opengameartu. Konkretně to sou:
Pro hudbu levelu sme použili muziku 'Waltz of the Ghosts' vod autora Aureolus_Omicron hele
Pro hudbu bonusu sme použili hudbu 'For a Few Shekels More' vod pana Alexandra Zhelanova hele
V menu je použitá hudba 'Ghost Mansion Dungeon Synth' vod pana sirsnowy7ho hele
Na vítězství sme použili znělku 'Victory!' vod pana Jona K. Fita hele
Všecky jakoby vosmibitový zvuky pocházej z tohodlectoho baličku hele vod SubspaceAudio
zvuk tamtoho terroristickýho vejskání je válčená kořist z tohodlectoho yt videjka hele
Noa taky sme použili budoucí hymnu uzemí gazy hele 😛 😛
Tiskni Sdílej:
videjko s gameplay v přiloze by sme si jako nekazili voči jenom gifama :P :P :D ;D
sem ho jenom trochu víc zrůžověla jinak to je voriginál :D ;D
boužel ty materiály který sem na to našla sou ňákejch 10 roků starý šikly by se ňáký víc novější :D ;D
taky se divim že to eště žije :O :D :O :D :O :D
jj to je dobrý :D :D :D :D
Ty farářovéTi
tehotny?Kdyz uz, tak tehotnAAA!!! Muzu se toto netyka. A byt tehotna je proste flag, takze neco jineho.
TehotnA kdyz uz! Muz nemuze mit tehotenstvi!Necpi mi prosím tyto levicové anti-rodinné feministické názory o 100% samostatnosti žen. Sorry, ale žena bez muže nemůže být těhotná, tj. muž "je těhotný" u manželky/partnerky, jelikož on to spoluzpůsobil.
"Když se zločinu dopustí stát, bude potrestán stát i se svými občany." Vít Rakušan
Mimochodem, mezitím izraelští zákonodárci rozhodli že Palesinci budou z Gazy vysídleni do Evropy.Odkaz?
S izraelskými představami se ovšem tento plán rozchází.
Tak tomu dáme ještě pár dníJo, to jsem taknějak čekal, že budeš potřebovat
Jen bych chtěl dodat následujíci: "Po okupaci zbytku okleštěné republiky německou armádou dne 15. března 1939 vydal Adolf Hitler výnos o zřízení Protektorátu Čechy a Morava, ve kterém byla mimo jiné upravena i otázka státního občanství. V článku 2 stojí konkrétně: „(1) Obyvatelé protektorátu, kteří jsou příslušníky německého národa, stávají se německými státními příslušníky a podle předpisu zákona o říšských občanech z 15. září 1935 (Říš. Zák. I., str. 1146) říšskými občany. Pro ně platí tudíž také ustanovení na ochranu německé krve a německé cti. Podléhají německé soudní pravomoci. (2) Ostatní obyvatelé Čech a Moravy stávají se státními příslušníky Protektorátu Čechy a Morava.“[12]" více zde Takže v roce 45 nebyli odsunuti Českoslovenští občané, ale občané říše, kteří žili na území Československa a dobrovolně se přihlásili k Německému občanství. Všichni tady nebyli na pozvání Otakaram ale přišli v několika vlnách později. Pozváni rozhodně byli. Odsunuti rozhodně měli být, mohlo to být ještě horší.
Kdo se prihlasil k nemeckemu obcanstvi nebyl nevinny, ale vlastizradce.Proc vlastizradce proboha? Ach jo, ten tvuj fanaticky etatismus...
Bylo lepsi je odsunout nez popravit, ne?Ne, nejlepsi by bylo neuplatnovat kolektivni vinu a nechat ceskoslovenske obcany zit a hospodarit na jejich majetcich svobodne... Ale ne, hrdinove konce valky se vyradili na svych sousedech, mnohdy je i okradli... A zbytek dokonal pan president se svymi dekrety...
Jihočeské dialekty němčiny sice byly proslulé svou nesrozumitelností i mezi ostatními vyriantami Bayrisch, ale silně pochybuji že se v nich slovo Ti (v německé transkripci asi Tjü) nějak prominentně vyskytovalo, pokud vübec. Na rozdíl od jihočeských nářečí češtiny (před sto lety i dnes).Jihočeši žádné "ti" nepoužívají (no dobře, jako třetí pád ve druhé osobě jednotného čísla, dejme tomu, ale zde se jedná o první pád množného čísla ve třetí osobě).Opravdovi jihocesi mluvili Nemecky, ale byli vyhnani novou republikou a dekrety pana presidenta.
Jihočeši žádné "ti" nepoužívajíJihocesi posrali pivo.
(no dobře, jako třetí pád ve druhé osobě jednotného čísla, dejme tomu, ale zde se jedná o první pád množného čísla ve třetí osobě)Ok.
Budvar je statni ci narodni podnik, ten nepiji. Regent sel za posledni roky nahoru. #PikadoruCmundeAScheissceZdarDej si Breznevaka a budes jak ta dvousecna koza z Futuramy. To uz radej ten Czechvar, posoudim snad brzy.
Patrne Breznak z Krasneho Brezna.Březňák je pivo a pivovar ve Velkém Březně u Ústí nad Labem s dlouholetou tradicí. Pivovar byl založen v roce 1753 ... Je dobre, ze se drzime Sudet.
Czechvar je Budvar v americe.No shit, bratan!
BrežněvákBrežněvák. Viz nize.
No shit, bratan!Nerozumět hluku tvého kmene. ...příjde mi to že nesouhlasíš, takže: tutaj kukni
Nerozumět hluku tvého kmene. ...příjde mi to že nesouhlasíša) Facepalm emoji may be used in cases, when constructive humor is intended. b) Please always carefully read. Especially the paragraphs above. c) Always remind your opponents of points a) and b). Cerny na bilym, kze jako moje, pokud to uz nekdo nerek drive... ... Ugh...
Cerny na bilym, kze jako moje, pokud to uz nekdo nerek drive... ... Ugh...a zase píšeš jak tatar... Co tvl znamená "kze jako moje", a co tvl nikdo "neřek" jakože dřív... pro info to tvl znamená tyvole.
tvl kde je tam nějakej fascepalm, to zaprvý... Zadruhý napiš prosím česky co si myslel tím: "No shit, bratan" Hlavně to "bratan" je z nějaký tatarštiny, což bych si prosil přeložit do češtiny přednostně. Cerny na bilym, kze jako moje, pokud to uz nekdo nerek drive... ... Ugh... a zase píšeš jak tatar... Co tvl znamená "kze jako moje", a co tvl nikdo "neřek" jakože dřív... pro info to tvl znamená tyvole.Vytecne zahrane! ... Ale pockat, ty jses Heroj co ma neco proti Tatarum!!
9. Ta studená věc bez chuti, u níž trváte na názvu pivo, vlastně pivem vůbec není. ... Americké značky budou označovány jako polozmrazená komáří moč, takže všechny tyto nápoje mohou být nadále prodávány bez rizika záměny.
Samson se třeba vyšvihl. Dřív to byl patok, ale dnešní jedenáctka už je chválena.Ale tak... chvalena je dnes i kdejaka zeme, nebo stat, ze?
Budvar jede klasikuAccess Denied! Posralo se to prave nejakych 20 let zpet. Nezbyva nez po par letech ty patoky asi znova vyzkouset, no...
Dej si místo těch plků raději pikador, nebo cmundu a bude dobře :).Pikador zamackni zpet, ale cmundu si dam rad vzdy. :) A bez nasich plku by se to tu asi zavrelo a prodalo v budoucnosti, rovnou s nami vybudovanejma psychologicke-jma prostorama! Nw.
Pikador zamackni zpet,Zrovna v neděli jsem v Bazileji viděl klasický český bazmek na párky v rohlíku. Akorát místo párku tam lili roztavený sýr a prodávali to jako Fondue Dog. K vidění online zde (celkem nic moc, ale co by člověk chtěl za 10 franků)
ale cmundu si dam rad vzdy. :)Taky mají
Akorát místo párku tam lili roztavený sýr a prodávali to jako Fondue Dog.Proste jina varianta Ghetto Pizzy.
Ty farářové maj poslední dobou fakt problémy, asi je posednul ďábel, nebo něco takovýho, hele
přešli na novej druh bylinek hele :O :D :D ;D
sem rada žese ti to jakože libí :D ;D
ze se nestydis, dva roky tu meles hovna o ukrajine ktery se prokazaly jako falesny, a ted mas tu drzost si hrat na experta na jiny konflikt. no kdyz se nestydis, tak ja se teda stydim za tebe a lituju kazdyho koho slepe podporujes, protoze ti to maji predem prohrany
jako třeba chudák pimp a někteří další tady?
diky 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 🐧 :D :D :D :D
snad se kuli tomu kolbáč ňák neurazí ale zase jako :O :D :O :D
sudo make install
No jo no, Ubuntu/Debian zas nemá balíček, co? Já fakt nechápu, jak lidi tyhle distra tolerují, zejména to Ubuntu.
Na pracovním PC, kde jsem pod hrozbou inkvizice nucen Ubuntu používat, to řešim Nixem. V tomhle případě by to ale asi nepomohlo, Nix sice raylib má, ale tahle věc určitě potřebuje (transitivně) grafickej driver a tam ta kooperace Nix → nativní balíčky je IIRC problém...
S lepším distrem tyhle blbosti člověk nemusí řešit...
No jo no, Ubuntu/Debian zas nemá balíček, co? Já fakt nechápu, jak lidi tyhle distra tolerují, zejména to Ubuntu.
Vždyť se na tom balíčku "pracuje" teprve rok a půl, to je v Debianu úplné nic...
Já fakt nechápu, jak lidi tyhle distra tolerují, zejména to Ubuntu.
prostě to funguje :D arch třeba potřebuje víc věčí skill na instalaci páč se prej třeba instaluje bez grafickýho rozhraní takže to asi jako neni žádná lidovka :D ;D
main()
to vlastně už je dost jedno. Ale best practices jsou best practices - zdroje je potřeba zase vrátit do systému - to se mi jako radikálnímu levicovému zelenému socialistovi samozřejmě líbí, to je taky jakoby malinká cirkulární ekonomika
Jenom možná by bylo lepší v tom zápisku ten main.c
a případně další soubory nedávat vždycky znova celej, ale udělat diff -u
, aby bylo vidět, co se změnilo, ale jako není to žádnej moc velkej problém, takhle taky dobrý.
věci zase uvolňuješ, i když před returnem z main() to vlastně už je dost jedno.
teroreticky by sme nemuseli na konci uvolňovat ani textury tou funkcí unload pokuď je současně nepouživá i ňákej jinej kontext nicmeně to sou asi jakoby ty druhy voptimalizací na kterejch moc neušetříš zato ale jako mužeš něco rozbít :D
Ale best practices jsou best practices - zdroje je potřeba zase vrátit do systému - to se mi jako radikálnímu levicovému zelenému socialistovi samozřejmě líbí, to je taky jakoby malinká cirkulární ekonomika
:D :D :D :D
Jenom možná by bylo lepší v tom zápisku ten main.c a případně další soubory nedávat vždycky znova celej, ale udělat diff -u , aby bylo vidět, co se změnilo, ale jako není to žádnej moc velkej problém, takhle taky dobrý.
jj něco takovýho přistě bude nutný páč se stala děsně divná věc ato že se ten blog prakticky locknul pro ňáký další editování páč je asi jako moc dlouhej (+-400kb) a háže to při ukladání ňákou java vyjímku :D věčinu blogu tvoří xkrát zkopirovanej +- stejnej zdroják takže by asi jakoby bylo dobrý se tomu podruhý ňák vyvarovat :D
nvm jestli diff ale bude dost 'inkluzivní' třeba pro lidi který moc neprogramujou nebo nevěděj nic vo vyrábění her a si proto jako našli tendlecten 'tutorial' třeba ale holt to asi jako bude nutný :D
a háže to při ukladání ňákou java vyjímku :DTohle mi ábíčko typicky dělá, když text obsahuje non-BMP znaky - jako třeba emojis - takže možná (jen hopytéza) se stalo to že ti to převedlo entity na unicode znaky, na kterejch si pak java vylámala zuby...
todlecto tam vyloženě vypisovalo ňáký dvě děsně velký čisla a byl mezi nima znak '<' a jedno bylo hezky kulatý se samejma nulama takže to spíš jako vypadá na ňákej limit kterej se nehlídá při vkládání zapisku ale až při editaci asi :D
emoji a tydlecty tady taky vobčas zloběj to máš pravdu :D :D de je sem nacpat asi jenom jako html kód noa kdyby si třeba ňákej blogisek kde máš použitý emoji votevřel pro editaci tak ti to tady ty html kódy z ňákýho duvodu tajemnýho nahradí unicode znakama noa to pak dělá ty problémy s ukládáním resp. nejde to s nima uložit :D :/ :D :/ řešim to tak že mam blogisek napsanej v *.txt bokem a sem ho pak jako hotovej vkládám a nahrazuju s nim komplet puvodní text blogu tady v html editoru (včetně těch samonahrazenejch unicode znaků pro emoji) :D
„Když před Izraelem utíkali a byli na bétchorónské stráni, vrhal na ně Hospodin z nebe balvany až do Azeky, tak umírali. Těch, kteří zemřeli po kamenném krupobití, bylo více, než těch, které Izraelci pobili mečem.“ (Joz 10,11); „Tak vybil Jozue celou zemi, pohoří i Negeb. Přímořskou nížinu i srázy, a všechny jejich krále. Nikoho nenechal vyváznout, vše, co dýchalo, vyhubil jako klaté, jak přikázal Hospodin, Bůh Izraele.“ (Joz 10,40); „Ajského krále dal pověsit na kůl, kde byl až do večera. Když slunce zapadlo, rozkázal Jozue, aby sňali jeho mrtvé tělo z kůlu. Pohodili je u vchodu do městské brány a navršili nad ním velikou hromadu kamení, která tam je až dodnes.“ (Joz 8,29).
A také: "Kdo chce válku, posílá zbraně." Jedno které straně. Což platí vždy a všude.Jjj, vzpomínám si, jak jsi tady v době po Majdanu ostře kritizoval dodávky zbraní z Ruska na Donbas...
Těžko dodáš odkaz na zdroj tam, kde se nepohodlné zprávy, které by se mohly za nějaký čas považovat za důkazy likvidují. To je jedna věc. A druhou je dnes tak oblíbená nálepka "dezinformace".
Pokud jde o sestřelení toho letadla. Nevím kdo ho sundal, ale vím že v té oblasti nemělo co dělat, takže za mne jsou jednoznačnými viníky ukrajinci. A s tou Bučou je to také z mého pohledu sporná záležitost. Ne v tom, že se to stalo, ale v tom kdo, komu a proč to udělal. Je totiž velice snadné popravit tzv. proruské kolaboranty a pak je vydávat za oběti ruské agrese.
V době kdy pravoseci upalovali lidi, armáda bombardovala civilisty a opolčenci obsazovali místní vojenské základny, některé bez jakéhokoliv odporu?Jo, protože bombardování civilistů / civilních cílů RF aktuálně vůbec nedělá žejo... No ale to je jedno, bylo mi jasné, že to tvoje "Což platí vždy a všude" tak úplně vždy a všude platit nebude a dál asi nemá smysl to řešit
Od invaze na ukrajinu maji rusaci na rukou krev asi 40000 civilistu zabitych, kolem 80000 ranenych a 20000 odvlecenychProdam gauc s kocickou se zvoneckem. Zn.: Kydsi na tom gauci sedaval vrchni stab SOHRu
Jak můžeš vidět aktuálně v Gaze, i tom bombardování civilistů jsou Rusové prostě žabaři.V tom případě sis tady neměl co stěžovat na bombardování Donbasu Ukrajinou, protože to byla ještě podstatně větší žabařina než min(Rusko, Izrael). Ale jasný, chápu, výjimečka pro Rusko je vždy potřeba najít.
Protiofenziva nepřinesla očekávané výsledky. Bojujeme proti druhé nejlepší armádě na světě, připustil Zelenskyj Aneb od hajlování k fňukání :-PNo tak jelikož západ těch zbraní až tak moc nedodal, tak tohle je celkem očekávatelné, ne? Takže teď bude Ukrajina opět přecházet do obrany a když to půjde dostatečně špatně, bude příležitost otestovat tvoje hypotézy o tom, že RF nemá zájem Ukrajinou postupovat moc daleko na západ, a zjistit, jestli ses ohledně Ruska opět mýlil.
západ těch zbraní až tak moc nedodalUkrajina jich az tak moc nezaplatila (ani na dlouhodoby uver).
No tak jelikož západ těch zbraní až tak moc nedodal,
Tak takhle bych to určitě neřekl. To byly vůbec největší zbrojní dodávky snad od druhý světový (lend-leasu), který přesáhly 100 miliard dolarů. A takovej třeba obyčejnej dělostřeleckej granát stojí okolo 2000 říšských marek 4. říše (€). Měsíčně jich vystřelí Ukrajina cca 200k, teď teda už asi míň. Tohle si nemůže dovolit financovat dlouhodobě nikdo. EUSSSR si právě dal socialistický závazek na dodávku děl. granátů, který ale nesplnil. Nicméně EUSSR prorazí na poli humanismu a porazí Putlera přijmutím doktorů a inženýrů, a až doputuje do Kremlu svěží vzduch z Green dealu bez nadbytečného CO2, tak Vladimíra Adolfoviče z toho klepne.
A ta válka dospěla dávno do fáze, kdy je to v podstatě poziční mjasorúbka (mlýnek na maso). To prorokoval na začátku baťka Lukašenko s tou mjasorůbkou a měl pravdu. Životnost nějaký techniky na bojišti se počítá v řádech minut (do deseti minut tam přiletí dron s granátem) a to platí vícméně pro obě strany a týká se to dá se říct i pěších jednotek. s. z Horní dolní tady konstruoval vítězné drony a ejakuloval u toho, ale v současnosti je situace taková, že Rusko má v dronech převahu 1:5 a tuhle informaci jsem zaznamenal dokonce někde na ČT."V Britském muzeu."Indeed, ale to uz je rozlite mleko. Dokonce mene, nez Kodax Gigastis. Pardon, Codex gigas, T9, znas to... Prodam byt v Gaza City, Zn.: V SVJ je to samej magor a stat me ruinuje
pan profesor nutella mi tam moc neštimmuje s nim dyštak vyrobim něco těsně pár dní před ňákejma volbama by to jako uďálo nejvěčí škody :D
davidoj nic špatnýho nepřeju páč asi jako uplně nemuže zato co dělá :D
btw kdyby david skutečně běhal v uniformě idf někde po jeruzalému a bojoval se zlejma duchama by sem mu navopak přála ať se mu jako daří :D ;D
by z toho časem mohlo být něco jako Prince of Persia, akorát košer
to je ale spíš taková jakože logická roguelike hra sem koukala :O :O sem spíš měla přectavu vo ňáký jakože akční hře nicmeně vidim tam věci který by se asi jako možná mohly z toho prince vykrást :D
SADAMZimak byl to nejelegantnejsi hovado na tomto portalu, co si pamatuju...
nejsem :D :D
ze sadámů navic znám jenom tamtoho voběšenýho diktátora :D ;D
https://web.archive.org/web/20090725082432/http://lordhoven.cz/
To byl taky on?V cem nemel svoje spinave Vladimir Usamin prstiky ze?
V každém případě zpětně pročítat ty blogy je poměrně depresivní...Me ta doba bavila. Ale jestli mas neco uplne konkretniho, tak se rad podivam, muzu to samo vnimat upe jinak dneska. mahTilda 15 let neni malo ani pro zelvu.
Jo, dodělal a začal se živit tim hliníkem... Je to fakt frajer!Dikes. Svaty howno tvl stejne, to je kolik! Nenadarmo sa na nas vsechny vysral.
Komunismus v praxi…To je fakt! Kibuce roz... coze, tvl???!! #aninahodou! Yo, a byl driv utlak silnejsich, nebo prevalence spojenych? Jo a asi novy PsyOps se v mediich rychle rodi...
Tak na abicku musi byt jakykoli funkcni model komunismu par jedinci velebenAle neee. Ja jen narazel na to idnesovske podtrzitkovske radoby disidentovani ve forme nepricetnych one-lineru ohledne chapani toho, co ze je a neni komunismus (kdyz uz spojuje ne-vzdy-nutne-spojitelne). Ale uz zabalil spacak a sel domu spat pred skoro asi tejdnem a neco...
To byly spíš ty lentilky co ti dal glee…Tojo, hlavne ze na nich bylo napsano od zacatku "_navodar=".
Co ti vadi vic? Ze ty podvody delali Cesi nebo Ukrajinci?Jemu to nevadí vůbec. Kdyby vadila korupce, náckové nebo útlak Rusů, musel by vadit i kremelský režim. Vadí pomoc Ukrajině, protože zpomaluje postup RF.
Místo nenávistných keců pošli Ukrajincům další penízeNo jo furt, tak teda jo no. Ani jsem se nechystal teď nic posílat, ale když na tom trváš, tak něco pošlu. Připíšu do poznámky, že to je na tvé přání.
ale když na tom trváš, tak něco pošlu. Připíšu do poznámkyA máš nějakou, alespoň rámcovou nebo přibližnou představu, kterému mafiánovi přispěješ na kousek kulometnostného pásu? Teď si třeba dávají důstojníci generálního štábu v Kyjevě granát k narozeninám, to docela frčí teďko, tyhle žertovné narozeninové dárky. Motivační dopis tam můžeš připojit, že tě motivoval Ráďa, to jest desolát prorusský
Teď si třeba dávají důstojníci generálního štábu v Kyjevě granát k narozeninám, to docela frčí teďko, tyhle žertovné narozeninové dárky.To bude celkově východoevropská móda, v Rusku prý taškařice s granáty přímo letí.
v Rusku prý taškařice s granáty přímo letí.Nojo, ale tady trockista králik nechce posílat granát do Kremlu, jestli se nepletu. A navíc nespíš ani netuší, kdy má Gerasimov narozeniny.
No dyť řikám, že to tam teďko dost frčí. A podlý podleš ukazuje prstem schválně někam jinam. To byla asi nějaká větší narozeninová párty.
Dejme tomu, že jsou trochu výbušnější povahy. Ale profesor Nutella řikal, že to bude ohromný přínos v EU, určitě nejen ekonomický ale i kulturní, to nemůže bejt dezinformace.