Skocz do zawartości

Grupowanie obiektów


Rekomendowane odpowiedzi

Tak jak w temacie, da się jakoś grupować obiekty w GM inaczej niż za pomocą "child-parent"? Ten system parentowania działa spoko, ale co w przypadku jak chcę mieć dwie grupy np: kwadraty(obj_player, obj_kwadrat) i postacie (obj_player, obj_enemy)? obj_player może mieć tylko jednego parenta, więc nie mogę go podpiąć pod jakiś jeden obiekt "kwadraty" i drugi "postacie". Chciałbym dość często wykonywać kod dla poszczególnej grupy obiektów, a obecny system parentowania nie pozwoli mi tworzyć takich grup. Wiem, że parent może mieć kolejnego parenta, ale to nie jest dobre jak chcesz mieć grupy, które mają jeden obiekt, a innego nie mają. Da się jakoś inaczej? Ja wpadłem na taki pomysł tworzenia własnych funkcji/skrpytu, który zwraca 1 wymiarowego arraya i później przepuszczam pętlę razy ilość tych rzeczy w tym array'u.

 

Wcześniej miałem taki kod i używałem obiektu "par_entity", który był taką jedną grupą. Niestety ta grupa zawierała w sobie obiekt gracza "obj_player", który występował też w innej grupie i jak się łatwo domyślić, to ten sposób powodował, że nie mogłem w różnych miejsach w kodzie odnosić się do obiektów typu par_

Cytuj

var list = ds_list_create();
var instances = instance_place_list(x, y-1, par_entity, list, false);
    
if (instances > 0 ){
    for (var i = 0; i < instances; i++){
        with (list[|i]){
        if (on_solid == 0){    
            on_lift = true;
            grounded = jump_buffer;
            vspd = 0;
            if (other.vdir == 1){
                    y = other.y+other.spd-1;
            } else if (other.vdir == -1){
                    y = other.y-other.spd-1;
            }
        }    
        }
    }
}    
ds_list_destroy(list)

 

Z czasem zmieniłem ten kod na coś takiego:

 

Cytuj

var group = g_entity();
var length = array_length_1d(group);

for (var i = 0; i < length; i++){
    
    var list = ds_list_create();
    var instances = instance_place_list(x, y-1, group[i], list, false);
    
    if (instances > 0 ){
        for (var j = 0; j < instances; j++){
            with (list[|j]){
            if (on_solid == 0){    
                on_lift = true;
                grounded = jump_buffer;
                vspd = 0;
                if (other.vdir == 1){
                        y = other.y+other.spd-1;
                } else if (other.vdir == -1){
                        y = other.y-other.spd-1;
                }
            }    
            }
        }
    }    
    ds_list_destroy(list);
}    

Działa idealnie, choć nie wiem czy to nie odbija się jakoś znacząco na wydajności.

 

Mój problem polega na tym, że nie wiem co wyczarować w kodzie, gdy chcę zrobić podobny myk w takim przypadku:

Cytuj

if collision_line_list(x, y, x+sight_range, y, par_spotted, false, true, spotted_list, true){  //seeing closest object
    var closest = spotted_list[|0];
    spotted_point = closest.x-x;
    ds_list_clear(spotted_list);
    if (instance_exists(obj_player)) && (closest == obj_player.id) && !(status = state.attack){ // change state to attack
        status = state.attack;
        angry = 30;
        attack_buffer = 60;
    } else {               // not seeing
    spotted_point = 0;    
    }
}

 

Zamiast par_spotted chciałbym odnieść się jakoś inaczej do wyznaczonej grupy obiektów. Mógłbym pokombinować tak jak wcześniej z grupą w array'u i pętlami. W przypadku jednak użycia jakiejkolwiek funkcji typu collision_list potrzebuję utworzyć listę wszystkich id występujących kolizji i posortowania ich w jednym kroku. Jakbym nie kombinował to po zmianie ten kod nie działa tak jak trzeba i obiekt zmienia swój status na attack, nawet jeśli gracz jest za ścianą. W oryginalnej formie kod działa perfekcyjnie, ale wtedy jestem zmuszony do używania parentowania, a chciałbym bez tego, bo w przyszłości i tak nie poparentuje tak swoich "grup" bym miał w nich te obiekty, które chcę. Da się jakoś inaczej?

