Development Tips

Developing for mobile devices is more difficult than developing for the PC or Mac as they have limited hardware by comparison. Most of the tips here will improve the performance of your apps when using AGK, others will help with the common problems of develping cross platform like differing screen resolutions.

Images

Sprites

Other

Images

Optimizing images is mostly about saving memory as raw image data can take up a lot of space. A single 1024x1024 image uses 4MB (1024*1024*4), whilst a 2048x2048 image uses 16MB (2048*2048*4) and with mobile devices usually sharing video memory with RAM a device can quickly run out and close your app without warning. This is different from the image format used to save an image like PNG or JPG since the GPU loads it into an uncompressed format that takes up more space.

Use the smallest image resolution necessary

Since AGK version 2.0.20 images no longer need to be a power of 2 in size, this means you are no longer limtied to sizes such as 1024x512, so if your image only needs 900x460 pixels then use an image size of 900x460 as that will help save memory. There are two exceptions to this, if you want to use UV offset or scaling to repeat the image multiple times, or if you want to use mipmapping, then the image must be a power of two in size. If you can get away with the quality reduction, you can also use a low quality image like 500x250 and display it using a sprite at a larger size using SetSpriteSize(SpriteID, 960, 460) as this improves loading times and reduces memory usage.

Keep as few images loaded as necessary

You should try to load images only when necessary, and delete them when you no longer need them. This is because images are one of the biggest consumers of memory (along with 3D objects), so keeping as few in memory as possible will prevent your app from crashing because the device ran out of memory. A rough guide is to have no more than about 250MB of image data loaded at any one time, but this will vary depending on the devices you are targetting. Higher end devices will be able to keep more in memory without crashing. To calculate how much memory an image will use when loaded into memory, multiply its width by its height and multiply by 4, for example a 500x500 image would consume 500*500*4 = 1,000,000 bytes, or 1MB

Use atlas textures

An atlas texture is an image that contains multiple textures on it which means a single image can be shared with multiple sprites to improve performance. Sprites that share the same atlas texture can be drawn at the same time which is faster than drawing sprites one at a time. One disadvantage to using atlas textures is that you cannot do UV scrolling with the textures it contains as modifying the UV will reveal the other textures sharing the image space. To load images from an atlas texture use LoadImage to load the atlas texture and then LoadSubImage to load images from it. You may use these sub images just like you would any other image. It is generally a good idea to leave 1 or 2 pixels of space between each image on an atlas texture to avoid them bleeding into each other during sampling. If you are using atlas textures with mipmapping turned on then you may need to increase this spacing further depending on how the image is used.

Do not use images greater than 2048 in size

Whilst some devices do support images greater than 2048, for maximum compatibility it is highly recommended that you do not exceed 2048x2048 for a single image. If you do end up using larger images and the device trying to load them does not support that size then AGK will scale the image down to the maximum supported size and continue loading as normal, however this may increase loading times.

Use LoadImageResized

Since AGK version 2.0.19 images can be loaded with LoadImageResized instead of LoadImage allowing you to scale an image as it is loaded. You might want to do this if you are using high quality images like 2048x1536 but you detect the device only has a 1024x768 screen. Scaling the image down by half during loading will make it look better on this device and consume much less memory. It also does not significantly affect loading time as the scaling is done in the background by the GPU. If the image uses a subimages.txt then its values will also be scaled during loading so you can continue to load sub images from it as if nothing happened. Note that this command should not be used to increase the size of an image, as you can assign the image to a sprite and use SetSpriteSize to achieve the same affect without using any more memory.

Mipmapping

Using SetGenerateMipmaps(1) to turn mipmapping on uses 33% more memory for images than turning it off but it can remove filtering artifacts created when using high resolution images on 3D objects or on objects that are far away. Therefore it may be beneficial to turn it on for 3D apps even though it uses more memory. However any images loaded whilst this is turned on must be a power of 2 in size, i.e. the image width and height must one of the following values 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048. The image does not need to be square, i.e. it could be 1024x256.

Use indexed color PNGs to reduce file size

APKs on Android are limited to 100MB, and some devices cannot install APKs above about 70MB (this varies by device), Google Play will list your app as incompatible with their device if they try to install your app. So you should aim to make your APK as small as possible. You can do this by using a service like TinyPNG that will convert your PNG images into indexed color PNGs, or using an art package that can export indexed color PNGs, this results in a much smaller file size for a similar quality.

