Quote: "I've run into an issue where I can't access my outputted file, since on Android it tucks it away in an area only ROOT access can touch"
Since version 2.0.21 you can use SetRawWritePath(GetDocumentsPath()) on Android to write to the SDCard. Be sure to save the old write path with GetWritePath before hand so you can set it back again.
Quote: "My animated sprites in the UI still play at normal speed when the rest of the game slows down"
Sprite animations are time based rather than frame based, so if a frame takes longer the animation will be advanced further
Quote: "The "tower" is randomly generated, so some configurations clearly aren't being liked.
I've noticed that I can get better framerates when I don't rely on the in-built occlusion culling and do it myself"
I assume the culling was for 3D tower objects? If so then this is almost definitely the cause. Out of interest what culling method did you use? AppGameKit uses simple frustum culling for 3D objects.
Are there a lot of transparent 3D objects? If so then the 3D transparent sorting could be causing it.
If they are mostly opaque how many different types of object are created an in what order? 3D opaque objects are drawn in the order they were created, and each has its own draw call, which can cause a lot of state changes (state changes in OpenGL are expensive). For example an object with image 1 is drawn, then an object with image 2 is drawn, then another object with image 1 is drawn would be slower than drawing both objects using image 1, then the object with image 2. Alternatively use an atlas texture containing as many images as possible and have the objects use sub images, then they won't need to change the image between draw calls. Other state changes would be different shaders or meshes, but if all the objects are using the same number of images and there are no point lights in the scene then they will all be using the same internal shader. If you are using point lights then reducing the number of lights should help. Meshes are only shared by instance objects (not cloned objects), so all instances should be created together if possible.
If you want to get really advanced you can take over the 3D render order manually by replacing all instances of Sync() with
Update(0) // 0 means use the frame time
Render2DBack() // render any sprites that are behind the SetGlobal3DDepth value
DrawObject(1) // choose the order objects are drawn, this replaces Render3D() which would draw all opaque and transparent objects in an AGK chosen order
DrawObject(3)
DrawObject(2)
// etc
Render2DFront() // render any sprites that are in front of the SetGlobal3DDepth value
Swap() // flip the back buffer to the screen
Note that transparent objects would have to be drawn after all opaque objects, and be drawn in back to front depth order to get correct results. Opaque objects can be drawn in any order, and should be grouped into objects with the same images, shaders, and meshes for best performance.
In summary try an atlas image first (you can use up to 2048x2048 safely), then try grouping objects so instances of an object are all created at the same time. If this isn't feasible then try drawing 3D objects manually so that instances of an object are all drawn together.