Loading screen while TGB starts.
by Nicolas Olhaberry · in Torque Game Builder · 03/21/2007 (12:42 pm) · 12 replies
Hi guys,
I'm developing a game which is now around 30MB zipped. The thing is when I run the exe I get like 45 seconds of a black screen before the actual game starts running.
I already load most of the assets dynamically, so I'm not sure why it takes that much time to start the game. The game has like 65 torquescript classes, so maybe the problem is there. Can you guys give me any advice on how is possible to reduce the time it takes to start the actual game?
Also I would like to display a loading screen with a progress bar instead of just showing a black screen and I don't have any idea of how to do it. I own the torque TGB source code, but if it's possible I would prefer to do it with torquescript, since I haven't messed with the source yet.
Thanks in Advance,
Nicolas.
I'm developing a game which is now around 30MB zipped. The thing is when I run the exe I get like 45 seconds of a black screen before the actual game starts running.
I already load most of the assets dynamically, so I'm not sure why it takes that much time to start the game. The game has like 65 torquescript classes, so maybe the problem is there. Can you guys give me any advice on how is possible to reduce the time it takes to start the actual game?
Also I would like to display a loading screen with a progress bar instead of just showing a black screen and I don't have any idea of how to do it. I own the torque TGB source code, but if it's possible I would prefer to do it with torquescript, since I haven't messed with the source yet.
Thanks in Advance,
Nicolas.
#2
03/21/2007 (4:51 pm)
Thanks David for your respone. But I'm not doing a loadlevel at the beginning of the game, it just goes to a main menu screen. The game is not really doing much up to that point, but still I get a 45 seconds waiting. To me it looks like that's just the time it takes to the TGB engine to load itself, my 65 scripts and like 20 png files (all the other assets are loaded dynamically while you play the game).
#3
How long does it take for TGB to start, by itself -- if you launch it into a blank project with no resources or scripts (other then main.cs and game.cs) ... from start to finish -- cause it could also just be your workstation ... I can et TGB to launch in under 2 seconds with a blank project, and a built game, such as the scroller demo ... launches in ~1-2s ...
If you can post your game.cs, or at least your startGame function -- and give the answer to the timing question, we can probably figure this out ... :)
03/21/2007 (7:33 pm)
@Nicolas, can you post your 'game.cs' ... if your loading any of these 65 scripts before you display your main menu ... or from within the scope of the startGame ... that could cause the hesitation ...How long does it take for TGB to start, by itself -- if you launch it into a blank project with no resources or scripts (other then main.cs and game.cs) ... from start to finish -- cause it could also just be your workstation ... I can et TGB to launch in under 2 seconds with a blank project, and a built game, such as the scroller demo ... launches in ~1-2s ...
If you can post your game.cs, or at least your startGame function -- and give the answer to the timing question, we can probably figure this out ... :)
#4
Here's my game.cs, as you can see, I am loading all the scripts at the beginning, I didn't knew there was any other way. Is is possible to load and unload scripts as needed just like I'm already doing for game assets?
Just to clarify, you can see I'm running a few scripts at the beginning of the game, but it is nothing heavy that would justify 45 seconds of waiting.
//---------------------------------------------------------------------------------------------
// Torque Game Builder
// Copyright (C) GarageGames.com, Inc.
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
// startGame
// All game logic should be set up here. This will be called by the level builder when you
// select "Run Game" or by the startup process of your game to load the first level.
//---------------------------------------------------------------------------------------------
exec("~/gui/arrest.gui");
exec("~/gui/debugWarpLevel.gui");
exec("~/gui/logoSplash.gui");
exec("~/gui/mainMenu.gui");
exec("~/gui/worldMap.gui");
exec("~/gui/welcome.gui");
exec("~/gui/diamond.gui");
exec("~/gui/suspectsIntro.gui");
exec("~/gui/worldMapIntro.gui");
exec("~/gui/timeOver.gui");
exec("~/gui/suspectSelection.gui");
exec("~/gui/suspectReportCard.gui");
exec("~/gui/processing.gui");
exec("~/gui/director.gui");
exec("~/gui/cityName.gui");
exec("~/gui/criminalWasHere.gui");
exec("~/gui/criminalIsHere.gui");
exec("~/gui/whereIsDiamond.gui");
exec("~/gui/whoHasDiamond.gui");
exec("~/gui/end.gui");
exec("~/gui/suspectNotHere.gui");
exec("~/gui/pause.gui");
exec("~/gui/tutorial.gui");
exec("~/gui/gameOver.gui");
exec("~/gui/chapterStart.gui");
exec("~/gui/caseClosed.gui");
exec("./CConst.cs");
exec("./CGameConfig.cs");
exec("./CAudio.cs");
exec("./CLevelBackground.cs");
exec("./CClueType.cs");
exec("./CClueTypeManager.cs");
exec("./CClue.cs");
exec("./CClueManager.cs");
exec("./CPieceType.cs");
exec("./CPieceTypeManager.cs");
exec("./CPiece.cs");
exec("./CCellBackground.cs");
exec("./CCell.cs");
exec("./CCellManager.cs");
exec("./CCluePanelClue.cs");
exec("./CCluePanel.cs");
exec("./CSelection.cs");
exec("./CScore.cs");
exec("./CLevel.cs");
exec("./CParticleManager.cs");
exec("./CClock.cs");
exec("./CIntermission.cs");
exec("./CScene.cs");
exec("./CDestinationType.cs");
exec("./CDestinationTypeManager.cs");
exec("./CPlane.cs");
exec("./CWorldMap.cs");
exec("./CMovingScore.cs");
exec("./CMovingScoreManager.cs");
exec("./CAllowTimePass.cs");
exec("./CTime.cs");
exec("./CData.cs");
exec("./CClickable.cs");
exec("./CDisplayClues.cs");
exec("./CSuspectType.cs");
exec("./CSuspectTypeManager.cs");
exec("./CSuspectSelection.cs");
exec("./CSuspectChoice.cs");
exec("./CHint.cs");
exec("./CArrest.cs");
exec("./CArrestAllowTimePass.cs");
exec("./CAssetLoader.cs");
exec("./CPause.cs");
exec("./CFirstWorldMapTutorial.cs");
exec("./CFirstSuspectSelectionTutorial.cs");
exec("./CFirstLevelTutorial.cs");
exec("./CBoxDebris.cs");
exec("./CBoxDebrisManager.cs");
exec("./CStorage.cs");
exec("./CAccount.cs");
exec("./CMainMenu.cs");
exec("./CProfileManager.cs");
exec("./CGlass.cs");
exec("./CGlassManager.cs");
exec("./CGlassDebris.cs");
exec("./CGlassDebrisManager.cs");
exec("./CKey.cs");
exec("./CKeyManager.cs");
exec("./CCaseClosed.cs");
exec("./CInputHandler.cs");
exec("./CTextBox.cs");
exec("./CBitmapFont.cs");
exec("./CBitmapFontManager.cs");
exec("./CMagnifyingGlass.cs");
exec("./CMagnifyingGlassManager.cs");
function startGame(%level)
{
// Set The GUI.
Canvas.setContent(mainScreenGui);
Canvas.setCursor(DefaultCursor);
moveMap.push();
$gLevel = $gConst.NULL;
$gProfileManager = new ScriptObject(CProfileManager);
$gProfileManager.init();
$gConst = new ScriptObject(CConst);
$gConst.init();
$gGameConfig = new ScriptObject(CGameConfig);
$gGameConfig.init();
$gBitmapFontManager = new ScriptObject(CBitmapFontManager);
$gBitmapFontManager.init();
$gStorage = new ScriptObject(CStorage);
$gStorage.init();
$gAccount = new ScriptObject(CAccount);
$gAccount.init();
$gData = new ScriptObject(CData);
$gData.init();
$gAssetLoader = new ScriptObject(CAssetLoader);
$gAssetLoader.init();
$gAudio = new ScriptObject(CAudio);
$gAudio.init();
$gClueTypeManager = new ScriptObject(CClueTypeManager);
$gClueTypeManager.init();
$gDestinationTypeManager = new ScriptObject(CDestinationTypeManager);
$gDestinationTypeManager.init();
$gSuspectTypeManager = new ScriptObject(CSuspectTypeManager);
$gSuspectTypeManager.init();
$gSuspectChoice = new ScriptObject(CSuspectChoice);
$gSuspectChoice.init();
$gMainMenu = new ScriptObject(CMainMenu);
$gMainMenu.init();
if (!$gStorage.loadPrefs())
{
$gStorage.createAllFiles();
%ret = $gStorage.selectOrNameAccount();
if (%ret)
{
$gMainMenu.mForceNameAccount = true;
}
}
$gScene = new ScriptObject(CScene);
$gScene.init();
if ($gGameConfig.getDisplayFPS())
{
metrics(fps);
}
if (isFullScreen() != $gGameConfig.getFullScreen())
{
toggleFullScreen();
}
}
//---------------------------------------------------------------------------------------------
// endGame
// Game cleanup should be done here.
//---------------------------------------------------------------------------------------------
function endGame()
{
$gScene.destroy();
$gMainMenu.destroy();
$gSuspectChoice.destroy();
$gSuspectTypeManager.destroy();
$gDestinationTypeManager.destroy();
$gClueTypeManager.destroy();
$gAudio.destroy();
$gAssetLoader.destroy();
$gData.destroy();
$gAccount.destroy();
$gStorage.destroy();
$gBitmapFontManager.destroy();
$gGameConfig.destroy();
$gConst.destroy();
$gProfileManager.destroy();
sceneWindow2D.endLevel();
moveMap.pop();
}
function startNewGame(%level)
{
if( isFile( %level ) || isFile( %level @ ".dso"))
{
sceneWindow2D.loadLevel(%level);
}
}
03/22/2007 (12:23 pm)
David, if I start an empty project, or load the Fish Demo, for instace, the built project launches in around two seconds, just as it does for you. So I don't think I have a problem with my workstation, it has to be some problem with the project itself.Here's my game.cs, as you can see, I am loading all the scripts at the beginning, I didn't knew there was any other way. Is is possible to load and unload scripts as needed just like I'm already doing for game assets?
Just to clarify, you can see I'm running a few scripts at the beginning of the game, but it is nothing heavy that would justify 45 seconds of waiting.
//---------------------------------------------------------------------------------------------
// Torque Game Builder
// Copyright (C) GarageGames.com, Inc.
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
// startGame
// All game logic should be set up here. This will be called by the level builder when you
// select "Run Game" or by the startup process of your game to load the first level.
//---------------------------------------------------------------------------------------------
exec("~/gui/arrest.gui");
exec("~/gui/debugWarpLevel.gui");
exec("~/gui/logoSplash.gui");
exec("~/gui/mainMenu.gui");
exec("~/gui/worldMap.gui");
exec("~/gui/welcome.gui");
exec("~/gui/diamond.gui");
exec("~/gui/suspectsIntro.gui");
exec("~/gui/worldMapIntro.gui");
exec("~/gui/timeOver.gui");
exec("~/gui/suspectSelection.gui");
exec("~/gui/suspectReportCard.gui");
exec("~/gui/processing.gui");
exec("~/gui/director.gui");
exec("~/gui/cityName.gui");
exec("~/gui/criminalWasHere.gui");
exec("~/gui/criminalIsHere.gui");
exec("~/gui/whereIsDiamond.gui");
exec("~/gui/whoHasDiamond.gui");
exec("~/gui/end.gui");
exec("~/gui/suspectNotHere.gui");
exec("~/gui/pause.gui");
exec("~/gui/tutorial.gui");
exec("~/gui/gameOver.gui");
exec("~/gui/chapterStart.gui");
exec("~/gui/caseClosed.gui");
exec("./CConst.cs");
exec("./CGameConfig.cs");
exec("./CAudio.cs");
exec("./CLevelBackground.cs");
exec("./CClueType.cs");
exec("./CClueTypeManager.cs");
exec("./CClue.cs");
exec("./CClueManager.cs");
exec("./CPieceType.cs");
exec("./CPieceTypeManager.cs");
exec("./CPiece.cs");
exec("./CCellBackground.cs");
exec("./CCell.cs");
exec("./CCellManager.cs");
exec("./CCluePanelClue.cs");
exec("./CCluePanel.cs");
exec("./CSelection.cs");
exec("./CScore.cs");
exec("./CLevel.cs");
exec("./CParticleManager.cs");
exec("./CClock.cs");
exec("./CIntermission.cs");
exec("./CScene.cs");
exec("./CDestinationType.cs");
exec("./CDestinationTypeManager.cs");
exec("./CPlane.cs");
exec("./CWorldMap.cs");
exec("./CMovingScore.cs");
exec("./CMovingScoreManager.cs");
exec("./CAllowTimePass.cs");
exec("./CTime.cs");
exec("./CData.cs");
exec("./CClickable.cs");
exec("./CDisplayClues.cs");
exec("./CSuspectType.cs");
exec("./CSuspectTypeManager.cs");
exec("./CSuspectSelection.cs");
exec("./CSuspectChoice.cs");
exec("./CHint.cs");
exec("./CArrest.cs");
exec("./CArrestAllowTimePass.cs");
exec("./CAssetLoader.cs");
exec("./CPause.cs");
exec("./CFirstWorldMapTutorial.cs");
exec("./CFirstSuspectSelectionTutorial.cs");
exec("./CFirstLevelTutorial.cs");
exec("./CBoxDebris.cs");
exec("./CBoxDebrisManager.cs");
exec("./CStorage.cs");
exec("./CAccount.cs");
exec("./CMainMenu.cs");
exec("./CProfileManager.cs");
exec("./CGlass.cs");
exec("./CGlassManager.cs");
exec("./CGlassDebris.cs");
exec("./CGlassDebrisManager.cs");
exec("./CKey.cs");
exec("./CKeyManager.cs");
exec("./CCaseClosed.cs");
exec("./CInputHandler.cs");
exec("./CTextBox.cs");
exec("./CBitmapFont.cs");
exec("./CBitmapFontManager.cs");
exec("./CMagnifyingGlass.cs");
exec("./CMagnifyingGlassManager.cs");
function startGame(%level)
{
// Set The GUI.
Canvas.setContent(mainScreenGui);
Canvas.setCursor(DefaultCursor);
moveMap.push();
$gLevel = $gConst.NULL;
$gProfileManager = new ScriptObject(CProfileManager);
$gProfileManager.init();
$gConst = new ScriptObject(CConst);
$gConst.init();
$gGameConfig = new ScriptObject(CGameConfig);
$gGameConfig.init();
$gBitmapFontManager = new ScriptObject(CBitmapFontManager);
$gBitmapFontManager.init();
$gStorage = new ScriptObject(CStorage);
$gStorage.init();
$gAccount = new ScriptObject(CAccount);
$gAccount.init();
$gData = new ScriptObject(CData);
$gData.init();
$gAssetLoader = new ScriptObject(CAssetLoader);
$gAssetLoader.init();
$gAudio = new ScriptObject(CAudio);
$gAudio.init();
$gClueTypeManager = new ScriptObject(CClueTypeManager);
$gClueTypeManager.init();
$gDestinationTypeManager = new ScriptObject(CDestinationTypeManager);
$gDestinationTypeManager.init();
$gSuspectTypeManager = new ScriptObject(CSuspectTypeManager);
$gSuspectTypeManager.init();
$gSuspectChoice = new ScriptObject(CSuspectChoice);
$gSuspectChoice.init();
$gMainMenu = new ScriptObject(CMainMenu);
$gMainMenu.init();
if (!$gStorage.loadPrefs())
{
$gStorage.createAllFiles();
%ret = $gStorage.selectOrNameAccount();
if (%ret)
{
$gMainMenu.mForceNameAccount = true;
}
}
$gScene = new ScriptObject(CScene);
$gScene.init();
if ($gGameConfig.getDisplayFPS())
{
metrics(fps);
}
if (isFullScreen() != $gGameConfig.getFullScreen())
{
toggleFullScreen();
}
}
//---------------------------------------------------------------------------------------------
// endGame
// Game cleanup should be done here.
//---------------------------------------------------------------------------------------------
function endGame()
{
$gScene.destroy();
$gMainMenu.destroy();
$gSuspectChoice.destroy();
$gSuspectTypeManager.destroy();
$gDestinationTypeManager.destroy();
$gClueTypeManager.destroy();
$gAudio.destroy();
$gAssetLoader.destroy();
$gData.destroy();
$gAccount.destroy();
$gStorage.destroy();
$gBitmapFontManager.destroy();
$gGameConfig.destroy();
$gConst.destroy();
$gProfileManager.destroy();
sceneWindow2D.endLevel();
moveMap.pop();
}
function startNewGame(%level)
{
if( isFile( %level ) || isFile( %level @ ".dso"))
{
sceneWindow2D.loadLevel(%level);
}
}
#5
If you want a moving progress bar, at the very least you'd need to add a GuiProgressCtrl (or something of your own design that does something similar) and in between each of your ~65 'exec' script calls, update the bar width and do repaint() again, but that's crude.
A "right" way to do this in script is to create a set of functions that act like a queue, where you'd call a function that queues up filenames that should be exec'd then a single call at the end to kick off the queue, looping through all the filenames and exec them, but pausing briefly in between to update the progress bar and allow the game process to tick (so mouse cursor can be updated, etc.). A parellel solution could be done in the engine code itself too.
03/22/2007 (1:38 pm)
You could get a splash screen with a static 'Loading...' message by execing and Canvas.pushDialog() one Gui containing the graphic before all your script exec calls. You may want to call Canvas.repaint() right after to force the screen redraw.If you want a moving progress bar, at the very least you'd need to add a GuiProgressCtrl (or something of your own design that does something similar) and in between each of your ~65 'exec' script calls, update the bar width and do repaint() again, but that's crude.
A "right" way to do this in script is to create a set of functions that act like a queue, where you'd call a function that queues up filenames that should be exec'd then a single call at the end to kick off the queue, looping through all the filenames and exec them, but pausing briefly in between to update the progress bar and allow the game process to tick (so mouse cursor can be updated, etc.). A parellel solution could be done in the engine code itself too.
#6
I would run safe to assume that you probably don't need all, if not most, of those scripts while the user is browsing the main menu or viewing the splash screen ... so you could perform those operations prior to loading the scripts that you do need at startup, then load the rest of the scripts on an 'as needed' basis ...
03/22/2007 (6:35 pm)
@Nicolas, if your aware that you do not need certain scripts until specific points in your game, you can load them when you need them ... like for example, if any of the scripts contain level specific code ... for say, level 5 of your game ... load it when you load level 5 ... I would run safe to assume that you probably don't need all, if not most, of those scripts while the user is browsing the main menu or viewing the splash screen ... so you could perform those operations prior to loading the scripts that you do need at startup, then load the rest of the scripts on an 'as needed' basis ...
#7
If you move the exec lines inside of startgame, for example, you'll most likely experience a different result ... and if you eliminate the unnecessary exec's that are not needed for main menu operation, and put them later in the code trail ... you'll notice an even better flow ...
03/22/2007 (6:38 pm)
Also, you might want to move the "exec" lines into a function, this is why it takes so long for anything to appear on screen ... the game engine loads up your main.cs, then the main.cs loads the game.cs ... when it compiles the game.cs it also loads it at that time ... and your basically saying "at the point that the game.cs is loaded, also load these additional files..." ... which, at this point, is unnecessary ... If you move the exec lines inside of startgame, for example, you'll most likely experience a different result ... and if you eliminate the unnecessary exec's that are not needed for main menu operation, and put them later in the code trail ... you'll notice an even better flow ...
#8
03/23/2007 (1:32 pm)
Thanks David and Luke, your suggestions were very helpful. I'm gonna try to write it right now and will let you know how it went. One thing that I still would like to know is how to figure out a file size. The doc on the Filesystem TorqueScript Commands doesn't seem to have any function for that. I want to use the file size to figure out which percentage on the loading bar each file should have.
#9
It would be suggested to get take the number of files to load, and use that as the 'max' value for your progress bar, then give each file a single credit of 1 ... it is OK, and usually expected that a progress bar will 'pause' for a while when it is performing a long task ... so long as the bar doesn't freeze up for minutes at a time ... haha
03/23/2007 (3:49 pm)
@Nicolas, the file size would not really tell you how much to credit to give that file in a progress bar, as the size is fairly irrelevant when compared to the other items that could effect it -- for example, if "file1.cs" is 1k in size, and "file2.cs" is 5k in size ... BUT, file1.cs is 1k of "exec" statements, file1.cs will take MUCH longer to load then file2.cs ... ;)It would be suggested to get take the number of files to load, and use that as the 'max' value for your progress bar, then give each file a single credit of 1 ... it is OK, and usually expected that a progress bar will 'pause' for a while when it is performing a long task ... so long as the bar doesn't freeze up for minutes at a time ... haha
#10
Would it be productive to put the exec calls of level functions inside a onLevelLoaded() callback?
Just wondering how to squeeze some extra horse power on slow machines.
03/24/2007 (8:33 am)
This thread is very interesting.Would it be productive to put the exec calls of level functions inside a onLevelLoaded() callback?
Just wondering how to squeeze some extra horse power on slow machines.
#11
03/24/2007 (12:06 pm)
@Ricardo, only if that script file is specific to that level ...
#12
04/04/2007 (12:26 pm)
Thanks David and Luke again, your suggestions were very useful to create a proper loading screen. In the end, it turned out that around 60% of the 45 seconds waiting was due to some very big music tracks I was preloading, so I ended loading them as needed between levels to reduce the waiting even further.
Associate David Higgins
DPHCoders.com
As for having a progress bar, the only way to do this, through TorqueScript, would be to completely forget about the 'level' concept ... and create some sort of a custom file that you can go through one by one, and load the sprites and animations, etc, etc out of it to build the level dynamically ... one piece at a time ...
If you want to dabble with the C++, you could probably add a fairly nice TorqueScript callback called "onLevelObjectLoad" or whatever that is triggered when ever the 'loadLevel' method actually reads in a new object ... though I've not looked at this code, so I'm not sure if it's chunked in one at a time, or just compiled and dumped into the scenegraph ....
But to bypass the black screen, you should be able to push a dialog ontop of the mainScreen GUI and then load your level 'in the background' ... after the levels loaded, pop the dialog ...