As classes que derivam da classe Editor (EditorWindow, EditorApplication) são simples de implementar e extremamente flexíveis, permitindo a criação de add-ons sem grande dificuldade para praticamente qualquer fim.
Na palestra da Aquiris na Unite 12, membros do estúdio mostraram um trabalho de animação renderizado dentro da própria Unity e exportado frame por frame no formato png, permitindo a manipulação do vídeo (que precisava ser mesclado com cenas reais) através de um software de edição externo.
Na tentativa de sanar a pouca experiência que tenho com a interface do blender e evitar ter de usar softwares craqueados para a captura de telas de futuros projetos, desenvolvi um add-on de animação baseado na lógica apresentada pela Aquiris: tirar screenshots de acordo com determinada taxa de quadros por segundo e exportar para junção externa.
Antes de disponibilizar o código, algumas instruções e explicações breves:
Para abrir a janela é necessário clicar em Animation Exporter na opção Window (ao lado de help). Estando ela aberta é possível modifica-la a seu bel-prazer como uma janela ou aba tradicionais da Unity.
Sobre os elementos mostrados:
- "Name of the animation" : Campo onde o usuário pode inserir o nome que as imagens exportadas terão. Cada imagem terá o nome inserido e um número, a identificação do frame. Por exemplo: Se no momento que um quadro for exportado já houverem sido exportados 199 outros, ele terá o id 200.
- "Frames per second": taxa de quadros por segundo da animação, define quantos frames serão salvos a cada segundo passado. Como padrão inseri as opções de 1, 12, 16, 24, 25, 29, 30, 48 e 60 quadros por segundo, opções mais usadas em codecs de video. Caso seja necessária uma taxa diferente há também a opção "Custom", que abre um campo onde o usuário pode digitar o valor que preferir.
- "Resolution": Define a resolução dos frames a serem salvos. "Normal" é a resolução da tela do jogo, "Better" é esse valor duplicado, "Beautiful" e "Amazing" são, respectivamente, a resolução da tela triplicada e quadruplicada.
- "Record on Start": Inicia a animação automaticamente assim que a aplicação é iniciada, desta maneira não se perde nada dos segundos iniciais.
- "Record": Botão que inicia a gravação. Assim que clicado, caso a gravação ainda não tenha sido iniciada, abre uma janela onde o usuário pode escolher a pasta onde salvar os frames. A pasta padrão é o local onde a aplicação está salva.
- "Pause" / "Unpause": Pausa ou retorna para a gravação.
- Informações da gravação: Status (Recording, Paused, Waiting for Application, etc), tempo de gravação e número de frames gravados.
Fiz algumas mudanças de ultima hora no algoritmo (a ideia de incluir o "Record on Start" me veio enquanto escrevia o post), então qualquer erro grotesco ou melhoramento interessante, por favor me avisem! Sugestões de recursos para próximas versões do código também são bem vindas. Em outras palavras, se estiver interessado em baixar e testar, por favor me mande um feedback sobre o que achou.
Para utilizar o Animation Exporter, crie um novo arquivo .cs e nomeie-o AnimationExporter. Copie e cole o código abaixo nesse arquivo:
using UnityEngine;
using UnityEditor;
using System.Collections;
//Enum que define a quantidade de quadros por segundo exportados
public enum FPSOPTIONS{
fps1 =0,
fps12 = 1,
fps16 =2,
fps24 =3,
fps25 =4,
fps29 =5,
fps30 =6,
fps48=7,
fps60 =8,
Custom =9
}
//Enum que define a qualidade dos quadros exportados
public enum RESOLUTION{
Normal = 0,
Better = 1,
Beautiful = 2,
Amazing = 3
}
public class AnimationExporter : EditorWindow {
private int imageCount =0, resolution = 1;
private float recordDelta = 0f, recordTime =0f;
private string path = Application.dataPath, checkPath;
private bool record = false, paused = false, recordOnStart = false;
public int framesPerSecond = 24;
public string animationName = "ExportedAnimation", status = "Waiting for application";
public FPSOPTIONS fpsOptions = FPSOPTIONS.fps16;
public RESOLUTION resolutionOp = RESOLUTION.Normal, oldResolution = RESOLUTION.Normal;
void OnGUI () {
DoWindow();
}
// Função que executa as funções da janela, chamada no OnGUI
void DoWindow () {
GUILayout.Space(20f);
if(!Application.isPlaying || Application.isPlaying && !record){
animationName = EditorGUILayout.TextField("Name of the Animation", animationName); //Define o nome
}
else{
EditorGUILayout.TextField("Name of the Animation", animationName);
}
if(!Application.isPlaying || Application.isPlaying && !record){
fpsOptions = (FPSOPTIONS)EditorGUILayout.EnumPopup("Frames per Second", fpsOptions); // Define o fps
}
else{
EditorGUILayout.EnumPopup("Frames per Second", fpsOptions);
}
switch(fpsOptions){
case FPSOPTIONS.fps1: framesPerSecond =1; break;
case FPSOPTIONS.fps12: framesPerSecond =12; break;
case FPSOPTIONS.fps16: framesPerSecond =16; break;
case FPSOPTIONS.fps24: framesPerSecond =24; break;
case FPSOPTIONS.fps25: framesPerSecond =25; break;
case FPSOPTIONS.fps29: framesPerSecond =29; break;
case FPSOPTIONS.fps30: framesPerSecond =30; break;
case FPSOPTIONS.fps48: framesPerSecond =48; break;
case FPSOPTIONS.fps60: framesPerSecond =60; break;
case FPSOPTIONS.Custom: framesPerSecond = EditorGUILayout.IntField("Frames per Second",framesPerSecond);break;
}
if(framesPerSecond < 1){
framesPerSecond = 1;
}
if(framesPerSecond >64){
framesPerSecond = 64;
}
if(framesPerSecond >120){
Debug.LogWarning("WARNING: A high frames per second rate may slow your computer's perfomance!");
}
if(!Application.isPlaying || Application.isPlaying && !record){
resolutionOp = (RESOLUTION)EditorGUILayout.EnumPopup("Resolution: ", resolutionOp); // Define a resolução
}
else{
EditorGUILayout.EnumPopup("Resolution: ", resolutionOp);
}
if(resolutionOp != oldResolution){
switch(resolutionOp){
case RESOLUTION.Normal: resolution = 1;
oldResolution = resolutionOp;
break;
case RESOLUTION.Better: resolution =2;
oldResolution = resolutionOp;
Debug.LogWarning("WARNING: Higher resolution may slow your computer's performance!");
break;
case RESOLUTION.Beautiful: resolution =3;
oldResolution = resolutionOp;
Debug.LogWarning("WARNING: Higher resolution may slow your computer's performance!");
break;
case RESOLUTION.Amazing: resolution =4;
oldResolution = resolutionOp;
Debug.LogWarning("WARNING: Higher resolution may slow your computer's performance!");
break;
}
}
if(!Application.isPlaying || Application.isPlaying && !record){
recordOnStart = EditorGUILayout.Toggle("Record on Start", recordOnStart); //Inicia a gravacao automaticamente ao iniciar o jogo
}
else{
EditorGUILayout.Toggle("Record on Start", recordOnStart);
}
if(GUILayout.Button ("Record")){ // Botão para iniciar a gravação
if(Application.isPlaying && !record && !paused){
checkPath = EditorUtility.SaveFilePanel("Choose Directory", path, animationName, "");
if(checkPath != ""){
path = checkPath;
record = true;
}
}
else if(Application.isPlaying && !record && paused){
record = true;
paused = false;
}
else if(Application.isPlaying && record){
Debug.LogWarning("Application is already being recorded!");
}
else if(!Application.isPlaying){
Debug.LogError("Cannot record if application is not running!");
}
}
if(!paused){
if(GUILayout.Button ("Pause")){ // Botão de pause
if(record){
record = false;
paused = true;
}
}
}
else{
if(GUILayout.Button ("Unpause")){
record = true;
paused = false;
}
}
EditorGUILayout.LabelField("Status: ", status); // Mostra o status
EditorGUILayout.LabelField("Recording Time: ", recordTime.ToString("F2")); // Mostra o tempo de gravação
EditorGUILayout.LabelField("Frames recorded: ", imageCount.ToString()); // Mostra o número de quadros exportados
if(record){
status = "Recording";
}
else if(paused && Application.isPlaying){
status = "Paused";
}
else if(!Application.isPlaying){
status = "Waiting for application";
}
}
void Update(){
if(recordOnStart && Application.isPlaying){
checkPath = EditorUtility.SaveFilePanel("Choose Directory", path, animationName, "");
if(checkPath != ""){
path = checkPath;
record = true;
}
recordOnStart = false;
}
if(record && Application.isPlaying){
recordTime += Time.deltaTime*10;
recordDelta +=Time.deltaTime;
if(recordDelta >= 1/framesPerSecond){ // Período de latencia entre a exportação de um frame e outro
imageCount++;
Application.CaptureScreenshot(path+"(" + imageCount.ToString() + ")" + ".png", resolution); // Tira a screenshot e salva no diretório escolhido
recordDelta = 0f;
}
}
if(!Application.isPlaying && !recordOnStart){
record = false;
paused = false;
if(imageCount >0){
imageCount =0;
recordTime =0f;
}
}
Repaint(); // Atualiza a janela durante a execução da animação
}
[MenuItem ("Window/Animation Exporter")]
static void Init () {
EditorWindow.GetWindow<AnimationExporter>();
}
}
Sintam-se a vontade para editar e utilizar o código como bem entenderem. Crédito não é necessário, mas é valorizado ;) .
Muito bom Hugo! Mas o código precisa ser salvo em uma pasta do projeto chamada Editor, certo? Abraços, Nery.
ResponderExcluirObrigado Nery!
ExcluirSó é necessário salvar o código na pasta Editor se ele tiver sido escrito em javascript, com o C# é arbitrário.