Detekcja kolizji myszki z obrazkiem w rzucie izometrycznym [Allegro 5][C++]

0

Głowię się z tym bardzo długo, szukam różnych opisów, poradników, ale nadal nie mogę wykombinować jak zrobić detekcję myszki i bitmapy...

Bitmapa to u mnie prostokąt 64x32.

Chodzi mi o coś takiego:

  1. Jeżeli myszka jest poza kafelkiem izometrycznym, świeci się on na biało:
    user image
  2. Jeżeli myszka jest na kafelku izometrycznym, świeci się on na zielono:
    user image

Niestety, nie mogę zrobić zwykłego ifa:

if(Myszka.x >= 0 && Myszka.x <= 64 && Myszka.y >= 0 && Myszka.y <= 32) Rysuj();

ponieważ wtedy wyświetla mi takie coś:
user image

Nie wiem jak zrobić, aby kafelek kolorował się tylko wtedy, kiedy myszka będzie na tej części widocznej...

0

Wydaje się że masz jakieś dziwne założenia (kafelek jest jeden i zawsze w tej samej pozycji) - jeśli o to chodziło, powinno działać coś takiego...

if (abs(myszka.x - 32) + 2 * abs(myszka.y - 16) < 32) Rysuj()

Pełna wersja to coś w rodzaju (x i y to pozycja kafelka, wysokość kafelka == 16, szerokość == 32):

if (abs(myszka.x - x - 32) + 2 * abs(myszka.y - y - 16) < 32) Rysuj()
0
	//x i y pozycja prostokata na ekranie
	//w i h szerokosc i wysokosc calego prostokata
	//
	if(!(myszka.x < x || myszka.y < y || myszka.x > x + w ||  myszka.y > y + h) && 
			/*  warunek jest prawdziwy jeśli myszka nie znajduje się na 4 trójkątach(te po rogach) */) {
		) {
		//jeśli cały if jest spełniony zaznaczamy figurę
	}
 

muisz uwzględnić te czarne trójkąty które znajdują się bo rogach tak gdy jak myszka się na nich znajduje to nie podświetla figury. Wpisz po prostu ten warunek w miejscu komentarza

0

No, a jak obliczyć te trójkąty? Przecież muszę obliczyć x i y każdego pixela tych trójkątów, racja?

0

No, a jak obliczyć te trójkąty?
Post powyżej nic za bardzo nie wnosi i jest po prostu tym co Ty już napisałeś z komentarzami, ja Ci podałem działające rozwiązanie, rozumiem że nawet tego nie przeczytałeś?...

Przecież muszę obliczyć x i y każdego pixela tych trójkątów, racja?
Nie.

0

Powinieneś zrobić to zupełnie inaczej, inaczej niż @msm czy @robcio sugerują. Potrzebujesz czegoś bardziej ogólnego bo będzie ci to potrzebne na każdym kroku - napisz funkcje konwertujące współrzędne między współrzędnymi ekranowych a współrzędnymi planszy.

0

Na razie wolę znaleźć sposób na detekcję kolizji myszki z tą widoczna częścią jednego kafelka, potem będę główkował jak to uogólnić w jednej funkcji.

Co do tego, co napisał @msm, nie wiem co to "abs" oznacza...

0

Na razie wolę znaleźć sposób na detekcję kolizji myszki z tą widoczna częścią jednego kafelka, potem będę główkował jak to uogólnić w jednej funkcji.

@adf88 chyba ma rację, mniej się namęczysz jeśli od razu zrobisz to za pomocą konwersji do współrzędnych planszy. No ale...

Co do tego, co napisał @msm, nie wiem co to "abs" oznacza...

Hmm, dokładnie to co pisze - wartość bezwzględna, patrz też http://www.cplusplus.com/reference/clibrary/cstdlib/abs/

Co to jest wartość bezwzględna chyba wiesz ;)
http://ideone.com/4XSzYF

#include <iostream>
#include <cstdlib>

using namespace std;

int main() {
    cout << abs(1) << endl;
    cout << abs(-1) << endl;
    cout << abs(666) << endl;
    cout << abs(-12) << endl;
}
1
1
666
12

Dlaczego tak:

if (abs(myszka.x - x - 32) + 2 * abs(myszka.y - y - 16) < 32) Rysuj()

myszka.x - x to odległość relatywna X myszki od kafelka (czyli 0 to lewa ściana kafelka, 20 to 20 pikseli od lewej ściany, etc)
myszka.x - x - 32 to odległość relatywna X myszki od /środka/ kafelka (czyli 0 to środek kafelka, -32 to lewa ściana, etc)
dla y analogicznie.
abs(myszka.x - x - 32) + 2 * abs(myszka.y - y - 16) < 32 - jeśli suma odległości od x i y jest mniejsza od 32, jesteśmy poza kafelkiem

