Skocz do zawartości

Amaterasu

Użytkownicy
  • Postów

    390
  • Dołączył

  • Ostatnia wizyta

Treść opublikowana przez Amaterasu

  1. 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
  2. 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.
  3. Amaterasu

    Anime

    jakiś czas temu oglądałem Assassination Classroom, Nobunagun i Sayonara Zetsubou Sensei, luźne show i przyjemnie się ogląda
  4. W dokumentacji jest, że jeżeli próba połączenia się nie powiedzie, to powinno zwrócić wartość mniejszą od 0. Zapewne tak samo jest przy podaniu niepoprawnych argumentów.
  5. Ja troszeczkę poopowiadam. Komputery są maszynami deterministycznymi: kolejny stan takiej maszyny zależy wyłącznie od poprzednich stanów. Oznacza to, między innymi, że nie istnieje algorytm, który zwróci liczbę prawdziwie losową - liczba, którą wygeneruje, zależy tylko od tego, jakie dane wejściowe ma ten algorytm. Istnieją metody do generowania liczb, które wydają się być losowe. Ponieważ jednak nie są one prawdziwie losowe, nazywa się je pseudo-losowymi. Każda z tych metod korzysta z czegoś, co się nazywa seed (po polsku byłoby pewnie "ziarno", ale "seed" jest krótsze). To, jakie liczby zostaną wygenerowane przez algorytm pseudo-losowy zależy od tego, jaką wartość posiada seed. Dla różnych wartości seed liczby wygenerowane przy wywołaniu funkcji z tymi samymi argumentami mogą być różne. Funkcje: choose(), irandon_range(), irandom(), random_range(), random() działają różnie, ale mają wspólną cechę: wszystkie generują liczby w ten sam sposób, korzystając z wewnętrznego algorytmu pseudolosowego. Oznacza to, że wszystkie funkcje losowe w GM-ie dzielą ten sam seed. GameMaker Studio dla każdego projektu przypisuje pewną domyślną wartość seed, więc przy każdym uruchomieniu aplikacji wywołania funkcji losowych w tej samej kolejności zwrócą te same wartości. GM posiada funkcje do interakcji z seedem: -randomize() jest tym, czego chcesz - ustawia seed na losową wartość (*) -random_set_seed() pozwala ręcznie ustawić wartość seed -random_get_seed() zwraca aktualną wartość seed. randomize() wystarczy wywołać raz, przy uruchomieniu aplikacji. (*) Ta losowa wartość nie jest losowa - to po prostu czas systemowy, wartość zmieniająca się bardzo często (chyba co milisekundę), więc nie ma mowy o przewidzeniu wartości seed przy uruchomieniu aplikacji, a o to przecież chodzi.
  6. no przecie widać, że to placeholdery
  7. Metoda Threefa powinna działać w następującej formie: GML angle=random(360) distance=sqrt(sqr(room_width/2)+sqr(room_height/2))+offset instance_create(room_width/2+lengthdir_x(distance,angle),room_height/2+lengthdir_y(distance,angle),obiekt) //poprawka offset oznacza minimalną odległość, jaką może mieć pewien punkt na okręgu do pewnego rogu ekranu - przyjmując wartość 0, okrąg, na którym będą pojawiać się obiekty, będzie styczny do prostokąta, jaki tworzy room. Im większy offset, tym dalej będą się pojawiać obiekty. @hgter: miałem z tym do czynienia tak często, że takie rzeczy pisze się instynktownie : >
  8. Sposób Threefa jest w miarę dobry, ale jego słabością jest to, że obiekty nie będą pojawiać się w tej samej odległości od krawędzi rooma. Natomiast "gęstość" pojawiających się obiektów będzie stała, co jest plusem. Metoda hgtera jest bardziej wiarygodna (obiekty pojawiają się zawsze w tej samej odległości od krawędzi), ale im bliżej rogów ekranu, tym więcej będzie stamtąd pojawiać się obiektów. Swoją drogą, tę metodę można skrócić: GML if(random(room_width+room_height)<room_width) instance_create(random(room_width),-20+(room_height+2*20)*choose(0,1),obiekt) else instance_create(-20+(room_width+2*20)*choose(0,1),random(room_height),obiekt) Liczba 20 oznacza liczbę pikseli od krawędzi, można ją przypisać do zmiennej i dowolnie zmieniać.
  9. Te boty są za dobre w unikaniu bomb, to tak jakbyś dał botom w Q3 Arena 100% celności na poziomie Hurt Me Plenty Zwiększ liczbę poziomów trudności, bo może się zdarzyć sytuacja, że poziom Easy jest zbyt łatwy, a Medium zbyt trudny i całą frajdę szlag trafi co z tego, że w to się gra na LAN-ie w party
  10. Upewnij się, że msg_id jest dobrze odczytywane (tzn. przy zapisie i odczycie zawsze używasz tego samego typu), miałem kiedyś podobny problem i mocno mnie to irytowało.
  11. jakbyś podał jakiś przykładowy error, który ci wywala, to byłoby prościej
  12. Ja to załatwiam w ten sposób, że robię specjalny obiekt (nazwijmy go p_tekst), który wyświetla liczbę (albo tekst, bez różnicy) na dany kolor, a podczas zadawania obrażeń piszę GML show_damage(dmg,kolor,pozycja_x,pozycja_y); przy czym show_damage jest skryptem opisanym następująco: GML var pt; pt=instance_create(argument2,argument3,p_tekst); pt.tekst=string(dmg); //lub pt.dmg=dmg, jeżeli pt.dmg jest liczbą pt.cl=kolor; Obiekt p_tekst ma w evencie Draw kod odpowiedzialny za rysowanie wartości tekst (dmg), w Create możesz dodać kolejne zmienne kontrolujące zachowanie obiektu (np. dodanie zmiennych odpowiedzialnych za przezroczystość tekstu, poruszanie się, czas trwania itd.). Obiekt p_tekst powinien mieć niski depth, żeby go nie zasłaniały inne obiekty. __poniższe niech tyczy się do przyszłych projektów, ten ukończ (o ile ukończysz) wg własnego planu__ *W ogólności powinno się napisać skrypt (powiedzmy, deal_damage) do zadawania obrażeń, biorący za argument ID obiektu i zadane obrażenia (plus dodatkowe, mniej ważne), i tam pisać cały kod odpowiedzialny za zadawanie obrażeń. Jeżeli dodatkowo wpisze się do niego odpowiednio linijkę show_damage(), to w całym projekcie za każdym razem, gdy będziesz chciał zadać obrażenia i je wyświetlić, wystarczy wpisać linijkę deal_damage(argumenty). Tak samo z odpychaniem obiektów, napisać skrypt push_object(direction,force) lub w tym rodzaju, nie pisać kodu "na goło". To jest dobry nawyk, żeby uogólniać do maksimum, inaczej przy większych projektach szybko robi się syf; nauczyłem się tego brutalną drogą : /
  13. Możesz takiemu obiektowi dać w Create zmienną (np. tekst_tab) i przypisać jej cokolwiek, a w Draw napisać coś, żeby ten tekst rysowało (pewnie coś z użyciem draw_text). Teraz możesz w Creation code w edytorze roomów każdemu obiektowi przypisać dowolny tekst.
  14. @Ignatus: Funkcja distance_to_object(x) działa poprawnie, jeżeli za x wstawisz ID instancji obiektu lub słowo specjalne "other". Jeżeli jako x wstawisz nazwę obiektu (czyli ID obiektu), to funkcja wyliczy odległość od tylko jednej instancji tego obiektu (prawdopodobnie tej, która powstała jako pierwsza). Zakładam, że istnieje tylko jedna instancja obiektu obj_eye i że obj_mobile_parent jest nazwą obiektu, a nie zmienną przechowującą ID pewnej instancji obiektu. Wtedy kod GML if (distance_to_object(obj_mobile_parent)<150){with obj_mobile_parent image_alpha=0} else with obj_mobile_parent image_alpha=1 wyliczy odległość od instancji obiektu obj_eye do pierwszej powstałej instancji obiektu obj_mobile_parent, i jeśli spełni warunek, to zmieni image_alpha WSZYSTKIM instancjom obiektu obj_mobile_parent. Następujący kod: GML with(obj_mobile_parent) image_alpha=(distance_to_object(other)>150) wykonywany w stepie obiektu obj_eye zrobi to, czego wymagasz. (a przynajmniej powinien : |) @kso: Jeśli to platformówka, to możesz zapisywać w obiekcie ostatnią pozycję, na której obiekt stoi na platformie czy na czymkolwiek, a w momencie rozbicia powracać do tej pozycji.
  15. Amaterasu

    random

    Jak to? Przed chwilą sprawdziłem i mogłem spokojnie działać na liczbach większych od 2^40, zawsze myślałem, że liczby w GM-ie są typu double.
  16. jak używasz draw_getpixel albo surface_getpixel, to przestań w przeciwnym wypadku nie da się niczego wywróżyć bez konkretów, np. jakiegoś podejrzanego wg ciebie kodu
  17. takie coś byłoby dostępne w pygame, ale nie tutaj masz dwie opcje: 1. jeśli twój obrazek jest wczytywany z pliku .bmp, to możesz napisać sobie skrypt, który będzie pobierał dane z pliku i zapisywał je w tabeli - z .bmp jest to banalna sprawa 2. w innych przypadkach pozostaje użycie getpixel - nie muszę mówić, że to nie jest szybkie rozwiązanie - albo korzystanie z rozszerzeń, o ile takie istnieją
  18. @XBlackX: GML if collision_point(128,122,object0,true,false) {with(object1 instance_destroy()} 1. Co oznaczają liczby 128 i 122? 2. W jakim evencie w którym obiekcie masz ten kod wpisany? 3. with(object1) spowoduje usunięcie wszystkich instancji obiektu object1 (czyli wszystkich object1 na mapie). Użycie with(other) spowoduje, że zostanie usunięty tylko obiekt kolidujący. Funkcja collision_point zwraca ID obiektu, z którym następuje kolizja w danym punkcie, lub -1, jeżeli żaden obiekt nie wywołuje kolizji. Powinieneś przypisać wartość collision_point do jakiejś zmiennej, dzięki temu pisząc with(zmienna) {kod} kod zostanie wykonany tylko dla tego obiektu, który wywołuje kolizję w punkcie. To może brzmieć nieco skomplikowanie, ale tak to jest, gdy chce się załatwiać kolizje samemu, zamiast korzystać z eventu Collision with (zakładam, że twój kod nie znajduje się w Collision with).
  19. GML //losowy obiekt l_ob = instance_find(nazwa_obiektu,irandom(instance_number(nazwa_obiektu)-1))
  20. bo najpierw trzeba się starać, żeby być rozpoznawalnym w gronie tych "fajnych gmclanów", a potem to już i tak wisi
  21. shameless plug-in https://forum.gmclan.org/index.php?showtopic=27437&hl= z czasów, gdy dawałem się łatwo strollować tam jest przykład value noise, jeśli ci się spodoba rezultat, to możesz to sobie zatrzymać - chyba łatwo przerobić ten przykład na dowolne wymiary, już nie pamiętam
  22. możesz skorzystać z application_surface do rysowania na innych surface'ach GML surface_set_target(twoj_surface) draw_surface(application_surface,jakies_x_najlepiej_0,jakies_y_najlepiej_0) surface_reset_target() teraz powinieneś mieć na swoim surface'u narysowane to, co masz na ekranie (a właściwie to, co było narysowane klatkę przedtem) - rzecz w tym, że powinieneś to wywołać w Post Draw Event, wcześniej możesz mieć nie wszystko narysowane
×
×
  • Dodaj nową pozycję...