Jak sprawiedliwie rozdzielić zasoby między fabryki, gdy zasoby są prawie wyczerpane?

12

Głównym zasobem w mojej grze jest masa , przechowywana jako liczba zmiennoprzecinkowa, która zmienia się w czasie. Węzły zasobów zwiększają masę, a fabryki ją wyczerpują. Na przykład, jeśli mam węzeł zasobów z wydajnością 5 mas na sekundę, zdobędę 5 * deltaTmasę z każdym krokiem gry. Masa jest wyświetlana w zaokrągleniu do najbliższej liczby całkowitej, a wskaźniki wzmocnienia / straty są wyświetlane w dziesiętnych częściach.

Jak mam radzić sobie z masą uderzającą w zero? Stwarza to warunki wyścigu, jeśli wiele fabryk próbuje budować naraz: fabryki w kolejce lub które zużywają mniej zasobów, mają pierwszeństwo, gdy pojawi się więcej zasobów, a więc budują się szybciej niż inne.

Jak sobie z tym poradzić? Czy powinienem całkowicie pominąć ten krok?

Tłum
źródło
Bah, mój komentarz nie zapisał. Miałem lepsze wyjaśnienie. Zasadniczo mam zasób, który jest dostępny na każdym kroku przez każdy obiekt. Każdy obiekt dodaje lub odejmuje od zasobu. Mój problem polega na tym, że jeśli zasób osiągnie wartość 0, nie wiem, co zrobić. Czy powinienem zrobić kolejkę? Czy powinienem pominąć krok obiektu. Co?
Mob
3
Round robin. Problem rozwiązany.
Patrick Hughes,
Odpowiedź Roya poniżej w połączeniu z komentarzem opisuje przyzwoity, łatwy w utrzymaniu i dostrajaniu okrągły system robin. Dopóki twój bezpośredni problem projektowy zostanie rozwiązany, wszystko jest w porządku =)
Patrick Hughes,

Odpowiedzi:

9

Zgadzam się z Petrem: nie ma na to ustalonego sposobu. To, jak chcesz to zrobić, zależy od tego, jak chcesz zaprojektować grę.

W tej sytuacji jednak myślę, że od razu oczywiste jest, jakiego rodzaju mechaniki próbujesz się nauczyć: po prostu chcesz, aby rzeczy wytwarzały się tak szybko, jak to możliwe, w ramach dostępnej masy.

Produkcja w ramach zdolności produkcyjnych

Mam zamiar wyciągnąć liść z książki Najwyższego Dowódcy, ponieważ tworzysz system bardzo podobny do ich: Jeśli produkujesz powyżej zdolności produkcyjnych, najlepszym sposobem na poradzenie sobie z tym jest spowolnienie produkcji na całym forum. Obniżenie mocy produkcyjnych jest w rzeczywistości dość proste.

Mechanik prędkości produkcji

Na każdym etapie aktualizacji fabryki nie wytwarzają tylko ustalonej ilości: działają z prędkością produkcji , która określa, ile postępów robią na każdym kroku i ile zużywają masy. Kiedy produkujesz przy 75% wydajności, twoje fabryki robią 75% więcej postępów na każdym kroku i zużywają 75% masy w porównaniu do 100% wydajności.

Aby obliczyć szybkość produkcji, zanim w ogóle coś zbudujesz, powinieneś zapytać fabryki, aby określić całkowite zasoby, które zostaną wykorzystane na tym etapie przy pełnej wydajności. Następnie wykonujesz proste obliczenia:

production speed = (total mass capacity / mass required this step)
if (production speed > 1.0) production speed = 1.0

Powiedzmy, że potrzebujesz 125 masy na tym etapie, aby uzyskać pełną wydajność, ale tylko 100 masy na tym etapie. To równanie zapewnia prędkość produkcji 0,8 (dziesiętna reprezentacja 80%). Kiedy mówisz swoim fabrykom, aby faktycznie wykonały budowę , podajesz im tę wartość, aby powiedzieć im, z jaką prędkością budują: a teraz twoja produkcja jest spowolniona na całej planszy.

Alternatywy

Możesz także rozpocząć tymczasowe zamykanie fabryk, dopóki moce produkcyjne nie zwolnią się, i bardzo interesujące może być to, że dzieje się tak w fabrykach znajdujących się dalej od generatorów, gdy są one bardzo niskie.

Wiele zasobów?

Od ciebie zależy, jak sobie z tym poradzisz; istnieje wiele opcji. Najprostszym jest prawdopodobnie obliczenie zdolności produkcyjnej dla każdego zasobu, a następnie wybranie najniższego , tak aby Twój najsłabszy zasób stał się wąskim gardłem dla całej reszty.

