2
\$\begingroup\$

I have seen guides on how to implement a loading screen using more than one thread. But sure it must be possible to do so with one thread, since older (even 2D) games that run on single-threaded machines had loading screens as well.

So, how do you do it?

\$\endgroup\$
  • \$\begingroup\$ This question asks about emulating the behaviour of multi threading. There is also no real use for having its own tag. I am reverting the edit to this post, as multithreading seems suitable; an expert in multithreading should be aware of similar implementations where multi threading is not supported. \$\endgroup\$ – Gnemlock May 11 '17 at 14:06
3
\$\begingroup\$

You basically have to break down the loaded data into smaller chunks, and intersperse UI updates in between those bits of processing. The finer the granularity of the chunks of data you synchronously load / process, the more frequently your UI can update. In essence, poor man's multi-threading. (This is how it was done before the advent of multi-core machines.)

Breaking down into smaller chunks may well not be possible unless you do one or more of:

  • Gut any system libraries for loading and substitute your own logic
  • Restructure your data (possibly a major change).

So you'd do something like:

while (chunksRemaining > 0)
{
    LoadNextChunk();
    UpdateUI();
}

Notice that the LoadNextChunk() call could take nanoseconds or whole seconds, but prevents us proceeding to UpdateUI() until Chunk's processing is done. So ensure LoadNextChunk() runs fast!

\$\endgroup\$
  • \$\begingroup\$ P.S. This is a form of time-slicing. \$\endgroup\$ – Engineer Aug 20 at 14:27
1
\$\begingroup\$

In very, very generic terms, you write something like this (pseudo-code):

while (!isNewLevelLoaded) {
    UpdateAndRenderLoadingScreen(percentageOfNewLevelLoaded);
    LoadNewLevelBytes(numberOfBytesTOLoad);
}

LoadNewLevelBytes (int bytes) {
   for (int i = 0; i < bytes; i++) {
        LoadByte();
    }
    if (endOfFileReached) isNewLevelLoaded = true;
}
\$\endgroup\$
  • \$\begingroup\$ Hmm. You've made an assumption about the format the OP is loading in. What if, for example, they're using JSON or XML, or some other externally-defined format? We can't necessarily assume a byte-loader. \$\endgroup\$ – Engineer May 10 '17 at 17:40
  • 1
    \$\begingroup\$ I used bytes just to show that he has to decide how big must be the next chunk of data to load between two updates of the loading screen. \$\endgroup\$ – Galandil May 10 '17 at 17:55
  • 1
    \$\begingroup\$ @ArcaneEngineer It's an abstract example. LoadNewLevelBytes could easily be LoadJson or LoadXml. \$\endgroup\$ – Pharap May 11 '17 at 16:51
1
\$\begingroup\$

I'm not sure what kind of "single-threaded machines" you have in mind, but you could also have a single-threaded programme running on a system that supports asynchronous I/O. In this case there is stuff happening in parallel with your code being executed but it's handled by the OS.

So in pseudocode your programme might look like this:

for (fileToLoad) {
    asyncRead(fileToLoad);
}

while(numberOfFilesLoaded != numberOfFilesToLoad) {
    updateLoadingScreen();
}

// all files loaded here
\$\endgroup\$
0
\$\begingroup\$

Some languages provide coroutines, which are similar to threads in that they allow a degree of multi-tasking, but can actually exist on one thread.

A couroutine is essentially a block of code that at some point decides to yield back to its caller, so you get code like this:

(Hypothetical Lua code)

function loadFile(path)
  --code
end

function loadFiles(paths)
  for i, file in ipairs(fileList) do
    local result = loadFile(file)
    coroutine.yield(result)
  end
end

function updateLoadingScreen()
--code
end

function drawLoadingScreen()
--code
end

function loadingScreen(files)
  updateLoadingScreen()
  drawLoadingScreen()
  local loadFilesCo = coroutine.create(loadFiles)
  local results = {}
  while coroutine.status(loadFileCo) ~= "dead" do
    local success, result = coroutine.resume(loadFilesCo, files)
    if ~success then error(result) end
    table.insert(results, result)
    updateLoadingScreen()
    drawLoadingScreen()
  end
  return results
end
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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