4

I have objects, and each object has a function pointer that will be assigned a function. When the object is clicked, the assigned function will be called. I want to have a function do things with delay, meaning it would look something like this:

void function() {
    doSomething();
    wait(1000);     // either 1000 frames, or 1000 miliseconds
    doSomethingElse();
}

I heard threading shouldn't be abused, and main script should all be put in the same thread. There are lots of game engines out there that can handle such functions with a delay in time. My idea was to call the function in a new thread every time.

Is this a bad idea? If it is, how else would I manage to achieve this functionality?

Edit: the website asked to confirm it's not a duplicate question and I think it's not because it's a bit more complicated of an issue, even though it may use a similar solution.

Dave Jecs
  • 41
  • 3

1 Answers1

4

You could try a central Job manager that performs jobs in an asynchronous way.

The skeleton of the feature could look like this bastard c++/pseudocode:

struct Job
  double mTimer;
  std::function<void>() mFunction;

class JobManager
  addJob(Job job) { mJobs.insert( job ); }

  std::list<Job> mJobs;

  perFrameExecuteJob(deltaTime){
    // Iterate through all the jobs that are to be done, reduce the time left by the dt.
    // If the delay has expired, perform the job, and remove it from the set of jobs to 
    // do. 
    std::list<Job>::iterator it = mJobs.begin();

    while( it != mJobs.end() ) {
      it->mTimer -= deltaTime;
      if ( it->mTimer <= 0 ) {
        it->mFunction();
        it = mJobs.remove(it);
      }
      else {
        it++;
      }
    }
  }

Basically, a Job represents a unit of work and it can be delayed.

You'd do something like this to add a Job to the JobManager:

void function(JobManager& aJobManager){
    doSomething();
    Job job;
    job.mTimer = 1000;
    job.mFunction = doSomethingElse;
    aJobManager.addJob( job );
}

And you'd call

mJobManager.perFrameExecuteJob( dt ); 

every frame from your main loop.

Vaillancourt
  • 16,325
  • 17
  • 55
  • 61
  • I have seen this used before, but isn't it terribly inefficient? If I had a ton of such functions, wouldn't it slow down the program a lot more than threads? – Dave Jecs May 05 '17 at 11:45
  • @DaveJecs I guess you'll have to test it. I guess with this implementation, the price to pay is that you'll get some cache misses, while with threads you'll need a context switch.. – Vaillancourt May 05 '17 at 12:13
  • You do not need to iterate through all jobs to be performed, or alter timestamps in the list. Let them be static, somewhere in future. Instead sort the list when inserting a new job (happens more sparsely), so the closest upcoming is topmost. Then just check the (advancing) game clock/whatever against the topmost/first job item only. Takes 3.14 nanosecs. If elapsed, then execute. This also means max 1 job will be executed per frame (if doing this work in thread 0), which may be good for balancing. – Stormwind May 05 '17 at 14:25
  • @Stormwind You're right, there are a couple of ways to implement this. – Vaillancourt May 05 '17 at 14:41
  • @Stormwind good idea, I'll try that out. On a side note, I made a rough test how much my laptop can go up to (it's pretty old) and it seems to be able to draw up to around 900 64x64 images/frame and keep the 60 frames per second (only drawing, nothing more). That is a lot more than I expected, so I guess performance may not be that much of an issue. – Dave Jecs May 06 '17 at 00:32
  • @DaveJecs Generally, unless you know you'll run into performance issue, it's better to implement a feature, run a profiler and improve the performance only if you notice a bottleneck there. Otherwise it's generally considered as premature optimization and a waste of time/energy/money. – Vaillancourt May 06 '17 at 00:37
  • @Dave Jecs Working on old hw often produces well-performing code. Afaik, games are still mostly fillrate-limited. The very important thing is to never ever have the runtime-code execute anything unnecessary, so cache, pre-calc, re-use, schedule (your original issue) are good things. As is adding fog :-). – Stormwind May 06 '17 at 20:37
  • @Stormwind thanks :) What do you mean by adding fog though? – Dave Jecs May 11 '17 at 10:17
  • @DaveJecs If there are tricky things that are hard or impossible to implement, hide them (behind the fog) => get rid of the problem. Example: A foggy blurry scenario with a right color composition may look much better than a pure/mathematically correct/detailed one (that is hard to implement). This is a matter of psychology and human perception. One must always scale the effort to the importance of the detail. When talking about your original matter, scheduling, an example: Do you need to update a distant character 60 times/s, or is it sufficient blur a bit and update every 10 seconds? – Stormwind May 11 '17 at 12:17