Como distribuo recursos de maneira justa entre fábricas quando os recursos estão quase esgotados?

12

O principal recurso do meu jogo é a massa , armazenada como um número de ponto flutuante que muda com o tempo. Os nós de recursos aumentam a massa e as fábricas a drenam. Por exemplo, se eu tiver um nó de recurso produzindo 5 massas por segundo, ganharei 5 * deltaTmassa a cada etapa do jogo. A massa é exibida arredondada para o número inteiro mais próximo e os indicadores de ganho / perda são exibidos em décimos.

Como devo lidar com massa atingindo zero? Isso cria uma condição de corrida se várias fábricas estiverem tentando construir de uma só vez: as fábricas primeiro na fila ou que drenam menos recursos têm prioridade quando mais recursos entram e, portanto, são construídas mais rapidamente do que as outras.

Como posso lidar com isso? Devo pular a etapa completamente?

Mob
fonte
Bah, meu comentário não salvou. Eu tive uma explicação melhor. Basicamente, eu tenho um recurso que é acessado a cada passo por cada objeto. Cada objeto adiciona ou subtrai do recurso. Meu problema é que, se o recurso atingir 0, não sei o que fazer. Devo fazer uma fila de algum tipo? Devo pular a etapa de um objeto. O que?
Mob
3
Pisco de peito vermelho redondo. Problema resolvido.
Patrick Hughes
A resposta de Roy abaixo, combinada com o comentário, descreve um sistema decente, fácil de manter e ajustar. Enquanto o seu problema de projeto imediato é resolvido é tudo de bom =)
Patrick Hughes

Respostas:

9

Concordo com Petr: não existe uma maneira definida de fazê-lo. Como você quer fazer isso é uma questão de como você deseja criar seu jogo.

Porém, nesta circunstância, acho imediatamente óbvio o tipo de mecânico que você está tentando encontrar: você só quer que as coisas produzam o mais rápido possível, dentro da quantidade de massa que você tem disponível.

Produção dentro da capacidade

Vou tirar uma folga do livro do Supremo Comandante, já que você está criando um sistema muito parecido com o deles: se você está produzindo acima da capacidade, a melhor maneira de lidar com isso é diminuir a produção em geral. Diminuir a capacidade de produção é realmente bastante simples.

Um mecânico de velocidade de produção

A cada etapa da atualização, suas fábricas não produzem apenas uma quantidade definida: elas operam a uma velocidade de produção , que determina quanto progresso eles fazem em cada etapa e quanta massa eles consomem. Quando você produz 75% da capacidade, suas fábricas avançam 75% a cada etapa e consomem 75% da massa em comparação com 100% da capacidade.

Para calcular a velocidade de produção, antes de construir qualquer coisa, você deve consultar suas fábricas para determinar o total de recursos que seriam usados ​​nesta etapa em plena capacidade. Então você executa um cálculo simples:

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

Digamos que você precise de 125 massas nesta etapa para produzir com capacidade total, mas tenha apenas 100 massas nessa etapa. Esta equação fornece uma velocidade de produção de 0,8 (a representação decimal de 80%). Quando você diz a suas fábricas para realmente executar a construção delas , você lhes entrega esse valor para dizer a que velocidade elas estão construindo: e agora sua produção é mais lenta.

Alternativas

Você também pode começar a encerrar fábricas temporariamente até que a capacidade de produção seja liberada, e pode ser muito interessante ver isso acontecendo com fábricas mais distantes dos geradores quando em capacidade extremamente baixa.

Vários recursos?

Você decide como você lida com isso; Há muitas opções. A mais simples é provavelmente calcular uma capacidade de produção para cada recurso e escolher a mais baixa , de modo que o recurso mais fraco se torne um gargalo para todo o resto.

doppelgreener
fonte
Acho que você nem precisa dizer à fábrica para produzir a 80% de velocidade, porque o que quer que sua fábrica crie use uma quantidade fixa de massa. Por exemplo: você constrói um tanque com 100 massas para construir, normalmente a fábrica pode usar 2 massas por ciclo. Isso significa que são necessários 50 ciclos para completar o tanque e a cada ciclo você adiciona 2 à massa atual do tanque. Agora, você só tem 1 massa disponível, ou seja, neste ciclo, os tanques atualmente gastos aumentam em 1 em vez de 2. Depois de cada ciclo, verifique a massa atual versus a massa total necessária para ver se o tanque está completamente construído ou não.
Thomas
Se você tiver 0 massa disponível, simplesmente não adicione massa ao tanque. O tempo que leva para construir algo dessa maneira pode variar dependendo da sua renda em massa. se você tiver 2 fábricas que podem usar 2 massa / ciclo e apenas 3 de renda, o tanque 1 é construído em 50 ciclos, o tanque 2 em 100. Outra maneira é dividir a quantidade total de massa disponível por todas as fábricas que o usam (são ativamente construindo algo). A quantidade total de massa que uma fábrica pode usar pode ser atualizada adicionando níveis à fábrica. Por exemplo no nível 1 eles podem gastar 2 em massa, no nível 2: 3 em massa etc. etc.
Thomas
@ Thomas A adição de massa a um produto em processo de fabricação parece uma maneira excessivamente complexa em comparação com o simples preenchimento de uma porcentagem do produto. Especialmente porque a sua fábrica precisa conhecer tudo sobre o seu sistema de recursos, em vez de uma simples propriedade de "taxa de produção". Se o resultado para o jogador for o mesmo, mantenha a implementação o mais simples possível. Isso também facilita as alterações no futuro, como quando você adiciona / remove recursos.
quer
@ Hackworth Eu costumo discordar, a fábrica não precisa saber sobre o sistema de recursos. A fábrica sabe apenas o que está construindo e a que distância está. O sistema de recursos informa à fábrica que adiciona um valor X à compilação. Dessa forma, você não precisa calcular a porcentagem da receita de recursos desta fábrica e não precisa converter a receita de recursos na porcentagem de conclusão adicionada.
Thomas
6

Embora eu goste da resposta de Jonathan Hobbs, acho que um sistema de filas é ainda mais simples:

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

Provavelmente, isso funcionará em média da mesma maneira que a implementação de Jonathan. No entanto, a solução de Jonathan pode dar problemas se a velocidade de trabalho estiver muito baixa e minha implementação puder ter uma fábrica com uma solicitação de recursos muito alta para esse quadro, caso ele bloqueie outras fábricas para vários quadros.

Roy T.
fonte
+1 Estou desenvolvendo um jogo com recursos duplos, como na pergunta, e quando resolvo o problema de bloqueio de suprimentos, pretendo usar algo semelhante a isso. Não é esse código exato, mas a ideia por trás dele. Os consumidores que puderem usar recursos em um tick terão uma prioridade mais baixa durante o tick seguinte. Também pretendo adicionar um sinalizador para indicar se esse consumidor é de alta prioridade e dar o controle desse sinal ao usuário.
26612 John McDonald
Cuidado com a fome se você lhes der prioridades separadas. :)
Roy T.
A fome com prioridades separadas seria o objetivo. Já jogou Settlers 4 ou Knights & Merchants? Se você construir demais construções, os recursos limitados vão para construções aleatórias e leva uma eternidade para terminar qualquer coisa. Mas eles também permitem que você selecione se um edifício é importante ou não; nesse caso, os recursos irão primeiro para esses edifícios importantes. A chave é nunca construir demais, ou se você construir, construir o mínimo possível.
John McDonald
5

Estou desenvolvendo um sistema de suprimentos semelhante no meu próprio jogo, então também estive pensando em como resolver o problema do bloqueio de suprimentos e o favoritismo. Para ilustrar o problema, criarei um exemplo simples:

Se você possui uma lista: [produtor1, consumidor1, consumidor2, consumidor3] e atualiza em ordem, iniciando no fornecimento = 0, obterá o seguinte:

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

O consumidor1 recebe toda a diversão, enquanto os consumidores 2 e 3 passam fome até que o consumidor 1 seja satisfeito. Dependendo do seu jogo, isso pode não ser desejável. Eu sei no meu jogo, não é. Quando eu chegar lá, vou criar uma fila na qual os consumidores que foram alimentados em um tick irão para o final da fila para o próximo tick, que eu acredito que é o que Roy T. está fazendo. O exemplo acima ficaria assim:

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

Dessa forma, todos receberão sua parte justa dos recursos.

Também planejo implementar uma fila adicional para ser usada como fila prioritária, para que o usuário possa selecionar determinadas estruturas para ter prioridade de recurso. A fila de prioridade sempre será atendida antes da fila padrão. Certifique-se de que todos os produtores sejam atualizados primeiro e, em seguida, consuma todos os recursos depois, caso contrário, a fila será interrompida quando você produzir recursos no meio de um tick e alguns consumidores já tiverem passado fome.

Para recapitular: atualize os produtores e, em seguida, a fila de prioridade, movendo os consumidores alimentados para o final da fila de prioridade e atualize a fila padrão, movendo os consumidores alimentados para o final da fila padrão.

John McDonald
fonte
Levado ao limite, isso pode significar que nenhum consumidor termina até que todo consumidor possa terminar. Em um cenário realista da cadeia de produção, isso é bastante improvável e pode ser bastante desastroso se alguém quiser ter uma fila de construção para um exército maciço. Ele estará praticamente sem tropas o tempo todo que o exército estiver sendo construído.
27612 Cardin
Essa resposta foi o meu pensamento inicial quando li a pergunta. Além disso, deve-se observar que a massa consumida por tick não é necessariamente a massa para criar uma unidade completa, mas sim o custo unitário pelo tempo necessário, portanto, não tenho certeza de que as preocupações do @ Cardin sejam válidas, a menos que sua unidade o custo por tick está muito próximo da sua taxa total de coleta. Eu simplesmente teria a fila de prioridade gerenciada explicitamente pelo jogador, para que ele decidisse quem passaria fome.
Br15
@ Cardin, você está certo, mas também pode ser usado como mecânico de jogo. Settlers 4, Knights & Merchants, Total Annihilation, Supreme Commander são jogos que eu sei que fizeram isso. O truque é não alcançar essa trava de suprimento e, se o fizer, certifique-se de sair da trava o mais rápido possível. Adicionar uma fila de prioridade permite que as pessoas saiam mais facilmente.
31412 John McDonald
3

Bem, vou expandir a ideia de John, já que discutimos isso um pouco no bate-papo .

editar: na verdade, esta solução é preferível apenas se a quantidade consumível for relevante para a frequência com que a fábrica deve obter um lote de recursos. Se é tudo a mesma coisa, você pode simplesmente usar uma fila.

Minha solução: todas as fábricas listadas em uma fila prioritária. A prioridade é aumentada, pois uma fábrica está sofrendo de fome. Fome, prioridade, definida como zero quando a fábrica consumir recursos. A prioridade máxima sempre obterá o próximo lote de recursos.

Ao determinar qual fábrica obtém quais recursos, em algum tipo de pseudo-código:

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

Dessa forma, suas fábricas produzirão 1 produto por vez, se você desejar ajustar levando em consideração o consumeAmount para que produtos mais baratos sejam produzidos com mais frequência, você poderá aumentar a prioridade em 1 / consumeAmount, por exemplo.

Toni
fonte
Eu gostaria de fazer um voto positivo, mas não tenho certeza do que isso deve fazer - ele acompanha a fome, mas de outra forma (presumivelmente) apenas gira pela fila exatamente como o normal, e a fome e a prioridade nunca acabam aparecendo para fazer qualquer coisa.
Doppelgreener
se a diferença for aparente se você aumentar a prioridade em + = 1 / amountThisFactoryConsumes, os que exigem mais recursos para criar um produto ficarão um pouco para trás, permitindo que os mais baratos tomem proporcionalmente mais lotes de recursos. Isso reduziria a proporção de recursos por fábrica. No entanto, isso ainda não é exatamente à prova de idiotas, já que o número de inanição é definido como número mágico 0 cada vez que os recursos são consumidos pela fábrica, portanto, não será colocado em quarentena para ser distribuído exatamente de maneira uniforme quando a renovação de recursos estiver interrompida.
Toni
Na verdade, agora não tenho tanta certeza se não é realmente garantido. Eu precisaria desenhar alguns gráficos e testá-lo para ter certeza.
Toni
ah, e a prioridade é uma qualidade da própria fila de prioridades. Não vou me incomodar em explicar como construir um. A fila de prioridade em si sempre é classificada de acordo com o valor da prioridade de seus membros.
Toni
+1 Entendendo que é uma fila prioritária, seu funcionamento agora parece simples: peneire a fila e escolha a coisa mais rápida que possa consumir recursos. Se a capacidade estiver fortemente dividida (você tem 1.000 recursos neste tick, mas cem compilações de 100 recursos), isso não deve ser um problema e as coisas são alimentadas razoavelmente bem. Mesmo que algo esteja bem acima dos seus recursos nesse ponto, ele pode economizar bastante o suficiente para progredir um pouco - o que tem o impacto de torná-lo mais lento ou fechar grandes coisas totalmente em favor de coisas menores, o que é uma coisa boa. Eu gosto muito disso. :)
doppelgreener
2

Pergunta estranha.

Meu problema é que, se o recurso atingir 0, não sei o que fazer. Devo fazer uma fila de algum tipo? Devo pular a etapa de um objeto. O que?

O que você precisa fazer depende da lógica do jogo que você cria. Você pode fazer uma fila, você pode pular. Depende de como você acha que seu jogo deve se comportar. Corrija-me, se eu estiver errado em sua pergunta.

Petr Abdulin
fonte
1

Você pode manter um número da demanda total de recursos por tick para todas as construções. Se um armazenamento de recursos atingir menos que essa quantidade necessária, toda a construção será interrompida completamente até que o armazenamento seja coletado o suficiente para suportar pelo menos 1 tick de produção. Então a produção pode retomar.

Portanto, em vez de armazenar a taxa de produção como um flutuador, é binário - sua fábrica produz a toda velocidade ou não.

Dito isto, essa abordagem é essencialmente a mesma que a resposta de Jonathan, para os casos especiais de taxa de produção 0,0 e 1,0 - um flutuador arbitrário f com 0,0 <= f <= 1,0 é provavelmente mais elegante, pois você não obtém movimentos bruscos na quantidade de armazenamento , mas a lógica deve ser um pouco mais simples.

Hackworth
fonte