Как мне справедливо распределить ресурсы между фабриками, когда ресурсы почти исчерпаны?

12

Основным ресурсом в моей игре является масса , хранящаяся в виде числа с плавающей запятой, которое меняется со временем. Ресурсные узлы увеличивают массу, а заводы истощают ее. Например, если у меня есть ресурсный узел, выдающий 5 масс в секунду, я буду набирать 5 * deltaTмассу с каждым шагом игры. Масса отображается округленной до ближайшего целого числа, а показатели прибыли / убытка отображаются на десятые доли.

Как мне справиться с массой удара ноль? Это создает состояние гонки, если несколько фабрик пытаются построить одновременно: фабрик, которые находятся в очереди сначала или которые истощают меньше ресурсов, получают приоритет, как только появляется больше ресурсов, и поэтому они строятся быстрее, чем другие.

Как я могу справиться с этим? Должен ли я пропустить шаг полностью?

чернь
источник
Бах, мой комментарий не сохранил. У меня было лучшее объяснение. В основном у меня есть ресурс, к которому каждый шаг обращается каждый объект. Каждый объект добавляет или вычитает из ресурса. Моя проблема в том, что если ресурс достигает 0, я не знаю, что делать. Должен ли я сделать какую-то очередь? Должен ли я пропустить шаг объекта. Какая?
Моб
3
По-круговой. Проблема решена.
Патрик Хьюз
Ответ Роя ниже в сочетании с комментарием к нему описывают приличную, простую в обслуживании и настройке систему циклического перебора. Пока ваша непосредственная проблема с дизайном решена, все будет хорошо =)
Патрик Хьюз

Ответы:

9

Я согласен с Петром: нет никакого способа сделать это. То, как вы хотите это сделать, зависит от того, как вы хотите создать свою игру.

Однако в этих обстоятельствах, я думаю, сразу становится очевидным тот тип механики, к которому вы стремитесь: вы просто хотите, чтобы вещи производились как можно быстрее, в пределах имеющейся массы.

Производить в пределах возможностей

Я собираюсь взять лист из книги Верховного главнокомандующего, так как вы делаете систему, очень похожую на их: если вы производите больше мощности, лучший способ справиться с ней - это замедлить производство по всем направлениям. Снижение производственных мощностей на самом деле довольно просто.

Механик скорости производства

На каждом этапе обновления ваши фабрики не просто производят определенное количество: они работают с производительной скоростью , которая определяет, какой прогресс они делают на каждом шаге и какую массу они расходуют. Когда вы производите с производительностью 75%, ваши заводы на 75% увеличивают прогресс на каждом этапе и расходуют 75% массы по сравнению со 100% производительностью.

Чтобы рассчитать скорость производства, прежде чем строить что-либо вообще, вы должны запросить свои фабрики, чтобы определить общие ресурсы, которые будут использоваться на этом этапе на полную мощность. Затем вы выполняете простой расчет:

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

Допустим, вам нужно 125 масс на этом шаге, чтобы произвести на полную мощность, но только 100 масс на этом шаге. Это уравнение обеспечивает скорость производства 0,8 (десятичное представление 80%). Когда вы говорите своим фабрикам, чтобы они действительно выполняли их строительство , вы передаете им это значение, чтобы сообщить им, с какой скоростью они строят: и теперь ваше производство замедляется по всем направлениям.

альтернативы

Вы также можете начать временное закрытие заводов до тех пор, пока не освободятся производственные мощности, и было бы очень интересно увидеть, что это происходит с заводами, расположенными дальше от генераторов, когда они работают на очень низкой мощности.

Несколько ресурсов?

До вас, как вы справляетесь с этим; вариантов много. Самый простой из них - это, вероятно, рассчитать производственную мощность для каждого ресурса, а затем выбрать самый низкий , чтобы ваш самый слабый ресурс стал узким местом для всех остальных.

