Korak-po-korak vodič za izradu C++ labirintske igre

Posljednje ažuriranje: 05/05/2026
  • Igre labirinta u C++ počinju s mrežom u memoriji, odvojenim stanjem igrača i jednostavnom petljom igre kojom upravlja unos s tastature.
  • Kolekcionarski predmeti, zamke, rezultat, životi i tajming pretvaraju jednostavnu navigaciju u potpunu petlju igranja s jasnim ciljevima i rizicima.
  • Konzolni projekti podučavaju ključne koncepte - reprezentaciju svijeta, sudare i ažuriranja stanja - koji se direktno skaliraju na 3D DirectX labirint igre.

Tutorijal za igru ​​labirinta u C++-u

Ako ste ikada željeli napraviti vlastitu igru ​​labirinta u C++ ali osjećate se preopterećeno grafičkim engine-ima, fizičkim bibliotekama ili audio middleware-om, ovaj vodič je za vas. Fokusirat ćemo se na ono što je zaista važno na početku: unutrašnju logiku igre, kako svijet živi u memoriji i kako igrač komunicira s njim putem tastature. Nakon što je ta jezgra čvrsta, dodavanje vizualnog sjaja ili otmjenih efekata postaje samo još jedan sloj, a ne nemoguća planina za popeti se.

Cilj je stvoriti potpuno igrivo iskustvo lavirinta u C++-u. Počevši od jednostavne konzolne verzije i konceptualnog povezivanja s onim što biste radili u naprednijem DirectX 3D projektu poput UWP-ovog "Marble Maze". Vidjet ćete kako predstaviti labirint pomoću mreža, upravljati pozicijom igrača odvojeno od mape, validirati kretanje, dodati kolekcionarske predmete i zamke, upravljati životima, pratiti vrijeme i strukturirati osnovnu petlju igre. Usput ćemo također pokazati kako se te iste ideje skaliraju do grafičkog 3D lavirinta s bogatijim ulazom i zvukom.

Dizajniranje labirinta kao mreže u memoriji

Osnova naše konzolne igre labirinta je mreža redova i kolona. ...koji se u potpunosti nalazi u memoriji prije nego što se išta ispiše na ekranu. Konceptualno, svaka ćelija u toj mreži pohranjuje određenu vrstu sadržaja: zidove, otvorene staze, početnu tačku igrača ili posebne elemente poput zamki i predmeta za sakupljanje. Ovaj pristup je ključan jer ekran treba biti samo odraz trenutnog stanja igre, a ne mjesto gdje se logika zapravo nalazi.

Vrlo praktičan način modeliranja ove mreže u C++ je korištenje vector<string>, gdje svaki niz predstavlja jedan red labirinta, a svaki znak u tom nizu je ćelija. Da bi mreža bila čista i konzistentna, svi redovi moraju imati isti broj kolona. Ta konzistentnost pojednostavljuje i renderiranje i provjere kolizije, jer možete sa sigurnošću pretpostaviti da map[row][column] važi sve dok se držite poznatih granica.

Svaki znak na mapi kodira različitu vrstu pločice. Na primjer, možete koristiti # za označavanje zidova koje igrač ne može preći, prazna mjesta (ili određeni znak poput razmaka ili tačke) za pod po kojem se može hodati, * za kolekcionarske predmete, a možda i X za opasne zamke. Sam igrač ne mora biti trajno upisan u mrežu; umjesto toga, privremeno ga renderirate prilikom crtanja scene na osnovu njegovih trenutnih koordinata.

Početna verzija programa jednostavno iterira kroz mrežu i ispisuje svaki red do konzole red po red. Iako ovo izgleda jednostavno, već definira granice, koridore i početni raspored. U ovoj fazi ste odvojili dvije ključne ideje: strukturu labirinta (statičku) i stanje sistema (dinamičko), čak i ako dinamički dio još nije u potpunosti implementiran.

Namjerno održavanje malog opsega je strateški izborU ovom projektu ne koristite endžine, eksterne grafičke biblioteke ili napredne sisteme za rad sa prozorima. Nema tekstura, nema animiranih spriteova, nema složenog audio cjevovoda. Izlaz se odvija putem standardne konzole, što vas fokusira na pravila igre, ažuriranja stanja i organizaciju koda, a ne na otklanjanje grešaka u API-ju za renderovanje.

