MindWare’s New Loading Screen and Why It’s Important

I’ve recently implemented a loading screen for an upcoming version of MindWare, and I would like to take a few moments to explain why and share the solution with you.

The Problem

One of the biggest and most persistent issues I’ve been dealing with (responsible for most bug reports I’ve received so far) has been missing or improperly initialized variables when starting a new game or loading a save. This can cause all sorts of weird behavior and frustrating bugs for players.

When a person with limited understanding of how the Twine engine works and virtually no experience with the creation of larger interactive fiction games sets out to create a game like MindWare, they may start by placing important variables in the StoryInit passage. All variables placed in this passage are initialized when you start a new game.

The same inexperienced developer may then add additional variables to the StoryInit passage, such as variables for a new part of the UI, and release a new version of their game. You then load your old save and are greeted with an error message because your save is missing the newly added variables. To fix the error, you would have to start a new game—not desirable at all.

The Solution

I’m not the first person in the world to have dealt with this problem (see here and here, for example), so my solution isn’t something groundbreaking, but I think that it’s polished and reusable enough that others might benefit from it. I also hope to receive feedback from those how are more knowledgable than me.

How it works:

  1. When a new game starts or a save is loaded, a function is called.
  2. This function navigates to a special variable initialization passage.
  3. The variable initialization passage displays a loading screen and includes passages with variables to be initialized (being able to divide variables in to different passages makes it easier to stay organized).
  4. A simulated loading process updates the progress bar.
  5. Once “loading” is complete, the player is returned to either the previous passage (for loaded games) or the Start passage (for fresh playthroughs).

With this solution, I’m able to load variables (most of them anyway) that I have been very generously collected by a Discord user and MindWare supporter UnwrappedGodiva (thank you again!). These variables provide a solid starting point on which I can expand. More importantly, I now have a way to declare future global variables in a way that won’t break old saves.

The Code

This script sets up the logic for when to run the initialization process:

:: Scripts-StartingVariables [script]
// Initialize a flag to track whether StartingVariables has run
setup.startingScriptsRun = false;

// Function to run StartingVariables
setup.runStartingVariables = function() {
    if (!setup.startingScriptsRun) {
        State.variables.previousPassage = State.passage;
        setup.startingScriptsRun = true;
    } else {
    }
};

// Run for new games
$(document).one(':passagestart', function (ev) {
    if (ev.passage.title === "Start") {
        setTimeout(setup.runStartingVariables, 0);
    } else {
    }
});
// Alternatively, you can delete the block of code above and call the runStartingVariables function directly from the Start passage like this:
// <<run setTimeout(setup.runStartingVariables, 0); >>

// Run for loaded games
Save.onLoad.add(function () {
    setup.startingScriptsRun = false;
    setTimeout(setup.runStartingVariables, 0);
});

This is the actual loading screen passage:

:: StartingVariables 
<<nobr>><div id="loading-screen">
  <div class="loading-content">
    <div class="loading-bar-container">
      <div id="loading-bar"></div>
    </div>
    <div class="loading-text">INITIALIZING...</div>
  </div>
</div><</nobr>>

// Include as many passages with variables as you want here
// You can also add your variables directly here 
<<include "UI Variables">>

<script>
(function() {
    // Simulate loading process
    var progress = 0;
    var loadingBar = document.getElementById('loading-bar');
    var loadingInterval = setInterval(function() {
        progress += Math.random() * 10;
        if (progress >= 100) {
            progress = 100;
            clearInterval(loadingInterval);
            setTimeout(function() {
                // Return to the previous passage
                SugarCube.Engine.play(SugarCube.State.variables.previousPassage || "Start");
            }, 500);
        }
        loadingBar.style.width = progress + '%';
    }, 100);
})();
</script>

A passage with variables to load:

:: UI Variables
<<if ndef $scrollToTopEnabled>>
    <<set $scrollToTopEnabled to true>>
<</if>>

The CSS code for the loading screen:

#loading-screen {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: #000;
    z-index: 9999;
    display: flex;
    justify-content: center;
    align-items: center;
}

.loading-content {
    width: 80%;
    max-width: 600px;
}

.loading-bar-container {
    width: 100%;
    height: 20px;
    background-color: #111;
    border: 2px solid #55BBBC;
    box-shadow: 0 0 10px #55BBBC;
}

#loading-bar {
    width: 0;
    height: 100%;
    background-color: #55BBBC;
    transition: width 0.3s ease;
}

.loading-text {
    color: #55BBBC;
    font-family: 'Orbitron', sans-serif;
    font-size: 24px;
    text-align: center;
    margin-top: 20px;
    text-shadow: 0 0 10px #55BBBC;
}

If you’ve noticed any mistakes or areas that could be improved, please share them with me here or on Discord. Any feedback I receive is greatly appreciated.

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top