doppelgreener
źródło
Myślę, że nie musisz nawet mówić fabryce, aby produkowała z prędkością 80%, ponieważ cokolwiek twoja fabryka tworzy, wymaga stałej masy. Na przykład: budujesz zbiornik, który wymaga 100 masy, normalnie fabryka może użyć 2 masy w każdym cyklu. Oznacza to, że wypełnienie zbiornika zajmuje 50 cykli, a każdy cykl dodaje 2 do aktualnej masy zbiorników. Teraz masz tylko 1 masę, co oznacza, że ​​ten cykl, obecnie wydane zbiorniki zwiększają się o 1 zamiast 2. Po każdym cyklu po prostu sprawdź aktualną masę w stosunku do całkowitej masy wymaganej, aby zobaczyć, czy zbiornik jest całkowicie zbudowany, czy nie.
Thomas
Jeśli masz 0 masy, po prostu nie dodawaj żadnej masy do zbiornika. Czas potrzebny na zbudowanie czegoś w ten sposób może się różnić w zależności od twojego dochodu masowego. jeśli masz 2 fabryki, które mogą wykorzystywać 2 masy / cykl i tylko 3 dochody, zbiornik 1 jest budowany w 50 cyklach, zbiornik 2 w 100. Innym sposobem jest podzielenie całkowitej ilości masy dostępnej dla wszystkich fabryk, które z niego korzystają (są aktywnie budując coś). Całkowita ilość masy, jaką fabryka może wykorzystać, może zostać zwiększona poprzez dodanie poziomów do fabryki. Na przykład na poziomie 1 mogą wydać 2 msze, na poziomie 2: 3 itd. Itd.
Thomas
@Thomas Dodawanie masy do wytwarzanego produktu wydaje się zbyt skomplikowanym sposobem w porównaniu do zwykłego uzupełnienia procentu produktu. Zwłaszcza, że ​​twoja fabryka musi wiedzieć wszystko o twoim systemie zasobów zamiast prostej własności „szybkości produkcji”. Jeśli wynik dla gracza jest taki sam, implementacja powinna być jak najprostsza. Ułatwia to także wprowadzanie zmian w przyszłości, na przykład podczas dodawania / usuwania zasobów.
Hackworth,
@Hackworth Nie zgadzam się, fabryka nie musi wiedzieć o systemie zasobów. Fabryka wie tylko, co buduje i jak daleko. System zasobów po prostu informuje fabrykę o dodaniu X do kompilacji. W ten sposób nie musisz obliczać procentu przychodu z zasobów tej fabryki i nie musisz przekładać dochodu z zasobów na dodatkowy procent ukończenia.
Thomas
6

Chociaż podoba mi się odpowiedź Jonathana Hobbsa, myślę, że system kolejek jest jeszcze prostszy:

Queue<Factory> queue = ...;
int numFactories = ...;

Update()
{
    int resources = GetAllResourcesForThisStep();
    for(int i = 0; i < numFactories; i++)
    {
        if(queue.Peak().RequiredResources <= resources)
        {
            Factory f = queue.Pop();
            resources -= f.RequiredResources;
            queue.Push(f);
        }
        else
        {
            break;
        }
    }
}

Prawdopodobnie będzie to działać średnio w taki sam sposób, jak implementacja Jonathana. Jednak rozwiązanie Jonathana może powodować problemy, jeśli szybkość pracy jest ustawiona na bardzo niskim poziomie, a moja implementacja mogłaby mieć fabrykę z bardzo wysokim zapotrzebowaniem na zasoby dla tej ramki, gdyby blokowała inne fabryki dla kilku ramek.

Roy T.
źródło
+1 Tworzę grę z podwójnymi zasobami, podobnie jak w pytaniu, a kiedy zajmuję się rozwiązaniem problemu blokady zaopatrzenia, planuję użyć czegoś podobnego do tego. Nie ten dokładny kod, ale pomysł za nim. Konsumenci, którzy mogą korzystać z zasobów w jednym ticku, otrzymają niższy priorytet podczas następnego tiku. Planuję również dodać flagę wskazującą, czy ten konsument ma wysoki priorytet i przekazać użytkownikowi kontrolę nad tą flagą.
John McDonald,
Strzeż się głodu, jeśli nadasz im osobne priorytety. :)
Roy T.
Głodem byłyby jednak odrębne priorytety. Grałeś kiedyś w Settlers 4 lub Knights & Merchants? Jeśli nadmiernie budujesz budynki, ograniczone zasoby trafiają do losowych budynków i ukończenie wszystkiego zajmuje wieczność . Ale pozwalają także wybrać, czy budynek jest ważny, czy nie, w którym to przypadku zasoby trafią najpierw do tych ważnych budynków. Kluczem jest, aby nigdy nie nadmiernie konstruować, a jeśli tak, to nadmiernie konstruować jak najmniej.
John McDonald
5