Predstavljanje stanja igrača odvojeno od mape

Nakon što je mapa labirinta postavljena, sljedeći ključni korak je odvajanje igrača od mreže.Umjesto postavljanja trajnog P znak u nizu mape ili vector<string>, lokaciju igrača održavate pomoću dvije odvojene varijable, obično cijelog broja u redu i koloni (na primjer, playerRow i playerCol). Ovo odvajanje vam daje jasnu razliku između nacrta labirinta i trenutnog statusa igranja.

Prilikom crtanja scene, prolazite kroz mapu i ubacujete simbol igrača samo tokom renderiranja.Uobičajeni obrazac je: za svaku ćeliju, ako se njene koordinate podudaraju s koordinatama igrača, nacrtajte 'P'; u suprotnom, nacrtajte šta god se nalazi na statičkoj mapi. Ovo olakšava kretanje igrača bez stalnog prepisivanja podataka na mapi i smanjuje mogućnost miješanja statičnih zidova s ​​dinamičkim entitetima.

Samo kretanje može se upravljati klasičnim WASD ulazom: W za gore, A za lijevo, S za dolje i D za desno. Svaki pritisak tipke predlaže novi potencijalni položaj (na primjer, newRow = playerRow - 1 prilikom kretanja prema gore). Prije stvarne primjene tog ažuriranja, igra provjerava da li se ciljna ćelija nalazi unutar važećih granica i da li je to lik zida. Ako bilo koja provjera ne uspije, kretanje se odbija i igrač ostaje tamo gdje se nalazi.

Ovaj korak validacije je srž logike sudara u labirintu zasnovanom na mreži.Umjesto da dozvolite liku da klizi kroz barijere ili nestaje van ekrana, primjenjujete pravila koja čine svijet konzistentnim: zidovi su čvrsti, labirint ima ograničenja i igrač se ne može teleportirati izvan dizajniranog područja. Čak i u jednostavnom konzolnom projektu, ovo su iste konceptualne provjere koje biste kasnije implementirali koristeći 3D sudarače i fizičke engine-e.

Budući da igrač ima svoje koordinate, možete slobodno proširiti model svijeta. sa dodatnim sistemima poput bodovanja, života ili inventara bez dodirivanja statičke reprezentacije mape. Labirint ostaje upravo to: raspored pločica. Sve što se mijenja tokom igre - pozicija, sakupljeni predmeti, izgubljeni životi - živi u odvojenim varijablama i strukturama koje se nezavisno ažuriraju u svakom kadru.

Kolekcionarski predmeti, bodovi i uslovi za pobjedu

Hodanje po praznom lavirintu brzo dosadi, pa je sljedeći logičan korak popuniti ga kolekcionarskim predmetima.Jednostavna konvencija je korištenje * znak za označavanje ćelija koje sadrže predmet koji igrač može podići. Oni mogu predstavljati novčiće, dragulje ili generičke "bodove", ali iz perspektive engine-a to su samo pločice sa određenim simbolom.

Tokom svakog kretanja, nakon što potvrdite da igrač može ući u novo polje, provjeravate da li to polje sadrži kolekcionarski predmet.Ako se to dogodi, povećavate brojač rezultata (na primjer, score++ ili dodajte neku fiksnu vrijednost poput 10 bodova) i zatim ažurirajte mapu na toj poziciji u praznu ćeliju po kojoj se može hodati. Ovo oponaša podizanje predmeta i ostavlja pod slobodnim za buduće posjete.

Da bi igra ostala orijentirana na cilj, možete pratiti koliko kolekcionarskih predmeta je ostalo u labirintu.Jednostavan način je inicijalizacija brojača prilikom učitavanja mape skeniranjem svih * likovi. Svaki put kada igrač sakupi jedan, smanjujete brojilo. Kada taj brojilo dostigne nulu, imate prirodni uslov za pobjedu: igrač je očistio lavirint od svih predmeta.

Alternativno ili dodatno, možete definirati određenu izlaznu ćeliju kao područje pobjedeNa primjer, donji desni ugao labirinta može predstavljati sobu s blagom. U datim idejama za kod često ćete vidjeti koordinate poput (60, 40) or (60, 4) označene kao posebne pozicije. Kada igrač dođe do jedne od ovih pločica sa svim obaveznim zadacima završenim (kao što je sakupljanje svega), prikazuje se čestitka i zaustavlja se igra.