(w pierwszej wersji przypadkowo napisałem >, to był ofc warunek na to że myszka jest poza kafelkiem)

0

Te kafelki mają jakieś określone, stałe współrzędne? Ułożone są może w planszę, która się nie zmienia? Bo jeżeli tak to całą sprawę będzie można załatwić wydajniej niż przeprowadzać detekcję kolizji dla 50 takich kafelków.

0

Tak, to zawsze jest jedna określona plansza. Kafelki są zawsze w tych samych miejscach. Pragnę osiągnąć wynik taki jak na tym filmie:

(Obchodzi mnie tylko początek tego filmu, od 0:10 do 0:14)

Jak można tam zauważyć, gdy ktoś najeżdża myszką na dany kafelek, wokół niego powstaje żółta otoczka - u mnie, koloruje się na zielono.

0

Przesłane przez DaKrazyKatt dnia 14 sty 2012

Dev-C++ 4.9.9.2... FAIL

Podglądnij sobie kod gry Battle For Wesnoth, tam mapa złożona jest z sześciokątów :D

0

Znam ten rzut, to jest rzut hexametryczny (czy coś w tym stylu, nie wiem czy dobrze to pisze). Sądzę, iż jest łatwiejszy, ale raczej zamierzam wykorzystać rzut izometryczny, wydaje mi się bardziej realistyczny.

1

Nie ma czegoś takiego jak rzut heksametryczny. Tam jest również rzut izometryczny tylko kafelki mają inny kształt.
Nie myl sposobu odwzorowania przestrzeni z kształtem odwzorowywanych przedmiotów, to zupełnie oddzielne rzeczy.

Konwersja współrzędnych to totalna podstawa, nie wiem czemu od początku nie zaczniesz od niej. Jako bazę do dalszych udoskonaleń może podam wzór w prostej postaci (zakładam, że kafelek jest na ekranie dwa razy szerszy niż wysoki):

x = 2 * mouse_y - mouse_x
y = 2 * mouse_y + mouse_x

0

Napisałem kiedyś w Delphi coś podobnego dla pól heksagonalnych. Program rysował siatkę pól o zadanej wielkości na Canvasie, zwracał współrzędne heksa pod kursorem i podświetlał go (dla zainteresowanych źródełko w załączniku).
Ogólna zasada działania wygląda tak, że każdy sprite z kafelkiem posiada określony punkt zaczepienia na mapie (Origin) oraz rozmiary. Na tej podstawie przeliczasz współrzędne kursora na współrzędne sprite'a (to już masz). W przypadku mapy izometrycznej, odnalezionego sprite'a dzielisz na ćwiartki: 59348988a.png i określasz w której ćwiartce jesteś (to jest banalne). Krawędź kafla jest jednocześnie przekątną danej ćwiartki, więc wystarczy sprawdzić, po której stronie przekątnej jesteś i ew. update'ować współrzędne. Dla pól heksagonalnych jest podobnie z tym, że pole trzeba podzielić na 5 kawałków (4 narożniki i duży prostokąt pośrodku).

PS. Kod w załączniku jest eksperymentalny i napisany dawno temu, więc musicie sami rozgryźć ;)

0

Ouh, jestem zainteresowany, ale niestety nie mogę tego otworzyć :/, nie mam programu, podejrzewam że potrzebuję Delphi tak jak mówisz...

0

Jeżeli byś mógł, byłbym bardzo wdzięczny. Chciałbym zobaczyć na własne oczy jak to wygląda po kompilacji.

// Co do tego, co powiedział @adf88, sprawdziłem ten kod i nie wiem czy jest to dobrze gdy myszka jest na środku okna 800x600 i pisze, że x = ok. 160, y = ok. 1000

// Hmm... A detekcja myszki na mapie nie może być np.

x = StatusMyszki.x;
y = StatusMyszki.y;

?
Bo ja mam tak i wtedy mi normalnie pokazuje że np. x = 254 a y = 442. Ty w tym przykładzie tak masz. Z kolei nie wiem jak zrobić współrzędne kafelka, czyli że XKafelka = 1, YKafelka = 2.

0

