Witam, zrobiłem taką odbijającą się piłeczkę od ścianek, ale nie wiem kompletnie dlaczego mi wywala mase błędów… oto kod:
#include "windows.h" //musimy zainkludowac glowna biblioteke windows
#include "stdio.h"
#include "stdlib.h"
//klasa kulki bedzie pierwsza klasa jaka wprowadzimy do gry
class AKulka
{
public:
HBRUSH PedzelKulki;
float x,y,r; // r promien
float Wektor[2]; // [x,y]
float Predkosc;
AKulka(){ // konstruktor klasy zaklada ze kulka rozpoczyna swa gre od srodka (pole gry to 500x500 punktow)
x=250;
y=250;
r=15;
Predkosc = 12.5f;
Wektor[0]=0.26f; //poczatkowy kierunek kulki (zaczyna lecac do gory)
Wektor[1]=0.23f;
}
}
void RysujKulke(HDC hdc) //funkcja rysujaca korzysta z koloru kulki
{
SelectObject(hdc,PedzelKulki);
Ellipse(hdc,int(x-r),int(y+r),int(x+r),int(y-r)); //funkcja ellipse rysuje elipsy
}
void PrzesunKulke()
{
x+=Wektor[0]*Predkosc; //przesuwamy kulke o predkosc w kierunku Wektora
y+=Wektor[1]*Predkosc;
}
void Kolizja()
{
if (x+Wektor[0]*Predkosc+r > 500) //Tutuj sprawdzamy kolizje ze sciankami bocznymi
Wektor[0]*=-1;
if (y+Wektor[1]*Predkosc+r > 500)
Wektor[1]*=-1;
if (x+Wektor[0]*Predkosc-r < 0)
Wektor[0]*=-1;
if (y+Wektor[1]*Predkosc-r < 0)
Wektor[1]*=-1;
}
}
;
AKulka kulka; //globalna deklaracja kulki
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam); //Deklaracja funkcji obslugujacej
int APIENTRY WinMain(HINSTANCE hInstance, //Windowsowy odpowiednik funkcji int main()
HINSTANCE hPrevInstance, //hInstance jest jednoznacznym identyfikatorem programu nadawanym podczas uruchamiania
PSTR szCmdLine, //hPrevInstance nie jest uzywany w dzisiejszych Windowsach
int iCmdShow) //szCmdLine jest stringiem zawierajacym komendy linii polecen Windowsa (Menu Start:: Uruchom)
//iCmdShow podaje domyslny (ustawiany w Windowsie) stan otwierania programu (maksymalizacja okna, minimalizacja, normalne)
{
MSG msg; //deklaracja komunikatu Windowsa
WNDCLASS wndclass; //deklaracja klasy okna
HWND hwnd; //deklaracja uchwytu okna, sluzacego do jednoznacznej identyfikacji okna
wndclass.style = CS_HREDRAW|CS_VREDRAW; //style okna CS_HREDRAW - oznacza ze okno bedzie odrysowane kiedy zmieni sie jego szerokosc, CS_VREDRAW- wysokosc
wndclass.lpfnWndProc = WndProc; //wskaznik na funkcje odpowiedzialna za przyjmowanie i obsluge komunikatow
wndclass.cbClsExtra = 0; //mozliwosc zadeklarowania dodatkowych bajtow pomagajacych w obsludze okna
wndclass.cbWndExtra = 0; //mozliwosc zadeklarowania dodatkowych bajtow pomagajacych w obsludze okna
wndclass.hInstance = hInstance; //klasa okna potrzebuje Identyfikatora programu, ktory pobieramy z deklaracji WinMain
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION); //Ikona ktora bedzie pokazywana w gornym pasku okna i w dolnym pasku programow otwartych. Jesli NULL to bez Ikony. Odznacz komentarz aby wczytac domyslna ikone.
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); //Kursor ktory bedzie widoczny na oknach utworzonych przy pomocy tej klasy okna. Jesli NULL to klepsydra.
wndclass.hbrBackground = CreateSolidBrush(0xCCCCCC);//(HBRUSH)GetStockObject(LTGRAY_BRUSH); //Tlo naszego okna. Obecnie wczytywany bedzie czarny pedzel. Okno nie musi posiadac tla(NULL);
wndclass.lpszMenuName = NULL; //Nazwa Menu Okna // na razie nie uzywane
wndclass.lpszClassName = "Program1"; //Nazwa klasy okna
RegisterClass(&wndclass); //Rejestrujemy klase okna, od tej pory mozemy uzywac jej nazwy ("Program1") w wywolaniu CreateWindow
hwnd = CreateWindow( //Funkcja CreateWindow
"Program1", //nazwa klasy okna na podstawie ktorej budujemy nasze okno
"Witam", //nazwa ktora pojawi sie w gornym pasku tytulowym
WS_POPUP|WS_VISIBLE, //WS_POPUP JEST STYLEM KTORY UDOSTEPNIA OKNO BEZ RAMKI I PRZYCISKOW JEZELI ROZSZERZYMY OKNO NA CALY EKRAN OTRZYMAMY TZW FULLSCREEN
0, //pozycja poczatkowa x (w gorym lewym rogu jest (0,0)
0, //pozycja poczatkowa y
GetSystemMetrics(SM_CXSCREEN), //szerokosc okna (GetSystemMetrics jest funkcja zwracajaca rozne parametry ustawien systemowych argument SM_CXSCREEN odpowiada za szerokosc pulpitu)
GetSystemMetrics(SM_CXSCREEN), //wysokosc okna (GetSystemMetrics jest funkcja zwracajaca rozne parametry ustawien systemowych argument SM_CYSCREEN odpowiada za wysokosc pulpitu)
NULL, //okno nadrzedne do tworzonego (poniewaz jest to pierwsze okno wiec wpisujemy NULL
NULL, //uchwyt menu okna (poniewaz nie ma menu wiec dajemy NULL)
hInstance, //identyfikator programu pobierany z WinMain
NULL); //dodatkowe parametry tworzenia
ShowWindow(hwnd,SW_NORMAL); //Mozemy wreszcie pokazac okno ktore wlasnie utworzylismy. Drugi Parametr funkcji sluzy do wybrania stanu w jakim okno sie pojawi np SW_MAXIMIZE -maksymalizuje je (SW_NORMAL - okno wyswietlane wedlug podanej przez nas serokosci/wysokosci)
UpdateWindow(hwnd); //i odmalowac je, gdyz dopiero co powstale okno ma tlo "zebrane" bezposrednio z ekranu (mozna to zaobserwowac komentujac te linijke)
// Tworzenie wszystkich kontrolek
HWND t=CreateWindow("button","Puszczaj",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,600,20,90,30,hwnd,(HMENU)przycisk_puszczaj,hInstance,NULL);
t=CreateWindow("button","Stop",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,700,20,90,30,hwnd,(HMENU)przycisk_stop,hInstance,NULL);
t=CreateWindow("button","OK",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,750,60,40,30,hwnd,(HMENU)przycisk_ok_promien,hInstance,NULL);
t=CreateWindow("static"," promien",WS_BORDER|WS_CHILD|WS_VISIBLE,600,64,70,22,hwnd,(HMENU)static_promien,hInstance,NULL);
t=CreateWindow("button","OK",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,750,100,40,30,hwnd,(HMENU)przycisk_ok_predkosc,hInstance,NULL);
t=CreateWindow("static"," predkosc",WS_BORDER|WS_CHILD|WS_VISIBLE,600,104,70,22,hwnd,(HMENU)static_predkosc,hInstance,NULL);
//Kontrolki wymagajace wartosci poczatkowej
char string[128]; //Inicjujemy poczatkowe wartosci w editach
sprintf(string,"%.0f",kulka.r);
t=CreateWindow("edit",string,WS_CHILD|WS_VISIBLE|ES_RIGHT|BS_PUSHBUTTON|WS_BORDER,680,64,60,22,hwnd,(HMENU)edit_promien,hInstance,NULL);
sprintf(string,"%g",kulka.Predkosc);
t=CreateWindow("edit",string,WS_CHILD|WS_VISIBLE|ES_RIGHT|BS_PUSHBUTTON|WS_BORDER,680,104,60,22,hwnd,(HMENU)edit_predkosc,hInstance,NULL);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg); //Komunikaty klawiatury tzw klawiszy wirtualnych (bede o nich szerzej mowil na zajeciach zwiazanych z obsluga klawiatury (ale sa to np klawisze F1..F12, ENTER ALT itp) musza zostac przetlumaczone na standard klawiszy Windowsa za pomoca funkcji TranslateMessage
DispatchMessage(&msg); //Funkcja DispatchMessage wysyla komunikat do odpowiedniego okna
}
return msg.wParam; //w przypadku zakonczenia programu zwracany jest ten parametr (jest on wysylany z funkcja PostQuitMessage(0) gdzie zero jest wlasnie tym parametrem - opis funkcji nizej)
}
//Przed nami mozg programu Win32 czyli funkcja przetwarzajaca komunikaty
//jej parametrami sa
//hwnd pozwala nam ustalic z ktorego okna nadszedl komunikat
//message jest to nazwa komunikatu (np WM_SIZE (czyli Windows Message Size) odpowiada za zmiane szerokosci/wysokosci okna)
//wParam jest pierwszym z dwoch parametrow komunikatu - mozna powiedziec ze niesie tresc komunikatu czyli np. nowa wysokosc/serokosc
//lParam drugi parametr pomocniczy (gdyby pierwszy nie starczyl)
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
//wszystkie komunikaty sa rozpatrywane za pomoca switcha
static HBRUSH Pedzel_Czerwony; //HBRUSH to uchwyt na pedzel ktorym bedziemy malowac wnetrza figur (slowo static oznacza ze bedzie zadeklarowane tylko po pierwszym wejsciu do funkcji WndProc i nie bedzie sie wyzerowywac
static HBRUSH Pedzel_Zielony;
static HBRUSH Pedzel_Niebieski;
static HBRUSH Pedzel_Zolty;
static HBRUSH Pedzel_Fioletowy;
static HPEN Pioro_Biale; //HPEN to uchwyt na pioro, ktorym bedziemy malowac obramowania figur oraz linie i punkty
switch (message)
{
case WM_CREATE: //komunikat wysylany podczas wywolania funkcji CreateWindow mozna w tym miejscu inicjowac zmienne
{
SetTimer(hwnd,1,10,NULL); //Inicjujemy zegar (Timer) aby zglaszal odrysowywanie pola gry co 10 milisekund (uzyskamy w ten sposob efekt animacji)
//pierwszy parametr to okno dla ktorego zegarek chcemy ustawic, drugi to identyfikator zegarka (moze byc kilka zegarkow)
//trzeci parametr to czas po jakim zegarek ma sie zglaszac, ostatni zas to wskaznik na funkcje TimerProc ktora obsluguje zegarki (my korzystamy z komunikatu WM_TIMER) wiec wstawiamy tu NULL
Pedzel_Czerwony=CreateSolidBrush(0x0000FF); //WM_CREATE to dobre miejsce by zainicjalizowac nasze pedzle odpowiednimi kolorami. Sluzy do tego funkcja CreateSolidBrush ktora jako argument pobiera wartosc koloru w jednostkach hexadecymalnych (tak jak w HTML'u) zasadnicza roznica jest jednak taka ze nie mamy skladowych koloru ustawionych w szyku RGB tylko BGR
Pedzel_Zielony=CreateSolidBrush(0x00FF00);
Pedzel_Niebieski=CreateSolidBrush(0xFF0000);
Pedzel_Zolty=CreateSolidBrush(0x00FFFF);
Pedzel_Fioletowy=CreateSolidBrush(0xFF00FF);
Pioro_Biale=CreatePen(PS_SOLID,2,0xFFFFFF); //Inicjalizujemy Pioro za pomoca funkcji CreatePen pierwszym argumentem jest styl piora (moze byc ciagle, kreskowane, kropkowane itp..), drugim jest grubosc piora (uwaga, tylko ciagle (PS_SOLID) moga miec grubosc wieksza niz 1), trzecim zas wytlumaczony wyzej kolor
kulka.PedzelKulki=Pedzel_Zielony; //Ustawiamy kolor Kulki
}
return 0; //po kazdym case WM_ dajemy return jezeli chcemy aby Windows nie przetwarzal po nas dalej tego komunikatu oraz dajemy break, aby to robil
case WM_KEYDOWN: //obsluga komunikatu nacisniecia klawisza klawiatury
switch(wParam) //robimy drugiego switcha od wParam w ktorym niesiona jest wiadomosc - "ktory przycisk zostal nacisniety"
{
case VK_ESCAPE: //obslugujemy klawisz Esc (jest to klawisz virtualny (stad przedrostek VK) klawisze virtualne to np. F1..F12 Enter, Insert, PageDown itp.)
PostQuitMessage(0); //kiedy nacisniemy Esc zostanie wyslana wiadomosc o zamknieciu programu z komunikatem powrotu rownym 0 (odpowiada to return 0 w typowym programie z funkcja main()
return 0;
}
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case przycisk_puszczaj:
SetFocus(hwnd);
return 0;
case przycisk_stop:
SetFocus(hwnd);
return 0;
case przycisk_ok_promien:
{
char string[8];
SetFocus(hwnd);
GetWindowText(GetDlgItem(hwnd,edit_promien),string,7); //Nie funkcja GetWindowText jest warta opisu (gdyz robi ona dokladnie to na co wskazuje nazwa (pierwszy parametr to - z ktorego okna, drugi to tablica charakterow(string) , trzeci to ilosc liter ktore ma zostac pobrana (nie zapominajmy o zerze)).. Wiekszy opis poswiece funkcji GetDlgItem ktora zwraca hwnd przycisku.
//GetDlgItem jest funkcja skierowana raczej do obslugi Dialogow (okien z kontrolkami) niz do samych okien, jednak mozemy potraktowac nasze okno jako Dialog. Pierwszym argumentem jest - na ktorym oknie znajduje sie (kontrolka,przycisk lub okno potomne), ktorego hwnd potrzebujemy, drugim argumentem zas Identyfikator tej kontrolki, okna potomnego..)
//To wystarcza funkcji GetDlgItem aby jednoznacznie okreslic o kogo nam chodzi
kulka.r=float(atoi(string)); //ustawiamy promien kulki
}
return 0;
case przycisk_ok_predkosc:
{
char string[8];
SetFocus(hwnd);
GetWindowText(GetDlgItem(hwnd,edit_predkosc),string,7);
kulka.Predkosc=float(atof(string)); //ustawiamy pedkosc kulki
}
return 0;
}
return 0;
case WM_PAINT: //obsluga komunikatu rysowania po oknie
{
HDC hdc; //HDC jest uchwytem na kontekst urzadzenia.
//czcionki, tlo, a nawet jaka jest czcionka. Nie bedziemy mogli nic narysowac jezeli nie bedziemy wiedzieli w jakim stylu to robic to robic. HDC mowi nam jaki mamy dostepny styl.
PAINTSTRUCT ps; //PAINTSTRUCT jest informacja dotyczaca OBSZARU po jakim bedziemy malowac
//Po narysowaniu kwadratu musimy powiadomic Windows ze poprzedni obraz ktory wyswietlal (bez kwadratu) jest juz niewazny. Zjawisko to nazywamy uniewaznianiem (Invalidate) po odswiezeniu ekranu z juz narysowanym kwadratem musimy z powrotem zatwierdzic
//narysowany obraz. Zjawisko to nazywamy zatwierdzaniem (Validate).
hdc = BeginPaint(hwnd,&ps); //Funkcja BeginPaint odpowiada za przygotowanie nam kontekstu poprzez powiadomienie jej po ktorym oknie mamy malowac, dodatkowo wypelni nam PAINTSTRUCT informujac nas ktory skrawek ekranu
kulka.RysujKulke(hdc);
MoveToEx(hdc,0,500,NULL); //Linie otaczajace pole gry
LineTo(hdc,500,500);
LineTo(hdc,500,0);
EndPaint(hwnd,&ps); //EndPaint konczy rysowanie poprzez zatwierdzenie CALEGO EKRANU rysowania (jest to ulatwienie - gdyz nie zatwierdzamy malych skrawkow ktore (po zatwierdzeniu wszystkich po kolei dadza nam i tak cala przestrzen rysujaca)
}
return 0;
case WM_TIMER: //komunikat WM_TIMER jest zglaszany przez ustawiony wczesniej przez nas zegarek
{
kulka.Kolizja(); //sprawdzamy kolizje
kulka.PrzesunKulke(); //przesuwamy odpowiednio kulke
RECT rect; //RECT to struktura - prostokat uzyjemy go by okreslic obszar pola gry do odrysowania
rect.top=0;
rect.right=500;
rect.left=0;
rect.bottom=500;
InvalidateRect(hwnd,&rect,true); //Tym razem InvalidateRect pobiera wskaznik na rect (aby odmalowac tylko ten obszar) oraz odmalowywuje tlo (ostatni parametr true)
}
return 0;
case WM_DESTROY: //komunikat wywolywany przy nacisnieciu iksa na pasku tytulowym, a takze np. przez funkcje DestroyWindow(HWND) -sluzaca do zamykania okna "z zewnatrz"
PostQuitMessage(0); //dopisana przeze mnie funkcja PostQuitMessage - wysyla do petli komunikatow wiadomosc przerywajaca ja .
return 0;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
z góry bardzo dziękuje za pomoc