Kombinacijom kolekcionarskih predmeta sa prostornom navigacijom, pretvarate jednostavno kretanje u stvarnu igru.Igrač sada ima razlog da istražuje slijepe ulice, riskira zaobilazeći zamke ili se vraća kroz već posjećene hodnike. Na višem nivou, ovo također odražava strukturu većih dizajna igre, gdje su istraživanje, pljačka i dovršavanje ciljeva osnovne petlje koje se ponavljaju kroz mnoge nivoe ili okruženja.

Zamke, životi i stalne prijetnje

Da biste dodali napetost svom labirintu, možete dodati zamke koje kažnjavaju nepažljivo kretanje.Uobičajeni obrazac je predstavljanje zamki sa X lik na mapi. Za razliku od kolekcionarskih predmeta, ovi predmeti nisu namijenjeni da nestanu kada se aktiviraju; oni djeluju kao trajne opasnosti koje igrač mora zapamtiti i izbjegavati u budućim igrama.

Kada igrač stane na ćeliju zamke, smanjujete brojač života i resetujete njegovu poziciju. do početnih koordinata labirinta ili do određene kontrolne tačke. Ovo uvodi novi resurs za upravljanje - živote - i uvodi rizik od neuspjeha čak i ako je labirint tehnički rješiv. Ako broj života padne na nulu, možete završiti igru ​​i ispisati poruku "Game Over" (Game Over).

Održavanje zamki trajnim nije samo stilska odluka; ono pojednostavljuje mentalni model igrača.Kolekcionarski predmeti mijenjaju mapu jer predstavljaju resurse koji se mogu iscrpiti. S druge strane, zamke mijenjaju stanje igrača (živote, poziciju) dok ostavljaju izgled svijeta nepromijenjenim. Ta jasna podjela pomaže u održavanju urednosti koda: ponašanje povezano s mapom razlikuje se od ponašanja povezanog s likom.

Ovaj isti princip se vrlo prirodno skalira na naprednije engine-ove i 3D okruženja.U igri stila Marble Maze zasnovanoj na DirectX-u, rupe na ploči djeluju kao zamke: upadanje u jednu šalje mramor natrag na najnoviju kontrolnu tačku bez promjene geometrije ploče. Same kontrolne tačke funkcioniraju kao logički markeri u svjetskom prostoru koji utječu na ponašanje prilikom ponovnog pojavljivanja, a ne kao uništive ili potrošne pločice.

Ako želite dodatno unaprijediti implementaciju konzole, možete kombinirati zamke sa nasumičnim rasporedima.Na primjer, generiranje određenih dijelova labirinta korištenjem rand() i dodjeljivanje vrijednosti pločicama poput 0, 1 ili 2, gdje neke predstavljaju zidove, druge sigurne puteve, a nekoliko ćelija postaju kandidati za zamke. Na taj način svaki novi prolaz može se osjećati drugačije, a istovremeno slijediti isti skup osnovnih pravila.

Obrada ulaza: Od blokiranja čitanja do prave petlje igre

Mnogi početnički konzolni programi oslanjaju se na blokiranje unosa, čekajući da korisnik pritisne Enter. nakon svake komande. To je u redu za menije, ali za stvarnu igru ​​želite kontinuiranu, responzivnu kontrolu. Umjesto zaustavljanja izvršavanja pri svakom potezu, program bi trebao nastaviti petlju, provjeravati je li tipka pritisnuta i ažurirati samo kada je to potrebno.

Na Windowsu, uobičajeni obrazac je korištenje _kbhit() i _getch() od <conio.h>. Funkcija _kbhit() govori vam da li postoji ključ koji čeka u ulaznom baferu bez pauziranja cijelog procesa. Ako vrati vrijednost "true", uzimate taj ključ sa _getch() i interpretirati ga kao komandu (WASD, tipke sa strelicama, escape za izlaz, itd.). Ako nema tipke, petlja se nastavlja, omogućavajući vam da ponovo iscrtate okvir, ažurirate tajmere ili animirate elemente.