Musisz wiedzieć, jak poukładane są wszystkie sprite'y z kafelkami na mapie. Dla każdego z nich określasz punkt zaczepienia i rozmiar. Punkt zaczepienia mówi nam, w którym miejscu na ekranie znajduje się określony piksel sprite'a (zwykle jego lewy górny róg). Układasz sprite'y w kolumny i rzędy. Współrzędne sprite'a otrzymujesz dzieląc współrzędne myszy przez rozmiar sprite'a w pionie i w poziomie. Potem robisz tak, jak napisałem w poprzednim poście. Dzielisz sprite'a na cztery, sprawdzasz, w której ćwiartce jest kursor, a potem kombinujesz z przekątnymi ćwiartek (rozrysuj sobie na kartce i zajrzyj do trygonometrii).
Prześledź sobie na spokojnie funkcję GetHex z mojego przykładu (Hex.pas). Do funkcji podajesz współrzędne myszy X,Y (program wyświetla je jako H i W), długość krawędzi heksa Size oraz współrzędne krawędzi mapy Rect. Funkcja zwraca współrzędne heksa.

0

No na razie wychodzi mi takie coś:
http://img90.imageshack.us/img90/8729/67280299.png

Część kodu do tej mapy i obliczeń:

//Mapa:
int map[5][5] = {
        0,0,1,0,0,
        0,1,1,1,0,
        1,1,1,1,1,
        0,1,1,1,0,
        0,0,1,0,0
    };
    int map_[4][4] = {
        0,2,2,0,
        2,2,2,2,
        2,2,2,2,
        0,2,2,0,
    };

for(int u = 0; u <= 3; u++){
            for(int uu = 0; uu <= 3; uu++){
                if(map_[u][uu] == 2) al_draw_bitmap(On,(uu*64+32)+PrzesunX,(u*32+16)+PrzesunY,0);
            }
        }
        for(int i = 0; i <= 4; i++){
            for(int ii = 0; ii <= 4; ii++){
                if(map[i][ii] == 1) al_draw_bitmap(On,(ii*64)+PrzesunX,(i*32)+PrzesunY,0);
            }
        }

// PrzesunX i PrzesunY to zmienne odpowiadające za przesuwanie, czyli jak ktoś kliknie strzałkę w góre to wtedy PrzesunY-- itd.

No i mam punkt zaczepienia, ale nie wiem jak nadać ten punkt kafelkowi, bo z kodu

x = StatusMyszki.x;
        y = StatusMyszki.y;

        xk = x / 32;
        yk = y / 16;

gdzie x i y to współrzędne myszki a xk i yk to współrzędne kafelka wychodzi "załóżmy" że pierwszy na samej górze to x=5,y=0; ale to jest ogólne... jak właśnie to dane x=5 i y=0 "przypiąć" do tego kafelka? (Wysokosc kafelka 32, szerokosc 64)

0
Temessis napisał(a):

Co do tego, co powiedział @adf88, sprawdziłem ten kod i nie wiem czy jest to dobrze gdy myszka jest na środku okna 800x600 i pisze, że x = ok. 160, y = ok. 1000
No i teraz rusz myszką wzdłuż linii kafelków. Zauważysz, że jedna ze współrzędnych rośnie bądź maleje a druga pozostaje bez zmian. I o to chodzi. Indeks kafelka dostaniesz dzieląc współrzędną przez wielkość kafelka. Do wyniku trzeba jeszcze dodać odpowiedni offset zależny od położenia pierwszego kafelka względem ekranu. To offsetowanie zrób jeszcze przed mnożeniem:

// dodajemy wspomniany offset
relative_mouse_x = mouse_x - offset_x;
relative_mouse_y = mouse_y - offset_y;
// obliczamy współrzędne kafelkowe
x = 2 * relative_mouse_y - relative_mouse_x;
y = 2 * relative_mouse_y + relative_mouse_x;
// obliczamy indeks kafelka dzieląc przez jego rozmiar (32)
tile_x = x / tile_size;
tile_y = y / tile_size;

offset_x i offset_y to współrzędne ekranowe górnego rogu najbardziej górnego kafelka.

0

Dobra. Mam X i Y kafelka. Teraz jak podzielić te kafelki na cztery części? Chciałem zrobić to z if'em ale nie mam pojęcia jak, gdyż mam 2 mapy, jedna jest [5][5] a druga [4][4]...

if( map[y][x] ==  1 ) dzielenie();
0

Dopóki nie zaimplementujesz mechanizmu konwersji współrzędnych problem będzie wracał co chwilę i co chwilę będziesz pytać "jak, jak, jak...". Zabierasz się do problemu złej strony. Nie idź za radą @Luc bo jest kijowa.

