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. 24 minuty temu, I am Lord napisał:

    kurcze, wygląda na trudne coś jednak nic dziwnego że sam na to nie wpadłem. 

     

    Nie jest to tak trudne, po prostu wymaga trochę innego myślenia niż GM-owe surface'y, podsyłam też niewielki przykład:

    render-target.zip

     

    Nie jestem pewien, czy z forum działa pobieranie plików, ale wgrałem też na inny hosting:
    https://www23.zippyshare.com/v/xXpprq1O/file.html

     

    - LPM "niszczy" surface

     

  2. @I am Lord Jasne, w czołgach używam właśnie metody "surface" żeby połączyć elementy otoczenia w teksturę, ale w Godocie działa to odrobinę inaczej. Viewporty w Godot to nic innego jak OpenGL'owe Framebuffery, ale aktualizują się dopiero po wyświetleniu kolejnej klatki, nie da się rysować bezpośrednio na Viewportach ze względu na VisualServer, który odpowiedzialny jest za interpretowanie komend rysowania np. VisualServer.canvas_item_add_line jest podpięte bezpośrednio pod CanvasItem.draw_line (Node2D, Sprite etc.), dotyczy to wszystkich funkcji rysowania. Dlatego Godot też udostępnia taką funkcję jak VisualServer.request_frame_drawn_callback, dzięki niej można nasłuchiwać, kiedy Viewporty zostały zaktualizowane. Żeby nie aktualizować Viewporta co klatkę, polecam ustawić parametr Viewport.render_target_update_mode na UPDATE_ONCE (ten parametr potem jest ustawiany na UPDATE_DISABLED przez VisualServer, także każda kolejna aktualizacja wymaga przełączenie tego parametru). Zalecałbym też przełączenie tej flagi Viewport.render_target_v_flip na True, bo standardowo obraz będzie odwrócony. Nie zapomnij też o parametrze Viewport.size.

     

    Dodatkowo wspomnę, że Viewport.get_texture zwraca referencję na teksturę i wystarczy pobrać teksturę jednorazowo np. w funkcji _ready i narysować ją raz w metodzie _draw bez wywoływania metody update. Ale nie można wtedy usunąć Viewporta, bo usunie nam też teksturę.

     

    Jakbyś chciał "ukryć" Node'y z drzewa sceny nie musząc dodawać Viewport node'ów, możesz użyć bezpośrednio metod z VisualServer tj. VisualServer.viewport_createVisualServer.viewport_set_activeVisualServer.viewport_get_textureVisualServer.viewport_attach_canvasVisualServer.canvas_createVisualServer.canvas_item_set_parentVisualServer.free_rid itd. np. do obsługi roboczych Viewportów.

     

    Jeszcze wspomnę o OpenGL ES na mobilkach - Texture.get_data nie działa ze względu na limity samego OpenGL ES, które uniemożliwiają przepływu danych z GPU do RAM, także nie da się pobrać pikseli z Viewporta czy jakiejkolwiek tekstury do zmiennej, natomiast na desktopach działa bez problemu.

  3. @I am Lord Czołgi poza technicznym aspektem, to gameplay-owo dalej ssie, więc nie ma czego tak naprawdę pokazywać, ale pochwalę się chociaż dość szybkim sposobem na zniszczalny teren :P

     

    5Yaoe05.gif

     

    Działa stabilnie na androidzie, co mnie cieszy :)

     

    A tutaj dodatkowo podsyłam gifa, co u Papervanii się dzieje:

     

    pYjtZwH.gif

     

    Wyłączyłem DOF, bo półprzezroczyste obiekty nie zapisują się do depth buffora, co bluruje mi też te obiekty :/

  4. Ewentualnie możesz zachować opcję z solid, ale w obiekcie obj_pocisk, w Step Event musiałbyś dodać kod odpowiedzialny za sprawdzanie kolizji zamiast w Collision Event:

    // sprawdzamy kolizje z wrogami
    with (obj_wrog) {
        if (place_meeting(x, y, other.id)) {
            // kolizja nastąpiła, ale w 2D, sprawdźmy wysokość pocisku
            if (other.z < 24) {
                with (other) {
                    // niszczymy pocisk
                    instance_destroy();
                }
                // niszczymy wroga
                instance_destroy();
                
                // przerywamy blok 'with'
                break;
            }
        }
    }

     

  5. Sprawdziłem na GMS i sytuacja wygląda tak samo, najwyraźniej GameMaker jakoś inaczej obsługuje kolizje z obiektami z włączoną flagą solid. Jedyne rozwiązanie jakie tu widzę, to wyłączenie obiektom funkcji solid i obsłużenie wszystkich kolizji własnym kodem.

  6. @Nirvan Pełna profeska, gratuluję, a artykuł też spoko jest :)
    @Wojzax Fajnie to wygląda, a animacje też są spoko

     

    Ja natomiast żeby odskoczyć od czołgów, gdzie przepisałem system kolizji, zaimplementowałem FSM, zoptymalizowałem rysowanie (custom-izowany silnik Godota) i napisałem system rozgrywki turowej, to zająłem się nowym projektem na GitHub GameOff, Na razie mam jakiś biedny system platformówki:

     

    7DoVNoQ.gif

     

    Czołgi piszę w Godot Engine 3.0, natomiast ten projekt jest trochę taki eksperymentem z wersją 3.1. Generalnie rozgrywka będzie polegać na toczeniu walk między papierowymi wycinankami na stole. Jeszcze nie mam jakiegoś szczególnego opisu fabuły i samej rozgrywki, ale inspirować się będę trochę castlevanią.

     

    Tekstury darmowe z internetu (https://hdrihaven.com/ i https://freepbr.com). Postać znaleziona na google w agregacie śmiesznych obrazków, także nie wiem do kogo należy. Ale postać mam zamiar zmienić na swoją narysowaną, także nie zostanie na długo.

  7. GMowa opcja solid jest użyteczna ale jedynie do gier 2D, tutaj Ci się nie przyda więc pociskowi musisz sprawdzać kolizję inaczej. Najprostszym sposobem byłoby sprawdzanie kolizji w obj_pocisk Collision Event [Wróg  - Pocisk]. Możesz ustawić jednego rodzica wrogom, żeby nie tworzyć eventów dla każdego z ich rodzaju. W tym evencie dopiero sprawdzaj warunek:

    // w tym evencie kolizja nastąpiła (w 2D)
    // sprawdźmy, czy również występuje ona na tej samej wysokości co pocisk
    if (z > other.z) { // 'z' pocisku porównujemy z pozycją 'z' wroga
    	if (z < other.z + wysokosc) { // ^ ale uwzględniając wysokość wroga
            with(other) {
                instance_destroy(); // usuń wroga
            }
            instance_destroy(); // usuń pocisk
        }
    }

     

  8. Możesz użyć jakiejś biblioteki np. lxml i sparsować odpowiedź od serwera. np.
     

    from io import StringIO
    from lxml import etree
    from httplib import HTTPSConnection
    
    conn = HTTPSConnection("perelki.net")
    conn.request("GET", "/random")
    res = conn.getresponse()
    data = unicode(res.read(), "utf-8")
    
    parser = etree.HTMLParser()
    doc = etree.parse(StringIO(data), parser)
    root = doc.getroot()
    
    joke = root.xpath("body/div[@class='content']/div[@class='container']")[0]
    print '\n'.join(joke.xpath("text()")).strip()

     

  9. Spróbuj tej metody:

    // wektor patrzenia kamery
    var xx = global.camcos * 3;
    var yy = global.camsin * 3;
    
    // odwracamy wektor o 90 stopni
    var x1 = yy;
    var y1 = -xx;
    
    // odwracamy wektor ale o -90 stopni
    var x2 = -yy;
    var y2 = xx;
    
    // rysujemy nasz billboard
    d3d_draw_wall(x + x1, y + y1, 12, x + x2, y + y2, 0, tex, 1, 1);

     

    Generalnie możesz tym też zastąpić kod Draw Event w obj_pocisk.

  10. Z tego co widzę, nie aktualizujesz zmiennych globalnych, powinno to wyglądać tak:

     

    obj_cam i nasz Create Event:

    // włączamy tryb 3D
    d3d_start();
    
    // wysokość naszej kamery nad podłogą
    z = 10
    
    // deklarujemy naszą zmienną do obracania się bohatera góra/dół
    pitch = 0;
    
    // dodatkowo użyjemy wbudowanej zmiennej tj.
    // 'direction' do obracania się lewo/prawo
    
    // deklarujemy nasze zmienne globalne,
    // będziemy je aktualizować w Step Event
    global.camcos = 1; // tymczasowo nadajemy wartość 1, ponieważ cos(0rad) = 1
    global.camsin = 0; // natomiast sin(0rad) = 0
    global.campitch = 0;

     

    Create Event głównie służy nam tutaj do deklaracji naszych zmiennych, potem będziemy na nich operować w Step Event:

    // obracamy naszego bohatera
    // operujemy tutaj na stopniach (degrees)
    direction -= (display_mouse_get_x() - display_get_width()/2)/10;
    pitch -= (display_mouse_get_y() - display_get_height()/2)/10;
    pitch = max(min(pitch,89.9),-89.9); // 180 stopni góra/dół, -0.1 żeby uniknąć błedu z przeskokiem
    
    display_mouse_set(display_get_width()/2,display_get_height()/2);
    
    // aktualizujemy nasze zmienne globalne
    // tutaj operujemy już na radianach (radians)
    // dlatego użyjemy funkcji degtorad
    var dirrad = degtorad(direction);
    var pitchrad = degtorad(pitch);
    
    // poniższe równanie to nic innego jak zredukowane mnożenie macierzy obrotu
    // więcej możesz się dowiedzieć np. na http://planning.cs.uiuc.edu/node102.html
    // jak pomnożysz macierze [Pitch] * [Yaw] przez wektor |xyz|, otrzymasz poniższe równanie:
    global.camcos = cos(dirrad) * cos(pitchrad);
    global.camsin = -sin(dirrad) * cos(pitchrad);
    global.campitch = sin(pitchrad);

     

    W Draw Event bohatera ustawiamy projekcję naszej kamery:

    // ustawiamy projekcję rysowania
    d3d_set_projection(
        // pozycja 'oka' kamery
        x, y, z,
        // punkt, na który 'patrzy' nasza kamera
        // użyjemy tutaj naszych zmiennych globalnych i dodamy do aktualnej pozycji
        x + global.camcos, y + global.camsin, z + global.campitch,
        // wektor 'góry' naszej kamery
        // pozycja Z jest naszą górą
        0, 0, 1
    );

     

    Teraz nasz pocisk musi dowiedzieć się, w którym kierunku 'patrzy' nasz bohater i następnie będziemy na tej podstawie obliczać.

    obj_pocisk i w Create Event deklarujemy następujące zmienne:

    // wysokość pocisku od podłogi
    z = 10
    
    // tekstura pocisku
    tex = sprite_get_texture(spr_pocisk, 0);
    
    // budujemy wektor lotu pocisku
    // ze zmiennych tymczasowych:
    var dir_x = global.camcos;
    var dir_y = global.camsin;
    var dir_z = global.campitch;
    
    // normalizujemy wektor dir_xyz 
    var dir_len = sqrt(dir_x*dir_x + dir_y*dir_y + dir_z*dir_z);
    dir_x /= dir_len;
    dir_y /= dir_len;
    dir_z /= dir_len;
    
    // predkosc pocisku jako zmienna tymczasowa
    // posłuży nam do obliczenia wektora motion_xyz
    var spd = 5;
    
    // tworzymy zmienną lokalną jako wektor motion_xyz
    // poruszania sie naszego pocisku:
    motion_x = dir_x * spd; // 'wydłużamy' wektor o wartość zmiennej spd
    motion_y = dir_y * spd;
    motion_z = dir_z * spd;

     

    Step Event w pocisku jest trywialny i chyba nie wymaga tłumaczenia:

    x += motion_x;
    y += motion_y;
    z += motion_z;

     

    Teraz czas na rysowanie naszego pocisku w Draw Event:

    // obliczamy obrót w stronę kamery:
    var dirtocam = point_direction(obj_cam.x, obj_cam.y, x, y);
    var dirtocamrad = degtorad(dirtocam);
    
    var xx = -cos(dirtocamrad + pi/2) * 5;
    var yy = sin(dirtocamrad + pi/2) * 5;
    
    // rysujemy 'ścianę' odwróconą w stronę naszej kamery, tzw. billboard:
    d3d_draw_wall(
        // x1, y1, z1
        x - xx, y - yy, z + 5,
        // x2, y2, z2
        x + xx, y + yy, z - 5,
        // tekstura i jej powtarzanie
        tex, 1, 1
    );

     

  11. Ok, zapisz jeszcze sobie zmienną pitch do zmiennej globalnej w takim razie i przenieś z Create Event do Step Event. IMO bardziej poprawne byłoby liczenie wektora lookat tak:

    var dirrad = degtorad(direction);
    var pitchrad = degtorad(pitch);
    
    global.camcos = cos(dirrad) * cos(pitchrad);
    global.camsin = -sin(dirrad) * cos(pitchrad);
    global.campitch = sin(pitchrad);

     

    Żeby nie liczyć tego 2 razy, użyj tych globalnych zmiennych do funkcji d3d_set_projection, zamiast liczyć je w kilku miejscach.

     

    Teraz możesz użyć tego skryptu co Ci podesłałem do pocisku.

  12. Zachowaj wartości obracania do zmiennej globalnej, np.:

    // parametry kamery
    global.cam_lookat_x = cos(direction*pi/180);
    global.cam_lookat_y = -sin(direction*pi/180);
    global.cam_lookat_z = -sin(pitch*pi/180);
    
    // projekcja (zwroc uwage na + zamiast -)
    d3d_set_projection(x,y,z,x+global.cam_lookat_x,y+global.cam_lookat_y,z+global.cam_lookat_z,0,0,1);

     

    Następnie w Create Event pocisku zmodyfikuj kod:

    // (...)
    
    // budujemy wektor lotu pocisku:
    var dir_x = global.cam_lookat_x;
    var dir_y = global.cam_lookat_y;
    var dir_z = global.cam_lookat_z;
    
    // (...)

     

    To powinno wystarczyć

  13. Wordpress generalnie słabo sobie radzi z cache, a tego typu wtyczki mają dość duży wpływ na wydajność, ponieważ strony są generowane co każdy request. Nie wiem, jak to jest z Elementorem, ale Visual Composer nie tworzył cache dla wygenerowanej treści, co dało się odczuć przy bardziej złożonych layoutach, zwłaszcza, że używał metody shortcodes od Wordpressa zamiast jakiegoś sensownego silnika template np. Twiga.

     

    Polecam też sprawdzić, czy masz możliwość włączenia cache'owania opcode'ów, np. APC daje dość niezłego boosta.

  14. Jest to niemożliwe z poziomu zwykłej wtyczki. Chyba, że masz na serwerze zainstalowany dodatek runkit (dla np. runkit_function_rename) bądź APD (rename_function), wtedy można by podmienić owe funkcje wordpressa i stworzyć skrypt monitorujący zasoby.

     

    Pracowałem głównie z Visual Composerem i trochę z Beaver Builderem, natomiast Elementora nie ruszałem, przynajmniej jeszcze.

  15. Cześć, WordPress nie posiada hook-ów umożliwiających monitorowanie działania każdej z wtyczek, dlatego zużycie CPU czy pamięci pojedynczej wtyczki jest prawdopodobnie niemożliwa (nie do końca niemożliwa, ale wymagałoby to ingerencji w core wordpressa ;) ). IMO wystarczyłoby monitorowanie wywołań akcji (do_action i apply_filters) każdej z wtyczek i np. liczyć czas, ile zajmują poszczególne akcje (np. za pomocą microtime) i ile pamięci zajmują (np. memory_get_peak_usage), niestety bez dostępu do shella nie można sprawdzić statusu CPU. Ew. modyfikacja pluginu Elementor i liczenie ww. wartości.

  16. Cześć, do tego typu obliczeń pomocne są wektory. W Create Event pocisku stwórz zmienne wektora odpowiedzialnego za kierunek ruchu np.

    // (...) pozostałe zmienne pocisku jak i pozycja 'z'
    
    // budujemy wektor lotu pocisku:
    var dir_x = global.camcos;
    var dir_y = global.camsin;
    var dir_z = global.campitch;
    
    // normalizujemy wektor dir_xyz 
    var dir_len = sqrt(dir_x*dir_x + dir_y*dir_y + dir_z*dir_z);
    dir_x /= dir_len;
    dir_y /= dir_len;
    dir_z /= dir_len;
    
    // predkosc pocisku
    var spd = 5;
    
    // tworzymy wektor poruszania sie naszego pocisku jako motion_xyz:
    motion_x = dir_x * spd;
    motion_y = dir_y * spd;
    motion_z = dir_z * spd;

    Nie jestem pewien, czy Twoje obj_camera.pitch jest rezultatem funkcji trygonometrycznych, podeślij może kod odpowiedzialny za obracanie się kamery góra dół to ew. poprawię powyższy kod.

    Następnie w Step Event dodajemy nasze zmienne motion_xyz do pozycji pocisku:

    x += motion_x;
    y += motion_y;
    z += motion_z;

     

  17. Niestety nie posiadam GM 8.0, musiałbyś otworzyć projekt w GM Studio i skopiować kod który Cię interesuje.

     

    EDIT: Możesz też zmienić nazwę pliku z .gmz na .zip i otworzyć archiwum. Pliki .gmx to XMLe, także jak otworzysz je edytorem tekstowym to możesz podejrzeć parametry obiektów i ich kod. Możesz w ten sposób skopiować kod obiektu objTorchLayer odpowiedzialny za fake-owe światło.

  18. Super, że działa, a co do tych dwóch linijek kodu:

    var factor = sin((distance / start_distance) * pi);

    Jak podzielimy start_distance przez distance, otrzymamy wartości 0 - gdy czar jest najdalej od gracza i 1 - gdy pocisk jest najbliżej gracza, jest to funkcja liniowa i wygląda tak:


    image.png.499cd0dc351e7d1c73090add5a1fc626.png

    Jak widzisz, funkcja liniowa nie jest za ciekawa, ale przy użyciu sinusa, można uzyskać taki efekt:


    image.png.376a084cdf06dbc13ca136a80f2df9d2.png
     

    Funkcja sin jest funkcją trygonometryczną i przyjmuje wartość w radianach, powyższy wykres przedstawia wartości od 0 stopni do 360 stopni, odpowiednio w radianach będzie to 0 radianów i 2*PI radianów. Wartość tej poprzedniej liniowej funkcji (od 0 do 1) mnożymy przez wartość PI (180 stopni, od 0 do liczby PI), wstawiamy tę wartość do funkcji sin i otrzymujemy interesujący nas łuk:

     

    image.png.f0f96dfcbf5f074f2171a5aa0af8899b.png

     

    Funkcja sinusowa zwróci nam wartość od 0 do 1 - gdzie 0 jest na początku łuku, 1 jest środkiem i 0 również występuje na końcu łuku.

     

    Jak widzisz, funkcję liniową zamieniliśmy na krzywiznę. Teraz przejdźmy do drugiej funkcji:

    var maxlen = sqr(log2(start_distance));

    Ta funkcja to nic innego jak logarytm naturalny do kwadratu. Generalnie to tę funkcję można zmieniać wedle uznania. Odpowiada ona za maksymalne wychylenie na naszym łuku. Jeżeli gracz będzie bliżej czarownika, to odchylenia będzie mniejsze, jak się gracz oddali od niego, to zwiększymy odpowiednio maksymalne odchylenie i pocisk będzie leciał po większym łuku. Funkcja logarytmiczna do kwadratu wygląda tak:


    image.png.c0d5edb63707dc09eb589f8cc1036592.png

     

    Możesz też np. użyć pierwiastka sqrt, której wykres funkcji wygląda tak:

     

    image.png.8071aa49ca2d1d64a55fe1796797be0a.png


      

×
×
  • Dodaj nową pozycję...