Ovaj neblokirajući unos pretvara vašu glavnu rutinu u pravu petlju igre.Umjesto krutog ciklusa "unos → ažuriranje → crtanje → čekanje" odvojenog korisničkim potvrdama, imate kontinuiranu petlju koja više puta: provjerava unos, ažurira pozicije i stanje i ponovo crta labirint. Takve petlje su u srži gotovo svake interaktivne igre, od tekstualnih avantura do AAA 3D ​​pucačina.

I dalje možete davati dodatne komande osim kretanjaNa primjer, u isječcima primjera koda spominju se ključevi poput R da izađem iz igre, N zatražiti novogenerirani labirint, pa čak i ključ poput T da prikaže konačno utrošeno vrijeme. Njihova obrada se svodi samo na poređenje pritisnutog tastera sa određenim znakovima unutar bloka za obradu unosa.

U naprednijim okruženjima kao što je UWP DirectX igra, ulazni izvori se umnožavaju.Isti koncept lavirinta može se kontrolisati tastaturom, gamepadom, mišem, akcelerometrom ili dodirom. API se razlikuje za svaki tip uređaja, ali ideja je nepromijenjena: petlja igre kontinuirano provjerava najnovije ulazno stanje, prevodi ga u radnje u igri i unosi ga u logiku simulacije prije renderiranja ažuriranog okvira.

Poboljšanje renderiranja konzole i HUD-a

Čak i u tekstualnoj konzoli, možete mnogo toga učiniti da vaš labirint izgleda kao uglađena igra.Umjesto stalnog ispisivanja novih redova i pomicanja lavirinta prema dolje, možete osvježavati isto područje ekrana iznova i iznova. U Windowsu, pozivanje system("cls") U svakom kadru se briše konzola, a zatim se crta ažurirani labirint, igrač, rezultat i sve poruke. Jednostavno je i nije najefikasnije, ali za male projekte dobro funkcioniše.

Da biste tekst pozicionirali tačno tamo gdje želite, možete koristiti pomoćnu alatku poput gotoxy(int x, int y)Pod Windowsom, ova funkcija poziva SetConsoleCursorPosition() od windows.h, koristeći a COORD strukturu za određivanje željenih koordinata. Eksplicitnim pomicanjem kursora možete postaviti labirint u jedan dio ekrana, a HUD s rezultatom, životima i preostalim predmetima na vrh ili dno.

Skrivanje trepćućeg kursora teksta također poboljšava vizualno iskustvoTo možete uraditi putem SetConsoleCursorInfo(), konfigurisanje CONSOLE_CURSOR_INFO struktura tako da bVisible je postavljeno na FALSETo je mali detalj, ali smanjuje smetnje i čini da mreža labirinta izgleda kao pravo igralište, a ne kao sirovi prikaz terminala.

ASCII znakovi vam omogućavaju crtanje okvira i zidova s ​​više ličnostiNa primjer, vrijednosti poput 205 (dvostruka horizontalna traka), 186 (dvostruka vertikalna traka), 201 (gornji lijevi ugao), 187 (gornji desni ugao), 200 (donji lijevi ugao) i 188 (donji desni ugao) mogu se pretvoriti u char i odštampani da formiraju dekorativni okvir oko lavirinta. Ispunjeni blokovi poput 219 odlično funkcionišu za debele zidove. Ovo daje mapi izgled više "igre", a ne nasumičnu kolekciju simbola.

Možete čak prikazati i mali "uvodni ekran" ili uvodni baner koji ispisuje naziv projekta, podatke o autoru i prijateljski pozdrav, a zatim čeka bilo koji taster prije pokretanja glavne petlje. Ovakav uvod postavlja ton, objašnjava osnovne kontrole (na primjer „Pritisnite bilo koji taster za početak, koristite tastere sa strelicama ili WASD za kretanje“) i čini da projekat izgleda završeno, uprkos korištenju samo tekstualnog izlaza.

Vremenski raspored, slučajni lavirinti i razmatranja performansi

Mjerenje koliko vremena je potrebno igraču da riješi labirint je još jedan jednostavan, ali moćan dodatak.Jednostavan pristup u C++ je korištenje clock_t tip i clock() funkcija od <time.h>Na početku trčanja zovete clock() za pohranjivanje vremena početka. Kada igrač dostigne cilj ili aktivira uslov završetka, izračunavate protekle tikove oduzimanjem originalne vrijednosti od nove clock() poziv.

