I am writing an application which only displays text and the occasional jpg. Using Unity for a GUI only application might seem a bit overkill but the ease to publish to different platforms outweighs the overhead issues.
There is only one problem though. Since the screen content is 100% static most of the time (apart from swipe animations), I do not want to waste CPU/GPU cycles by rendering static stuff, which eats up the battery. So I only want to render the camera when I tell it to.
So isn’t there another way to render the screen only once and then somehow freeze the frame buffer? If I just disable the camera game object or camera component, the screen turns black. If I disable the camera and then call .Render() on it, it doesn’t do anything, presumably because .Render() only renders into a render texture and not into the frame buffer. I tried playing with the camera clear flags, but that didn’t work as well. I suppose I could render the frame to a render texture, point the camera to that render texture, and disable all content. But that requires Pro and I want to avoid using that.
When 1. doesnt work try to enable a camera which has Nothing as culling mask and DepthOnly as clearflag. Also disable the MainCamera.
If even this fails, try to take a screenshot of the rendered image (Texture2D offers a method for this) and display this screenshot only.
Also you can try to use the Unity feature which stops rendering anything when the window has no focus, by removing the focus, but this requires some PInvoke magic and Windows.
EDIT:
I just found out that it doesn’t work on Android (tested on HTC One) but it works fine on Windows. This is very unfortunate because the battery on my laptop is a lot larger then the one in my phone. Bug report filed.
Here are some pointers:
-You cannot disable the camera. If you do so, will clear the frame buffer and nothing will be shown.
-Camera.Render() after disabling a camera will not work as it will only render to a RenderTexture and not to the frame buffer. It does work if used together with setting the camera clear flags and culling mask as shown below.
-Rendering (or suspending rendering) is controlled by setting the culling mask to “everything” (render) and “nothing” (stop rendering).
-To prevent the camera from overwriting the frame buffer, set the clear flags to “depth only” or “don’t clear”.
-GuiText needs a geometry (plane) background, if the clear flags and culling mask are set in the Editor, otherwise the text is not anti-aliased. This bug doesn’t seem to be an issue when the clear flags and culling mask are set via a script.
-Do not use build in GUI elements such as buttons (text is ok) as these are drawn even though the camera is set to “StopRender” as described below. Hopefully the new Unity GUI system in the works will be better in this regard.
The code below allows you to “freeze camera”, “stop rendering”, or “render only once”, useful to save the battery in static scenes.
private CameraClearFlags storedClearFlags;
private int storedCullingMask;
void Start () {
//save clear flags
storedClearFlags = Camera.main.clearFlags;
//save the culling mask
storedCullingMask = Camera.main.cullingMask;
}
void Update () {
if(Input.GetKeyDown(KeyCode.LeftArrow)){
StartRender();
}
if(Input.GetKeyDown(KeyCode.DownArrow)){
StopRender();
}
if(Input.GetKeyDown(KeyCode.RightArrow)){
RenderOnce();
}
}
void StartRender(){
//first change the clear flags to nothing
Camera.main.clearFlags = storedClearFlags;
//now change the culling mask to nothing
Camera.main.cullingMask = storedCullingMask;
}
void StopRender(){
//first change the clear flags to nothing
Camera.main.clearFlags = CameraClearFlags.Nothing;
//now change the culling mask to nothing
Camera.main.cullingMask = 0;
}
void RenderOnce(){
StartRender();
Camera.main.Render();
StopRender();
}
This method works (not on mobile) but it is a bit clunky. I think Unity should allow for a more elegant way of using GUI-only apps.
-Screen capture using ReadPixels() This takes about 3 seconds on an HTC One and freezes everything in the process.
-RenderTexture is fast but I only have a desktop Pro version.
-Application.targetFrameRate set to 1: still rendering albeit slow. User input is super laggy though, even if the routines are placed in FixedUpdate with the frame rate set to normal if user input is detected. Still too slow.
-System.Threading.Thread.Sleep() is asking for trouble.
So after using Unity for 2 years, I have finally run into a limitation. Unity is a battery hog. You cannot even simply stop rendering.
I’ve tested and work very nice. On xcode debug my app in idle state (running 60 fps UI only) consumes 37% of CPU, in the moment that render is stoped the consume drop down to 16%, but the best is that the Input is continued working at 60 fps, perfect. Another cool fact is that not have any heavy processing to Start/Stop render, the swap is immediatly!
Perhaps with the upcoming custom render loop it will be possible to pause the render loop and thus save battery. But I agree that the best solution is a dedicated native 2d solution like Xamarin. Especially since it is free now.
Unless you want to support the Mac. Xamarin’s support for the Mac was very weak before (trust me, I really tried it), and now that it’s been bought out by Microsoft, I consider it walking dead now. For cross-platform desktop games, Unity is still by far the best tool. For cross-platform desktop apps, you’d be better off using Xojo (which has done a lot of dumb things in the last five years, but is still the best tool in that space).
Xamarin is a nightmare. Extremely familiar with it. Write once, debug everywhere. Hardly cross platform at all. Besides, I need desktop support.
The only proper way to do this is OpenGL cross platform kit. Admittedly, my particular app (graphical dashboard) works well for this particular use case, but I think it would be extended to a lot of others non game apps.