Game Development Community

How to reduce loading times

by Mike Moore · in iTorque 2D · 06/14/2009 (3:34 am) · 24 replies

Hello,
I’m currently trying to reduce the loading time of my iPhone app, but I’m not getting very far at all. At the moment it’s taking about 45 seconds to load and it’s not a big game.
So far I’ve converted all the gf/x that I can to PVR’s using the tool included with iTGB, which makes my nice gf/x look crap and didn’t do anything for loading times even thou it save almost 1.5 megs (I have about 8 512*512 static screens).

PVR settings (Just in case I’m doing something wrong here):
==================
PVR conversion settings:
OpenGL ES1.x
PVRTC 2BPP
Top MIP-map only

The next thing I did was to just remove all the sf/x to see if that made any difference, but it didn’t.

Below are the options I’m using when I build the app as I don’t know what the differences are, so I try to keep them at the defaults (changing them doesn’t seem to make any difference any way).

Build settings:
==================
iPhone SDK V2.2.1
Mode: Debug
Target: Device
Optimizations: None
(Every other options is left at its default)
Clean & Build

Does anyone have any advice as I’m getting to the stage now where I don’t know what to do next and please keep in mind I’m more of an artist then a coder so no big words please).

Thanks in advance for any help / pointers.

Mike.






Page «Previous 1 2
#1
06/14/2009 (5:16 am)
There are two major things that are required for loading times:

1. There are various scripts executed much too early (long before they are needed). Check the script execution order and check which of the things you need to be executed at the start of the app and which not. The default execution order is far from realistic.
Also get rid of any script you don't need, check especially those in the common folder
2. Only load the datablocks required in a specific level, instead of loading all datablocks. The top reason for long loading times are datablocks
#2
06/14/2009 (6:09 am)
Hi Mike,

i am a PC Developer and i don't really know the iTGBEngine so it might be false. But normally it makes a big different if you build your game as a debug-build or as a release-build. If you can change it, try to build in release mode.

cu
Oliver
#3
06/14/2009 (6:55 am)
Debug builds for this don't make a lot of difference. All you get with debug builds is more references for the debugger to hook into while tracing, but iy's basically deadweight otherwise. iTGB's binary is less than 3MB without debug data, about twice with. It's mainly the scripts, when you load them and how often you load them which matters.
#4
06/14/2009 (10:05 am)
Hello,
Thanks to all.

@Marc,
I´ve tried loading stuff -datablocks- only when needed and there is no difference with the loading time. Really stuck with this :(

#5
06/14/2009 (10:16 am)
Do you have many sounds?
#6
06/15/2009 (12:55 am)
No, I've even removed gfx and audio files to see if that would help, but it's still the same time.
Is there a crib sheet /tutorial anywhere with this information as it's something everyone will have to up through to finish the game?
Just really annoying knowing you are so near to finishing your project.

Thanks
Mike
#7
06/15/2009 (4:22 am)
That part of the loading is what I mentioned in 1. above
#8
06/15/2009 (9:01 am)
Hi Marc,
Thanks for getting back. I’m really not sure about this, so feel free to slap me if I’m wrong, but I assume the process goes something like this:
So as the iPhone version is loading, it’s loading up all the default bit’s and bob’s in the background (a lot that I will not need?).
You mention that the order is far from realistic? Could you expand on that a bit for me please as I realise that it can’t be the actual game assets (gf/x & sf/x) causing the problem as I have removed most of them and there’s no improvement. So what is the rough order I should be looking at?
As it stands, I’m doing the standard thing of Company logo, title screen, main menu and then branching off into the game / help screen / credits.

Have I missed any documentation that talks about this by the way? I hate bugging people in the forums if I could be reading some documentation that explains this to me, even if it does go over my head, at least I’ve got something to work from and when I do ask questions, at least some of it will click in to place.

Thanks again for you and everyone else for your help.

Cheers,
Mike.

#9
06/15/2009 (9:04 am)
There is no documentation on anything of that.

As for the startup order: As a mather of fact, many scripts are executed in the init function which is the one the iPhone OS is checking for "is it done, if not within 30s, just kick the app". This includes various scripts that might or might not be of importance to you.
Cutting those things you don't need and additionally move the execution of non-init critical things post the init function will reduce the initial startup time fairly (keep in mind that you won't get below 6 - 8s I would guess as the whole engine source code needs to be loaded. Thats just the price of a general purpose engine ...).