Sirova vrijednost koju vraća clock() predstavlja taktove procesora, a ne sekundeDa biste to pretvorili u jedinice prilagođene ljudima, podijelite s konstantom CLOCKS_PER_SECOvo vam daje broj sekundi koje je igrač proveo snalazeći se u lavirintu. Prikazivanje tog broja i davanje korisniku opcije poput „Pritisnite T da vidite svoje vrijeme“ može dodati vrijednost ponovnom igranju dok pokušavaju oboriti svoj lični rekord.

Ako se osjećate avanturistički, možete i dinamički generirati nove labirinte.U jednom od fragmenata koda, nalazi se primjer dodjeljivanja slučajnih vrijednosti ćelijama mapiranja pomoću izraza poput map[i][j] = rand() % 3Ove nasumične brojeve možete interpretirati kao različite tipove pločica, na primjer 0 za zid, 1 za otvorenu stazu i 2 za posebnu pločicu kojom se može hodati. Uz malo više logike, možete ih transformirati u konzistentne labirintne strukture koje se mijenjaju svaki put kada igrač zatraži novi labirint s ključem poput N.

Malo kašnjenje u petlji može spriječiti da CPU bude opterećen na 100%.Funkcija Windowsa Sleep(30) pauzira program na oko 30 milisekundi između iteracija glavne petlje. Ovo daje glatkije performanse i izbjegava rasipanje procesorske snage, što je posebno važno kada se igra pokreće na goloj konzoli bez vertikalne sinhronizacije ili ograničenja broja sličica u sekundi.

Dok integrirate tajming i randomizaciju, odvojite logiku igre od prezentacije.Generisanje slučajnih podataka treba da se odnosi samo na osnovne podatke mape, vremenski proračuni trebaju ažurirati varijable, a kod za renderovanje treba jednostavno da čita te vrijednosti kako bi ih prikazao. To odvajanje će znatno olakšati kasnije proširenje igre, otklanjanje grešaka ili prenošenje osnovne logike na drugu platformu ili sistem za renderovanje.

Od konzolnog lavirinta do 3D DirectX mramornog lavirinta

Kada se jednom upoznate s labirintom na konzoli, prirodno je da se zapitate kako se to može prenijeti na 3D igru.Na Windowsu 10, dobar primjer je projekat Universal Windows Platform koji koristi C++ i DirectX za izgradnju 3D Mramornog Labirinta. U ovoj vrsti igre, ne pomjerate lik ćeliju po ćeliju; umjesto toga, naginjete ploču koja izgleda fizički tako da se čelični ili stakleni kliker kotrlja kroz labirint pod simuliranom gravitacijom.

Glavni cilj 3D mramornog labirinta je i dalje vođenje objekta od početka do kraja bez upadanja u rupe.Labirint se ponaša kao stolna igračka napravljena od drveta, ali implementirana u kodu. Naginjete ploču - putem akcelerometra, kontrola na gamepadu, pokreta miša ili dodirnih gesta - i kliker reaguje na fizikalne proračune, sudarajući se sa zidovima i padajući u jame ako niste oprezni. Kontrolne tačke omogućavaju klikeru da se ponovo pojavi na posljednjoj sigurnoj lokaciji koju je dostigao nakon nesreće.

Da biste ovo izgradili u C++ sa DirectX-om, oslanjate se na nekoliko ključnih API-ja i biblioteka.Direct3D i Direct2D renderiraju 3D labirint i sve 2D slojeve, dok Windows Runtime API-ji upravljaju životnim ciklusom UWP aplikacije. Geometrijski i fizički proračuni često koriste DirectXMath za vektorske i matrične operacije, detekciju sudara i integraciju pokreta. Za zvuk, XAudio2 služi kao glavni mehanizam za obradu muzičkih numera i zvučnih efekata poput kotrljanja, udara ili padanja u rupe.

Iako je grafička strana složenija, logička struktura odražava labirint konzole.I dalje imate reprezentaciju svijeta (ploču i rupe), entitet koji se kreće (kuglicu), prepreke (zidove) i uslove neuspjeha (pad u jamu). Ono što su nekada bili likovi u vector<string> sada postaju 3D modeli i kolizijski volumeni, ali vaš mentalni model - pravila i ciljevi - ostaju u suštini isti.