Odnośnik do komentarza
Udostępnij na innych stronach

W zasadzie nie opisałeś problemu, a problem ze swoim rozwiązaniem, które z założenia może być niepoprawne. W GMie, tak jak w wielu innych językach programowania, paradygmat pojedynczego dziedziczenia jest dość powszechnie stosowany. Nie powinieneś myśleć w kategoriach wielodziedziczenia tak jak chociażby pozwala na to C++ tworząc logikę gry. Nawet w C++ wielodziedziczenie jest dość powszechnie uznawane jako złe podejście do rozwiązania większości problemów, częściej stosuje się po prostu kompozycje obiektów - proste i efektywne.

 

Z tego co widzę, tworzysz jakiś obiekt "system" odpowiedzialny za obsługę innych instancji w jakiejś dziwacznej pętli - a w zasadzie nie widzę tutaj sensownego wyjaśnienia, dlaczego. A nie możesz przenieść po prostu logiki do tych obiektów? Możesz chociażby napisać skrypty scr_unit_* i komponować z nich obiekty, np.:

/// scr_unit_create(team)
team = argument0;
some_crazy_variable = 10;
/// scr_unit_step()
some_crazy_variable++;
/// scr_unit_draw()
draw_self();

 

Potem w obiektach np. obj_enemy, obj_player wykonujesz odpowiednio skrypty scr_unit_create w Event Create, scr_unit_step w Event Step i scr_unit_draw w Event Draw.

Odnośnik do komentarza
Udostępnij na innych stronach

4 godziny temu, Konrad-GM napisał:

W zasadzie nie opisałeś problemu, a problem ze swoim rozwiązaniem, które z założenia może być niepoprawne. W GMie, tak jak w wielu innych językach programowania, paradygmat pojedynczego dziedziczenia jest dość powszechnie stosowany. Nie powinieneś myśleć w kategoriach wielodziedziczenia tak jak chociażby pozwala na to C++ tworząc logikę gry. Nawet w C++ wielodziedziczenie jest dość powszechnie uznawane jako złe podejście do rozwiązania większości problemów, częściej stosuje się po prostu kompozycje obiektów - proste i efektywne.

 

Z tego co widzę, tworzysz jakiś obiekt "system" odpowiedzialny za obsługę innych instancji w jakiejś dziwacznej pętli - a w zasadzie nie widzę tutaj sensownego wyjaśnienia, dlaczego. A nie możesz przenieść po prostu logiki do tych obiektów? Możesz chociażby napisać skrypty scr_unit_* i komponować z nich obiekty, np.:


/// scr_unit_create(team)
team = argument0;
some_crazy_variable = 10;

/// scr_unit_step()
some_crazy_variable++;

/// scr_unit_draw()
draw_self();

 

Potem w obiektach np. obj_enemy, obj_player wykonujesz odpowiednio skrypty scr_unit_create w Event Create, scr_unit_step w Event Step i scr_unit_draw w Event Draw.

 

Nie do końca rozumiem jak to ma działać. Mam sobie stworzone trzy obiekty: obj_player, obj_enemy, obj_pietruszka. Wstawiam sobie je do room editora. W create event dla każdego z nich odpalam mój (twój) skrypt scr_unit_create(grupa). Nie wiem jaki cel jest pozostałych skryptów i po co miałbym je wywoływać? Teraz wszystkie moje trzy obiekty mają zmiennę "team" która równa się grupa? Hmmm, czyli scr_unit_create("grupa"), dobra teraz mam zmienną team ze stringiem "grupa". Co to mi daje? Chcę gdzieś w kodzie odpalić jakąś funkcję i zamiast odwołać się do danego obiektu odwołuję się do grupy np.

 

Cytuj