Additional time can be gotten if you move over script things to source code as the scripts execute significantly slower than C++
#10
06/15/2009 (9:32 am)
yeah as Marc said, get rid of any scripts you aren't using in the common folder - I managed to shave off 3 seconds of load time just by removing scripts I knew we weren't using at all.
#11
06/15/2009 (9:53 am)
I'm at around 12 seconds just by loading the iTGB splash screen at the first chance it gets.

I could use an extra 3 seconds...

I'm staring at the common folder - what scripts in particular are you able to remove? I'm not familiar with any of them.
#12
06/15/2009 (9:59 am)
In that case I would recommend to become familiar with it.
Just removing stuff otherwise can and likely will lead to issues.
Also it allows you to understand more of the engine and squish more out of it
#13
06/15/2009 (12:59 pm)
After much tweaking and getting to get the game loading consistently on my iPhone under the good graces of the built-in Watchdog Timer, I've decided to post my game/main.cs file, in hopes that other people could use it to speed up loading time of the game, and also how to incorporate the iTGB logo as their second screen, if they decide to use their own company logo as the startup splash.

I'm also hoping to get constructive criticism if you can help me beat the 12-second startup I currently have. No other modifications or omission of the iTGB source code or other CS files, nor have I removed any yet, as Marc stated I could, which could bring it down even further.

This implementation will appease the iPhone timer by pulling in ONLY the splash screen as early as possible in the code, to which your program could then take its sweet time to propagate the rest as needed.

Create a level called game/data/levels/splash.t2d with just the iTGB logo and nothing else. Using code from another thread to load datablocks, the code below will load managed/splash_datablocks.cs so that it only loads the proper objects for the appropriate level file.

//---------------------------------------------------------------------------------------------
// Torque Game Builder
// Copyright (C) GarageGames.com, Inc.
//---------------------------------------------------------------------------------------------
// initializeProject
//---------------------------------------------------------------------------------------------
function initializeProject()
{
	$bypassIntro=false;
	$gameInProgress=false;

	exec("~/gui/mainScreen.gui");
   
	splashScreen( expandFilename("~/data/levels/splash.t2d") );
}

function splashScreen(%level)
{
	Canvas.setContent(mainScreenGui);
	Canvas.setCursor(DefaultCursor);
	new ActionMap(moveMap); moveMap.push();
	$enableDirectInput = true; activateDirectInput(); 

	if ($platform $= "iphone")
	{ $iPhoneMode=true; Canvas.hideCursor(); enableJoystick(); }
	else
	{ $iPhoneMode=false; Canvas.showCursor(); }

	if ($bypassIntro) nowStartGame();
	else
	{
		LevelManager::loadLevel("splash");
		schedule(4000,0,"nowReallyStartGame");
	}
}

function nowReallyStartGame()
{
	// Exec game scripts.
	exec("./gameScripts/game.cs");
	startGame( expandFilename($Game::DefaultScene) );
}

function LoadProjectLastLevel()
{
	%projectFile = "project.t2dProj";
	%xml = new ScriptObject() { class = "XML"; };
	%lastlevel = "";
	if( %xml.beginRead( %projectFile ) )
	{
		if( %xml.readClassBegin( "TorqueProject" ) )
		{
			%lastlevel = %xml.readField( "LastLevel" );            
		}    
		%xml.endRead();
	}
	%xml.delete();
	return %lastlevel;   
}

function LevelManager::loadLevel(%level)
{
	%file = "game/data/levels/" @ %level @ ".t2d";
	%datablock = "game/managed/" @ %level @ "_datablocks.cs";
	%script = "./" @ %level @ ".cs";
	%count = $CurrentLevelDatablockSet.getCount();  
	for( %i = 0; %i < %count; %i++ )
	{
		%object = $CurrentLevelDatablockSet.getObject( %i );  
		if (isObject(%object)) %object.delete();
	}
	$CurrentLevelDatablockSet.delete();
	if (isFile(expandFilename(%script))) exec(expandFilename(%script));
	if (isFile(expandFilename(%datablock))) exec(expandFilename(%datablock));
	sceneWindow2D.endLevel(); //if we are in a level, end it
	SceneWindow2D.loadLevel(expandFilename(%file));
	$thescenegraph = sceneWindow2D.getSceneGraph();
}