Opracowuję podobny system zaopatrzenia we własnej grze, więc zastanawiałem się również, jak rozwiązać problem blokady dostaw i faworyzować. Aby zilustrować problem, stworzę prosty przykład:

Jeśli masz listę: [producent1, konsument1, konsument2, konsument3] i aktualizujesz w kolejności, zaczynając od dostawy = 0, otrzymasz:

producer1 produces 5 mass. You now have 5 mass
consumer1 wants 3 mass. Success, you now have 2 mass
consumer2 wants 3 mass. Fail
consumer3 wants 3 mass. Fail
[next tick]
producer1 produces 5 mass. You now have 7 mass
consumer1 wants 3 mass. Success, you now have 4 mass
consumer2 wants 3 mass. Success, you now have 1 mass
consumer3 wants 3 mass. Fail
etc...

konsument1 czerpie radość, podczas gdy konsumenci 2 i 3 głodują, dopóki konsument 1 nie będzie zadowolony. W zależności od gry może to nie być pożądane. Wiem, że w mojej grze tak nie jest. Kiedy do tego dojdę, utworzę kolejkę, w której konsumenci, którzy zostali nakarmieni jednym tikiem, przeniosą się na tył kolejki dla następnego tiku, co, jak sądzę, zmierza Roy T. Powyższy przykład wyglądałby tak:

producer1 produces 5 mass. You now have 5 mass
consumer1 wants 3 mass. Success, you now have 2 mass. <-- Move to end of queue
consumer2 wants 3 mass. Fail
consumer3 wants 3 mass. Fail
[next tick]
producer1 produces 5 mass. You now have 7 mass
consumer2 wants 3 mass. Success, you now have 4 mass  <-- Note the order change
consumer3 wants 3 mass. Success, you now have 1 mass
consumer1 wants 3 mass. Fail
etc...

W ten sposób wszyscy otrzymają sprawiedliwą część zasobów.

Planuję również zaimplementować dodatkową kolejkę, która będzie używana jako kolejka priorytetowa, aby użytkownik mógł wybrać określone struktury, które będą miały priorytet zasobów. Kolejka priorytetowa będzie zawsze obsługiwana przed kolejką standardową. Upewnij się, że wszyscy producenci są najpierw zaktualizowani, a następnie zużyj wszystkie zasoby na drugim miejscu, w przeciwnym razie kolejka się zepsuje, gdy będziesz produkować zasoby częściowo przez tyknięcie, a niektórzy konsumenci byli już głodni.

Podsumowując: zaktualizuj producentów, a następnie kolejkę priorytetową, przenosząc klientów karmionych na koniec kolejki priorytetowej, a następnie zaktualizuj standardową kolejkę, przenosząc klientów karmionych na koniec kolejki standardowej.

John McDonald
źródło
Doprowadzony do limitu może oznaczać, że żaden konsument nie skończy, dopóki każdy konsument nie skończy. W realistycznym scenariuszu łańcucha produkcyjnego jest to dość mało prawdopodobne i mogłoby być katastrofalne, gdyby ktoś chciał mieć kolejkę budowania dla ogromnej armii. Będzie praktycznie bez żołnierzy przez cały czas budowy armii.
Cardin
Ta odpowiedź była moją pierwszą myślą, kiedy przeczytałem pytanie. Należy również zauważyć, że masa zużywana na kleszcza niekoniecznie jest masą do utworzenia kompletnej jednostki, ale raczej koszt jednostkowy w wymaganym czasie, więc nie jestem pewien, czy obawy @ Cardina są ważne, chyba że twoja jednostka koszt za kleszcza jest bardzo zbliżony do całkowitego wskaźnika zbierania. Chciałbym po prostu wyraźnie zarządzać kolejką priorytetową, aby gracz mógł zdecydować, kto umrze z głodu.
Brice
@Cardin, masz całkowitą rację, ale można go również wykorzystać jako mechanikę gry. Settlers 4, Knights & Merchants, Total Annihilation, Supreme Commander to gry, które o tym wiem. Sztuczka polega na tym, aby nie dotrzeć do tej blokady zaopatrzenia, a jeśli to zrobisz, upewnij się, że wydostałeś się jak najszybciej z blokady. Dodanie kolejki priorytetowej pozwala ludziom łatwiej się wydostać.
John McDonald
3

Cóż, rozwinę pomysł Johna, ponieważ rozmawialiśmy o tym trochę na czacie .

edytuj: To rozwiązanie jest właściwie preferowane tylko wtedy, gdy ilość materiałów eksploatacyjnych jest istotna dla tego, jak często fabryka powinna otrzymywać partię zasobów. Jeśli wszystko jest takie samo, możesz po prostu użyć kolejki.