doppelgreener
источник
Я думаю, вам даже не нужно указывать фабрике производить на 80% скорости, потому что все, что создает ваша фабрика, использует фиксированное количество массы. Например: вы строите танк, который требует 100 масс, чтобы построить, обычно завод может использовать 2 массы за цикл. Это означает, что для завершения бака требуется 50 циклов, и каждый цикл добавляет 2 к текущей массе бака. Теперь у вас есть только 1 масса, это означает, что в этом цикле в настоящее время расходуемая масса танков увеличивается на 1 вместо 2. После каждого цикла просто проверяйте текущую массу по сравнению с общей массой, необходимой, чтобы увидеть, полностью ли построен танк или нет.
Томас
Если у вас есть масса 0, просто не добавляйте массу в бак. Время, необходимое для создания чего-либо таким образом, может варьироваться в зависимости от вашего массового дохода. если у вас есть 2 фабрики, которые могут использовать 2 массы / цикл и только 3 дохода, резервуар 1 строится за 50 циклов, резервуар 2 - за 100. Другим способом является разделение общего количества массы, доступной на все фабрики, которые ее используют ( активно строит что-то). Общее количество массы, которое может использовать фабрика, может быть улучшено путем добавления уровней к фабрике. Например, на уровне 1 они могут потратить 2 массы, на уровне 2: 3 массы и т. Д. И т. Д.
Томас
@Thomas Добавление массы к продукту в процессе производства кажется слишком сложным способом по сравнению с простым заполнением процента продукта. Тем более, что ваша фабрика должна знать все о вашей ресурсной системе, а не о простом свойстве «скорость производства». Если результат для игрока одинаков, сделайте реализацию максимально простой. Это также облегчает внесение изменений в будущем, например, при добавлении / удалении ресурсов.
Хакворт
@ Хакворт Я склонен не соглашаться, фабрике не нужно знать о системе ресурсов. Фабрика знает только, что она строит и как далеко. Система ресурсов просто сообщает фабрике, что нужно добавить X к сборке. Таким образом, вам не нужно рассчитывать процент дохода ресурса, который имеет эта фабрика, и вам не нужно переводить доход ресурса в добавленный процент завершения.
Томас
6

Хотя мне нравится ответ Джонатана Хоббса, я думаю, что система очередей еще проще:

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;
        }
    }
}

Это, вероятно, будет работать в среднем так же, как реализация Джонатана. Однако решение Джонатана может создать проблемы, если рабочая скорость установлена ​​на очень низком уровне, и моя реализация может иметь фабрику с очень высоким запросом ресурсов для этого кадра, если она блокирует другие фабрики на несколько кадров.

Рой Т.
источник
+1 Я занимаюсь разработкой игры с двумя ресурсами, во многом как в вопросе, и когда я найду решение проблемы блокировки поставок, я планирую использовать нечто похожее на это. Не тот точный код, но идея, стоящая за ним. Потребители, которые могут использовать ресурсы за один тик, получат более низкий приоритет в течение следующего тика. Я также планирую добавить флаг, чтобы указать, имеет ли этот потребитель высокий приоритет, и передать контроль над этим флагом пользователю.
Джон Макдональд
Остерегайтесь голода, если вы даете им отдельные приоритеты. :)
Рой Т.
Голод с отдельными приоритетами был бы целью все же. Вы когда-нибудь играли в «Поселенцы 4» или «Рыцари и Торговцы»? Если вы чрезмерно строите здания, ограниченные ресурсы уходят в случайные здания, и для того, чтобы что-то закончить, требуется вечность . Но они также позволяют вам выбрать, важно ли здание или нет, и в этом случае ресурсы пойдут в эти важные здания в первую очередь. Ключ в том, чтобы никогда не переоценивать, или, если вы делаете, переконструировать как можно меньше.
Джон Макдональд
5

Я разрабатываю аналогичную систему снабжения в своей собственной игре, поэтому я также думал о том, как решить проблему блокировки снабжения и фаворитизма. Чтобы проиллюстрировать проблему, я создам простой пример:

Если у вас есть список: [продюсер1, потребитель1, потребитель2, потребитель3] и вы обновляете по порядку, начиная с supply = 0, вы получите следующее:

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

Потребитель1 получает все удовольствие, в то время как потребители 2 и 3 голодают, пока потребитель 1 не будет удовлетворен. В зависимости от вашей игры это может быть нежелательно. Я знаю, в моей игре это не так. Когда я подойду к этому, я собираюсь создать очередь, в которой потребители, которых кормят за один тик, переместятся в конец очереди для следующего тика, что, как я полагаю, является тем, к чему идет Рой Т. Пример выше будет выглядеть так:

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

Таким образом, каждый получит свою справедливую долю ресурсов.

Я также планирую реализовать дополнительную очередь, которая будет использоваться в качестве очереди с приоритетами, чтобы пользователь мог выбирать определенные структуры, чтобы иметь приоритет ресурсов. Очередь с приоритетом всегда будет обслуживаться до стандартной очереди. Убедитесь, что все производители обновляются в первую очередь, а затем потребляют все ресурсы, в противном случае очередь сломается, когда вы производите ресурсы на полпути через галочку, а некоторые потребители уже голодали.

Итак, подведем итоги: обновите производителей, затем очередь с приоритетами, переместив фид-потребителей в конец приоритетной очереди, затем обновите стандартную очередь, переместив фид-потребителей в конец стандартной очереди.