//---------------------------------------------------------------------------------------------
// shutdownProject
//---------------------------------------------------------------------------------------------
function shutdownProject()
{
   endGame();
}

//---------------------------------------------------------------------------------------------
// setupKeybinds
// Bind keys to actions here..
//---------------------------------------------------------------------------------------------
function setupKeybinds()
{
   new ActionMap(moveMap);
}

//---------------------------------------------------------------------------------------------
// setupiPhoneScenarios
//---------------------------------------------------------------------------------------------
function oniPhoneResignActive(){
	echo("Con::executef( 1, oniPhoneResignActive )");
	if ($gameInProgress) saveGameData();
}

function oniPhoneBecomeActive(){
	echo("Con::executef( 1, oniPhoneBecomeActive)" );
	loadGameData();
}

function oniPhoneWillTerminate(){
	echo("Con::executef( 1, oniPhoneWillTerminate)" );
	stopBGM();
	if ($gameInProgress) saveGameData();
}

function oniPhoneChangeOrientation(){
	echo("Con::executef( 1, oniPhoneChangeOrientation)" );
}
#14
06/15/2009 (1:01 pm)
Quote:
I believe I was able to appease the iPhone timer by pulling in the splash screen as early as possible in the code, to which your program could then take its sweet time to propagate the rest as needed.

Thats actually one of the most important parts.
The app needs to leave the init function as soon as possible and the the normal "game splashscreen -intro movie - main menu" stuff post that point.


Offering the main.cs is a very generous step and will hopefully motivate people to share their modifications and thoughts behind them
#15
06/15/2009 (1:05 pm)
Curse you, 5000-character limit! Ok, it's posted above. I hope it's not cryptic for some, it should be pretty self-explanatory. Remember, I'm only a month-old newbie myself!

No matter how big your game gets, as long as you have just the splash screen as the first level, and keep your datablocks.cs file empty, you should be consistent in much shorter loading times.

Since my level's datablocks are in different files, the editor pulls up blank boxes, since it can't find the sprites. (Be careful, as saving the .t2d without sprites will remove the assignments in the .t2d file!) So, for editing purposes, when I work on a game, I modify my .t2dProj file to the appropriate .t2d level file, then copy the text in my game_datablocks.cs file into datablocks.cs, and change the first line from $CurrentLevelDataBlockSet to $managedDataBlockSet. When I'm done adding or modifying the objects on that level, I pull it back into game_datablocks.cs and change the first variable name back.

If someone could help me automate this procedure, it would certainly save some time.
#16
06/15/2009 (3:02 pm)
I have commented all the scripts calling I think are not necessary for the game in common.cs and have separated files for the datablocks. Now the loading time is faster but need to check again and again until get satisfied.

Marc,
Is that what you have suggested?

