Skocz do zawartości

Mój kod, czy widzisz sens?


woyager_pl

Rekomendowane odpowiedzi

Cześć.

 

Jestem całkowicie nowy w tej branży, od kilku miesięcy uczę się C#, lecz programowanie gier i pisanie własnego silnika graficznego w tym języku jest dla mnie jeszcze trochę za bardzo skomplikowane. Po 6 miesiącach dopiero dobrze zrozumiałem klasy, delegacje, zdarzenia itp...

 

Natknąłem się na program Game Maker przypadkiem jakiś czas temu, korzystając z dokumentacji napisałem skrypt na poruszanie się bohatera w grze. Proszę o ocenę kodu, czy idę w dobrym kierunku, czy robię jakieś podstawowe błędy? Kod na poruszanie bohatera umieściłem w evencie STEP.

 

GML
//////////////////////////////////////// Biegnie - zmienna przyspieszenie - SHIFT

if (keyboard_check_pressed(vk_shift))

{

if (global.WcisnietyShift = 0 && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

global.WcisnietyShift = 1;

 

else if (global.WcisnietyShift = 1 && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

global.WcisnietyShift = 0;

 

}

 

////////////////////////////////////////

if (keyboard_check(vk_left) && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

{

 

global.kierunek = 2;

PlayerClass.sprite_index = PlayerGraph_Left;

PlayerClass.image_speed = 0.3;

PlayerClass.newX -= 32;

 

}

 

if (keyboard_check(vk_right) && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

{

global.kierunek = 3;

PlayerClass.sprite_index = PlayerGraph_Right;

PlayerClass.image_speed = 0.3;

PlayerClass.newX += 32;

}

 

if (keyboard_check(vk_up) && (PlayerClass.newY == PlayerClass.y) && (PlayerClass.newX == PlayerClass.x))

{

global.kierunek = 1;

PlayerClass.sprite_index = PlayerGraph_Up;

PlayerClass.image_speed = 0.3;

PlayerClass.newY -= 32;

}

 

if (keyboard_check(vk_down) && (PlayerClass.newY == PlayerClass.y) && (PlayerClass.newX == PlayerClass.x))

{

global.kierunek = 0;

PlayerClass.sprite_index = PlayerGraph_Down;

PlayerClass.image_speed = 0.3;

PlayerClass.newY += 32;

}

 

 

// poruszanie lewo i prawo

if (PlayerClass.newX < PlayerClass.x)

{

if (global.WcisnietyShift = 1)

{

hspeed = -4;

}

else

{

hspeed = -2;

}

}

 

if (PlayerClass.newX > PlayerClass.x)

{

if (global.WcisnietyShift = 1)

{

hspeed = 4;

}

else

{

hspeed = 2;

}

}

 

if (PlayerClass.x = PlayerClass.newX)

{

hspeed = 0;

 

if (global.kierunek == 2 || global.kierunek == 3)

{

PlayerClass.image_speed = 0;

PlayerClass.image_index = 0;

}

 

}

 

// ---------------------------------------

// poruszanie gora i dol

if (PlayerClass.newY < PlayerClass.y)

{

if (global.WcisnietyShift = 1)

{

vspeed = -4;

}

else

{

vspeed = -2;

}

}

 

if (PlayerClass.newY > PlayerClass.y)

{

if (global.WcisnietyShift = 1)

{

vspeed = 4;

}

else

{

vspeed = 2;

}

}

 

if (PlayerClass.newY = PlayerClass.y)

{

vspeed = 0;

if (global.kierunek == 0 || global.kierunek == 1)

{

PlayerClass.image_speed = 0;

PlayerClass.image_index = 0;

}

 

}

 

// ---------------------------------------

 

Z góry dziękuję...

Odnośnik do komentarza
Udostępnij na innych stronach

Hej. Do pisania kodu na forum używaj tagów [ gml ] - już poprawiłem twój post :)

Mimo, że kod wydaje się na pierwszy rzut oka poprawny, to jest on strasznie długi, a brakuje w nim jeszcze sprawdzania kolizji.

Popraw mnie jeżeli się mylę. To jest poruszanie się po siatce ze zmianą grafiki i możliwością biegania.

Odnośnik do komentarza
Udostępnij na innych stronach

Z tego co widzę, to jest kod na poruszanie się w stylu gier z serii Pokemon (i wielu innych, ale te przychodzą mi na myśl jako pierwsze). Mimo iż jest to poprawny skrypt w GML-u, będę go traktował jako pseudokod dla języków niższego rzędu.

Przeanalizuję krok po kroku, co jest w porządku, a co wymaga poprawy.

 

1.

GML
//////////////////////////////////////// Biegnie - zmienna przyspieszenie - SHIFT

if (keyboard_check_pressed(vk_shift))

{

if (global.WcisnietyShift = 0 && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

global.WcisnietyShift = 1;

 

else if (global.WcisnietyShift = 1 && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

global.WcisnietyShift = 0;

 

}

 

////////////////////////////////////////

Zauważ, że w każdym if masz podane trzy warunki, ale tylko dwa się zmieniają. Dlatego tutaj można praktycznie natychmiast uprościć kod:

GML
if (keyboard_check_pressed(vk_shift))

{

if(PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

{

if(global.WcisnietyShift == 1) global.WcisnietyShift = 0;

else if(global.WcisnietyShift == 0) global.WcisnietyShift = 1;

}

}

 

2.

GML
if (keyboard_check(vk_left) && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

{

 

global.kierunek = 2;

PlayerClass.sprite_index = PlayerGraph_Left;

PlayerClass.image_speed = 0.3;

PlayerClass.newX -= 32;

 

}

 

if (keyboard_check(vk_right) && (PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y))

{

global.kierunek = 3;

PlayerClass.sprite_index = PlayerGraph_Right;

PlayerClass.image_speed = 0.3;

PlayerClass.newX += 32;

}

 

if (keyboard_check(vk_up) && (PlayerClass.newY == PlayerClass.y) && (PlayerClass.newX == PlayerClass.x))

{

global.kierunek = 1;

PlayerClass.sprite_index = PlayerGraph_Up;

PlayerClass.image_speed = 0.3;

PlayerClass.newY -= 32;

}

 

if (keyboard_check(vk_down) && (PlayerClass.newY == PlayerClass.y) && (PlayerClass.newX == PlayerClass.x))

{

global.kierunek = 0;

PlayerClass.sprite_index = PlayerGraph_Down;

PlayerClass.image_speed = 0.3;

PlayerClass.newY += 32;

}

Uwaga w punkcie 1. odnosi się także tutaj. W każdym if w powyższym kodzie powtarzają się te same dwa warunki, więc można je "wziąć przed nawias".

 

Dla każdego klawisza przypisujesz liczbę będącą identyfikatorem danego kierunku. U ciebie wygląda to mniej więcej tak:

GML
1

|

2-- --3

|

0

Dla gier, w których istnieją tylko cztery kierunki poruszania (prawo, góra, lewo, dół) nie ma to większego znaczenia, ale gdybyś chciał kiedyś zrobić poruszanie we wszystkich kierunkach na płaszczyźnie i miał do czynienia ze sprite'ami, a nie modelami, to napisanie kodu na obrót sprite'a w zależności od kierunku postaci byłoby w tym przypadku niepotrzebnie utrudnione.

Przykład:

Niech kierunek będzie liczbą z zakresu 0-360. Przyjmijmy, że rotacja w grze jest typu counter-clockwise (odwrotnie do wskazówek zegara, tak jak w GameMakerze), i kierunek 0 odpowiada kierunkowi w prawo. Niech postać ma przypisany pewien kierunek oraz cztery sprite'y indeksowane 0-3 odpowiednio dla kierunków: prawo, góra, lewo, dół. Jaki sprite powinien się wyświetlić w zależności od kierunku?

GML
1 (90)

|

(180) 2-- --0 (0)

|

3 (270)

Widać, że istnieje liniowa zależność między faktycznym kierunkiem, a indeksem sprite'a. Korzystając z prostych operacji matematycznych wychodzi, że sprite, który powinien się wyświetlić ma indeks

GML
sprite_index = (kierunek-45) div 90 //a div b - dzielenie bez reszty

Tutaj wszystko wygląda ładnie. Przyjmijmy teraz, że sprite'y mają indeksowanie takie, jak przyjęte u ciebie. Masz dwie opcje:

a ) napisać własną funkcję przypisującą danemu kierunkowi odpowiedni skrypt,

b ) zmienić kolejność sprite'ów.

Ad. a ) W tym prostym przypadku funkcja wyglądałaby, bez wnikania w szczegóły, następująco:

GML
sprite_index = 0+((kierunek > 45)&&(kierunek <= 225))*((kierunek-45) div 90)+((kierunek > 225)&&(kierunek <= 315))*3

Tylko dla czterech kierunków wygląda brzydko.

Ad. b ) Dla czterech sprite'ów nie ma dużego problemu. Po prostu sprite o indeksie 0 jest skierowany w dół, 1 - w górę, 2 - w lewo, 3 - w prawo. Nie jest to ani clockwise, ani counter-clockwise, ale łatwo zapamiętać.

A teraz, co by było, gdyby było więcej niż jedna postać i każda miałaby zestaw, powiedzmy, 16 sprite'ów? Powstaje niepotrzebny chaos, wywołany nieintuicyjnością w tworzeniu assetów (grafik postaci) oraz brakiem porządku w kodzie. Przypisywanie indeksów dla sprite'ów i odpowiednich im kierunków powinno być tak proste, jak tylko jest to możliwe z programistycznego punktu widzenia. Liniowe zależności są lubiane przez komputer, bo są proste do pisania i do liczenia. Dla n sprite'ów kod wyglądałby tak:

GML
sprite_index = (kierunek-(180/n)) div (360/n) //wypadałoby, żeby n|360

 

3.

GML
if (PlayerClass.newX < PlayerClass.x)

{

if (global.WcisnietyShift = 1)

{

hspeed = -4;

}

else

{

hspeed = -2;

}

}

 

if (PlayerClass.newX > PlayerClass.x)

{

if (global.WcisnietyShift = 1)

{

hspeed = 4;

}

else

{

hspeed = 2;

}

}

 

if (PlayerClass.x = PlayerClass.newX)

{

hspeed = 0;

 

if (global.kierunek == 2 || global.kierunek == 3)

{

PlayerClass.image_speed = 0;

PlayerClass.image_index = 0;

}

 

}

O ile x,y,hspeed,vspeed są liczbami całkowitymi, dwa pierwsze bloki są w porządku (pomijając zbyt szerokie pisanie kodu). Dla liczb zmiennoprzecinkowych (float, double) może się nagle okazać, że PlayerClass.x nigdy nie będzie równe PlayerClass.newX, co wynika z zaokrąglania liczb wymiernych do najbliższej możliwej wartości liczby zmiennoprzecinkowej - która nie musi być liczbą całkowitą!

Co do trzeciego bloku, sprawdzanie, jaką wartość ma global.kierunek nie jest konieczne. Skoro obiekt gracza może w dowolnym momencie posiadać tylko prędkość zerową, poziomą albo pionową, wystarczy sprawdzać, jaką wartość ma prędkość pozioma - jeżeli 0, to postać gracza porusza się pionowo lub stoi, więc nie trzeba nic robić; jeżeli wartość różną od zera - postać porusza się poziomo, więc można ją zatrzymać. Żeby to zadziałało, trzeba przesunąć zerowanie hspeed na koniec bloku, inaczej sprawdzanie zawsze zakończy się stwierdzeniem, że postać porusza się pionowo lub stoi.

Analogicznie dla trzech pozostałych bloków.

 

W kolejnym poście (sorry moderacja) przepiszę kod wg powyższych wskazówek, abyś wiedział na przyszłość, że skracanie, upraszczanie i porządkowanie kodu jest warte trudu.

Odnośnik do komentarza
Udostępnij na innych stronach

Kod został opatrzony komentarzami, aby było wiadomo, co i jak.

GML
//Wyciągam przed nawias poniższy warunek, żeby skrócić kod i zwiększyć czytelność

//Poniższy kod wykona się tylko i wyłącznie, gdy postać gracza znajduje się w punkcie kratowym (czyli podzielnym przez wysokość/szerokość komórki)

if(PlayerClass.newX == PlayerClass.x) && (PlayerClass.newY == PlayerClass.y)

{

//w GameMakerze speed != 0 jest równoważne (vspeed != 0)||(hspeed != 0)

//jeżeli postać się porusza, to wiemy, że w poprzednim kroku wykonywała ruch; skoro teraz jest na punkcie kratowym, to można już na tym etapie

//zatrzymać animację i ruch postaci

if(speed != 0)

{

PlayerClass.image_speed=0;

PlayerClass.image_index=0;

vspeed = 0;

hspeed = 0;

}

//zmienne pomocnicze

var sprite_table,tmp_speed;

//tablica, która każdemu sprite'owi przyporządkowuje pewien indeks; służy skróceniu kodu

//btw. w normalnym programie nie inicjuj tablicy w ten sposób, zrób to raz gdzieś na początku wykonywania programu

sprite_table[0]=PlayerGraph_Right; sprite_table[1]=PlayerGraph_Up; sprite_table[2]=PlayerGraph_Left; sprite_table[3]=PlayerGraph_Down;

//teraz sprawdzanie, czy jest wicśnięty jakiś klawisz; zauważ, że z wykorzystaniem if .. else if .. else if .. else upewniam się, że wykona się

//tylko jeden z poniższych czterech bloków - zastanów się, co by było, gdyby wciśnięto więcej niż jeden klawisz

if(keyboard_check(vk_right))

{

global.kierunek = 0;

PlayerClass.newX += 32;

}

//dla każdego klawisza przypisuję pewną wartość liczbową odpowiadającą kierunkowi

else if(keyboard_check(vk_left))

{

global.kierunek = 2;

PlayerClass.newX -= 32;

}

//jeżeli spojrzysz na tablicę powyżej, zauważysz, że ta wartość odpowiada indeksowi sprite'a, który postać przybierze przy wykonaniu ruchu

else if(keyboard_check(vk_down))

{

global.kierunek = 3;

PlayerClass.newY += 32;

}

else if(keyboard_check(vk_up))

{

global.kierunek = 1;

PlayerClass.newY -= 32;

}

//osobno rozpatrujemy wciśnięcie Shifta, tutaj jest inne rozwiązanie, niż w twoim kodzie

//tutaj wartość global.WcisnietyShift przybierze wartość 1, gdy klawisz Shift pozostaje wciśnięty, a wartość 0 w przeciwnym przypadku

if(keyboard_check(vk_shift))

global.WcisnietyShift=1;

else

global.WcisnietyShift=0;

//kolejne dwie linijki (rozpoczęcie animacji postaci gracza) zostały tak naprawdę również wyciągnięte przed nawias, aby skrócić kod

//tutaj korzystam z utworzonej wcześniej tablicy, dzięki czemu niezależnie od wciśniętego klawisza, postać przybierze poprawny sprite

PlayerClass.sprite_index = sprite_table[kierunek];

PlayerClass.image_speed = 0.3;

/*poniższy kod wymaga trochę uwagi: rozpatruje on 5 przypadków:

-gracz ma poruszać się w jednym z czterech kierunków

-gracz stoi

jest pewne, że jednocześnie zachodzi TYLKO JEDEN z powyższych przypadków, więc można je rozpatrzyć osobno

jeżeli zajdzie któryś z czterech pierwszych przypadków, oznacza to, że gracz nacisnął któryś z klawiszy kierunkowych, więc powyższe dwie linijki

kodu na pewno powinny zostać wykonane

jeśli zaś gracz nie nacisnął żadnego klawisza, to zajdzie przypadek, gdy gracz ma stać - zatem trzeba zatrzymać animację, którą zaczęliśmy wyżej

*/

tmp_speed=2+2*global.WcisnietyShift;

if(PlayerClass.x < PlayerClass.newX) hspeed = tmp_speed;

else if(PlayerClass.x > PlayerClass.newX) hspeed = -tmp_speed;

else if(PlayerClass.y < PlayerClass.newY) vspeed = tmp_speed;

else if(PlayerClass.y > PlayerClass.newY) vspeed = -tmp_speed;

else PlayerClass.image_speed = 0;

//końcowa uwaga: jeżeli po spełnionym if wykonuje się tylko jedna linijka kodu, nie trzeba do tego tworzyć osobnego bloku

}

To nie jest maksymalne skrócenie kodu, ale jest on czytelny i uporządkowany (akcje wykonują się chronologicznie, zgodnie z zamysłem projektanta).

 

@Uzjel, I am Lord: ja wiem, że to by pomogło, ale chciałem to zrobić, korzystając z terminologii autora tematu - zwłaszcza, że chce on pisać w jakimś normalnym, powszechnym języku

Odnośnik do komentarza
Udostępnij na innych stronach

Nie nie, pomyliłem się, prędzej nie zatrzyma się wcale bo nie trafi idealnie w środek jak Amaterasu napisał. Tak czy siak sam pomysł by sztywny ruch po kratkach implementować za pomocą manipulowania prędkością a nie samą pozycją, wydaje mi się sporym utrudnieniem.

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...