Implementacja wektorów 2D
ubek666
Implementacja wektorów 2D by Willem van Doesburg Š Marzec 2000
Oryginał: Wersja angielska
Tłumaczył UBEK - UPortal
Przykłady do artykułu:
2Dvector.h
2Dvector.cpp
2Dvector.h
2Dvector.h
Tłumaczenie może być niedokładne, ale dopiero się uczę! Więc jakby coś to poprawię!
Instrukcja
</p>W moich badaniach jako początkujący programista gier posługiwałem się
internetem i różnymi książkami, nie mam jak dotąd robić dokumentu
wyjaśniającego jak implementować wektory w grach. Faktycznie, Mam
założyć liczne dokumenty o: wektorach w matematyce, rysowaniu linii,
klasach wektorów w fizyce. Dokument o wektorach w matematyce nie
zajmuje się systemem współrzędnych na ekranie komputera.
Dokument o rysowaniu linii nie wyjaśnia jak przedstawiać linie za pomocą
klasy. Dokument o klasie wektora nie wyjaśnia operacji na tej klasie.
Dokument o fizyce nie daje tobie wszystkich przydatnych algorytmów
dla twojego programu. Ten dokument probóje zaspokoić wszystkie braki
innych dokumentów. Dokument używa notacji C++ dla przykładów,
ponieważ jest to jedyny język jaki znam.
Wektor (klasa)
</p>Wektory generalnie mogą być reprezentowane na 2 różne sposoby.
1: przez długość i jakiś kąt
2: przez początkowy i końcowy punkt
Te dwie metody wykorzystują inną matematykę. Metoda 1 używa
trygonometri do obliczenia kątów i normalnych. Metoda 2 używa
macierzy do obliczenia obrotu i innych operacji. W tym dokumencje
my implementujemy wektor reprezentowany w metodzie 1.
Oto klasa:
class C2DVector
{
private:
long x, y; //współrzędne punktów
int angle; //kąt wektora
long length; //długośc wektora
public:
void setX(long Xin); //ustawia punkt x
void setY(long Yin); //ustawia punk y
void setLength(long Lin); //ustawia długość w pikselach
void setAngle(int Ain); //ustawia kąt w stopniach
long getX(); //zwraca punkt x
long getY(); //zwraca punkt y
long getLength(); //zwraca długość
int getAngle(); //zwraca kąt
public:
void operator =(C2DVector aVector); //porównuje ten wektor z innym
C2DVector(); //konstruktor
C2DVector(long Xin, long Yin); //konstruktor przeładowany
~C2DVector(); //destruktor
};
Zauważ że dołączyłem współrzędne w mojej klasie. Chwila przecież
powiedziałem wcześniej że potrzebna jest ci tylko długość i kąt wektora,
aby go określić. Wybieram dołączenie współrzędnych, ponieważ wtedy
klasa jest sprawniejsza. Potrzebujesz kosinusa i sinusa, aby obliczyć
współrzędne. Żeby mieć współrzędne musimy tylko obliczyć
je raz na jakiś czas, natomiast zawsze potrzebujemy spółrzędne gotowe
do użycia. Długość wektora jest określona przez liczbe pikseli wtedy
może swobodnie zaspokoić wszystkie twoje potrzeby.
Inną ważną rzeczą jest używanie systemu współrzędnych. Normalne
wektory matematyczne zazwyczaj używają kartezjańskiego układu
współrzędnych.
Spójrz na kartezjański układ współrzędnych:
Projektancji komputerowi oczywiście wybrali inne podejście do
sprawy(jak zwykle).
Ekran komputerowy używa następującego systemu współrzędnych:
Nie potrzebujemy przebudowywać wielu formuł.
Chcemy używać układu kartezjańskiego! Później w tym dokumencie wszystko ci powiem o zamienianiu na twój układ z układu kartezjańskiego i
odwrotnie.
Przygotowania
</p>Potrzebujemy kosinusa i sinusa, żeby obliczyć obrót. Doświadczeni czytelnicy zobaczą, że Ja wyrażam kąt wektora w stopniach, z chwilą obecną większość bibliotek matemtycznych wykorzystuje do obliczenia kosinusa i sinusa radiany. Więc musimy zamienić stopnie na radiany dla tej funkcji. Dlatego zdefiniujemy liczbę PI:
#define PI 3.141592654
Jeżeli chodzi o wydajność będę przechowywał sinusa i kosinusa dla każdego kąta w tablicy, więc nie musimy używać wolnych funkcji sin() i cos() dla ostatnich liczb. Teraz zadeklarujemy tablice:
float costable[360];
float sintable[360];
My będziemy potrzebować ostatnie liczby, żeby uzupełnić te tablice.
Ta funkcja konwertuje stopnie na radiany:
red = deg / 180 * PI
void initTables()
float temp;
for (int i = 0; i < 360; i++)
{
temp = ((float)(360 - i) / 180);
/*
potrzebne obliczenia. używam tego (360 - i), dla tego dziwnego
układu współrzędnych.
*/
costable[i] = (float)cos(temp)
sintable[i] = (float)sin(temp);
}
}
Definicja klasy
Teraz zdefiniujemy dwie ważne funkcje w naszej klasie. setLength(long Lin) i
setAngle(long Ain) obliczają one x i y.
My możemy użyć tych wzorów do obliczeń:
x = cos(angle) * length
y = sin(angle) * length
Oto kod obydwu funkcji:
void C2DVector::setLength(long Lin)
{
length = Lin;
x = costable[angle] * length;
y = sintable[angle] * length;
}
void C2DVector::setAngle(int Ain)
{
angle = Ain;
x = costable[angle] * length;
y = sintable[angle] * length;
}
Jak używać klasy
</p>Skoro już mamy gotową klasę, chcemy dowiedzieć się jak z niej korzystać.
Dobrze teraz przetestujemy nasz wektor na stole bilardowym.
W tej scenerii my musimy mieć obiekt piłki dla której chcę stworzyć wektor
określający tor jej lotu i prędkość. To jest całkiem proste,
określamy ruch naszej piłki wokół kąta i długości, a następnie
kontynuujemy dodawanie punktów x, y do punktów x, y naszej piłki.
Problem powstaje, kiedy nasza piłka trafia w bande stołu. Czasem zdarza się
że musimy obliczyć nową prędkość wektora dla naszej piłki, żeby odbiła się
naturalnie. Jeśli piłka odbiła się od bandy normalnie możemy obliczyć
nowy wektor dla tej dorgi:
- odwracamy wektor prędkości
- obliczamy delta(v, n)
- angle = angle +/- delta(v, n)
Oto kod:
void CBall::HitBoundary(C2DVector *BoundaryNormal)
{
int angle;
int OppAngle;
int NormDiffAngle;
angle = Velocity.getAngle();
OppAngle = (angle + 180) % 360;
if (BoundaryNormal->getAngle() >= OppAngle)
{
NormDiffAngle = BoundaryNormal->getAngle() - OppAngle;
angle = (BoundaryNormal->getAngle() + NormDiffAngle) % 360;
}
if (BoundaryNormal->getAngle() < OppAngle)
{
NormDiffAngle = OppAngle - BoudnaryNormal->getAngle();
angle = BoudnaryNormal->getAngle() - NormDiffAngle;
}
Velocity.setAngle(angle);
}
W implementacji klasy mojego wektora. Dostosowałem go dla dziwnego
układu współrzędnych. Jeśli kiedyś będziesz potrzebował zamienić układ
kartezjański na ten dziwny to masz tu kod:
void ConvertCoordinates(long *X, long *Y, bool convert)
{
int Xmiddle;
int Ymiddle;
Xmiddle = (int)(XRES / 2); //XRES to rozdzielczość ekranu w poziomie
Ymiddle = (int)(YRES / 2); //YRES to rozdzielczość ekranu w pionie
if (convert)
{
*Y = -*Y;
*X = *X + Xmiddle;
*Y = *Y + Ymiddle;
} else {
*X = *X - Xmiddle;
*Y = *Y - Ymiddle;
*Y = -*Y;
}
}
Zakończenie
</p>Teraz wiesz jak skutecznie używać wektorów w twoich grach. Na górze są
zamieszczone linki do kodu źródłowego załączonego do artykułu.<url>
Tak pomysł dobry, ale wykonanie do kitu. Po pierwsze Primo: ta klasa powinna mieć tylko współrzędne jako obiekty składowe, bo szkoda pamięci na coś co i tak będzie się zmieniało, a zamiast tego robi się funkcje do obliczenia długości wektora, obliczenia kąta między wektorami itp. Po drugie Primo: defy funkcji robi się jako inline - bo tak jest szybciej, nie? Po trzecie Primo: operator= nie porównuje (bo do tego jest operator==) tylko przypisuje, oczywiście to zależy od ciebie, bo po to zostało wymyślone przeładowanie, ale z defa wynika przypisanie.
I nie bardzo mi się podoba ten "konstruktor przeładywujący". Pewnie było overloaded constructor? Jeśli tak, to bardziej konstruktor przeładowany :)
Hehe :) Może masz rację!
A moze to taki uklad gdzie kazda wspolrzedna jest traktowana funkcją ABS :p
nie wiem dlaczego tak jest. Mnie też to dziwiło! :)
popraw fatalne błędy ortograficzne!
Pomysl dobry. Ale uwazam, ze powinienes jeszcze troche czasu poswiecic nauce angielskiego, bo z twojego tlumaczenia - nie obraz sie - wychodza totalne bzdury :)
Co ma wspolnego wektor 2d z psychika ? :D
physics - fizyka
Nic się nie stało, a angielskiego dopiero się uczę!
Spróbuje poprawić ten tekst! :)
mnie troche martwi wygląd tego układu kartezjańskiego - czemu są aż cztery strzałki, a nie tylko 2 (góra, prawo)?