Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Why might Twine 2.0 / Sugarcube gameplay slow down as play proceeds?

What can cause a Twine 2 / Sugarcube game to slow down radically as the player loops through a turn structure comprised of several passages?

So as I posted when asking questions last month, I'm making a basic RPG in Twine 2.0, and have switched over to Sugarcube. I've got the basics working, enough that I can playtest. (Which is awesome! Thanks for your direct help, and for all the great posts I've read here!) What I've run into is this: the longer a game goes, the slower it runs. After ~30 minutes of play, it's taking more than a second to display even simple passages, which is de facto unplayability. (I have used the custom CSS explained in in other threads to remove the transitions between passages.)

My basic structure is like many of the "let's make a basic RPG in Twine" tutorials out there. There's a main passage which the game returns to each turn, with side passages to interact with characters, buy and equip items, etc. Once satisfied, the player ends the turn and the game proceeds through a series of passages to the next turn. In code terms, it's all very simple if/elseif, click/replace, and for loops through which the game selects the appropriate character to affect.

The text count is still under 50,000 words and there's fewer than 100 passages, so I doubt it's that. Also, I experimented by creating an advanced-start function with many characters and inventory items present, and that doesn't start slow -- suggesting to my novice understanding that the issue is aggravated by the number of turn cycles played through, not the number of variables in the character and inventory arrays or the number of for loops happening. The issue persists if the game is saved and reloaded using Sugarcube's save function.

I am way too new to this to be suggesting possible explanations, but there are a few things I jury rigged that I suspect might have something to do with it just because they're outside what the tutorials recommend. First, the "next" button in game is always located in the Sugarcube sidebar, with code in the StoryCaption passage that looks at $nextPassage to decide where to send the player next. It works, but perhaps the passage-by-passage changes to where that button goes are a problem? Also, the sets of variables that comprise characters are perpetually being moved in and out of $characters and $activeCharacter. Can the constant use of
<<set $characters[$i] to $activeCharacter>>
inside a for loop that finds the value of $i for which $activeCharacter.name is $characters[$i].name somehow produce enough spagetti to slow gameplay down? That can easily happen 20+ times per turn, but that isn't an issue until several turns are played. Or something else?

I am at a loss. Thoughts?

Comments

  • edited August 2015
    This is impossible to debug without actually seeing the code. In general slowdowns happen due to too many recursive functions and making things too complex than needed. Excessive use of <<print>> can also prove problematic.

    I'm at a loss for imagining why you need to change characters over 20 times a turn, though.
  • Have you disabled History Tracking (config.disableHistoryTracking)?

    Basically by default each time a story moves from one passage to another a snapshot of all the current variables is made and this snapshot as well as information about the previous passage shown is added to History. In a story like a RPG game the History can become quite large/long which could over time slow things down.

    Do you use the visited() function, and if so do you use it a lot or in many places?
    This function loops through History so the larger/longer that history is the longer it takes to loop through it which could also slow things down.
  • I'm actually physically away from the code at the moment, so I will check these things out when I get back to it.
    I'm at a loss for imagining why you need to change characters over 20 times a turn, though.

    Because most of the code that affects a character affects $activeCharacter, which is dumped back into $characters (based on matching $activeCharacter.name to the $character[$i].name that should be overwritten) when the changes to that character's variables are complete. This system is based on advice here. It runs as many times as a character's variables are affected by the player. I don't see how this could be the problem: it's just a simple for loop that finds $character[$i] for which $character[$i].name is $activeCharacter.name and overwrites $character[$i] with $activeCharacter.name. At no time have I tried more than 10 characters, so I do not see why looping through 10 times would cause a huge slowdown.

    I emphasize that the slowdown affects everything, including passages that do not affect characters at all. It gets to the point that even simple passage-to-passage links that involve no variables at all are slow -- it's like the game is doing an immense amount of stuff in the background that I don't want it doing.
    Do you use the visited() function, and if so do you use it a lot or in many places?

    Not at all.
    Have you disabled History Tracking (config.disableHistoryTracking)?

    I will try this, but does History Tracking apply to a published game file being played in browser as an .html? I tested it, and the problem applies equally there, too. 200+ variables in total being changed every turn could very easily be the culprit, if it can.
  • edited August 2015
    config.disableHistoryTracking (I assume you're using v1.x here) is a SugarCube run-time configuration setting, it affects all instances. Enabling it limits the history to a single state, which disables everything related to history navigation (the browser's back/forward buttons, Rewind menu, and <<back>>/<<return>> macros), but also solves "big history" issues.

    At a guess, since I've not seen the code, the likely culprit is the size of the history serialization (some combination of the number of history states and what's being stored in each state's $variable store).

    If you'd like, I could examine an exported save to see if this is the case. Just play to a point where things are lagging badly, export a save to disk, and then upload it somewhere I can look at it.
  • edited August 2015
    You can also improve passage load speeds by using the PassageDone special passage for anything not strictly relating to passage text.

    My game is extremely graphic heavy with multiple background layers and I was getting slowdowns when I had graphic changes coded into the passages themselves. I put the graphics code into the PassageDone special passage and it cleared it up.

    E.g. if in a passage where characters visited the Town Hall you might write <<set $townhall to true>> then in PassageDone you could write:
    <<if $townhall>><<townhallwidget>><</if>>
    

    This will execute the <<townhallwidget>> after the passage loads its data, preventing <<townhallwidget>> from getting in the way of the display data.

    But obviously the code being loaded this way should not be related to the actual passage display text - by the time PassageDone executes, the passage text is all locked in. That's why it's good for things like background layers, sounds, or fancy math calculations that won't affect the current passage display.

Sign In or Register to comment.