Pytasz jak sprawdzić czy kursor znajduje się w odpowiednim trójkącie? Otóż będziesz musiał przeprowadzić transformację współrzędnych podobną do tej którą podałem wyżej. Dlatego nie ma sensu w ogóle robić tego kroku o którym wspomniał @Luc. NAJPROŚCIEJ będzie od razu przetransformować współrzędne.

0

No dobrze, przeprowadziłem to co powiedziałeś

int offset_x = 160;
        int offset_y = 0;

        int relative_mouse_x = StatusMyszki.x - offset_x;
        int relative_mouse_y = StatusMyszki.y - offset_y;

        x = 2 * relative_mouse_y - relative_mouse_x;
        y = 2 * relative_mouse_y + relative_mouse_x;

        xk = x / 64;
        yk = y / 32;

No to teraz, ja to Twoje ma mi pomóc w przeprowadzeniu "testu" tego trójkąta, bo nie rozumiem :/
Myszka mi jakoś dziwnie chodzi :D

2

To już jest wynik - współrzędne kafelka, jego wiersz i kolumna. No i zrobiłeś błąd - masz dzielić przez rozmiar kafelka w jednostkach kafelkowych - w tych jednostkach kafelki są kwadratowe. W ogóle nie czaisz czym są te zmienne. Może rysunek pomoże:

kafelki.png

Odwrotny proces przeprowadź rysując kafelki.

0

No rzeczywiście, dzięki, bardzo pomógł :) Teraz mam tak jak mówisz, tylko że dzielę przez 64, a nie przez 32, gdyż gdy dzielę przez 32 to wtedy mam jednostki 2 razy większe, czyli np zamiast y = 4 mam y =8.
No dobrze. Czyli mam daną pozycję kafelka, niestety nadal nie wiem co z tym zrobić :/
Trochę się nad tym zastanawiałem i zobaczyłem, że kafelek to tak naprawdę (dzieląc go na 4 części) romb o bokach ok. 36 (~35,777...) i o przekątnych: 32 i 64. Nie wiem czy może mi to pomóc, ale wyliczyłem że jedna ćwiartka tego ów kafelka...
http://imageshack.us/photo/my-images/832/trojkat.png/
Krótsza przyprostokątna: 16
Dłuższa przyprostokątna: 32
Przeciwprostokątna: ~36

No i teraz muszę wyliczyć jakoś, aby "coś się działo", kiedy myszka jest w tym polu tego trójkąta i tu mam problem. Nie wiem czy nie muszę obliczać pozycji każdego piksela :/

0
Temessis napisał(a):

kafelek to tak naprawdę (dzieląc go na 4 części) romb o bokach ok. 36 (~35,777...) i o przekątnych: 32 i 64. Nie wiem czy może mi to pomóc, ale wyliczyłem że jedna ćwiartka tego ów kafelka...

Akurat boki kafelka będą miały 32 piksele, a nie ok. 36. Masz tutaj do czynienia z wartościami dyskretnymi. Kwadrat o przekątnych długości 32 pikseli, miałby bok równy 8 pikseli.

Temessis napisał(a):

No i teraz muszę wyliczyć jakoś, aby "coś się działo", kiedy myszka jest w tym polu tego trójkąta i tu mam problem. Nie wiem czy nie muszę obliczać pozycji każdego piksela :/

Dalej męczysz te trójkąty, po konwersji współrzędnych twój problem jest już rozwiązany. Wyciągasz ze swojej tablicy kafelki[x][y] konkretnego kafelka i mówisz mu, że ma się "coś z nim dziać".

2
Temessis napisał(a):

tylko że dzielę przez 64, a nie przez 32, gdyż gdy dzielę przez 32 to wtedy mam jednostki 2 razy większe
Możliwe że się pomyliłem. Na szybko to przeliczyłem.

Temessis napisał(a):

Trochę się nad tym zastanawiałem i zobaczyłem, że kafelek to tak naprawdę (dzieląc go na 4 części) romb o bokach ok. 36 (~35,777...) i o przekątnych: 32 i 64. Nie wiem czy może mi to pomóc, ale wyliczyłem że jedna ćwiartka tego ów kafelka...
http://imageshack.us/photo/my-images/832/trojkat.png/
Krótsza przyprostokątna: 16
Dłuższa przyprostokątna: 32
Przeciwprostokątna: ~36