Moje rozwiązanie: wszystkie fabryki wymienione w kolejce priorytetowej. Priorytet wzrasta, ponieważ fabryka cierpi z powodu głodu. Głód, priorytet, ustawiony na zero, gdy fabryka zużyje zasoby. Najwyższym priorytetem zawsze będzie zdobycie kolejnej partii zasobów.

Po określeniu, która fabryka otrzymuje jakie zasoby, w jakimś pseudo-kodzie:

iterator i = factoryqueue.start()
bool starvation = false
while(i.next())
  if(i.ready)
    if (!starvation) 
      if (i.consumeAmount < resource.count) starvation = true
      else 
        i.consume(resource)
        i.priority = 0
    if (starvation)
      i.priority += 1

W ten sposób fabryki będą wytwarzać po 1 produkt po kolei, jeśli chcesz dostosować, biorąc pod uwagę wartość ConsumeAmount, aby tańsze produkty były wytwarzane częściej, możesz na przykład zwiększyć priorytet o 1 / ConsumeAmount.

Toni
źródło
Chciałbym głosować za tym, ale nie jestem pewien, co to ma zrobić - śledzi głód, ale w przeciwnym razie (prawdopodobnie) po prostu obraca się w kolejce dokładnie tak jak zwykle, a głód i priorytet nigdy się nie pojawiają robić cokolwiek.
doppelgreener
różnica stanie się widoczna, jeśli zwiększysz priorytet o + = 1 / ilośćTenisFactoryConsumes, wtedy te, które wymagają więcej zasobów do stworzenia produktu, będą nieco opóźnione, pozwalając tym tańszym na przyjęcie proporcjonalnie większej ilości zasobów. Wyrównałoby to stosunek zasobów do fabryki. Jednak nie jest to do końca głupi dowód, ponieważ liczba głodu jest ustawiona na magiczną liczbę 0 za każdym razem, gdy zasoby są zużywane przez fabrykę, więc nie będzie oczywiste, że będzie dokładnie równomiernie rozłożona, gdy odnawianie zasobów będzie się zmieniać.
Toni
Właściwie nie jestem teraz pewien, czy tak naprawdę nie ma gwarancji. Musiałbym narysować kilka wykresów i przetestować, aby się upewnić.
Toni
aha, a priorytet to jakość samej kolejki priorytetowej. Nie zamierzam zawracać sobie głowy wyjaśnianiem, jak je zbudować. Sama kolejka priorytetowa jest zawsze sortowana według wartości priorytetu jej członków.
Toni
+1 Rozumiejąc, że jest to kolejka priorytetowa, jej działanie wygląda teraz prosto: przesuń w dół kolejkę i wybierz najwcześniejszą rzecz, która może zużyć zasoby. Jeśli pojemność jest mocno podzielona (ty masz 1000 zasobów, ale sto 100 kompilacji zasobów), nie powinno to stanowić problemu, a wszystko dość dobrze się odżywia. Nawet jeśli coś jest znacznie powyżej twoich zasobów w tym kleszczu, może w końcu zaoszczędzić wystarczająco dużo, aby zrobić postęp - co ma wpływ na spowolnienie lub wyłączenie dużych rzeczy całkowicie na rzecz mniejszych rzeczy, co jest dobre. Lubię to bardzo. :)
doppelgreener
2

Dziwne pytanie.

Mój problem polega na tym, że jeśli zasób osiągnie wartość 0, nie wiem, co zrobić. Czy powinienem zrobić kolejkę? Czy powinienem pominąć krok obiektu. Co?

Co trzeba zrobić, zależy od logiki gry Ci stworzyć. Możesz zrobić kolejkę, możesz pominąć. Zależy, jak twoim zdaniem powinna się zachowywać Twoja gra. Popraw mnie, jeśli pomylę się w twoim pytaniu.

Petr Abdulin
źródło
1

Możesz zachować liczbę całkowitych zapotrzebowań na zasoby na tik dla wszystkich konstrukcji. Jeśli jeden magazyn zasobów osiągnie mniej niż wymaganą ilość, wówczas cała budowa zostanie całkowicie zatrzymana, dopóki magazyn nie zgromadzi wystarczającej ilości, aby utrzymać co najmniej 1 tik produkcji. Następnie można wznowić produkcję.

Zamiast zapisywać szybkość produkcji jako liczbę zmiennoprzecinkową, jest ona binarna - albo twoja fabryka produkuje z pełną prędkością, albo nie.

To powiedziawszy, to podejście jest zasadniczo takie samo jak odpowiedź Jonathana, dla specjalnych przypadków tempa produkcji 0,0 i 1,0 - dowolna liczba zmiennoprzecinkowa f o wartości 0,0 <= f <= 1,0 jest prawdopodobnie bardziej elegancka, ponieważ nie występują gwałtowne ruchy magazynowania , ale logika powinna być nieco prostsza.

Hackworth
źródło