29 July 2018

Take a screenshot in Unity

I am working on a mobile app for the home poker tournament manager (HPTM) and while preparing the google play store for release I had to create some screenshots to display in the store. In the editor I can easily select the different resolutions and aspect ratios that are supported by the app, so I googled for the best way to take a screenshot from within the editor. It surprised me how many different solutions exist for this mundane task.

A lot of people write scripts that take a camera in the scene and render a screenshot from that. There are basically three methods that these scripts can use.

  • Texture2D.ReadPixels can even capture a part of the screen.
  • A slight different way is using a RenderTexture but that is almost the same as using ReadPixels, since at some point you'll need that call anyway. Instead of using the default rendertarget you're reading from a custom render texture.
  • ScreenCapture.CaptureScreenshot which replaces the deprecated Application.CaptureScreenshot. The main advantage is that this method also captures the UI that is rendered as "Screenspace - Overlay". The HPTM is almost entirely rendered in overlay, so this proved to be the only viable option.

A possible implementation of all three these methods can be found here.

I read some post where a user complained that this should be a default thing to do in the editor, supported by Unity itself. But reading all the different posts and comments on the forums and answers, it seems to me that there are a lot of different use cases, making it impossible for Unity to come with a standard default way to capture screenshots that would be useful to all developers (and simple to configure and use).

There are also some plugins available on the asset store that promise to be awesome tools. I tried only this one and it did not fit my needs. Perhaps there are paid solutions that cover it all, but since it is so easy to write it yourself, why bother?

Another "disadvantage" of all these scripts is that you need to attach them to some gameobject in your scene, while all I want is some editor code that has no impact on the game at all. It shouldn't even be compiled together with the game code! So I quickly wrote my own version of a screen capture script, adding yet another one to all the others than can be found on the internet :)

I added it to my UnityTools repo. Main advantages of this script? It uses the ScreenCapture functions, generates filenames with increasing numbers and a very, very, simple UI.

Always happy to receive feedback!

10 July 2018

Bend the world with Unity's Shader Graph

Hurray! Unity 2018.2 is out and that means: vertex positions are now accessible in graph shaders!

This means that the world bend shader from this previous post can now be implemented as a node! The cool part is: I only added one .cs file containing a custom node and it works with the existing code.

Add the "World Bend" node into the graph, and define 3 properties "Horizon", "Spread" and "Attenuation" (check my previous post to see what they are for). Connect the output as input for the position of your Master Node. Do not expose the three properties, but give them these reference values respectively: "_HORIZON", "_SPREAD", "_ATTENUATE". That way they receive the global values set by the World Bender script on the camera. And that's it! There is only one extra file:

using System.Reflection;
using UnityEditor.ShaderGraph;
using UnityEngine;

[Title("Custom", "World Bend")]
public class BendNode : CodeFunctionNode
{
 public BendNode()
 {
  name = "World Bend";
 }

 protected override MethodInfo GetFunctionToConvert()
 {
  return GetType().GetMethod("Bend",
   BindingFlags.Static | BindingFlags.NonPublic);
 }

 static string Bend(
  [Slot(0, Binding.ObjectSpacePosition)] Vector3 v,
  [Slot(1, Binding.None)] Vector1 Horizon,
  [Slot(2, Binding.None)] Vector1 Spread,
  [Slot(3, Binding.None)] Vector1 Attenuate,
  [Slot(4, Binding.None)] out Vector3 Out)
 {
  Out = Vector3.zero;
  return @"
{
 Out = mul (unity_ObjectToWorld, v);
 float dist = max(0.0, abs(Horizon.x - Out.z) - Spread);
 Out.y -= dist * dist * Attenuate;
 Out = mul(unity_WorldToObject, Out);
}";
 }
}

As always, you can find a small demo on my unity toolset repo. I'm open for feedback!