Autor Wątek: SI zbiór / wiele przeciwników  (Przeczytany 1990 razy)

Offline Nsuidara

  • Użytkownik
    • Site

# Czerwiec 24, 2010, 00:02:28
Wyobrażam sobie zarys.
- reprezentacja lista/tablica... etc.. przecników
- każdy przeciwnik ma swoje SI (czy to automaty czy sieć... mało istotne)


Mam jedno pytanie jak jest to zorganizowane dla wielu przeciwników w sensie.
np: Mamy Skrypt AI
Kod: (cpp) [Zaznacz]
Start()
{
- ustawienie parametrów poczatkowych
- ...
- Mozg()
};

Mozg()
{
- ...
while(poki zyje)
{
//SI....
}
};
...
...
Załóżmy że każdy NPC ma reprezentacje skryptową.
Więc jak pogodzić ten SI.. wszystkich NPCów w sensie, żeby bez zarzutu się wykonywały ?


Rozwiązanie mi się widzi :
- kontroler pozycji linijki wykonanego kodu.
- dla każdego SI jest określony czas wykonania np 10ms - wiec jak napotka na pętle
while()
{-poczatek

}-koniec

i jak będzie "-koniec" to sprawdza kontroler czy jeszcze SI posiada swój czas jeżeli tak wykonuje jeszcze raz (o ile spełniony jest warunek) jak nie idzie dalej....
- po upłynięciu czasu przejdź do kolejnej jednostki,

Więc proszę o podpowiedz.
Czy w dobrym kierunku myślę czy też może i nie ?
Jakie pojęcia są przydatne (nie samego SI) a zarządzaniem wielu "Skryptów" etc ?


ps. sporo szukałem, ale nigdzie nie mogę znaleźć "formy tej co potrzebuję" a może to raczej z "braku wiedzy nazewnictwa" - bo wszystkie problemy u ludzi z programowania się biorą że nie mogą znaleźć tego co chcą ponieważ nie umieją to poprawnie technicznie określić :D
« Ostatnia zmiana: Czerwiec 24, 2010, 00:04:05 wysłana przez Nsuidara »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xirdus

  • Redaktor

# Czerwiec 24, 2010, 17:13:44
Ja to widzę tak: każdy mózg na wejściu dostaje stan obecny (niezmieniony przez pozostałe skrypty) i daje na wyjściu zmiany tego stanu. Kiedy skończą się wykonywać wszystkie skrypty, lecisz z logiką, fizyką, renderem i Bóg wie jeszcze czym, stateBefore = stateAfter i czekasz do końca klatki. Proste.

Offline Nsuidara

  • Użytkownik
    • Site

# Czerwiec 24, 2010, 19:12:50
Troszeczkę o co mi innego chodzi :D

Struktura SI (AI):
Kod: (cpp) [Zaznacz]
include "..."
// ...
var me = Self();
program StartOrganizmu()
// ...
Mozg();
endprogram

function Mozg()
// ...
while(!me.dead)
// ...
me.speed = ....;
var ev := event;
// ...
case(ev.type)
....
endcase
// ...
endwhile
// ...
endfunction
- gdzie //... to kod lub zmienne... etc...

- gdzie Self() zwraca ID / lub odnośnik do "Siebie" w sensie że te SI mogą wykorzystywać różne stworzenia i trzeba wiedzieć o kogo chodzi...