function initializeCommon()
{

   // Common keybindings.
   // GlobalActionMap doesn't get preference anymore so need special sequence to toggle console.
   // This also allows ~ to be used in the console now ;p   
   //GlobalActionMap.bind(keyboard, $Game::ConsoleBind, toggleConsole);
   //GlobalActionMap.bind(keyboard, $Game::ScreenshotBind, doScreenShot);
   //GlobalActionMap.bindcmd(keyboard, $Game::FullscreenBind, "toggleFullScreen();","");
   
   // Very basic functions used by everyone.
   exec("./audio.cs");
   exec("./canvas.cs");
   exec("./cursor.cs");

   // Seed the random number generator.
   setRandomSeed();
   
   // Set up networking.
   if( $Game::UsesNetwork )
      setNetPort(0);
      
   // Initialize the canvas.
   initializeCanvas( $Game::ProductName );
   
   // Start up the audio system.
   if( $Game::UsesAudio )
      initializeOpenAL();
   
   // Content.
   exec("~/gui/profiles.cs");
   exec("~/gui/cursors.cs");

   // Common Guis.
   //exec("~/gui/messageBoxOk.gui");
   //exec("~/gui/messageBoxYesNo.gui");
   //exec("~/gui/messageBoxYesNoCancel.gui");
   //exec("~/gui/messageBoxOKCancel.gui");
   //exec("~/gui/messageBoxOKCancelDetailsDlg.gui");
   //exec("~/gui/messagePopup.gui");
   //exec("~/gui/options.gui");
   //exec("~/gui/remap.gui");//no estoy seguro si descomentar esta o no
   //exec("~/gui/console.gui");
   //exec("~/gui/NetworkMenu.gui");
   //exec("~/gui/startServer.gui");
   //exec("~/gui/joinServer.gui");
   //exec("~/gui/waitingForServer.gui");
   //exec("~/gui/helpDlg.gui");
#17
06/15/2009 (3:03 pm)
// Gui Helper Scripts.
   //exec("~/gui/messageBox.cs");
   
   //exec("~/gui/help.cs");

   // Chat gui
   //exec("~/gui/chatGui.gui");

   // Random Scripts.
   //exec("./screenshot.cs");
   exec("./metrics.cs");
   //exec("./scriptDoc.cs");
   //exec("./keybindings.cs");
   exec("./options.cs");
   exec("./levelManagement.cs");
   exec("./projectManagement.cs");
   exec("./projectResources.cs");
   exec("./align.cs");

   // Load client and server scripts.
   if( $Game::UsesNetwork )
   {
      initBaseClient();
      initBaseServer();
   }
   // Set a default cursor.
   Canvas.setCursor(DefaultCursor);
   
   loadKeybindings();

   $commonInitialized = true;

}

//---------------------------------------------------------------------------------------------
// _shutdownCommon
// Shuts down common game functionality.
//---------------------------------------------------------------------------------------------
function _shutdownCommon()
{
   if(isFunction("shutdownProject"))
      shutdownProject();
      
   shutdownOpenAL();
}

//---------------------------------------------------------------------------------------------
// dumpKeybindings
// Saves of all keybindings.
//---------------------------------------------------------------------------------------------
function dumpKeybindings()
{
   // Loop through all the binds.
   for (%i = 0; %i < $keybindCount; %i++)
   {
      // If we haven't dealt with this map yet...
      if (isObject($keybindMap[%i]))
      {
         // Save and delete.
         $keybindMap[%i].save("~/prefs/bind.cs", %i == 0 ? false : true);
         $keybindMap[%i].delete();
      }
   }
}

//---------------------------------------------------------------------------------------------
// initBaseClient
// Initializes necessary client functionality.
//---------------------------------------------------------------------------------------------
function initBaseClient()
{
   // Base client scripts.
   //exec("./client/client.cs");
   //exec("./client/message.cs");
   //exec("./client/serverConnection.cs");
   //exec("./client/chatClient.cs");
}

//---------------------------------------------------------------------------------------------
// initBaseServer
// Initializes necessary server functionality.
//---------------------------------------------------------------------------------------------
function initBaseServer()
{
   // Base server scripts.
   //exec("./server/server.cs");
   //exec("./server/message.cs");
   //exec("./server/clientConnection.cs");
   //exec("./server/kickban.cs");
   //exec("./server/chatServer.cs");
}
#18
06/16/2009 (4:27 am)
Yes, for example :)
Its always a balance act what you can comment out and what you need.
Especially when you put it in relation to when you need it (right at the start, somewhen later, ...)

There are various ways to handle it and it highly depends on the project and its requirements.

For example you might need the YesNo messagebox (saving the game and alike), but you know you won't need it at start. So to solve that you would check if the box is present before using it and if not, executing its gui script. This ensures that it does not use precious loading time yet you still have it at hand when needed.
#19
06/16/2009 (6:05 am)
Out of curiosity, do these server and client scripts get loaded if you unchecked 'Use Network' in the iPhone Options tab?

Also, should I remark the unused ones completely, or can I use flagging code like this?
function initBaseServer()
{
   if ($customOptimization) return;
   // Base server scripts.
   exec("./server/server.cs");
   exec("./server/message.cs");
   ...

Will iTGB still parse out the CS files and create DSO files, even if it never gets to run them? If it saves on space (and downloading time from the App Store), I'll just rem the out, otherwise, I'd like to keep my options open by using an 'if' command.
#20
06/16/2009 (6:06 am)
something not exec-ed will never be DSO compiled and thus unusable on the iphone
Page «Previous 1 2