[英]C# and Unity. Take pictures while rotating the camera
So I have the Screenshot (Tool) working and the camera rotation working.所以我有截图(工具)工作和相机旋转工作。 By themselves.
通过他们自己。 I would like to take a picture maybe every second as the camera is rotating automatically.
我想每秒钟拍一张照片,因为相机会自动旋转。 I don't know enough about C# to decipher what to extract from the Screenshot class.
我对 C# 了解得不够多,无法破译从 Screenshot 类中提取的内容。 Also I don't know if I can call the class from my camera rotation class.
另外我不知道我是否可以从我的相机旋转类中调用该类。
Thanks.谢谢。
Here are the 2 classes.这是2个班级。
Screenshot tool.截图工具。
//C# Example
using UnityEditor;
using UnityEngine;
[ExecuteInEditMode]
public class Screenshot : EditorWindow
{
int resWidth = Screen.width*4;
int resHeight = Screen.height*4;
public Camera myCamera;
int scale = 1;
string path = "";
bool showPreview = true;
RenderTexture renderTexture;
bool isTransparent = false;
// Add menu item named "My Window" to the Window menu
[MenuItem("Tools/Screenshot/Instant High-Res Screenshot")]
public static void ShowWindow()
{
//Show existing window instance. If one doesn't exist, make one.
EditorWindow editorWindow = EditorWindow.GetWindow(typeof(Screenshot));
editorWindow.autoRepaintOnSceneChange = true;
editorWindow.Show();
editorWindow.title = "Screenshot";
}
float lastTime;
void OnGUI()
{
EditorGUILayout.LabelField ("Resolution", EditorStyles.boldLabel);
resWidth = EditorGUILayout.IntField ("Width", resWidth);
resHeight = EditorGUILayout.IntField ("Height", resHeight);
EditorGUILayout.Space();
scale = EditorGUILayout.IntSlider ("Scale", scale, 1, 15);
EditorGUILayout.HelpBox("The default mode of screenshot is crop - so choose a proper width and height. The scale is a factor " +
"to multiply or enlarge the renders without loosing quality.",MessageType.None);
EditorGUILayout.Space();
GUILayout.Label ("Save Path", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.TextField(path,GUILayout.ExpandWidth(false));
if(GUILayout.Button("Browse",GUILayout.ExpandWidth(false)))
path = EditorUtility.SaveFolderPanel("Path to Save Images",path,Application.dataPath);
EditorGUILayout.EndHorizontal();
EditorGUILayout.HelpBox("Choose the folder in which to save the screenshots ",MessageType.None);
EditorGUILayout.Space();
//isTransparent = EditorGUILayout.Toggle(isTransparent,"Transparent Background");
GUILayout.Label ("Select Camera", EditorStyles.boldLabel);
myCamera = EditorGUILayout.ObjectField(myCamera, typeof(Camera), true,null) as Camera;
if(myCamera == null)
{
myCamera = Camera.main;
}
isTransparent = EditorGUILayout.Toggle("Transparent Background", isTransparent);
EditorGUILayout.HelpBox("Choose the camera of which to capture the render. You can make the background transparent using the transparency option.",MessageType.None);
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField ("Default Options", EditorStyles.boldLabel);
if(GUILayout.Button("Set To Screen Size"))
{
resHeight = (int)Handles.GetMainGameViewSize().y;
resWidth = (int)Handles.GetMainGameViewSize().x;
}
if(GUILayout.Button("Default Size"))
{
resHeight = 1440;
resWidth = 2560;
scale = 1;
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
EditorGUILayout.LabelField ("Screenshot will be taken at " + resWidth*scale + " x " + resHeight*scale + " px", EditorStyles.boldLabel);
if(GUILayout.Button("Take Screenshot",GUILayout.MinHeight(60)))
{
if(path == "")
{
path = EditorUtility.SaveFolderPanel("Path to Save Images",path,Application.dataPath);
Debug.Log("Path Set");
TakeHiResShot();
}
else
{
TakeHiResShot();
}
}
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if(GUILayout.Button("Open Last Screenshot",GUILayout.MaxWidth(160),GUILayout.MinHeight(40)))
{
if(lastScreenshot != "")
{
Application.OpenURL("file://" + lastScreenshot);
Debug.Log("Opening File " + lastScreenshot);
}
}
if(GUILayout.Button("Open Folder",GUILayout.MaxWidth(100),GUILayout.MinHeight(40)))
{
Application.OpenURL("file://" + path);
}
if(GUILayout.Button("More Assets",GUILayout.MaxWidth(100),GUILayout.MinHeight(40)))
{
Application.OpenURL("https://www.assetstore.unity3d.com/en/#!/publisher/5951");
}
EditorGUILayout.EndHorizontal();
if (takeHiResShot)
{
int resWidthN = resWidth*scale;
int resHeightN = resHeight*scale;
RenderTexture rt = new RenderTexture(resWidthN, resHeightN, 24);
myCamera.targetTexture = rt;
TextureFormat tFormat;
if(isTransparent)
tFormat = TextureFormat.ARGB32;
else
tFormat = TextureFormat.RGB24;
Texture2D screenShot = new Texture2D(resWidthN, resHeightN, tFormat,false);
myCamera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidthN, resHeightN), 0, 0);
myCamera.targetTexture = null;
RenderTexture.active = null;
byte[] bytes = screenShot.EncodeToPNG();
string filename = ScreenShotName(resWidthN, resHeightN);
System.IO.File.WriteAllBytes(filename, bytes);
Debug.Log(string.Format("Took screenshot to: {0}", filename));
Application.OpenURL(filename);
takeHiResShot = false;
}
EditorGUILayout.HelpBox("In case of any error, make sure you have Unity Pro as the plugin requires Unity Pro to work.",MessageType.Info);
}
private bool takeHiResShot = false;
public string lastScreenshot = "";
public string ScreenShotName(int width, int height) {
string strPath="";
strPath = string.Format("{0}/screen_{1}x{2}_{3}.png",
path,
width, height,
System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
lastScreenshot = strPath;
return strPath;
}
public void TakeHiResShot() {
Debug.Log("Taking Screenshot");
takeHiResShot = true;
}
}
Camera rotation.相机旋转。
using UnityEngine;
using System.Collections;
public class CamRotation_for_camera : MonoBehaviour
{
public float speed = 1;
void Update()
{
transform.Rotate(0, (speed * 5) * Time.deltaTime, 0);
}
}
You can't call Screenshot
from CamRotation_for_camera
because Screenshot
is an EditorWindow (to be used by the editor) and CamRotation_for_camera
is a Monobehaviour (a component for Gameobjects used by the engine).您不能从
CamRotation_for_camera
调用Screenshot
,因为Screenshot
是一个 EditorWindow(供编辑器使用),而CamRotation_for_camera
是一个 Monobehaviour(引擎使用的游戏对象的组件)。 Basically the component has no knowledge of the windows the editor uses.基本上该组件不知道编辑器使用的窗口。
You can move some of the code for photo-taking into a component though and add that to an object in the scene, then your components will be able to interact.您可以将一些用于拍照的代码移动到组件中并将其添加到场景中的对象中,然后您的组件将能够进行交互。
Eg.例如。
using System.IO;
using UnityEngine;
public class RunTimeScreenshots : MonoBehaviour
{
public float timeBetweenPhotos = 3;
private float nextPhotoTime;
public bool takeScreenshot = false;
public int resWidth;
public int resHeight;
public int scale = 1;
public bool isTransparent;
// using a local path here since it's less of a pain to work with
public string localPath;
// set up the absolute path here
private string absolutePath => Path.Combine(Application.dataPath, localPath);
private string lastScreenshot = "";
private Camera myCamera;
void Start()
{
myCamera = Camera.main;
}
void Update()
{
// Almost just like the code on Screenshot.OnGUI() but a little different
// a little bit of timing code for repeated photo-taking
if (takeScreenshot && Time.time > nextPhotoTime) {
// make sure to move this forward every time
nextPhotoTime = Time.time + timeBetweenPhotos;
int resWidthN = resWidth * scale;
int resHeightN = resHeight * scale;
RenderTexture rt = new RenderTexture(resWidthN, resHeightN, 24);
myCamera.targetTexture = rt;
TextureFormat tFormat;
if (isTransparent)
tFormat = TextureFormat.ARGB32;
else
tFormat = TextureFormat.RGB24;
Texture2D screenShot = new Texture2D(resWidthN, resHeightN, tFormat, false);
myCamera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidthN, resHeightN), 0, 0);
myCamera.targetTexture = null;
RenderTexture.active = null;
byte[] bytes = screenShot.EncodeToPNG();
string filename = ScreenShotName(resWidthN, resHeightN);
File.WriteAllBytes(filename, bytes);
Debug.Log(string.Format("Took screenshot to: {0}", filename));
// we don't open the photo right away since thats a pain,
// and we aren't going to turn off "takeScreenshot"
}
}
public string ScreenShotName(int width, int height) {
string strPath = "";
strPath = string.Format("{0}/screen_{1}x{2}_{3}.png",
absolutePath,
width, height,
System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
lastScreenshot = strPath;
return strPath;
}
}
Set up the component how you'd like and use the takeScreenshot
checkbox with the game running to turn this on and off.根据您的喜好设置组件,并在游戏运行时使用
takeScreenshot
复选框来打开和关闭此功能。
Since you want it to work on runtime you shouldn't use an EditorWindow
at all.由于您希望它在运行时运行,因此根本不应使用
EditorWindow
。
I would still keep the custom inspector so you can still use it just like before but now as a component attached to a GameObject in the scene:我仍然会保留自定义检查器,因此您仍然可以像以前一样使用它,但现在作为附加到场景中游戏对象的组件:
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
public class Screenshot : MonoBehaviour
{
[SerializeField] private Vector2Int resolution = new Vector2Int(Screen.width * 4, Screen.height * 4);
[SerializeField] private Camera _myCamera;
[SerializeField] [Range(1,15)] private int _scale = 1;
[SerializeField] private string _path = "";
[SerializeField] private bool _showPreview = true;
[SerializeField] private bool _isTransparent = false;
[SerializeField] private string _lastScreenshot = "";
private RenderTexture renderTexture;
private string ScreenShotName(string path, int width, int height)
{
strPath = $"{path}/screen_{width}x{height}_{System.DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png";
_lastScreenshot = strPath;
return strPath;
}
public void TakeScreenshot()
{
TakeScreenshot(_myCamera, _resolution, _scale, _path, _isTransparent, _showPreview);
}
public void TakeScreenshot(Camera camera, Vector2Int resolution, float scale, string path, bool isTransparent, bool showPreview)
{
var finalResolution = resolution * scale;
if(!renderTexture) renderTexture = new RenderTexture(finalResolution.x, finalResolution.y, 24);
myCamera.targetTexture = renderTexture;
var tFormat = isTransparent ? TextureFormat.ARGB32 : TextureFormat.RGB24;
var screenShot = new Texture2D(finalResolution.x, finalResolution.y, tFormat, false);
myCamera.Render();
RenderTexture.active = renderTexture;
screenShot.ReadPixels(new Rect(0, 0, finalResolution.x, finalResolution.y), 0, 0);
myCamera.targetTexture = null;
RenderTexture.active = null;
var bytes = screenShot.EncodeToPNG();
var filename = ScreenShotName(finalResolution.x, finalResolution.y);
WriteFileAsync(filename, bytes);
}
private async void WriteFileAsync(string path, byte[] data)
{
using(var file = File.Open(path))
{
await file.WriteAsync(data, 0, data.Length);
}
Debug.Log(string.Format("Took screenshot to: {0}", filename));
if(showPreview) Application.OpenURL(filename);
}
#if UNITY_EDITOR
[CustomEditor(typeof(Screenshot))]
private class ScreenshotEditor : Editor
{
private SerializedProperty resolution;
private SerializedProperty myCamera;
private SerializedProperty scale;
private SerializedProperty path;
private SerializedProperty showPreview;
private SerializedProperty isTransparent;
private SerializedProperty lastScreenshot;
private void OnEnable()
{
resolution = serilaizedObject.FindProperty(nameof((Screenshot._resolution));
myCamera = serilaizedObject.FindProperty(nameof((Screenshot._myCamera));
scale = serilaizedObject.FindProperty(nameof((Screenshot._scale));
path = serilaizedObject.FindProperty(nameof((Screenshot._path));
showPreview = serilaizedObject.FindProperty(nameof((Screenshot._showPreview));
isTransparent = serilaizedObject.FindProperty(nameof(Screenshot._isTransparent));
lastScreenshot = serilaizedObject.FindProperty(nameof(Screenshot._lastScreenshot));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginDisabledGroup(true);
{
EditorGUILayout.ObjectField("Script", MonoScript.FromMonoBehaviour((Screenshot)target), typeof(Screenshot), false);
EditorGUILayout.Space();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.LabelField ("Resolution", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(resolution);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(scale);
EditorGUILayout.HelpBox("The default mode of screenshot is crop - so choose a proper width and height. The scale is a factor " +
"to multiply or enlarge the renders without loosing quality.",MessageType.None);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Save Path", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PropertyField(path, GUILayout.ExpandWidth(false));
if(GUILayout.Button("Browse",GUILayout.ExpandWidth(false)))
{
path = EditorUtility.SaveFolderPanel("Path to Save Images",path,Application.dataPath);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.HelpBox("Choose the folder in which to save the screenshots ",MessageType.None);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(isTransparent, new GUIContent("Transparent Background"));
EditorGUILayout.LabelField("Select Camera", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(myCamera);
if(!myCamera.objectReferenceValue) myCamera.objectReferenceValue = Camera.main;
EditorGUILayout.PropertyField(isTransparent, new GUIContent("Transparent Background"));
EditorGUILayout.HelpBox("Choose the camera of which to capture the render. You can make the background transparent using the transparency option.", MessageType.None);
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
{
EditorGUILayout.LabelField ("Default Options", EditorStyles.boldLabel);
if(GUILayout.Button("Set To Screen Size"))
{
resolution.vector2ntValue = new Vector2Int((int)Handles.GetMainGameViewSize().x, (int)Handles.GetMainGameViewSize().y);
}
if(GUILayout.Button("Default Size"))
{
resolution.vector2ntValue = new Vector2Int(2560, 1440);
scale.intValue = 1;
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
EditorGUILayout.LabelField ("Screenshot will be taken at " + resWidth*scale + " x " + resHeight*scale + " px", EditorStyles.boldLabel);
if(GUILayout.Button("Take Screenshot", GUILayout.MinHeight(60)))
{
if(string.IsNullOrWhitespae(path.stringValue))
{
path.stringValue = EditorUtility.SaveFolderPanel("Path to Save Images", path.stringValue, Application.dataPath);
Debug.Log("Path Set");
((Screenshot)target).TakeHiResShot();
}
else
{
((Screenshot)target).TakeHiResShot();
}
}
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if(GUILayout.Button("Open Last Screenshot",GUILayout.MaxWidth(160),GUILayout.MinHeight(40)))
{
if(lastScreenshot != "")
{
Application.OpenURL("file://" + lastScreenshot);
Debug.Log("Opening File " + lastScreenshot);
}
}
if(GUILayout.Button("Open Folder", GUILayout.MaxWidth(100), GUILayout.MinHeight(40)))
{
Application.OpenURL("file://" + path.stringValue);
}
if(GUILayout.Button("More Assets", GUILayout.MaxWidth(100), GUILayout.MinHeight(40)))
{
Application.OpenURL("https://www.assetstore.unity3d.com/en/#!/publisher/5951");
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.HelpBox("In case of any error, make sure you have Unity Pro as the plugin requires Unity Pro to work.",MessageType.Info);
serializedObject.ApplyModiefiedProperties();
}
}
#endif
}
Then simply attach it to the camera and reference it in your script like然后只需将其附加到相机并在您的脚本中引用它,例如
public class CamRotation_for_camera : MonoBehaviour
{
// I would rather not "hide" a secret multiplication by 5 but rather
// directly assign the correct angle per second here
[SerializeField] private float speed = 5;
// hre drag the Screenshot instance
[SerializeField] private Screenshot screenshot;
// (estimate) time in seconds between screenshots
[SerializedField] private float screenshotInterval;
private float timer;
void Update()
{
transform.Rotate(0, speed * Time.deltaTime, 0);
timer += Time.deltaTime;
if(timer < screenshotInterval) return;
timer = 0;
screenshot.TakeScreenshot();
}
}
or alternatively you could go by the rotated angle instead或者你可以通过旋转的角度来代替
// (estimate) angle between screenshots
[SerializedField] private float screenshotIntervalAngle;
private float currentAngle;
void Update()
{
var rotationStep = speed * Time.deltaTime;
transform.Rotate(0, rotationStep, 0);
currentAngle += rotationStep;
if(currentAngle < screenshotIntervalAngle) return;
currentAngle = 0;
screenshot.TakeScreenshot();
}
For both I commented estimate
in particular for the first.对于这两个,我特别评论了第一个
estimate
。 I already moved the File IO to a different thread by using async
but the most expensive thing is that EncodeToPNG
which will always create a very noteable lag and wil thereby itself also interfer with the time intervals.我已经通过使用
async
将 File IO 移动到不同的线程,但最昂贵的事情是EncodeToPNG
总是会产生非常明显的滞后,因此本身也会干扰时间间隔。
Note: Completely typed on smartphone but I hope the idea gets clear.注意:完全在智能手机上输入,但我希望这个想法变得清晰。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.