- kiedy stworzenie ma 0 hp wywołuje się Skrypt Death(); // po to jest while(me.dead) aby sprawdzić czy nadal ma SI sie wykonywać...
Ktoś powiedział by po co niech skrypt zawsze sie od nowa wykonuje, ale StartOrganizmu to ustawienie parametrów przy narodzinach... etc... (można by oddzielną funkcje zrobić jak dla Death(); ale mi się tu nie widzi.

- Innym słowem Organizacja SI może mieć wiele sposobów :
Zbiór funkcji dla jegnego SI :
- Poczęcie
- Mózg
- Smierć
I zależnie od czegoś jest wywoływana dana funkcja.
I wiele innych metod np takich jak ja zaprezentowałem (Jest Organizm od razu narodzinami i kontrolą mózgu + funkcja Death() ogólna)

No i do sedna sprawy :
Ciekawi mnie sposób jak jest realizowany (Kontroler) wszystkich Organizmów.
Tzn. Mamy 4 Psy i 5 Kotów
No i teraz jak kontroler zarządza kodem AI (SI) tzn jak napotka na pętlę while(!dead) - nie może ciągle się wykonywać ta pętla, bo co z pozostałymi 3 psami i 5 kotami nie wliczając z innymi "Skryptami" czy też rzeczami...

Jedynie do głowy mi pojęcie przychodzi "Procesy" ponieważ też "jakiś kwant czasu dla danego procesu jest przyznawany na wykonanie" i potem leci na koniec kolejki etc...
« Ostatnia zmiana: Czerwiec 24, 2010, 19:22:05 wysłana przez Nsuidara »

Offline Krzych

  • Użytkownik

# Czerwiec 24, 2010, 19:19:49
Zainteresuj sie moze mikrowatkami, bodajze python to ma (bylo o tym w ktorejs czesci Perelek). Wyglada to podobnie do tego co chcesz uzyskac:
while(!end)
{
//....
yield;
}
Konkretna funkcja mysli ze ma caly czas dla siebie, ale podczas wykonania yield przechodzi do innej funkcji, i tak dalej. W koncu wykonanie powraca do tej samej funkcji w miejscu, w ktorym sie ostatnio skonczyla.

Offline Nsuidara

  • Użytkownik
    • Site

# Czerwiec 24, 2010, 19:32:49
Ciekawe innym słowem mój problem jest "błachy" odnośnie kontrolowania wielu jednostek przeciwników ? ^^"
Wędzie pisze wszystko odnośnie 1 przeciwnika a jak jest kontrolowani wszyscy przeciwnicy ? (w skryptach)

Offline Blackmoore

  • Użytkownik

# Czerwiec 24, 2010, 22:39:48
masz klasę przeciwnik i tworzysz nowy obiekt, który ma określone zachowanie

Offline Nsuidara

  • Użytkownik
    • Site

# Czerwiec 24, 2010, 23:19:03
Nikt mi nie odpowiedział na pytanie...
Start Gry
-> Loading
-> Wczytanie mapy
...
-> Wczytanie przeciników Jamnik, Wilk, Kot
-> Ustawienie parametrów dla danego przeciwnika:
-> HP
-> ...
-> Skrypt SI
-> Wczytanie parametrów graczy
-> ...
-> Utworzenie obiektów...
-> ...
-> ...
-> Rozpoczęcie rozgrywki while(1)
-> ...
-> Logika
-> ...
-> SI
-> Uruchamianie SI danego stworzenia (skrypt)
-> Start Organizmu -> wywołanie Mózgu()
-> Mózg (mamy tu pętle while(me.dead) ...EVENTY... endwhile - wiec tutaj program by się zatrzymał ...
-> ...
-> Rysowanie
-> ...
- SI jest uznawane jako zwykły skrypt (jak np w Lau),
- Jeśli chcemy zęby SI pracowało ciągle dla NPCa aby reagował musimy dać pętlę while(me.dead) ... endwhile, lecz niestety jak tak zrealizujemy to w normalnym języku skryptowym taka realizacja będzie działać normalnie jak zwykła pętla, a nam potrzeba coś co po chwili przeleci też przez XXX innych przeciwników... sprawdzi stany gracza... i narysuje nam a nie zatrzyma się w miejscu.
Więc jedną z możliwych realizacji jest danie kwantu czasu i po tym zapamiętać przy której linijce jest i potem od tej startować.

- Inny sposób realizacji : dać funkcje Poczęcie, Mózg, Śmierć i wywoływać podczas działania SI sam Mózg (póki żyje jednostka) więc w tej funkcji Mózg już więcej nam nie trzeba "while(me.dead)"

- I wiele innych sposobów

// mało istotne ale :
EVENTY - to coś co jest wysyłane z zewnątrz skryptu np: jak gracz kliknie na przeciwnika (to jest wysyłany komunikat o ataku )etc...


Jestem ciekawy jak to zrealizować.

Jak ktoś nadal nie rozumie to niech sobie przejrzy jak jest zbudowane skrypty : http://polserver.com/ AI dla npców tutaj to tylko przykład ^^ A może lepiej jak dam skrypt który kiedyś napisałem odnośnie Stworzeń :
Kod: (cpp) [Zaznacz]
use npc;
include ":attributes:include/attributes";
include ":attributes:include/AI/Funkcje";

var RASY_CFG := ReadConfigFile(":StarterPostaci:config/rasy");
var NPC_CFG := ReadConfigFile("::npcdesc");
var me := Self();
var cme := NPC_CFG[me.NpcTemplate];

program StartOrganizmu()
if(!GetEquipmentByLayer(me,21))
SetObjProperty(me,"P",{me.x,me.y});
Loot();
if(cme.UWyglad)
U_Wyglad();
endif
endif
if(CInt(cme.SpeedMove) < 200)
me.run_speed := 200;
else
me.run_speed := CInt(cme.SpeedMove);
endif
EraseObjProperty(me,"#Uciekam");
EraseObjProperty(me,"--Atakuje");
Wylacz_Eventy();
EventyWL();
Set_Event_Queue_Size(100);
Mozg();
endprogram

function Mozg()
var Typ := Cstr(cme.Typ_Istoty);
var ev := os::wait_for_event(0);
var Czekanie := 180;
        while(me && !me.dead)
EraseObjProperty(me,"--Atakuje");
if(cme.Normal_Sound && RandomInt(8) == 0)
var Dzwiek := SplitWords(CStr(cme.Normal_Sound));
Dzwiek := CInt(Dzwiek[(RandomInt(Dzwiek.size())+1)]);
PlaySoundEffect(me,Dzwiek);
endif
if(GetObjProperty(me,"Tryb_Zycia")  && me.warmode == 0)
var T_Z := GetObjProperty(me,"Tryb_Zycia");
if(Tryb_Noc_Dzien(T_Z[1],T_Z[2],T_Z[3],T_Z[4]))
SystemEventow(1);
endif
endif
if(CStr(cme.Specialne_Ataki)["Atak-z-ukrycia"])
me.concealed := 2;
endif
foreach osoba in ListMobilesInLineOfSight(me,CInt(cme.Zasieg_Widzenia))
var Atakujesz := GetObjProperty(osoba,"--Atakuje");
if(!osoba.NpcTemplate && (Typ == "Zwierze_Agresywne" || (Typ == "Zwierze_Normalne_Agresywne" && CInt(Atakujesz[1]) == me.serial)))
Start_Walki(osoba);
elseif(!osoba.NpcTemplate && (Typ == "Zwierze_Plochliwe" && CInt(Atakujesz[1]) == me.serial))
Ucieczka(osoba);
endif
endforeach
Czekanie := 180;
foreach osoba in ListMobilesInLineOfSight(me,CInt(cme.Zasieg_Widzenia))
if(!osoba.NpcTemplate)
Czekanie := 1;
endif
endforeach
ev := os::wait_for_event(Czekanie);
                if(CInt(cme.ObjType) == 207 && GetObjProperty(me,"ZebranyMaterial")
&& (CInt(GetObjProperty(me,"ZebranyMaterial"))+600) <= ReadGameClock())
me.graphic := CInt(cme.ObjType);
EraseObjProperty(me,"ZebranyMaterial");
elseif(cme.ObjType == 209 && GetObjProperty(me,"ZebranyMaterial")
&& (CInt(GetObjProperty(me,"ZebranyMaterial"))+600) <= ReadGameClock())
me.graphic := CInt(cme.ObjType);
EraseObjProperty(me,"ZebranyMaterial");
elseif(cme.ObjType == 0xD8 && GetObjProperty(me,"ZebranyMaterial")
&& (CInt(GetObjProperty(me,"ZebranyMaterial"))+600) <= ReadGameClock())
EraseObjProperty(me,"ZebranyMaterial");
endif
////////////////////////////////////////////////
if(ev && Typ == "Zwierze_Plochliwe")
case(ev.type)
SYSEVENT_LEFTAREA:
SYSEVENT_ENTEREDAREA:
var op := ev.source;
if(CheckLineOfSight(me,ev.source) == 1 && op.serial != me.serial && op.cmdlevel < 1
&& (!op.NpcTemplate || GetObjProperty(op,"Wlasciciel")))
Ucieczka(op);
endif
break;
SYSEVENT_ENGAGED:
SYSEVENT_DAMAGED:
if(ev.source.cmdlevel < 1)
Ucieczka(ev.source);
endif
break;
endcase
////////////////////////////////////////////////////////
elseif(ev && Typ == "Zwierze_Normalne")
case(ev.type)
SYSEVENT_DAMAGED:
SYSEVENT_ENGAGED:
Ucieczka(ev.source);
break;
endcase
//////////////////////////////////////////////////////////////////
elseif(ev && Typ == "Zwierze_Normalne_Agresywne")
case(ev.type)
SYSEVENT_ENGAGED:
SYSEVENT_DAMAGED:
if(ev.source.cmdlevel < 1)
Start_Walki(ev.source);
endif
break;
endcase
//////////////////////////////////////////////////////////////////
elseif(ev && Typ == "Zwierze_Agresywne")
case(ev.type)
SYSEVENT_LEFTAREA:
SYSEVENT_ENTEREDAREA:
var op := ev.source;
if(CheckLineOfSight(me,ev.source) == 1 && op.serial != me.serial && op.cmdlevel < 1
&& (!op.NpcTemplate || GetObjProperty(op,"Wlasciciel")))
Start_Walki(op);
endif
break;
SYSEVENT_ENGAGED:
SYSEVENT_DAMAGED:
if(ev.source.cmdlevel < 1)
Start_Walki(ev.source);
endif
break;
endcase
endif
///////////////////////////////////////
if(GetObjProperty(me,"NieChodz"))
var Pointy := GetObjProperty(me,"P");
if(CInt(CoordinateDistance(me.x,me.y,CInt(Pointy[1]),CInt(Pointy[2]))) > 5)
Obroc_Postac_do_Miejsca(me,CInt(Pointy[1]),CInt(Pointy[2]));
else
var los := RandomInt(4)+1;
var Zgoda_Los := {0,0,1,1};
if(CInt(Zgoda_Los[los]) == 1)
var Kierunek := RandomInt(8);
me.facing := CInt(Kierunek);
endif
endif
Pointy := {me.x,me.y};
wander();
if(CInt(Pointy[1]) == me.x && CInt(Pointy[2]) == me.x)
var Kierunek := RandomInt(8);
me.facing := CInt(Kierunek);
wander();
endif
wander();
endif
EraseObjProperty(me,"--Atakuje");
endwhile
endfunction

function EventyWL()
SystemEventow(1);
endfunction

function Start_Walki(opponent)
        var Atakujesz := GetObjProperty(me,"--Atakuje");
        if(opponent.cmdlevel > 0)
                SetWarMode(0);
                SetOpponent(0);
                return 0;
        elseif(GetProcess(CInt(Atakujesz[2])))
                return 0;
        endif
        if(CInt(GetObjProperty(me,"NAtk")) == opponent.serial && Distance(me,opponent) > 1)
                return 0;
        endif
if((CInt(ReadGameClock())-CInt(GetObjProperty(opponent,"POZYW"))) >= 120)
EraseObjProperty(opponent,"POZYW");
elseif(GetObjProperty(opponent,"POZYW"))
                return 0;
endif
        SetOpponent(opponent);
        SetWarMode(1);
        var LICZ := {Distance(me,opponent),0};
if(!GetObjProperty(me,"--Atakuje"))
        Start_Script(":combat:Walka_AI",{me,opponent});
endif
        while(!me.dead && !opponent.dead && !opponent.hidden && !opponent.concealed &&  Distance(me,opponent) <= CInt(cme.Zasieg_Widzenia))
GetProcess(GetPid()).clear_event_queue();
                if(opponent.cmdlevel > 0)               
                        break;
                endif
if((GetObjProperty(me,"DYS") || cme.Typ_Stworzenia == "Strzelec") && Distance(me,opponent) <= CInt(cme.Zasieg_Widzenia))
                        LICZ[2] := 0;
                        LICZ[1] := Distance(me,opponent);
                elseif(Distance(me,opponent) == LICZ[1] && Distance(me,opponent) > 1)
                        LICZ[2] += 1;
                else
                        LICZ[2] := 0;
                        LICZ[1] := Distance(me,opponent);
                endif
                if(LICZ[2] >= 20 || CInt(opponent.serial) == 0 || Distance(me,opponent) < 1)
                        EraseObjProperty(me,"--Atakuje");
                        Sleep(1);
                        break;
                endif
               
                if(CInt(GetMaxWartosc(me,"Hits")/20) >= GetWartosc(me,"Hits"))
                        me.run_speed := 5;
                        Ucieczka(opponent);
                        break;
                endif
                if(!GetObjProperty(me,"Strzelam"))
                        Odleglosc_Walki(opponent);
                endif
                Sleepms(30);
                if(RandomInt(40) == 1)
                        Atakujesz := GetObjProperty(me,"--Atakuje");
                        foreach osoba in ListMobilesInLineOfSight(me,5)
                                var Atak := GetObjProperty(osoba,"--Atakuje");
                                if(CInt(Atak[1]) == CInt(me.serial) && osoba.serial != CInt(Atakujesz[1]) && !osoba.NpcTemplate)
                                        opponent := osoba;
                                        GetProcess(CInt(Atakujesz[2])).kill();
                                        if(!GetProcess(CInt(Atakujesz[2])))
                                                Start_Script(":combat:Walka_AI",{me,opponent});
                                                EraseObjProperty(me,"--Atakuje");
                                        endif
                                        break;
                                endif
                        endforeach
                endif
        endwhile
        EraseObjProperty(me,"--Atakuje");
        Po_Walce();
        return;
endfunction
function Ucieczka(opponent)
        if(CheckLineOfSight(me,opponent) == 0)
                EraseObjProperty(me,"--Atakuje");
                return;
        elseif(opponent.cmdlevel > 0)
                EraseObjProperty(me,"--Atakuje");
                return;
        endif
        var WBez := 0;
        SetObjProperty(me,"#Uciekam",1);
var ONAtak := GetObjProperty(opponent,"--Atakuje");
        if(CInt(ONAtak[1]) == me.serial && !GetObjProperty(me,"--Atakuje") && opponent.warmode)
                Start_Script(":combat:Walka_AI",{me,opponent});
        endif
        while(1)
                if(!SWalki(opponent))
                        if(!Odleglosc(opponent))
                                Sleepms(1);
                        endif
                        WBez := 0;
                else
                        WBez := WBez + 1;
                endif
                if(WBez == 10)
                        me.run_speed := CInt(cme.SpeedMove);
                        EraseObjProperty(me,"#Uciekam");
                        EraseObjProperty(me,"--Atakuje");
                        return;
                endif
Sleepms(30);
        endwhile
        me.run_speed := CInt(cme.SpeedMove);
        EraseObjProperty(me,"#Uciekam");
        EraseObjProperty(me,"--Atakuje");
        return;
endfunction
Więc jak widzimy dla różnych stanów mamy różne pętle wykonywając.
- tutaj jest raz wysyłany event i go już nie ma
- jakby w Start_Walki() nie było by pętli while(...) ... a graczów było by 2 to by mogli ogłupić stworzenie po przez wysyłanie ciągle "EVENTÓW" tzn obaj są łucznicy : jeden strzela a jak za blisko podejdzie drugi strzela : albo obaj strzelają ciągle... przeciwnik nie podejdzie :P dlatego tutaj zastosowanie "że zajmuje się jednym przeciwnikiem" nie patrząc potem na eventy na dodatek (dałem możliwość)
los(40) == 1 szanse na zmienna celu :P
« Ostatnia zmiana: Czerwiec 24, 2010, 23:28:14 wysłana przez Nsuidara »

Offline JCoder

  • Użytkownik

# Lipiec 03, 2010, 14:20:42
Scala ma do tego aktorów (klasa Actor).  Erlang chyba podobnie.
Rozwiązuje to dokładnie Twój problem:

class Stworzenie extends Actor {
   

  // Tu kod inicjujący....

  // Tu kod obsługujący reakcję na zdarzenia:
  override def act = loop {   
     react {
        case Zdarzenie1 => reagujNaZdarzenie1
        case Zdarzenie2 => reagujNaZdarzenie2
        ....
     }
  }

}

"loop" to dokładnie Twój  "while(1)", tyle że nieco lepszy, bo nie zapycha CPU w trakcie, gdy aktor nie ma nic do roboty.
Aktorzy mogą się ze sobą (i z grą) komunikować wysyłając komunikaty synchronicznie i asynchronicznie.
Aktorzy mogą również wykonywać długotrwałe zadania nie blokując reszty gry (oczywiście jak za dużo aktorów będzie na raz liczyć coś ciężkiego, to możesz spowodować, że np. na rendering nie starczy czasu - wtedy dobrze, aby miały niski priorytet).
Liczba aktorów jest niezależna od liczby wątków - powiązaniem tym można łatwo sterować.
Nie jest potrzebna kosztowna synchronizacja (muteksy itp).

http://ruben.savanne.be/articles/concurrency-in-erlang-scala

« Ostatnia zmiana: Lipiec 03, 2010, 14:37:15 wysłana przez JCoder »

Offline Nsuidara

  • Użytkownik
    • Site

# Lipiec 06, 2010, 05:12:18
Jak pokazałem Skrypt AI Stowrzenia tam jest
while(!me.dead)
// kod
ale tu mamy magiczne "ev := os::wait_for_event(Czekanie);"
// kod
endwhile
Można rzecz powiedzieć robi ten sam schemat jak wspomniałeś "czeka na event" - zależnie jakie są włączone to na takie reaguje...
Tylko  ::) nie w tym cały problem...

Problemem jest "kontener"/"pojemnik" tego wszystkiego - ale sądzę że jednak sam do tego jakoś dojdę (z wyszukanymi i otrzymanymi informacjami) - tak zwanymi "słówkami" kluczowymi :D