your game runs in a browser, you're letting your users skip the "download and install" step common in other games- at least, from
the user's point of view. You still need to get your art and code files over to the user's machine; this chapter will go
over some ways to make this transfer of assets more efficient, and your wait times shorter. This chapter will also go over some resource
specific tips that might be helpful. The various types of resources that we'll discuss here are:
- Files in general
- WTStudio Levels
- Models and actors
General file concernsThe following points apply to all sorts of resource files:
- Think about how you want your files to load, and set
up a download queue: If, when a user accesses your application, you move every
single one of your files to the user's machine before starting the app, chances are good that your
user may lose interest in your content before it's loaded. If you instead determine what files are needed to start the scene, and
what files are needed later on, you can move the critical files over, and let the rest of the files trickle in
while the user is already using your application. A typical order for resource download is the starting level first, then geometry assets, then textures and images,
and lastly sounds. If you have a particularly large file (like a level) that you need to load first, you can reduce
the size of the download queue by using
setMaxDownloads(); this will make your resource load faster
by giving it more of a share of the user's bandwidth. The default max number of downloads for the Web Driver
object is 4; if you keep to this default setting, your large files could finish loading last, even if you start loading them
before any other files. Once your application is started, you can start the download of the rest of the assets right away, rather
than waiting for the user to get to the point in the app where they are needed. If you want to start downloading a resource without
actually creating an object with it, you can call
readFile() on it, and it will be available from cache later.
Also see the WTStudio Levels section below for information on setting up the download queue within a level.
- Trap resource load errors: If a resource fails to load, this will throw up an annoying error message. You can plan for
this occurence by turning off error notification (use
setErrorHandling(), and specify that no dialog
boxes are to be shown). You can then read the status of the resource by calling
getLastError() on it after you
attempt to load it; if the resource could not load, the error number will be set to 5.
- Use the caching settings for content that doesn't need to be downloaded each time: The methods that create an object from a resource (the various Create*() methods, and ReadFile) have
an optional parameter to specify reading out of the user's cache- the default browser settings are to read cached files
if they are available (and not check the webserver), but if the user has changed this, you can override it by
setting the optional
cache parameter of these methods to 2. You may run into problems when you want to
change your files later on, however; a solution to this problem would be to create different subdirectories on your web server
for different versions of your game, like so:
Since resources are cached by full path, the two versions will be cached separately.
- As mentioned in the last chapter, don't create a loop
that sleeps the Web Driver until the resource loads. Since loading resources is
asynchronous, you can deal with a resource before it's fully loaded, but
you will be dealing with a stub resource that gets filled in when
the resource loads- this works for some applications (such as setting position or adding to
a stage), but does not work for applications which actually use the data
in the object (such as applying an alpha map to a bitmap); in these
cases, use a load callback to detect when a resource loads.
- Keep file counts low: Even if files are cached locally, loading them still requires a network
roundtrip to the server to see if the file is up to date;
if you have a lot of files, this can introduce a lot of latency into your application. A
good guideline to follow would be to keep file counts below 50 or so.
If you have more than this, try to think of ways that you can convert several files into one, such as concatenating several data
files into one large file, or assembling several texture files into one larger image file, and create smaller texture
bitmaps in code from the larger one using
The level files that WTStudio produces have some very advanced features that are very easy to use; however, the files that make up
your level can wind up being very large, and take a while to download. Some ways you can optimize this are:
- Set Download Priority:
As metioned above, you should move files over to the user's machine in the order that they are needed; WTStudio gives you a
handy way to do this by setting the download priority on the files that make up your level.
- Use multiple levels wisely: Due to the way that files are cached, you can't load a level twice from the same file; if
for some reason you need two identical levels to be loaded at the same time, you will need to use two separate files to do so.
Also, it is a good idea to rename the root object to something besides Default if you have more than one level.
- Experiment with level construction time vs download
time: WTStudio offers two ways to trade off between bandwidth and processor
workload: The BSP tree and the lightmap. Either component can be stored with
the level file (increasing download time, but also decreasing level load
time), or can be generated on the client machine (which has the opposite
effect). To access this setting, select the Model node in the Lists tab in
WTStudio. In the properties page on the right, you can specify Save Lighting
to include the level's lightmap, or you can specify Compact Size (to generate
the BSP tree on the fly) or Normal Size (to include it in your level). You
should test your level either way, to see if overall load times improve with
one method or the other.
- Don't use a number as the last character in your level objects: WTStudio's automatic naming scheme uses an incrementing
number to keep track of objects that are created; your lights will be called light1, light2, etc. This naming scheme is also
referenced internally; you might see unpredictable results if you use this scheme as well. If you need to use an incrementing
number to keep track of your resources, put it somewhere else in the name, or put an underscore character after the number.
Video card performance is largely dependent on what sort of texture throughput the card is being asked to handle; as a result,
this is an excellent area for optimization. Here are a few ways you can manage your textures efficiently:
- Keep file counts low. Reuse your textures as much as you can; a good way to do this is to group multiple images into
one file, and use
copyRect() to create multiple images from that
file, as described above. Note that image files that tile the textures
in them horizontally compress better than images that tile vertically.
- Keep textures to an appropriate size: For maximum efficiency, make sure that your textures are square, with a
length/ width that is a multiple of 2. You will get the best performance if your textures are 256 pixels on a side or less; if
you go beyond texture sizes of 256, some older video cards will compress your textures down to 256, with results that you
might not have intended (This usually results in blurring the textures). Also note that
file size on disk and texture size in memory are not proportional; your textures are compressed on disk, and will not
be compressed when loaded onto the video card. To figure out how much texture memory each texture takes up:
So, a 1024x1024 32-bit texture
would take up four megs of ram on the video card- this is half of what the
typical user has available. If the video card runs out of memory, the textures
will begin to be optimized and compressed; since there's no sophisticated algorithm for this, the textures will appear to blur as they are optimized. It's
also possible that textures will start to be swapped in and out of video memory, creating a huge performance hit.
- Round the largest dimension of the texture up to the nearest power of 2
- Square the resulting value
- For a 16 bit texture, multiply by 2. For a 24 bit
texture, multiply by 3, and so on.
- Avoid creating textures with
setPixel() is a very inefficient way to create
images on the fly- look into using
- While copying out of a large image into smaller images is a good idea, try to avoid going the other way- writing to large
images is expensive, since the whole image needs to be moved in and out of the video card for every write.
- To optimize download times, use JPG files for images where exact color isn't necessary (such as photographs or non-alpha textures), and use the highest
degree of compression that you can live with.
- You are free to destroy images used for alpha maps after the map is applied. Since the alpha map data is stored in the target bitmap, the Web Driver doesn't
refer back to the original alpha map. If you don't plan on calling
setOpacityMask() again, you should call
WTBitmap::destroy() on the alpha
map to conserve texture memory.
Models and Actors
Ways to use models and actors efficiently include:
- Attach the same model to multiple groups: If you need to have lots of an identical model (like, say, bullets), you can create
the model once and attach it to multiple groups; this can save you quite a few API calls.
- Keep actor polycounts down: With all the power that the WTActor interface
gives you, it's tempting to go overboard and create photorealistic actors with tens of thousands of polygons. While you can get away
with this if all your app does is display a single actor, this can seriously bog down your application if your app
does anything else. A good ceiling for a game or other interactive 3d application is 1000 polygons per actor.
- Wait for your actor to load before using
makeCollisionBox(): This is not so much an efficiency tip, but
a way to avoid bugs. Using
makeCollisionBox() uses the current geometry
extents of the object; so, if your actor isn't loaded yet, you might run into
problems creating its collision box this way.
- When optimizing your animations, the parameter to watch is the time, rather than the number of keyframes- The position of the bones is sampled thirty times a second, so
the longer your animation takes to play (in realtime), the larger the motion files will be. One way to reduce the animation time is to look into ways you can create tighter loops for your animations;
for example, if you need an animation in which your character is shouldering the recoil from a machine gun, create the animation using only one firing of the gun- it will look the same when looped as if
you used several firings to create the loop.
In the next chapter, we'll take another look at Java, and examine some more ways to improve your code's efficiency and reliability.
©2000 WildTangent Inc. All Rights Reserved.
Website Terms & Privacy Statement