No i teraz muszę wyliczyć jakoś, aby "coś się działo", kiedy myszka jest w tym polu tego trójkąta i tu mam problem. Nie wiem czy nie muszę obliczać pozycji każdego piksela :/
Nie, nie, nie. Kafelki są kwadratowe. Żadnych trójkątów, rąbów i innych. To tylko w rzucie prostokątnym kafelki wyglądają na romby. W "rzeczywistości" jest to zwykła siatka kwadratów. Cały myk polega na tym by operować na współrzędnych "rzeczywistych" (tych kafelkowych x i y) - tam wszystko jest prostokątne i o żadne trójkąty martwić się nie musisz.

Temessis napisał(a):

No dobrze. Czyli mam daną pozycję kafelka, niestety nadal nie wiem co z tym zrobić :/

Teraz musisz zrobić funkcję odwrotną która współrzędne kafelkowe zamieni na współrzędne ekranowe by móc rysować kafelki (i ten "zaznaczony" w innym kolorze).

Po podstawieniu i uporządkowaniu zmiennych wzór na konwersję ekranowe->kafelkowe wygląda tak:

    *x = 2 * screen_y - screen_x + offset_x - 2 * offset_y*
    *y = 2 * screen_y + screen_x - offset_x - 2 * offset_y*

x i y są niewiadomymi które wyliczamy ze wzoru. Aby sytuację odwrócić i przeliczyć kafelkowe->ekranowe zwyczajnie przyjmujemy screen_x i screen_y za niewiadome, a x i y za dane. Wtedy wychodzi nam układ równań (zwykły układ dwóch liniowych równań z dwiema niewiadomymi taki jak się w podstawówce rozwiązuje). Musisz sobie na kartce rozwiązać ten układ równań (najlepiej dodaj do siebie obydwa równania, innym razem odejmij), w rozwiązaniu otrzymasz wzory na przeliczanie jednostek kafelkowych na ekranowe:

    *screen_x = cośtam * x + cośtam * y + cośtam*
    *screen_y = cośtam * x + cośtam * y + cośtam*

Te wszystkie cośtam wyjdą ci po rozwiązaniu układu równań.

Mając już wzory na konwersję odwrotną można malować kafelki np. namalujmy siatkę 5x5:

/* rozmiar kafelka we współrzędnych kafelkowych, 64 jak sprawdziłeś, a nie 32 jak mówiłem */
const unsigned tile_sie = 64;

/* rozmiar sprajta kafelka we współrzędnych ekranowych */
const unsigned tile_sprite_width = 64;
const unsigned tile_sprite_height = 32;

/* indeks zaznaczonego kafelka, dla przykładu kafelek środkowy */
int sel_tx = 2;
int sel_ty = 2;

/* rysujemy siatkę 5x5 */
for (int ty = 0; ty < 5; ty++) {
    for (int tx = 0; tx < 5; tx++) {
        /* najpierw obliczmy współrzędne kafelkowe górnego rogu kafelka czyli mnożymy indeks kafelka przez jego rozmiar */
        int x = tx * tile_size;
        int y = ty * tile_size;

        /* teraz konwertujemy współrzędne kafelkowe->ekranowe przy pomocy wzorów które wyznaczysz */
        int screen_x = cośtam * x + cośtam * y + cośtam;
        int screen_y = cośtam * x + cośtam * y + cośtam;

        /* rysujemy kafelek, ale trzeba wziąć pod uwagę jeszcze jedną rzecz
         * wyznaczyliśmy pozycję na ekranie na której ma się znaleźć róg kafelka natomiast potrzeba nam znać
         * lewy górny róg sprajta - będzie on na lewo od górnego rogu kafelka, oddalony o połowę szerokości sprajta */
        screen_x -= tile_sprite_width / 2;
        /* sprawdzamy jaki kafelek namalować (zwykły czy zaznaczony) */
        sprite = (tx == sel_tx && ty == sel_ty) ? selected_tile_sprite : tile_sprite;
        /* przenosimy sprajta na ekran*/
        al_draw_bitmap(sprite, screen_x, screen_y, 0);
    }
}

No i koniecznie powrzucaj te wszystkie etapy w odpowiednie funkcje (przeliczanie indeksu kafelka na współrzędne kafelka, przeliczanie współrzędnych ekranowe<->kafelkowe itd.). Przyda się struktura opisująca punkt (para x i y) oraz struktura opisująca indeks kafelka (para tx i ty).

0

Udało się. Udało się!!! 2 tygodnie pracy... Dziękuje za wszystko! W końcu tutaj, na tym forum mi rozjaśniliście!!!
Jeden wzór i wszystko działa.

Jeszcze raz dziękuję za wszelką pomoc!

1 użytkowników online, w tym zalogowanych: 0, gości: 1