(For more resources related to this topic, see here.)
Having more than one viewport displayed can be useful in many situations. For example, you might want to show simultaneous events going on in different locations, or maybe you want to have a separate window for hot-seat multiplayer games. Although you could do it manually by adjusting the Normalized Viewport Rect parameters on your camera, this recipe includes a series of extra preferences to make it more independent from the user's display configuration.
For this recipe, we have prepared a package named basicLevel containing a scene. The package is in the 0423_02_01_02 folder.
To create a picture-in-picture display, just follow these steps:
using UnityEngine;
public class PictureInPicture: MonoBehaviour {
public enum HorizontalAlignment{left, center, right};
public enum VerticalAlignment{top, middle, bottom};
public HorizontalAlignment horizontalAlignment =
HorizontalAlignment.left;
public VerticalAlignment verticalAlignment =
VerticalAlignment.top;
public enum ScreenDimensions{pixels, screen_percentage};
public ScreenDimensions dimensionsIn = ScreenDimensions.
pixels;
public int width = 50;
public int height= 50;
public float xOffset = 0f;
public float yOffset = 0f;
public bool update = true;
private int hsize, vsize, hloc, vloc;
void Start (){
AdjustCamera ();
}
void Update (){
if(update)
AdjustCamera ();
}
void AdjustCamera(){
if(dimensionsIn == ScreenDimensions.screen_percentage){
hsize = Mathf.RoundToInt(width * 0.01f * Screen.
width);
vsize = Mathf.RoundToInt(height * 0.01f * Screen.
height);
} else {
hsize = width;
vsize = height;
}
if(horizontalAlignment == HorizontalAlignment.left){
hloc = Mathf.RoundToInt(xOffset * 0.01f *
Screen.width);
} else if(horizontalAlignment == HorizontalAlignment.
right){
hloc = Mathf.RoundToInt((Screen.width - hsize)
- (xOffset * 0.01f * Screen.width));
} else {
hloc = Mathf.RoundToInt(((Screen.width * 0.5f)
- (hsize * 0.5f)) - (xOffset * 0.01f * Screen.height));
}
if(verticalAlignment == VerticalAlignment.top){
vloc = Mathf.RoundToInt((Screen.height -
vsize) - (yOffset * 0.01f * Screen.height));
} else if(verticalAlignment == VerticalAlignment.
bottom){
vloc = Mathf.RoundToInt(yOffset * 0.01f *
Screen.height);
} else {
vloc = Mathf.RoundToInt(((Screen.height *
0.5f) - (vsize * 0.5f)) - (yOffset * 0.01f * Screen.height));
}
camera.pixelRect = new Rect(hloc,vloc,hsize,vsize);
}
}
In case you haven't noticed, we are not achieving percentage by dividing numbers by 100, but rather multiplying them by 0.01. The reason behind that is performance: computer processors are faster multiplying than dividing.
Our script changes the camera's Normalized Viewport Rect parameters, thus resizing and positioning the viewport according to the user preferences.
The following are some aspects of your picture-in-picture you could change.
Making the picture-in-picture proportional to the screen's size
If you change the Dimensions In option to screen_percentage, the viewport size will be based on the actual screen's dimensions instead of pixels.
Changing the position of the picture-in-picture
Vertical Alignment and Horizontal Alignment can be used to change the viewport's origin. Use them to place it where you wish.
Preventing the picture-in-picture from updating on every frame
Leave the Update option unchecked if you don't plan to change the viewport position in running mode. Also, it's a good idea to leave it checked when testing and then uncheck it once the position has been decided and set up.
Choosing from a variety of cameras is a common feature in many genres: race sims, sports sims, tycoon/strategy, and many others. In this recipe, we will learn how to give players the ability of choosing an option from many cameras using their keyboard.
In order to follow this recipe, we have prepared a package containing a basic level named basicScene. The package is in the folder 0423_02_01_02.
To implement switchable cameras, follow these steps:
using UnityEngine;
public class CameraSwitch : MonoBehaviour {
public GameObject[] cameras;
public string[] shortcuts;
public bool changeAudioListener = true;
void Update (){
int i = 0;
for(i=0; i<cameras.Length; i++){
if (Input.GetKeyUp(shortcuts[i]))
SwitchCamera(i);
}
}
void SwitchCamera ( int index ){
int i = 0;
for(i=0; i<cameras.Length; i++){
if(i != index){
if(changeAudioListener){
cameras[i].
GetComponent<AudioListener>().enabled = false;
}
cameras[i].camera.enabled = false;
} else {
if(changeAudioListener){
cameras[i].
GetComponent<AudioListener>().enabled = true;
}
cameras[i].camera.enabled = true;
}
}
}
}
The script is very straightforward. All it does is capture the key pressed and enable its respective camera (and its Audio Listener, in case the Change Audio Listener option is checked).
Here are some ideas on how you could try twisting this recipe a bit.
Using a single-enabled camera
A different approach to the problem would be keeping all the secondary cameras disabled and assigning their position and rotation to the main camera via a script (you would need to make a copy of the main camera and add it to the list, in case you wanted to save its transform settings).
Triggering the switch from other events
Also, you could change your camera from other game object's scripts by using a line of code, such as the one shown here:
GameObject.Find("Switchboard").GetComponent("CameraSwitch").
SwitchCamera(1);
As anyone who has played a game set in an outdoor environment in the last 15 years can tell you, the lens flare effect is used to simulate the incidence of bright lights over the player's field of view. Although it has become a bit overused, it is still very much present in all kinds of games. In this recipe, we will create and test our own lens flare texture.
In order to continue with this recipe, it's strongly recommended that you have access to image editor software such as Adobe Photoshop or GIMP. The source for lens texture created in this recipe can be found in the 0423_02_03 folder.
To create a new lens flare texture and apply it to the scene, follow these steps:
We have used Unity's built-in lens flare texture as a blueprint for our own. Once applied, the lens flare texture will be displayed when the player looks into the approximate direction of the light.
Flare textures can use different layouts and parameters for each element. In case you want to learn more about the Lens Flare effect, check out Unity's documentation at http://docs. unity3d.com/Documentation/Components/class-LensFlare.html.
If you want your game or player to take in-game snapshots and apply it as a texture, this recipe will show you how. This can be very useful if you plan to implement an in-game photo gallery or display a snapshot of a past key moment at the end of a level (Race Games and Stunt Sims use this feature a lot).
In order to follow this recipe, please import the basicTerrain package, available in the 0423_02_04_05 folder, into your project. The package includes a basic terrain and a camera that can be rotated via a mouse.
To create textures from screen content, follow these steps:
using UnityEngine;
using System.Collections;
public class ScreenTexture : MonoBehaviour {
public int photoWidth = 50;
public int photoHeight = 50;
public int thumbProportion = 25;
public Color borderColor = Color.white;
public int borderWidth = 2;
private Texture2D texture;
private Texture2D border;
private int screenWidth;
private int screenHeight;
private int frameWidth;
private int frameHeight;
private bool shoot = false;
void Start (){
screenWidth = Screen.width;
screenHeight = Screen.height;
frameWidth = Mathf.RoundToInt(screenWidth * photoWidth *
0.01f);
frameHeight = Mathf.RoundToInt(screenHeight * photoHeight
* 0.01f);
texture = new Texture2D (frameWidth,frameHeight,TextureFor
mat.RGB24,false);
border = new Texture2D (1,1,TextureFormat.ARGB32, false);
border.SetPixel(0,0,borderColor);
border.Apply();
}
void Update (){
if (Input.GetKeyUp(KeyCode.Mouse0))
StartCoroutine(CaptureScreen());
}
void OnGUI (){
GUI.DrawTexture(new Rect((screenWidth*0.5f)-
(frameWidth*0.5f) - borderWidth*2,((screenHeight*0.5f)-
(frameHeight*0.5f)) - borderWidth,frameWidth + borderWidth*2,borde
rWidth),border,ScaleMode.StretchToFill);
GUI.DrawTexture(new Rect((screenWidth*0.5f)-
(frameWidth*0.5f) - borderWidth*2,(screenHeight*0.5f)+(frameHeigh
t*0.5f),frameWidth + borderWidth*2,borderWidth),border,ScaleMode.
StretchToFill);
GUI.DrawTexture(new Rect((screenWidth*0.5f)-
(frameWidth*0.5f)- borderWidth*2,(screenHeight*0.5f)-(frameHeight*
0.5f),borderWidth,frameHeight),border,ScaleMode.StretchToFill);
GUI.DrawTexture(new Rect((screenWidth*0.5f)+(frameWidth*0.
5f),(screenHeight*0.5f)-(frameHeight*0.5f),borderWidth,frameHeight
),border,ScaleMode.StretchToFill);
if(shoot){
GUI.DrawTexture(new Rect (10,10,frameWidth*thumbPropo
rtion*0.01f,frameHeight*thumbProportion* 0.01f),texture,ScaleMode.
StretchToFill);
}
}
IEnumerator CaptureScreen (){
yield return new WaitForEndOfFrame();
texture.ReadPixels(new Rect((screenWidth*0.5f)-(frameWidth
*0.5f),(screenHeight*0.5f)-(frameHeight*0.5f),frameWidth,frameHeig
ht),0,0);
texture.Apply();
shoot = true;
}
}
Clicking the mouse triggers a function that reads pixels within the specified rectangle and applies them into a texture that is drawn by the GUI.
Apart from displaying the texture as a GUI element, you could use it in other ways.
Applying your texture to a material
You apply your texture to an existing object's material by adding a line similar to
GameObject.Find("MyObject").renderer.material.mainTexture = texture;
at the end of the CaptureScreen function.
Using your texture as a screenshot
You can encode your texture as a PNG image file and save it. Check out Unity's documentation on this feature at http://docs.unity3d.com/Documentation/ScriptReference/ Texture2D.EncodeToPNG.html.