Sprites

Optimizing sprites is about reducing the potential GPU bottlenecks, throughput and fill rate. Throughput is the number of polygons the GPU can process each frame, and fill rate is the number of pixels the GPU can process each frame.

Use opaque sprites where possible

Drawing opaque sprites is faster than drawing transparent sprites, and on some chips like Tegra 2 it can be significantly faster. Therefore if you have a sprite that does not have any transparent pixels you should use SetSpriteTransparency(0) to allow AGK to draw it without transparency. Background images are a good example of sprites that might not need transparency. As of AGK version 2.0.1, sprites will check the image assigned to them and automatically turn their transparency on or off depending on if the image has any transparent parts. Using SetSpriteTransparency with any value will stop this, so from that point on you need to set the sprite transparency appropriately.

Do not use oversized sprites

Using lots of large sprites that fill the screen will increase the number of pixels the GPU must draw, even if the sprites overlap and hide each other. For example a HUD might be composed of a single large sprite that fills the screen with lots of transparent pixels in the center, in this case it would be better to have a separate sprite for each HUD element leaving the center free of transparent pixels that the GPU would waste time on.

Do not overlap too many sprites

Overlapping sprites causes the GPU to redraw a section of the screen multiple times, for example 10 sprites at the same position and size would cause the GPU to redraw the pixels covered by those sprites 10 times, even though the top sprite may be the only one to affect the final outcome. Sometimes this is unavoidable and is not a concern, but when it is known that a sprite will not affect the final outcome it should be made invisible with SetSpriteVisible(ID,0) so it is never sent to the GPU. An example of where this might be the case is sprites that are behind a background sprite, like a full screen menu that appears above an active game, the sprites behind the menu should be hidden whilst the menu is being displayed, so that they don't affect performance, and then shown again when the menu is removed.

Use atlas textures

In addition to saving memory, atlas textures allow AGK to group sprites together into a single draw call that the GPU can process faster than drawing each sprite individually. The more sprites that share the atlas texture the more affect this will have.

Others

Removing black borders and handling different aspect ratios

When setting your virtual resolution you will get black borders on devices that don't match the aspect ratio exactly, for example a virtual resolution of 1024x768 on a 1280x720 screen. To work around this you can use the SetScissor(0,0,0,0) command to display sprites where the black borders would normally be. In this case placing a sprite at -10 or 1200 on the X axis would normally be hidden by a black border on a 1280x720 device, but with the scissor command active it will display as much of the negative portion of your app as possible on the device. This will vary by device, some might display between -100 to 1124 others might display -150 to 1174 whilst devices that do match the aspect ratio will only display 0 to 1024. To find out how much extra space you have to play with you can use GetScreenBoundsLeft and GetScreenBoundsRight on the X axis and GetScreenBoundsTop and GetScreenBoundsBottom on the Y axis. These will allow you to place sprites at the edges of the screen no matter what device your app is running on.

Setting the sync rate

By default AGK aims for a refresh rate of 60 frames per second, but if you are targetting mobile devices you may want to reduce this to improve battery life and generate less heat. If your game or app does not need to run at 60fps then you can use SetSyncRate(fps, mode) with an fps value between 5 and 60 and with mode equal to 0 to slow the refresh rate and let the device conserve power.

Use OGG instead of WAV to reduce file size

As mentioned in the PNG file size section, APKs are limited to 100MB and in some cases less than this, so as of AGK version 2.0.20 you can use OGG files with LoadSoundOGG instead of using WAV files and LoadSound. The result is exactly the same as AGK will decompress you OGG files into WAVs when loaded so you can continue to use the same sound commands after the sound is loaded. OGG files take up much less space than WAV files allowing you to save some space in the APK file.

Filenames

Note that filenames may be case sensitive on some platforms. This will affect commands such as LoadImage, OpenToRead and GetFileExists.

Executables and bytecode

In the event you are running an executable on Windows or Mac when using Tier 1 be aware that bytecode is generated for your program and must be copied if you want to run your program in another location. For example, if your project has an executable called myapp.exe there will also be a media folder generated and inside there a file named bytecode.byc. The executable (myapp.exe) and media folder (containing bytecode.byc) must be copied if you want to run your program on another computer/location.