Джон Макдональд
источник
Взятые до предела, это может означать, что ни один потребитель не заканчивает, пока каждый потребитель не может закончить. В реалистичном сценарии производственной цепочки это маловероятно и может быть катастрофическим, если кто-то хочет построить очередь для огромной армии. Он будет практически без войск в течение всего времени строительства армии.
Cardin
Этот ответ был моей первоначальной мыслью, когда я прочитал вопрос. Кроме того, следует отметить, что масса, потребляемая за тик, не обязательно является массой для создания целой единицы, а скорее - стоимостью единицы в течение требуемого времени, поэтому я не уверен, что опасения @ Cardin действительны, если только ваша единица цена за тик очень близка к общей сумме сбора. Я бы просто имел приоритетную очередь, явно управляемую игроком, чтобы он мог решить, кто голодает.
Брайс
@Cardin, вы совершенно правы, но это также может быть использовано в качестве игровой механики. Settlers 4, Knights & Merchants, Total Annihilation, Supreme Commander - игры, которые, как я знаю, сделали это. Хитрость заключается в том, чтобы не достичь этого замка питания, и если вы это сделаете, убедитесь, что вы выходите из замка питания как можно скорее. Добавление очереди приоритетов позволяет людям легче выйти.
Джон Макдональд
3

Что ж, я более подробно остановлюсь на идее Джона, так как мы немного обсудили это в чате .

редактировать: это решение на самом деле является предпочтительным только в том случае, если в зависимости от того, как часто фабрика должна получать пакет ресурсов, имеет отношение расходный объем. Если все одинаково, то вы действительно можете просто использовать очередь.

Мое решение: все заводы перечислены в очереди с приоритетами. Приоритет увеличивается, поскольку фабрика страдает от голода. Голод, приоритет, устанавливается на ноль, когда фабрика использует ресурсы. Главным приоритетом всегда будет получение следующей партии ресурсов.

При определении того, какая фабрика получает какие ресурсы, в каком-то псевдокоде:

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

Таким образом, ваши фабрики будут производить по 1 продукту по очереди, если вы захотите откорректировать, приняв во внимание учет потребления, чтобы более дешевые продукты производились чаще, например, вы можете повысить приоритет на 1 / потребление.

Toni
источник
Я хотел бы выразить это, но я не уверен, что это означает - он отслеживает голодание, но в противном случае (предположительно) просто вращается через очередь точно так же, как обычно, и голодание и приоритет никогда не заканчиваются делать что-либо.
doppelgreener
Разница становится очевидной, если вы увеличите приоритет на + = 1 / amountThisFactoryConsumes, тогда те, которые требуют больше ресурсов для создания продукта, будут немного отставать, позволяя менее дорогим потреблять пропорционально больше пакетов ресурсов. Это позволит выровнять соотношение ресурсов на фабрику. Тем не менее, это еще не совсем надежная защита, так как число голодания устанавливается на магическое число 0 каждый раз, когда ресурсы потребляются фабрикой, поэтому не будет гарантировано, что оно будет точно распределено равномерно, когда обновление ресурса колеблется.
Тони
На самом деле я сейчас не уверен, действительно ли это не гарантировано. Я должен был бы нарисовать некоторые графики и проверить это, чтобы быть уверенным.
Тони
о, а приоритет - это качество самой очереди приоритетов. Я не собираюсь объяснять, как его построить. Сама очередь приоритетов всегда сортируется в соответствии со значением приоритета ее участников.
Тони
+1 Понимая, что это приоритетная очередь, ее работа теперь выглядит просто: просмотрите очередь и выберите самое быстрое, что может потреблять ресурсы. Если емкость сильно разделена (у вас есть 1000 ресурсов на этот тик, но сто сборок по 100 ресурсов), это не должно быть проблемой, и все будет достаточно хорошо. Даже если что-то намного выше ваших ресурсов в этом тике, оно может в конечном итоге накопить достаточно, чтобы немного продвинуться - что приводит к его замедлению или к полному отказу от больших вещей в пользу меньших, что хорошо. Мне это очень нравится. :)
doppelgreener
2

Странный вопрос.

Моя проблема в том, что если ресурс достигает 0, я не знаю, что делать. Должен ли я сделать какую-то очередь? Должен ли я пропустить шаг объекта. Какая?

То, что вам нужно сделать, зависит от логики игры, которую вы создаете. Вы можете сделать очередь, вы можете пропустить. Зависит от того, как, по вашему мнению, должна вести себя ваша игра. Поправьте меня, если я ошибаюсь по вашему вопросу.

Петр Абдулин
источник
1

Вы можете сохранить количество общей потребности в ресурсах на один тик для всех конструкций. Если на одно хранилище ресурсов попадет меньше этого требуемого количества, то все строительство будет полностью остановлено, пока хранилище не соберет достаточно для поддержки как минимум 1 тика производства. Тогда производство может возобновиться.

Таким образом, вместо того, чтобы хранить объем производства в виде числа с плавающей запятой, он является двоичным - либо ваша фабрика производит на полной скорости, либо нет.

Тем не менее, этот подход, по сути, такой же, как ответ Джонатана, для особых случаев производительности 0,0 и 1,0 - произвольный float f с 0,0 <= f <= 1,0, вероятно, более элегантен, поскольку вы не получаете резких движений объема хранения , но логика должна быть немного проще.

Hackworth
источник