Kreiranje UWP labirintske igre pomoću DirectX-a pretpostavlja da već poznajete C++ i osnovne DirectX koncepte.Trebali biste biti upoznati sa COM-om, kako upravljati resursima poput tekstura i shadera, te kako se UWP razlikuje od klasičnih desktop aplikacija u smislu strukture aplikacije. Službena dokumentacija za Marble Maze obično prolazi kroz aspekte kao što su raspored projekta, podešavanje grafičkog cjevovoda, rukovanje unosom za dodir i senzore i integracija zvuka, sve na modularan način tako da možete ponovo koristiti komponente u vlastitim projektima.

Strukturiranje kodne baze: Zaglavlja, izvorni kodovi i klase

Bez obzira da li radite u konzolnom okruženju ili na UWP DirectX projektu, organizacija vašeg koda je ključna.Umjesto da sve bacamo u main.cpp, bolje je podijeliti funkcionalnost u namjenske zaglavne i izvorne datoteke. Na primjer, mogli biste kreirati Maze.h i Maze.cpp za logiku mape, Player.h i Player.cpp za stanje igrača i zasebnu uslužnu datoteku za rukovanje konzolom (uključujući gotoxy i konfiguracija kursora).

Zaglavlja deklarišu interfejs vaših klasa i funkcija, dok izvorne datoteke definišu njihovo ponašanje.Uključujete zaglavne datoteke u main.cpp korišćenje #include tako da kompajler zna o dostupnim tipovima i funkcijama. Ova modularnost poboljšava čitljivost i održavanje, posebno kada dodate više elemenata poput neprijatelja, više nivoa ili naprednih HUD komponenti.

U C++ klasama, konstruktori i destruktori upravljaju životnim vijekom resursa.Konstruktor se pokreće kada se objekt kreira i može postaviti početno stanje, dodijeliti memoriju ili konfigurirati ručke. Destruktor se poziva kada se objekt uništi i pravo je mjesto za oslobađanje resursa, zatvaranje datoteka ili oslobađanje dinamički dodijeljene memorije. Čak i u malom projektu lavirinta, pravilno korištenje destruktora pomaže u sprječavanju curenja i zalutalih ručki.

Za dijelove specifične za Windows, također ćete komunicirati sa ručicama i strukturama na nivou sistema.Kontrola konzole se oslanja na HANDLE vrijednosti dobijene putem GetStdHandle(), koji prosljeđujete funkcijama poput SetConsoleCursorPosition() i SetConsoleCursorInfo()Tretirajte ove ručke s istim poštovanjem kao i bilo koji drugi resurs: ili ih enkapsulirajte u malu klasu ili njima pažljivo upravljajte kako ne biste miješali različite standardne izlazne ručke ili izgubili trag stanja.

Ova disciplinovana struktura donosi dodatne dividende kada prelazite na napredne API-je poput DirectX-a.Tamo ćete se baviti uređajima, kontekstima, lancima zamjene, baferima i teksturama, svaki sa svojim pravilima kreiranja i uništavanja. Ako ste već naučili da konzolne pomoćne funkcije, logiku lavirinta i stanje igrača držite odvojenima, bit će vam prirodno da isto učinite i sa podsistemima za renderiranje, unos i zvuk u većem projektu.

Spajanjem svega zajedno, igra labirinta u C++-u - od jednostavnih konzolnih labirinta do potpunog 3D Mramornog labirinta - počiva na nekoliko solidnih ideja.: predstavite svijet kao strukturirane podatke u memoriji, odvojite stanje igrača od mape, potvrdite kretanje i sudare, dodajte značajne ciljeve s kolekcionarskim predmetima i uvjetima za pobjedu, unesite stvarnu opasnost kroz zamke i živote te orkestrirajte sve responzivnom petljom igre i jasnom organizacijom koda. Savladavanje tih osnova u minimalnom kontekstu konzole čini prelazak na bogatiju grafiku i unos s više uređaja daleko manje zastrašujućim, te vam ostavlja višekratno upotrebljiv mentalni set alata za bilo koju buduću C++ igru ​​koju odlučite napraviti.

Slični postovi: