Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

The main resource in my game is mass, stored as a floating point number that changes over time. Resource nodes increase mass and factories drain it. For example, if I have a resource node yielding 5 mass per second, I'll gain 5 * deltaT mass each game step. Mass is displayed rounded to the nearest integer, and gain/loss indicators are displayed by tenths.

How should I handle mass hitting zero? This creates a race condition if multiple factories are trying to build at once: Factories first in the queue or which drain less resource get priority once some more resource comes in and so build faster than the others.

How can I deal with this? Should I skip the step entirely?

share|improve this question
    
Bah, my comment didn't save. I had a better explination. Basically I have a resource that is accessed every step by every object. Each object adds or subtracts from the resource. My problem is that if the resource hits 0 I don't know what to do. Should I make a queue of some sort? Should I skip an object's step. What? –  Mob Apr 26 '12 at 4:01
3  
Round robin. Problem solved. –  Patrick Hughes Apr 26 '12 at 6:20
    
The answer from Roy below combined with the comment to it describe a decent, easy to maintain and tune round robin system. As long as your immediate design problem gets solved it's all good =) –  Patrick Hughes Apr 26 '12 at 16:02

6 Answers 6

I agree with Petr: There is no set way to do it. How you want to do it is a matter of how you want to design your game.

In this circumstance, however, I think it's immediately obvious the sort of mechanic you're trying to get at: you just want things to produce as fast as possible, within the amount of mass you have available.

Producing within capacity

I'm going to take a leaf out of Supreme Commander's book, since you're doing a system very much like theirs: If you're producing above capacity, the neatest way to deal with it is have production slow down across the board. Lowering production capacity is actually pretty simple.

A production speed mechanic

Each update step, your factories don't just produce a set amount: they operate by a production speed, which determines how much progress they make in each step and how much mass they use up. When you're producing at 75% capacity, your factories make 75% as much progress each step and use up 75% the mass compared to 100% capacity.

To calculate the production speed, before building anything at all, you should query your factories to determine the total resources that would be used this step at full capacity. Then you perform a simple calculation:

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

Let's say you need 125 mass this step to produce at full capacity, but only have 100 mass this step. This equation provides you with a production speed of 0.8 (the decimal representation of 80%). When you tell your factories to actually perform their building, you hand them this value to tell them what speed they're building at: and now your production is slowed down across the board.

Alternatives

You could also begin shutting down factories temporarily until production capacity frees up, and it could be very interesting to see that happening to factories farther away from generators when at extremely low capacity.

Multiple resources?

Up to you how you handle this; there are a lot of options. The simplest one is probably to calculate a production capacity for each resource and then pick the lowest one, such that your weakest resource becomes a bottleneck for all the rest.

share|improve this answer
    
I think you don't even need to tell the factory to produce at 80% speed, because whatever your factory creates uses a fixed amount of mass. For example: you build a tank which takes 100 mass to build, normally the factory can use 2 mass every cycle. This means it takes 50 cycles to complete the tank and every cycle you add 2 to the tanks current mass. Now, you only have 1 mass available, this means this cycle, the tanks currently spent mass increases by 1 instead of 2. After each cycle just check the current mass vs the total mass required to see if the tank is completely build or not. –  Thomas Apr 26 '12 at 9:46
    
If you have 0 mass available, just don't add any mass to the tank. The time it takes to build something this way can vary depending on your mass income. if you have 2 factories that can use 2 mass / cycle and only 3 income, tank 1 is build in 50 cycles, tank 2 in 100. Another way is to divide the total amount of mass available over all the factories that use it (are actively building someting). The total amount of mass a factory can use could be upgraded by adding levels to the factory. For example at level 1 they can spend 2 mass, at level 2: 3 mass etc. etc. –  Thomas Apr 26 '12 at 9:47
    
@Thomas Adding mass to a product in the making seems an overly complex way compared to simply completing a percentage of the product. Especially so since your factory must know all about your resource system instead of a simple "production rate" property. If the outcome to the player is the same, keep the implementation as simple as possible. That also makes it easier to make changes in the future, such as when you add/remove resources. –  Hackworth Apr 26 '12 at 10:42
    
@Hackworth I tend to disagree, the factory doesn't need to know about the resource system. The factory only knows what it's building and how far it is. The resource system just tells the factory add X amount to the build. This way you don't need to calculate the percentage of resource income this factory has and you don't need to translate resource income to added completion percentage. –  Thomas Apr 26 '12 at 11:37

Although I like Jonathan Hobbs' answer I think a queue system is even simpler:

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

This will probably work on average in the same way as Jonathan's implementation. However Jonathan's solution might give problems if the workspeed is set very low and my implementation could have a factory with a very high resourcerequest for this frame have it block other factories for several frames.

share|improve this answer
    
+1 I'm developing a game with dual resources, much like in the question, and when I get around to solving the supply lock issue, I plan to use something similar to this. Not that exact code, but the idea behind it. Consumers that are able to use resources in one tick will get a lower priority during the following tick. I also plan to add a flag to indicate if this consumer is high priority, and give control of that flag to the user. –  John McDonald Apr 26 '12 at 15:14
    
Beware of starvation if you give them separate priorities. :) –  Roy T. Apr 27 '12 at 10:05
    
Starvation with separate priorities would be the purpose though. Ever played Settlers 4 or Knights & Merchants? If you over-construct buildings, the limited resources go to random buildings and it takes forever to finish anything. But they also allow you to select whether a building is important or not, in which case, resources will go to those important buildings first. The key is to never over-construct, or if you do, over-construct as little as possible. –  John McDonald Apr 27 '12 at 14:39

I am developing a similar supply system in my own game, so I have also been thinking about how to resolve the supply-lock issue, and favoritism. To illustrate the problem, I'll create a simple example:

If you have a list: [producer1, consumer1, consumer2, consumer3] and you update in order, starting at supply = 0, you'll get this:

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

consumer1 gets all the fun, while consumers 2 and 3 starve until consumer 1 has been satisfied. Depending on your game, this may not be desirable. I know in my game, it's not. When I get around to it, I'm going to create a queue where consumers that have been fed in one tick will move to the back of the queue for the next tick, which I believe is what Roy T. is getting at. The example above would look like this:

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

This way, everyone will get their fair share of the resources.

I also plan to implement an additional queue to be used as a priority queue so that the user can select certain structures to have resource priority. The priority queue will always be served before the standard queue. Make sure that all producers are updated first, then consume all resources second, otherwise the queue will break down when you produce resources part way through a tick and some consumers have already been starved.

So to recap: Update the producers, then the priority queue, moving fed consumers to the end of the priority queue, then update the standard queue, moving fed consumers to the end of the standard queue.

share|improve this answer
    
Taken to the limit, it could mean that no consumer finishes until every consumer can finish. In a realistic production chain scenario, that's quite unlikely, and could be quite disastrous if someone wanted to have a build-queue for a massive army. He'll be practically troop-less for the entire time the army is being built. –  Cardin Apr 27 '12 at 8:11
    
That answer was my initial thought when I read the question. Also, It should be noted that the mass consumed per tick isn't necessarily the mass to create a complete unit, but rather the unit cost over the time required, so I'm not sure that @Cardin's concerns are valid, unless your unit cost per tick is very close to your total gathering rate. I would simply have the priority queue explicitly managed by the player, so that he gets to decide who starves. –  brice Apr 27 '12 at 9:15
    
@Cardin, you are quite correct, but it can also be used as a game mechanic. Settlers 4, Knights & Merchants, Total Annihilation, Supreme Commander are games that I know of that did this. The trick is to not reach this supply lock, and if you do, make sure you get out of the supply lock asap. Adding a priority queue allows people a way to get out more easily. –  John McDonald Apr 27 '12 at 14:49

Well, i'll expand on John's idea, since we discussed this a bit in chat.

edit: This solution is actually only preferable if the consumableAmount is relevant to how often the factory should get a batch of resources. If it's all the same, then you can indeed just use a queue.

My solution: all factories listed in a priority queue. Priority is increased as a factory is suffering from starvation. Starvation, priority, set to zero when factory has consumed resources. Top priority is always going to get the next batch of resources.

On determining which factory gets what resources, in some kind of pseudo code:

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

This way your factories will make 1 product each in turn, if you want to adjust by taking in to account the consumeAmount so that cheaper products are made more frequently, you could increase priority by 1/consumeAmount for example.

share|improve this answer
    
I'd like to upvote this but I'm not sure what this is meant to do - it keeps track of starvation, but otherwise (presumably) just rotates through the queue exactly as normal, and both the starvation and priority never end up appearing to do anything. –  Jonathan Hobbs Apr 26 '12 at 21:50
    
the difference becomes apparent if you increase the priority by += 1/amountThisFactoryConsumes , then the ones which require more resources to create a product will lag behind a little, allowing less expensive ones to take proportionally more batches of resources. This would even out the resource per factory ratio. However, this is not exactly fool proof still, as starvation number is set to magic number 0 each time resources are consumed by factory, so it's not going to be quarenteed to be exactly evenly distributed when resource renewal is flunctuating. –  Toni Apr 27 '12 at 6:07
    
Actually I'm now not so sure if it's actually not guaranteed. I would need to draw some graphs and test it out to be sure. –  Toni Apr 27 '12 at 6:18
    
oh, and the priority is a quality of the priority queue itself. I'm not going to bother explain how to build one. Priority queue itself is always sorted according to the priority value of its members. –  Toni Apr 27 '12 at 6:20
    
+1 Understanding that it's a priority queue, its workings now look simple: sift down through the queue and pick out the soonest thing that could consume resources. If capacity is heavily divided (You have 1,000 resources this tick, but a hundred 100-resource builds) this shouldn't be a problem, and things get fed fairly well. Even if something's well above your resources this tick, it might eventually save up enough to make a bit of progress - which has the impact of slowing it down, or shutting big things out entirely in favour of smaller things which is a good thing. I like this A LOT. :) –  Jonathan Hobbs Apr 27 '12 at 6:55

Strange question.

My problem is that if the resource hits 0 I don't know what to do. Should I make a queue of some sort? Should I skip an object's step. What?

What you need to do, depends on a game logic you create. You can do a queue, you can skip. Depends on how you think your game should behave. Correct me, if I wrong on your question.

share|improve this answer

You can keep a number of the total resource demand per tick for all constructions. If one resource storage hits less than this required amount, then all construction would stop completely until the storage has gathered enough to support at least 1 tick of production. Then production can resume.

So instead of storing production rate as a float, it is binary - either your factory produces at full speed or it doesn't.

That being said, this approach is essentially the same as Jonathan's answer, for the production rate special cases 0.0 and 1.0 - an arbitrary float f with 0.0 <= f <= 1.0 is probably more elegant as you don't get jerky storage amount movements, but the logic should be a bit simpler.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.