Biologie | Chimie | Didactica | Fizica | Geografie | Informatica | |
Istorie | Literatura | Matematica | Psihologie |
CHICKEN INVADERS
Tema
Algoritmi de survival-razboi. Chicken Invaders.
Se va dori implementarea unei aplicatii asemanatoare jocului "Chicken Invaders", programarea jocului facandu-se cu DirectX. Atentie, nu se va folosi "game ingine", totul realizandu-se in cod C++ si DirectX
Introducere
Pentru realizarea acestui proiect am fost nevoiti sa ne reimprospatam cunostintele noastre de Algebrea liniara de care ne-am lovit intens la manipularea vectorilor. Cunostintele C++ combinate cu un nou stil de programare datorat intrarii pe scena a DirectX-ului ne-a condus la realizarea pas cu pas a acestui proiect. In realizarea proiectului nu am folosit nici o librarie de "game ingine".
La realizarea acestui proiect ne-am folosit de: Microsoft Visual Studio 2005 acesta fiind mediul de programare, Maya pentru creare de mesh-uri, DirectX 9 SDK instalat si foarte foarte multa cautare pe Google pentru tutoriale.
Documentatia privind aspectele legate de proiect
Crearea aplicatiei windows de baza a proiectului
Am inceput acest proiect construind o aplicatie windows simpla, alegand Win32 de la tipul de proiect la Visual Studio 2005.
#include <windows.h>Codul de mai sus este codul de baza, de inceput al proiectului nostru care creeaza o aplicatie windows de baza.
Exista doar doua standard functii: WinMain () si WndProc (). Functia WinMain () include definitia ferestrei, precum si principalele mesaje din bucla. Functia WndProc () prelucreaza mesajele, atata timp cat aplicatia ruleaza.
In acest exemplu, am taiat mesajele lasand doar 2 mesaje in curs de procesare de catre functia WndProc () . Aceste mesaje sunt: WM_CLOSE si WM_DESTROY.
Mesajul WM_CLOSE este trimis, atunci cand utilizatorul inchide fereastra. In cazul in care este primit, fereastra este distrusa chemand metoda DestroyWindow (hwnd). Metoda DestroyWindow ()de asemenea genereaza un mesaj WM_DESTROY.
Mesajul WM_DESTROY duce la oprirea aplicatiei. Acest lucru se realizeaza prin apel la metoda PostQuitMessage (0).
In interiorul WinMain () vom folosi WNDCLASSEX pentru a specifica tipul de fereastra care ne-o dorim pentru a o crea. Hwnd este un handle la fereastra si msg este mesajul pe care il vom trimite.
Verificam daca fereastra a fost inregistrata cu succes, daca nu aratam un mesaj de eroare in MessageBox. Dupa ce fereastra a fost inregistrata o cream transmitand titlul, latimea si inaltimea printre alti parametri.
Urmatorul pas este afisarea si actualizarea ferestrei.
In cele din urma este bucla de mesaje care in mod constant verifica daca exista mesaje in coada prin apel la metoda GetMessage ().Daca da, acestea sunt traduse si expediate astfel incat sa poata fi prelucrate de catre functia WndProc ().
Crearea dispozitivului DirectX device ( DirectX device )
Pentru a folosi avantajele puse la dispozitie de cardul grafic, vom crea un DirectX device pentru a interactiona cu ea. In cazul in care sunt mai multe carduri grafice, atunci il vom folosi doar pe cel principal.
In cazul in care am exista un card grafic care nu se potriveste DirectX-ului instalat atunci se introduce variabila D3DCREATE_SOFTWARE_VERTEXPROCESSING care va face o prelucrare software . Astfel se verifica mai intai compatibilitattea cu cardul grafic iar daca nu e compatibil se trece pe o prelucrarea software.
Inainte de toate, vom include urmatoarele fisiere header:
#include<d3d9.h>Prima variabila este un pointer la Direct3D9. Ce-a dea doua variabila este un pointer spre un dispozitiv DirectX.
In metoda WinMain() am adaugat apoi urmatorele:
// Init DirectX objectsInainte de a intra in bucla de mesaje, apelam metoda initDevice careia i se transmite ca parametru un handler al ferestrei curente.
Am folosit aici metoda PeekMessage () deoarece ea nu asteapta o confirmare a faptului ca un mesaj a fost plasat in coada de asteptare ci se intoarce imediat. GetMessage () nu se intoarce pana cand mesajul este plasat in coada de asteptare si aceasta produce intarzieri
Codul sursa a metodei initDevice():
void initDevice(HWND myWindow)Mai intai am definit dxPresentParams, care este un obiect care reprezinta toate caracteristicile DirectX sustinut de un dispozitiv. Apoi, vom crea obiectul mediului DirectX, vom verifica daca mediu DirectX este disponibil (instalat in sistem), daca nu vom da un mesaj de eroare care indica acest lucru. Dupa care initializam proprietatile dxPresentParams, incercand sa cream un dispozitiv de sustinere a DirectX prin 'prelucrare hardware a varfurilor'. Daca nu reusim sa cream un astfel de dispozitiv, vom incerca sa cream un ' dispozitiv software' care sa sustina ' prelucrarea software a varfurilor' intr-un mod tip simulare. In cele din urma setam proprietatile dispozitivului.
Urmatoarea metoda initiaza punctual de vedere:
void initCamera()Ochiul punctului de vedere situat la (0,0, -30) 'se uita la' tinta ce se afla la (0, 0, 0), care este centrul lumii noastre 3D (numerele reprezinta coordonatele x, y, z) .
Apoi am definit matricea de proiectie care este responsabila pentru o vizualizare in perspectiva, care creeaza o iluzie de adancime pe ecran. In cele din urma vom transforma matricea de proiectie prin apelarea metodei SetTransform() a dispozitivului nostru DirectX.
Apoi metoda cleanup() pentru a elibera resursele DirectX din memorie.
void cleanup()
if(directXDevice)
if(directX)
// change
if( g_pTexture )
g_pTexture->Release();
Desenarea pe ecran a diferite obiecte
Incepem cu o definitie a unui Boolean isWireframe , variabila globala. Apoi vom defini un mesh pt a retine varfurile obiectului pe care vrem sa il desenam intial. Initial va o fi un cub peste care se va pune apoi un alt mesh.
bool isWireframe = false;Urmatorul lucru verifica modul in care se va rula: in wireframe sau nu si modifica starea dispozitivului.
if(isWireframe)In cadrul metodei InitScene adaugam urmatorul cod:
D3DXCreateBox(DirectX vine incorporat cu mai multe primitive cum ar fi :
D3DXCreateCylinder()
D3DXCreateLine()
D3DXCreateFont()
D3DXCreatePolygon()
D3DXCreateSphere() dar din toate astea
s-a ales pt proiectul prezent lucrul cu cuburi si sfere peste care se aplica
sau nu un alt mesh.
Metoda drawScene () este un loc unde vom desena toate figurile noastre geometrie. Vom scrie tot codul intre metodele BeginScene () si EndScene ().
Iata cum ar arata:
D3DXMATRIX matrix;In primul rand vom defini matrice. Dorim cubul nostru sa apara in
centrul lumii 3D create de noi. De aceea am stabilit coordonatele la 0. Apoi
vom modifica matricea cu metoda SetTransform
(), care ia
Este intotdeauna important sa curatam memoria de obiecte cand e indicat momentul. In acest exemplu, vom efectua curatenie cand vom iesi din aplicatie. Asta inseamna ca ne putem folosi de metoda cleanup () si sa adaugam urmatorul cod acolo pentru a elibera meshBox din memorie:
if (meshBox) )Crearea animatiei cu DirectX. Miscarea in jos si in sus a cubului (liliacului)
Vom defini constantele de a stabili unele limite cubului nostru:
const float MAX_BOX_Y = 3.0f , Const float MAX_BOX_Y = 3.0f,
MIN_BOX_Y = - 4.0f ; MIN_BOX_Y = - 4.0f;
O alta variabila globala:
float timeDelta = 0.0f;
timeDelta poate fi mai bine inteleasa ca 'masura de miscare'. Cu alte cuvinte cat de mult se va misca cubul nostru in fiecare cadru. Este initializata aici la 0.
void drawScene()Dupa cum se poate vedea am adaugat definitia statica a variabilei y pentru a retine coordonata y a cubului intre apelurile metodelor si definirea statica a unui vector de directie. Vector de directie se va schimba pe masura ce cubul se va misca in sus si in jos pe ecran.
Avem nevoie de vectorA si vectorB sa retina pozitia de plecare si de destinatie a cubului respectiv, precum si un vector newPosition pentru a stoca rezultatele manipulari vectorului.
Cubul se misca in sus, daca coordonta y este mai mare decat minimul permis, de exemplu, -4.2. De asemenea, se misca in sus in cazul in care coordonata y este mai mica sau egala cu maximul permis (de exemplu 2.8) si, in acelasi timp, in cazul in care valoarea de directie a lui.y este mai mica sau egala cu 0.
Dupa cum puteti vedea in toate celelalte cazuri, cubul nostru se va misca in jos.
Dupa scaderea vectorB din vectorA pentru a calcula vectorul directie trebuie normalizat. Apoi vom calcula noul vector de pozitie si vom efectua transformarea matricei. Matricea modificata este stocata in variabila matrice. Dupa ce a fost modificata matricea informam directXDevice de transformare prin apelarea metodei SetTransform () si trecerea constantei D3DTS_WORLD si a matricii ca parametrii. Dupa care vom desena cubul prin apel de DrawSubset (0) a obiectului meshBox
// Message loop with timeDelta / / Mesaj bucla cu timeDeltaControlarea totala a cubului de la tastatura.
De asemenea, vom construi un foarte simplu HUD (heads-up display) pentru jocul nostru cu ferestre de dialog si vom utiliza un viewport DirectX pentru a crea lumea noastra 3D, in functie de dimensiunile specificate.
Cu Resource Editor in Visual Studio am creat fisierul ControlPanel.rc, care cuprinde definitiile: 2 ferestre de dialog (una pentru a fi folosita ca un panou de control pentru a prezenta informatii suplimentare pentru utilizator si una ca sa fie folosita ca un Viewport sa creeze lumea 3D DirectX). Panoul de control al fereastrei de dialog are un numar de controale statice (etichete) pentru a retine valorile pozitiilor X, Y, Z ale cubului.
Continutul ControlPanel.rc
// Microsoft Visual C++ generated include file.
// Used by ControlPanel.rc
#define ID_VIEWPORT 102
#define ID_CONTROLPANEL 103
#define ID_GAMEMENU 104
#define ID_BOX_X 1001
#define ID_BOX_Y 1002
#define ID_BOX_Z 1003
#define ID_BOX_X_LABEL 1004
#define ID_BOX_Z_LABEL 1005
#define ID_BOX_Y_LABEL 1006
#define ID_FIREBALLS 1007
#define ID_FIREBALLS_LABEL 1008
#define ID_ASTEROIDS 1009
#define ID_ASTEROIDS_LABEL 1010
#define ID_ASTEROIDS_VECTOR 1011
#define ID_ASTEROIDS_VECTOR_LABEL 1012
#define ID_FIREBALLS_VECTOR_LABEL 1013
#define ID_FIREBALLS_VECTOR 1014
#define ID_SCORE 1015
#define ID_SCORE_LABEL 1016
#define ID_GAME_OVER 1017
#define ID_FIREBALLS_LABEL2 1018
#define ID_GAMEMENU_NEWGAME 40004
#define ID_GAMEMENU_EXIT 40005
// Next default values for new objects
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40006
#define _APS_NEXT_CONTROL_VALUE 1020
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Continutul ControlPanel.rc
Urmatoarele constante au fost adaugate pentru a defini limitele de miscare a cubului
const float MAX_BOX_X = 9.0f ,
MIN_BOX_X = - 9.0f ,
MAX_BOX_Y = 4.5f , MAX_BOX_Y = 4.5f,
MIN_BOX_Y = - 5.5f ;
Vom defini un viewport ca o variabila globala:
D3DVIEWPORT9 viewPort;Urmatoarele variabile sunt definite la nivel global asa ca vom avea un acces usor la ferestrele de dialog.
HWND hwndControlPanel = NULL;Am adaugat variabila isBoxMove pentru a determina daca trebuie sa miscam cubul cand se deseneaza o scena.
bool isWireframe = trueVariabila boxMove este definita ca un WPARAM asa ca noi sa-l putem trimite ca un parametru mesaj la o anumita fereastra de dialog.
WPARAM boxMove;Avem nevoie, de asemenea, de vectorul posBox pentru a salva pozitia actuala a cubului nostru in lumea 3D.
Urmatoarea functie de conversie de la dublu la sir de caractere:
string CStr(double dVal)Motivul pentru care am facut variabila posBox este de a ne permite accesul la pozitia cubului din orice metoda.
Apoi vom defini vectorul de directie si vectori A si B de pozitie, care semnifica pozitia de plecare si de destinatie a cubului respectiv. Ultima definitie este o matrice care va contine cubul transformat in spatiu.
void drawBoxMove()Singurul lucru care il facem in functia DrawScene este de a verifica, in cazul daca valoarea variabilei isBoxMove este adevarata ceea ce inseamna ca trebuie sa se deseneze miscarea cubului.
Adaugam urmatoarea metoda pentru a initializa fereastra de dialog de gazduire a DirectX viewport-ului nostru.
BOOL CALLBACK DlgProcViewPort(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)Ceea ce primeste in plus metoda DlgProcControlPanel() sunt mesajele WM_COMMAD de updatare a etichetelor x, y, z de pozitie a cubului.
BOOL CALLBACK DlgProcControlPanel(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)iar in WinProc(), pentru a face posibila miscarea cubului am pus:
case WM_KEYDOWN:Daca vom apasa tasta stanga, la dreapta, in sus sau in jos initializam boxMove cu valoarea lui wParam si setam valoarea lui isBoxMove sa fie adevarat. Daca orice alta cheie este apasata atunci variabila isBoxMove va fi setata pe fals, fixand cubul in pozitia curenta in spatiu.
Tragerea cu mingi de foc in spatiu la apasarea tastei SPACE
De fiecare data cand tasta [spatiu] este apasata trebuie sa creem un obiect sfera cu un mesh de minge de foc si sa modificam pozitia in raport cu pozitia de zbor a cubului..
Am decis sa cream o noua clasa Fireball care va tine mesh-ul mingilor de foc si pozitia lor in spatiu.
Fireball.cpp
#include <d3d9.h>
#include <d3dx9.h>
class Fireball
#endif
Fireball.h
#ifndef __Fireball_h__Clasa constructor a Fireball accepta pozitia si pointerul mesh a oricarui obiect , ceea ce face posibil utilizarea aceastei clase pentru multe scopuri. Mai intai de toate, trebuie sa includem headerul fireball.h in clasa unde WinMain este definit astfel incat sa putem sa profitam de aceasta clasa in aplicatia noastra.
Apoi definim vectorul de fireballs in care putem stoca mai multe obiecte de tip mingi de foc pe care l-am creat mai devreme.
vector<Fireball> fireballs;Din moment ce orice minge de foc
poate fi creata utilizand aceasta metoda, trebuie sa avem
grija ca doar vizibile (acestea, care nu au ajuns inca la valoarea
maxima a lui z definita
de
Dupa ce matricea a fost transformata , informam directXDevice ca
am efectuat de modficarea apeland metoda SetTransform () si
transmitand
In interiorul metodei drawScene () trebuie sa apelam metoda drawBoxFireMove ()pentru fiecare minge de foc stocata in vectorul fireballs astfel:
// draw fireballsIn interiorul metodei DlgProcControlPanel () vom defini o variabila statica pentru a retine numarul de fireballs trase de cubul zburator pana acum.
static int numberOfFireballsFired = ;si adaugam procesarea urmatorului caz:
case ID_FIREBALLS:Apoi in metoda WndProc () adaugam procesarea evenimentului apasarii tastei SPACE
case VK_SPACE: // box fireballDaca tasta [spatiu] este apasata atunci vom crea o noua minge de foc si o adaugam la vectorul fireballs apeland metoda push_back () a vectorului. Apoi vom trimite un mesaj la fereastra hwndControlPanel pentru o prelucrare ulterioara.
Crearea asteroizilor
Vom implementa asteroizi in acelasi fel cum am implementat mingile de foc si vom folosi clasa Fireballs deja creata.
vector<Fireball> asteroids;Metoda generateAsteroid () creeaza un nou asteroid si-l adauga la vectorul de asteroizi. Se selecteaza aleatoriu valorile pozitiilor X si Y ale asteroid. Valoarea Z este mereu aceeasi si este definita de constanta MAX_ASTEROID_Z.
void generateAsteroid()Metoda drawAsteroidMove() este asemanatoare metodei drawBoxFireMove() singura diferenta fiind ca se misca in directia opusa fata de mingile de foc. De aceea adunam de fiecare data la pozitia lui Z pe timeDelta si nu scadem ca in cazul migii de foc.
void drawAsteroidMove(D3DXVECTOR3 & position, LPD3DXMESH mesh)Am creat de asemenea metoda eraseInvisibleObjects() pentru a sterge obiectele pentru a nu incetini rularea programului. Astfel, cele care depasesc limitele devin invizibile, aceasta functie stergand pe aceste obiecte invizibile.
void eraseInvisibleObjects()De asemenea, vom avea nevoie de stergerea tuturor obiectelor de example la sfarsitul jocului sau la inceput . aici apare functia eraseAllObjects().
void eraseAllObjects()In cadrul metodei drawScene() adaugam randarea asteroizilor la fel ca cea a migilor de foc.
// draw asteroidsDetectare coliziunii
Am ales ca tehnica de detectarea a coliziunii urmatoarea: inconjurarea obiectelor cu mici cutiute si apoi verificam daca aceste cutiute se intersecteaza cu un anumite grad de precizie.
Metoda isCollisionDetected () ia pozitiile vectorilor a doua obiecte ca parametrii si determina daca nu exista nici o coliziune intre ele prin calcularea distantei dintre pozitia vectorilor. Distanta se calculeaza in functie de bine cunoscuta formula de Algebra liniara.
bool isCollisionDetected(const D3DXVECTOR3 & pos1, const D3DXVECTOR3 & pos2)In interiorul metodei drawScene ()vom adauga prelucrarea de coliziune dupa ce toate obiectele au fost desenate:
// collision detectionCe se intampla aici este looping prin toate elementele din interiorul vectorului de asteroizi si apoi looping prin toate elementele vectorului fireballs (bile trase de cub) si apelarea la metoda isCollisionDetected ()in timp ce se transmit ca parametrii pozitia vectorilor de asteroizi si de mingi de foc.
Daca este detectata coliziunea vom transforma ambele obiecte in invizibil asa ca vor fi sterse de catre metoda eraseInvisibleObjects () dupa ce au terminat de desenat scena si actualizat scorul prin trimiterea unui mesaj la fereastra hwndControlPanel.
Apoi verificam daca cutiutele noastre zburatoare s-au ciocnit cu un asteroid, daca da - jocul s-a terminat.
Dupa ce scena a fost randata, verificam daca s-a terminat jocul pentru a trimite mesajul corespunzator unei ferestre wndControlPanel si pentru a sterge toate obiectele.
if(isGameOver)De asemena mai adaugam 2 case-uri in metoda DlgProcControlPane. Una pt updatare scor iar una pentru procesarea GAME OVER-ului.
case ID_SCORE:Bibliografie
Copyright © 2024 - Toate drepturile rezervate