Skocz do zawartości

Konrad-GM

Użytkownicy
  • Postów

    2 728
  • Dołączył

  • Ostatnia wizyta

  • Wygrane w rankingu

    44

Odpowiedzi opublikowane przez Konrad-GM

  1. Polecam odświeżyć sobie podstawy z fizyki newtona :)

     

    Przypisz każdemu obiektowi co ma masę wektor prędkości liniowej (np. stwórz wektor linear_velocity). Posługuj się przyśpieszeniem grawitacyjnym i dodawaj do tej zmiennej (wektora) wartość przyśpieszenia co step pomnożoną przez delta_time. Wzór ogólny dla przyśpieszenia grawitacyjnego:

     

    a_1 = G \frac{m_2}{r^2} - warto chociaż przejrzeć https://pl.wikipedia.org/wiki/Przyspieszenie_grawitacyjne

     

    Żeby obliczyć dla każdego obiektu przyciąganie, możesz posłużyć się opcją z with, np.

    var gc = 0.0000000000667408 // 6.67408E-11, stała grawitacyjna
    
    with (obj_asteroid) {
        if (other.id == id) {
            continue;
        }
        // zakladamy rowniez, ze (x,y) to srodek masy obiektu
        var r = point_distance(x, y, other.x, other.y)
        var r2inv = 1.0 / (r * r);
        var accel = gc * mass * r2inv * delta_time
        other.linear_velocity_x += accel;
        other.linear_velocity_y += accel;
    }

     

    Potem tylko już aplikujemy ruch:

    x += linear_velocity_x * delta_time;
    y += linear_velocity_y * delta_time;

     

  2. 16 minut temu, gnysek napisał:

     

    Generalnie, jeśli ktoś ma pomysł na cykl z nagrodami, można robić kilka "sezonów" na raz (w sensie osobny konkurs)

     

    W sumie mam kilka gier z bundli, Samorost 3, Warhammer: End Times - Vermintide z jakimś DLC, Verdun a nawet Solstice i jeszcze parę - z możliwością wysłania jako gift na steam, nawet mam Game Maker Studio 1 Pro z Android Exportem. Ale to stary GM , to nie wiem, czy się nadaje na ligę. Bundle kupowałem specjalnie dla jakichś pojedynczych gier czy eksportów do GMa, więc większość kluczy nawet nie aktywowałem i nie wiem do końca, co w nich jest, musiałbym jeszcze przejrzeć.

     

    Też nie wiem, jak jest z żywotnością tych kluczy na steam, czy nie przedawniają się po jakimś czasie.

  3. 15 godzin temu, gnysek napisał:

    To co @Konrad-GM odstawił w swojej grze, zasługuje na zwycięstwo :D Właśnie po to jest liga 24. Nie dość, że zrobił to w innym edytorze niż GM, to jeszcze... a nie będę spojlerował. Warto zagrać: https://gmclan.org/index.php?liga24&act=rounddetails&zm=179

     

    Przyznam, że to "gra żart", ale super, że się koncept spodobał. Chociaż miałem okazję przetestować Godot 3.1 Beta. BTW widziałem też, że w akademii jest sekcja Godot, może udałoby mi się napisać jakiś artykuł bądź dwa. :D

     

    Ja zagłosowałem natomiast na grę @Uzjel, ciekawie wygląda, fajny klimat a i fajnie się gra, do tego bonusy, chociaż brakuje oddzielnego licznika punktacji ogólnej, bo za wszystko się płaci punktami. Gra @Korodzik też jest super, pewnie jakby nie wyświetlanie punktów w tabeli podczas głosowania, zagłosowałbym na tę grę, ale IMO podium dla @Uzjel też się należy za fajną grę :P 

     

    Bonus: mały hack w grze @SLy :D

    setInterval(() => { Array.from(document.getElementsByClassName('clown')).forEach(clown => clown.click()) }, 250)

     

    Fajnie, że liga ożyła, oby tak było co najmniej do następnego sezonu :D 

  4. Cześć, najłatwiej byłoby po prostu dla każdej jednostki z oddziału "zapamiętać" z jakiego spawna pochodzą. Po prostu zrobić jakiś prosty licznik i sprawdzać, czy został on wyzerowany:

     

    W obiekcie powiedzmy obj_spawn dodajesz zmienną w Create Event:

    units_alive = 0;

     

    Potem przy tworzeniu jednostki obj_unit nadać mu dodatkowo ID instancji obj_spawn jako "pochodzenie", np. w obiekcie obj_spawn w User Event 0 dać coś takiego:

    // tworzymy instancje jednostki
    var unit = instance_create(x, y, obj_unit);
    unit.spawn_id = id;
    
    // dodajemy jednostke do licznika
    units_alive += 1;

     

    A na koniec w obiekcie obj_unit w Destroy Event wystarczy dekrementować licznik powiązanej instancji obj_spawn:

    spawn_id.units_alive -= 1;

     

    Potem sprawdzenie ile jednostek pozostało żywych jest chyba oczywiste :)

  5. Możesz jeszcze użyć dezaktywacji obiektów, ale trzeba pamiętać, aby obiekt popup-a był aktywny, bo inaczej nie będzie można wrócić do gry ;) W Create Event obiektu obj_popup daj taki kod:

    instance_deactivate_all();
    instance_activate_object(obj_popup);

    Ale pamiętaj też o aktywacji obiektów po zniknięciu komunikatu, np. w Event Destroy obiektu obj_popup daj:

    instance_activate_all();

     

    Edit:

    Godzinę temu, Ice Cube napisał:

    ps. ale klikniecie myszka,wyswietlanie napisu było by własnie w petli,ewentualnie zamiast myszki

    zrobiłbym klawiature

     

    GameMaker nie udostępnia funkcji do przetworzenia zdarzeń systemowych -> pętla w grze po prostu Ci nie zadziała, bo nie będziesz w stanie obsłużyć żadnego kliknięcia, nawet krzyżyka na belce okna. Jeżeli pisałeś kiedykolwiek aplikacje na Windowsa, to jest tam taka funkcja jak GetMessage/PeekMessage, GameMaker nie udostępnia swojego odpowiednika ;) Pętla musiałaby wyglądać tak:


    1. obsłuż zdarzenia systemowe

    2. jezeli nacisniete esc lub myszka na ok skocz do 6<- tutaj można sprawdzić, czy esc/myszka została naciśnięta bo zdarzenia systemowe zostały zinterpretowane

    3. wyswietl napis

    4. swap buffers - wyświetl na ekran wygenerowany obraz

    5. skocz do 1

    6. <kontynuuj program>

     

    Ale w GameMakerze jest to niemożliwe. Przynajmniej nie znalazłem funkcji do obsługi systemowych zdarzeń.

  6. Cześć, musisz przyblokować wykonywanie się kodu innym obiektom, pętla while Ci nie zadziała ze względu na to, jak działa pętla główna Game Makera. Jak używasz pętli while, to GameMaker nie przetwarza systemowych zdarzeń jak np. kliknięcie myszką czy klawisza.

     

    Żeby przyblokować wykonywanie kodu w obiektach, możesz stworzyć np. zmienną globalną jak:

    global.is_paused = false;

    A potem w każdym z obiektów w Step Event, przed blokiem całego kodu dodaj po prostu taką linijkę:

    if (global.is_paused) exit;

    Jak chcesz zapałzować grę, to po prostu ustawiasz zmiennej global.is_paused na true, analogicznie odblokowujesz grę po ustawieniu global.is_paused na false.

  7. 7 godzin temu, I am Lord napisał:

    Czyli co zamiast pełnego zakresu UV mapy, trzeba zabrać z niej z każdego krańca po połowie pixela?

    A wyłączenie texture_repeat nie dawało nic? Ewentualnie można tak narysować textury by te miały na krawędziach takie same pixele po jednej i drugiej stronie.

     

    Bezszwowe tekstury to normalna praktyka by się pozbyć tego efektu, ale czasami to nie wystarcza, zwłaszcza jak się podzieli teksturę na mniejsze elementy, bo po prostu limit rozmiaru tekstury nie pozwala na to. Efekt co prawda nie jest idealny, ale można i z tym powalczyć, np. przekopiowując krańce tekstury z tekstury sąsiadującej. Coś na zasadzie dodawania 1px obramowania do sprite'ów w atlasie tekstur, żeby też ominąć te brzydkie łączenia z sąsiednim spritem. (w 3D tekstura musi być rozmiaru potęgi dwójki, dlatego np. 2050px odpada i trzeba obraz skalować)

  8. 58 minut temu, gnysek napisał:

    ja chyba to tak rozwiązywałem, że dawałem pozycje rysowania tekstur od 0.01 do 0.99 z tego co pamiętam. Ale zabawa z 3D w GameMakerze, to jak wożenie lodówki maluchem, albo jazda na sankach latem po trawniku. Generalnie średnio wychodzi :D

     

    Ważne, że działało i dawało zadowalające efekty, tyle, że to takie mini-piekło dla perfekcjonisty :P Jednak ten problem z interpolacją dotyczy każdego silnika czy biblioteki graficznej, brak shaderów czy chociaż podstawowych ustawień tekstur to już rzeźnia po prostu. Ale warto wiedzieć jak sobie radzić z takimi problemami gdy ogranicza narzędzie :D 

  9. Cześć, problem z interpolacją tekstur jest jak najbardziej "normalną" odpowiedzią od karty graficznej, generalnie ten problem występuje od zawsze, odkąd istnieje interpolacja sampli. Jednym z rozwiązań jest wyłączenie powtarzalności tekstur np. w OpenGL jest to ustawienie w teksturze flagi GL_TEXTURE_WRAP_S/T na GL_CLAMP_TO_EDGE, jednak GameMaker nie udostępnia takowych funkcji ;) 

     

    Przykładowe ustawienia wrap-owania tekstur w OpenGL (opcje te występują również w DirectX);

    c3_clamping.png

     

    W Twoim przypadku GameMaker prawdopodobnie ustawia teksturom odpowiednik GL_REPEAT w DirectX, dlatego pojawiają Ci się te "artefakty" na krańcach tekstury. 

     

    Dodatkowo każda tekstura musiałaby być "stworzona" jako oddzielna tekstura w VRAM, atlas tekstur nie zadziała, jak to się dzieje w przypadku GMS1.4+ (tzw. grupy obrazków w ustawieniach GMa, ale da się to akurat ominąć).
     

    Więc w skrócie problem polega na tym, że interpolacja sampli w fragment shaderze miesza sąsiadujące kolory, tworząc tzw. texel, jak się pewnie domyślasz, artefakty które pojawiają Ci się na krańcach modelu podłogi to po prostu zmieszane kolory z drugiego krańca tekstury, dlatego też warto czasami używać tekstur seamless ;) Po więcej teorii dot. tekstur i metod samplowania odsyłam do https://cglearn.eu/pub/computer-graphics/textures-and-sampling

     

    Jako, że nie możemy wyłączyć powtarzalności tekstur, można za to wykluczyć krańce tekstury z samplowania, efekt nie jest idealny, ale nie ma tak oczojebnych artefaktów. Przykład poniżej:


    P63NYqz.png e2tzEwc.png

     

    Jednak aby wykluczyć krańce tekstury w GMie, trzeba stworzyć własny model podłogi, bo draw_floor nie udostępnia funkcji do modyfikacji UV per vertex, ani GameMaker 8.0 nie wspiera shaderów.

    Kod obj_floor w Create Event u mnie wygląda tak:

    // tworzymy model podlogi tylko raz i przypisujemy model do zmiennej globalnej
    if (!variable_global_exists("model_floor")) {
        
        // obliczamy polowe texela tekstury do wykluczenia go z sampla
        // teraz kazdy kafelek Twojej podlogi musi miec taka sama wielkosc
        var u, v;
        u = (1.0 / background_get_width(texTile)) * 0.5;
        v = (1.0 / background_get_height(texTile)) * 0.5;
    
        // szerokosc i dlugosc podlogi
        var w, h;
        w = 64;
        h = 64;
        
        // tworzymy model podlogi (tylko raz)
        global.model_floor = d3d_model_create()
        d3d_model_primitive_begin(global.model_floor, pr_trianglefan);
        d3d_model_vertex_texture(global.model_floor, 0, 0, 0, 0.0 + u, 0.0 + v);
        d3d_model_vertex_texture(global.model_floor, w, 0, 0, 1.0 - u, 0.0 + v);
        d3d_model_vertex_texture(global.model_floor, w, h, 0, 1.0 - u, 1.0 - v);
        d3d_model_vertex_texture(global.model_floor, 0, h, 0, 0.0 + u, 1.0 - v);
        d3d_model_primitive_end(global.model_floor);
    }
    
    // tekstura podlogi
    texid = background_get_texture(texTile);

     

    Natomiast obj_floor i Draw Event:

    d3d_model_draw(global.model_floor, x, y, 0, texid);

     

    Można też poprawić wygląd łączeń, ale tutaj jest trochę więcej roboty - musiałbyś zeskalować tekstury powiedzmy z 2048 do 2046 na środek i te wolne piksele na krańcach przekopiować z sąsiadujących ze sobą tekstur (tekstura nadal musi mieć 2048px).

  10. W poprzednim temacie napisałem co można by zrobić, ale powtórzę:

     

    Dnia 11.12.2018 o 21:18, Konrad-GM napisał:

    Widzę, że wkraczasz w bardziej złożone kwestie z programowaniem fizyki w grach :) Podejrzewam, że problem jest z dużą ilość wrogów na mapie - albo użyj dezaktywacji obiektów poza pewnym rejonem dookoła gracza funkcjami z rodziny instance_(de)activate_*, albo porzuć stosowanie flagi solid i oprogramuj swoją obsługę kolizji w Collision Event-ach.

     

    Nie mam pojęcia, jak GameMaker obsługuje kolizje i czy stosuje coś na wzór spatial partitioning - quadtree, spatial hash itp., może ktoś bardziej obeznany w tych kwestiach z GMem by podpowiedział. Ale mógłbyś też posilić się własną implementacją sprawdzania kolizji używając quadtree czy prostszym w implementacji spatial hashing (quadtree vs spatial hashing).

     

    PS. Możesz skrócić swój kod with(obj_potwor) do with(obj_potwory) ustawiając wrogom tego samego rodzica parent obj_potwory.

     

    BTW. Nazwa tematu nie jest dostatecznie opisowa.

  11. Cześć, funkcja collision_circle (jak każda funkcja zaczynająca się od collision_*) zwraca ID instancji, także other Ci tutaj nie zadziała. Musisz zapisać ID do zmiennej tj.

    var potwor = collision_circle(x, y, 100, obj_potwor, false, true);
    while (potwor != noone) {
        with (potwor) {
            instance_destroy();
        }
        potwor = collision_circle(x, y, 100, obj_potwor, false, true);
    }

    Dałem funkcję w pętli, ponieważ funkcje collision_* zwracają Ci tylko ID jednej instancji. 

     

    Dla GMS1.4+ zamiast with(potwor) można zapisać instance_destroy(potwor)

  12. 38 minut temu, gnysek napisał:

    No jak każdy sprite będzie miał kod do sprawdzania, czy jest w środku view czy poza, to samo sprawdzanie może zająć więcej czasu, niż rysowanie bez tych warunków.

     

    Dokładnie, bez sensownego mierzenia wydajności, jakakolwiek próba optymalizacji nie ma sensu, bo może to być zwykłą stratą czasu.

     

    Edit: Na szybko sprawdziłem, czy zmiana visible połowy sprite na false ma jakiś wpływ na FPS - i nie ma żadnego, FPS tak samo niski jaki był :P 

  13. 20 godzin temu, nowy_user napisał:

    Dzięki wszystkim za odpowiedzi, dla pewności będę optymalizował kod w taki sposób aby na 100 % nie rysowało nic poza viewem.

     

    IMO przedwczesna optymalizacja też jest zła, optymalizuj rysowanie sprite, kiedy faktycznie będzie miało to wpływ na wydajność ;) Przejrzyj może artykuł dot. optymalizacji na Yoyo, jest tam kilka wzmianek o optymalizacji rysowania spritów.

  14. 7 godzin temu, gnysek napisał:

    Sprity poza view są odrzucane przez DirectX/OpenGl. Także to powyżej to nieprawda :)

     

    To też nie do końca prawda, odrzucane są trójkąty podczas renderingu, po vertex shaderze (Vertex Post-Processing). Także żeby zminimalizować draw call'e, które też mają wpływ na wydajność, stosuje się własny clipping. Podejrzewam, że o to chodziło autorowi :)

  15. Widzę, że wkraczasz w bardziej złożone kwestie z programowaniem fizyki w grach :) Podejrzewam, że problem jest z dużą ilość wrogów na mapie - albo użyj dezaktywacji obiektów poza pewnym rejonem dookoła gracza funkcjami z rodziny instance_(de)activate_*, albo porzuć stosowanie flagi solid i oprogramuj swoją obsługę kolizji w Collision Event-ach.

     

    Nie mam pojęcia, jak GameMaker obsługuje kolizje i czy stosuje coś na wzór spatial partitioning - quadtree, spatial hash itp., może ktoś bardziej obeznany w tych kwestiach z GMem by podpowiedział. Ale mógłbyś też posilić się własną implementacją sprawdzania kolizji używając quadtree czy prostszym w implementacji spatial hashing (quadtree vs spatial hashing).

     

    PS. Możesz skrócić swój kod with(obj_potwor) do with(obj_potwory) ustawiając wrogom tego samego rodzica parent obj_potwory.

  16. Też raczej bym się tym nie przejmował, jak zrobisz interesującą grę, to więcej osób po prostu ją pobierze (co jest chyba ważniejsze). Zamiast tracić czas na implementację *byle* zabezpieczeń (które i tak można złamać) to poświęć ten czas na jakiś fajny feature w grze :)

     

    Ale żeby nie odchodzić od tematu kompletnie bez rozwiązania żadnego, to można zastosować jakieś proste triki jak np. oszukiwać gracza "hakera" o wartościach zmiennych, np. złoto w grze byłoby przesunięte o 3 bity, co dawałoby kompletnie inne wartości w Cheat Engine (potem trzeba tego pilnować przy odczycie/zapisie ilości złota!), albo użyć coś na wzór XOR-owania zmiennych przed i po zapisie w pamięci RAM. Albo jak gnysek wspomniał, użyj hashowania zmiennych kluczowych, ale też w pamięci gry, a jak ktoś Cheat Enginem spróbuje je zmienić, to hash się nie będzie zgadzał przed np. odczytem ilości złota albo dodatków. Wtedy będziesz mógł obsłużyć próbę "zhakowania" gry i odczytać te zmienne np. z zaszyfrowanego save-a.

  17. 3 godziny temu, Ice Cube napisał:

    ok juz mam prawie wszystko co chciałem wiedzieć,ale jeszcze zostaje to .SELF jak idzie to zastosować i po co to jest??

    W większości przypadków self praktycznie Ci się nie przyda, osobiście sam nie stosowałem tego rodzaju odwołania i nie potrafię powiedzieć, do czego byłoby to użyteczne, ponieważ i tak blok kodu jest wykonywany w kontekście self.

  18.  

    2 godziny temu, Ice Cube napisał:

    aha

    a co jeżeli w tym evencie collision pocisku będzie

    instance_create(x,y,obj_wybuch);z=5

     

    czyli kolejny obiekt -wybuch

    z będzie przypisane do czego? do pocisku,czy do wybuchu?

     

     

     

    Dnia 19.11.2018 o 16:22, I am Lord napisał:

    (...)

    Jednak to tylko jest mało użyteczne adresowanie po nazwie obiektu, dużo bardziej użyteczne jest adresowanie bezpośrednio po id instancji ale takie id najpierw trzeba znać.

    Musisz sobie zwracać uwagę na to jakie funkcje zwracają id instancji, do tych najpopularniejszych należą

    (...)

     

    3.

    
    // EVENT PRZYCISKU STRZAŁU
    var ID;
    ID = instance_create(x, y, obj_bullet);
    ID.z = z+64; 
    /* nowo utworzona instancja obiektu pocisków: obj_bullet będzie miała na start przypisaną wartość 'z' 
    do wartości 'z' gracza + wysokość gdzie znajduje się pistolet*/

     

     

    Mam wrażenie, że nie przeczytałeś tego co napisał @I am Lord

     

    Średnik ';' służy tylko do oddzielania wyrażeń, także jeżeli napiszesz dwa wyrażenia w jednej linii oddzielając je średnikiem, to nie ma to żadnego znaczenia, poza formatowaniem kodu. Także w Twoim przypadku zmienna 'z' będzie zmieniona w instancji obiektu obj_pocisk.

  19. Jeżeli w obiekcie obj_pocisk, stworzysz event Collision Event z obiektem obj_wrog, to ten kod:

    z = 10;

    Będzie dotyczyć instancji obiektu, w którym ten event (kod eventu) jest wykonywany. W tym wypadku będzie to instancja obiektu obj_pocisk. Aby odwołać się do instancji obiektu obj_wrog, używasz referencji other, tj.

    other.z = 10;

     

    Dodatkowo, w GM jak edytujesz skrypt, to w belce edycji jest taka opcja jak Applies To.

    skrypt.png.9acaadfd603982703256673fe4e88b7f.png

     

    Jeżeli zaznaczysz np. opcję Other, to cały kod będzie objęty tak jakby blokiem with:

    with(other) {
    	<kod_skryptu>
    }

    Nie polecam tej opcji, utrudnia tylko czytanie kodu, także warto mieć to na uwadze, żeby z tej opcji nie korzystać.

  20. Cześć, mi to wygląda na to, że Twój obiekt obj_krew nie ma zmiennej 'z'. Zmienne przeważnie tworzy się w Create Event, bo jest to event, który jest wykonywany wraz z funkcją instance_create i potem te zmienne są dostępne w referencjach na instancję zaraz po jej stworzeniu (np. "other.zmienna = 10", czy "var inst=instance_create(x,y,obj_krew); inst.zmienna = 10"). Artykułów dot. zmiennych na GMClan jest kilka, np. https://gmclan.org/index.php?czytajart=30 , przejrzyj listę artykułów na stronie https://gmclan.org/index.php?artykuly=17 mogą Ci się przydać w zrozumieniu zmiennych, argumentów, funkcji, operacji na nich itd.

     

    EDIT: v

  21. Jeżeli używasz tego kodu na obj_pocisk co Ci przesłałem, to ten pocisk nie używa zmiennej direction, dlatego direction zawsze będzie równe 0. Spróbuj odczytać kierunek pocisku od jego zmiennych motion_xyz. Jest to wektor lotu pocisku i można za jego pomocą policzyć wektor normalny w którym kierunku leci pocisk na płaszczyźnie 2D tj.:

    // normalizujemy wektor 2D motion_xy
    var motionlen = sqrt(other.motion_x*other.motion_x + other.motion_y*other.motion_y);
    var xdir = other.motion_x / motionlen;
    var ydir = other.motion_y / motionlen;
    
    // teraz przesuwamy wroga
    x += xdir * 5;
    y += ydir * 5;

     

×
×
  • Dodaj nową pozycję...