if collision_line_list(x, y-hot_spot, x+sight_range, y-hot_spot, "grupa", false, true, spotted_list, true){  //seeing closest object
    var closest = spotted_list[|0];
    spotted_point = closest.x-x;
    ds_list_clear(spotted_list);
    if (instance_exists(obj_player)) && (closest == obj_player.id) && !(status = state.attack){ // change state to attack
        status = state.attack;
        angry = 30;
        attack_buffer = 60;
    } else {               // not seeing
    spotted_point = 0;    
    }
}

par_spotted zamieniłem na "grupa", ale to bez sensu, to odwołam się zmiennej team

 

Cytuj

if collision_line_list(x, y-hot_spot, x+sight_range, y-hot_spot, team, false, true, spotted_list, true){  //seeing closest object
    var closest = spotted_list[|0];
    spotted_point = closest.x-x;
    ds_list_clear(spotted_list);
    if (instance_exists(obj_player)) && (closest == obj_player.id) && !(status = state.attack){ // change state to attack
        status = state.attack;
        angry = 30;
        attack_buffer = 60;
    } else {               // not seeing
    spotted_point = 0;    
    }
}

Ok, to nadal bez sensu. Konrad-GM, nadal nie rozumiem co te twoje skrypty robią i jak mają rozwiązać mój problem? W funkcjach zamiast odnosić się do jednego obiektu chcę do pewnej grupy obiektów. Co bym nie kombinował z twoimi skryptami i co nie ładował do pierwszego argumentu jak nazwa obiektu, czy co, to co ja zrobię z tą zmienną? Mogę zapisać kilka id do jednej zmiennej? Jeśli możesz to podaj jakiś przykładowy kod z zastosowaniem tego twojego sposobu.

Odnośnik do komentarza
Udostępnij na innych stronach

Moje skrypty są tylko przykładem kompozycji, pomysłem jak można rozwiązać problem z wielokrotnym dziedziczeniem. Nie bardzo rozumiem, dlaczego nie możesz wykonywać skryptu:

if collision_line_list(x, y-hot_spot, x+sight_range, y-hot_spot, "grupa", false, true, spotted_list, true){  //seeing closest object
    var closest = spotted_list[|0];
    spotted_point = closest.x-x;
    ds_list_clear(spotted_list);
    if (instance_exists(obj_player)) && (closest == obj_player.id) && !(status = state.attack){ // change state to attack
        status = state.attack;
        angry = 30;
        attack_buffer = 60;
    } else {               // not seeing
    spotted_point = 0;    
    }
}

 

W każdym obiekcie z osobna. Dodatkowo żeby sprawdzić "grupę" obiektów, to możesz zrobić coś takiego:

var spotted_list = ds_list_create();
var instances = collision_line_list(x, y - hot_spot, x + sight_range, y - hot_spot, par_entities, false, true, spotted_list, true);
var enemy_spotted = false;

for (var i = 0; i < instances; i++) {
    var inst = spotted_list[| i];

    // sprawdzamy przynaleznosc do teamu
    if (inst.team == "red" && status != state.attack) {
        enemy_spotted = true;
        spotted_point = inst.x - x;
        status = state.attack;
        angry = 30;
        attack_buffer = 60;
        break;
    }
}

if (!enemy_spotted) {
    spotted_point = 0;
}

ds_list_destroy(spotted_list);

 

Jeżeli boisz się o optymalizację, możesz ten fragment kodu zamiast wykonywać w Step Event, np. ustawić Infinite Alarm (alarm ustawiający sam siebie) i np. uruchamiać ten skrypt co jakiś czas np. 100ms. (16ms to jedna klatka przy 60fps)

 

Jeżeli chcesz dodać kilka team-ów, możesz zamiast zmiennej team dodawać zmienną teams jako tablicę, np.

teams[0] = "red";
teams[1] = "green";

 

Stworzyć skrypt do sprawdzania zawartości tablicy:

/// scr_array_includes(arr, value)
var arr = argument0;
var len = array_length_1d(arr);

for (var i = 0; i < len ; i++) {
    if (arr[i] == argument1) {
        return true;
    }
}

return false;

 

I wtedy powyższy skrypt z collision_line_list można przerobić na:

var spotted_list = ds_list_create();
var instances = collision_line_list(x, y - hot_spot, x + sight_range, y - hot_spot, par_entities, false, true, spotted_list, true);
var enemy_spotted = false;

for (var i = 0; i < instances; i++) {
    var inst = spotted_list[| i];

    // sprawdzamy przynaleznosc do teamu (z grupy teamow)
    if (scr_array_includes(inst.teams, "red") && status != state.attack) {
        enemy_spotted = true;
        spotted_point = inst.x - x;
        status = state.attack;
        angry = 30;
        attack_buffer = 60;
        break;
    }
}

if (!enemy_spotted) {
    spotted_point = 0;
}

ds_list_destroy(spotted_list);

 

Odnośnik do komentarza
Udostępnij na innych stronach

Problem rozwiązałem już parę dni temu. Jeden z użytkowników GMclanu podpowiedział mi alternatywne podejście do tematu. Co prawda problem tworzenia grup nie odpadł całkowicie i bardziej się rozwiązał problem z tym konkretnym przypadkiem.

 

Co do kodu powyżej, to podoba mi się pomysł, sprawdzaniem konkretnej zmiennej u danej istancji, ale niektórych rzeczy nie rozumiem. Możliwe, że się nie zrozumieliśmy dokładnie i stąd pewne nieścisłości.

 

Cytuj

var spotted_list = ds_list_create();
var instances = collision_line_list(x, y - hot_spot, x + sight_range, y - hot_spot, par_entities, false, true, spotted_list, true);
var enemy_spotted = false;

for (var i = 0; i < instances; i++) {
    var inst = spotted_list[| i];

    // sprawdzamy przynaleznosc do teamu (z grupy teamow)
    if (scr_array_includes(inst.teams, "red") && status != state.attack) {
        enemy_spotted = true;
        spotted_point = inst.x - x;
        status = state.attack;
        angry = 30;
        attack_buffer = 60;
        break;
    }
}

if (!enemy_spotted) {
    spotted_point = 0;
}

ds_list_destroy(spotted_list);

 

Ten kod sprawdza wszystkie instancje, które kolidują z linią i jeśli choć jedna z nich będzie danego typu, to zmienia się state machine obiektu, który ma ten kod. Jak widać w moim poprzednim kodzie, używałem sortowania listy, by zobaczyć czy dany obiekt jest widoczny, czy nie (jeśli obiekt gracza był pierwszy na liście, to oznaczało, że nie jest on za żadną osłoną). Mój wcześniejszy kod działał idealnie, ale wtedy byłem zmuszony do używania jakiegoś parenta, a chciałem znaleźć jakieś alternatywny sposób do tworzenie grup. Z parentami czasami dochodzi do sytuacji, gdzie chcemy mieć jeden obiekt w kilku grupach, a system parentowania to wyklucza. Znalazłem własny sposób by tworzyć własne skrypty przechowując grupę w tablicy. Później odpalałem pętlę razy ilość obiektów/pozycji tablicy. Ktoś mi doradził, że to głupie i zaproponował banalniejszy rozwiązanie. Twoje rozwiązanie jest nawet w pewnym sensie podobne do mojego, ale bawienie się w takie pętle zostało mi odradzone. Tak, czy siak dzięki za odzew, jestem wdzięczny za każdą radę od GMclanowiczów.

Odnośnik do komentarza
Udostępnij na innych stronach

12 minut temu, Temporal napisał:

Ten kod sprawdza wszystkie instancje, które kolidują z linią i jeśli choć jedna z nich będzie danego typu, to zmienia się state machine obiektu, który ma ten kod. Jak widać w moim poprzednim kodzie, używałem sortowania listy, by zobaczyć czy dany obiekt jest widoczny, czy nie (jeśli obiekt gracza był pierwszy na liście, to oznaczało, że nie jest on za żadną osłoną).

 

To super, że rozwiązałeś problem. Tak dodam jeszcze, że można sprawdzić, czy inst[ i ] nie jest ścianą (np. czy należy do grupy/teamu "walls"), jeżeli tak, to można przerwać pętlę break-em.

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ę...