Noticias

¡RECUERDA QUE SI ERES UN NUEVO USUARIO, DEBES PRESENTARTE PARA PODER PUBLICAR MENSAJES! | TENEMOS CANAL OFICIAL DE TELEGRAM: t.me/unity3dspain

Programar en el editor LayerMask

Iniciado por ftejada, Enero 17, 2017, 06:29:10 AM

Tema anterior - Siguiente tema
Enero 17, 2017, 06:29:10 AM Ultima modificación: Enero 17, 2017, 06:30:54 AM por ftejada
Hola a todos! he estado mirando por internet pero no he podido encontrar la solución a lo que me ocurre, a ver si alguien sabe orientarme un poco.
 
 
 
Estoy programando una interface en el editor para una de mis clases, vendría a ser algo así la estructura basica de mi codigo:
 

public class ClaseB : MonoBehaviour
{
public LayerMask    collisionLayer = ~(1 << 8);   //Para que esten todas las capas activadas menos la 8
}
[RequireComponent(typeof(ClaseB))]
[ExecuteInEditMode]
public class ClaseA : MonoBehaviour
{
public ClaseB             claseB = new ClaseB();
....
}
 
[CustomEditor(typeof(ClaseA))]
public class ClaseA_Inspector : Editor
{
ClaseA claseA;
....
void OnEnable()
{
   claseA = (ClaseA)target;
   ...
}

public override void OnInspectorGUI()
{
   //Con la funcion "LayerField" sale un campo en el editor de LayerMask pero sin nada...
         //es decir no hay ni letras a menos que abra el campo...
   //Pero no hay ninguna capa marcada, cuando deberia haberla ya que lo configuré al principio en la ClaseB:
        //public LayerMask collisionLayer = ~(1 << 8);   //Para que esten todas las capas activadas menos la 8
   claseA.claseB.collisionLayer = EditorGUILayout.LayerField(claseA.claseB.collisionLayer, GUILayout.Width(119));
   
   //Con MaskField no se como utilizarla... No se como, ni que hacer con el parametro que debo pasarle de "string[] displa***Options"
   //claseA.claseB.collisionLayer = EditorGUILayout.MaskField(claseA.claseB.collisionLayer, ..., GUILayout.Width(119));
}
}

 
 
 
El caso es que necesito que se muestre el estado actual de layerMask de mi claseB desde la claseA_Editor. Y tambien poder modificarla...
 
 
 
Saludos y gracias por adelantado.

Hola, algunas cosas:
 
- todos esas clases me supongo que las tenes en scripts separados, sino es asi deberias. (para los assembly del Editor en una carpeta "Editor")
 
- Cuando instancias un objeto de la clase A se crea uno de la clase B (por eso el RequiereComponent), y aun asi estas creando otra instancia mas de B con el "new", no se si es lo que deseas, casi seguro que no.
 
- Podes especificar mas porque queres que la clase A tenga tambien a la clase B, que tratas de realizar? por que no tenerlas separadas sin el RequiereComponent? Si las necesitas juntas siempre porque no usar el Layermask directamente en claseA?
 
Yo haria lo siguiente, los datos se pasan o reciben a traves de los script monobehaviours (claseA y claseB), el custom Inspector (claseA_Inspector) solo realiza tareas del inspector (mostrar variables en este caso). Siempre que el objeto de clase A tenga una referencia al objeto de clase B podes tomar su layermask publica, y desde el inspector pedirla prestada con el target :
 

claseA = (ClaseA)target;

 
Luego en OnInspectorGUI() mediante el target de claseA le pedis a claseA, esta le pide a la referencia de clase B su layermask. Y luego haces un Repaint() al final para que se actualice. No se si se entendió bien.
 
Saludos
 
 

Gracias por responder.... Te explico
 
Tengo scripts separados como indicas. La "ClaseA_inspector" la tengo en una carpeta llamada Editor, pero esta no cuelga directamente de la carpeta Assets sino que la ruta sería la siguiente (Assets->JerarquiaProyecto->Script->.......->Editor)
 
La verdad es que no se si me puede generar algun problema por no tener la carpeta "Editor" colgando directamente de la carpeta "Assets", aunque en principio no me ha dado ninguno.
 
 
 
Referente a lo de que al instanciar un objeto de la ClaseA se crea uno de la ClaseB y que estoy creando otra instancia al hacer el new, lo hago porque necesito tener acceso a toda la ClaseB desde la ClaseA, ya que ClaseA ejecuta varias funciones de distintas clases, incluida la ClaseB, en su Update()...  y el RequireComponent pues para añadir esas clases en el gameobject.
 
Desconozco si hay alguna manera mejor de hacerlo.
 
 
 
Referente a esto que me indicas abajo, ¿¿¿Como pides la referencia de su layerMask??? porque precisamente eso es lo que no consigo hacer correctamente con los layerMask
 



   
      Yo haria lo siguiente, los datos se pasan o reciben a traves de los script monobehaviours (claseA y claseB), el custom Inspector (claseA_Inspector) solo realiza tareas del inspector (mostrar variables en este caso). Siempre que el objeto de clase A tenga una referencia al objeto de clase B podes tomar su layermask publica, y desde el inspector pedirla prestada con el target :
   
   

claseA = (ClaseA)target;

   
      Luego en OnInspectorGUI() mediante el target de claseA le pedis a claseA, esta le pide a la referencia de clase B su layermask. Y luego haces un Repaint() al final para que se actualice. No se si se entendió bien.
   

[/quote]
 
 
 
Con otro Tipo de variables (float, Vector3) no he tenido problemas para desde la ClaseA_inspector poder mostrar y modificar los valores de las variables en ClaseB, a traves de su variable creada en la ClaseA:
 

//Dentro de la clase ClaseA_inspector
claseA.claseB.UnNumeroFloat = EditorGUILayout.FloatField(claseA.claseB.UnNumeroFloat, GUILayout.Width(119));   //Por ejemplo

 
Y funciona perfectamente. El problema se me plantea con las LayerMask que no se como gestionarlas desde la claseA_inspector para poder verla y modificarla...
 
Como dije en el primer post, probé con
 

EditorGUILayout.LayerField

 
pero no parece funcionar para lo que yo digo. Eso o hay algo de como hacerlo que desconozco. Cuando utilizo LayerField el layerMask de mi ClaseB muestra lo de la imagen de abajo, ya que inicializo la variable con esas layers marcadas:
 

 
MIentras que lo que se muestra la ClaseA_Inspector por el inspector es esto:
 

 
 
 
 
 
Y con:
 

EditorGUILayout.MaskField

 
A pesar de que leo la documentación no veo como utilizarla correctamente...
 
 
 
Me vendría bien algun ejemplo de codigo adaptado a la estructura de codigo que menciono, para poder aprender como gestionar este asunto.
 
 
 
Saludos
 
 

- La carpeta Editor puede estar en cualquier lado no hay problema
 
- Otra cosa que me di cuenta es que no referencias nunca el "claseB" (el del new) con tu componente claseB, osea solo creas lo que seria el puntero pero sin apuntar a nada. Solo creas el espacio en memoria.
 
 
 
- Lo de obtener la referencia se puede hacer de la formas que quieras, simplemente con un getcomponent se me ocurre. Es decir, podrias usar en el OnEnable del customInspector un getcomponent, y si no existe crearlo. Yo Prefieriria esto en vez del "public claseB = new ClaseB". Osea como tenes el requiereComponent no es necesario jamas crear el componente ya que es requerido.
 
- Eso de que no te aparecen marcadas puede ser por la falta del RePaint() y/o el punto anterior, no estoy seguro.
 
yo tengo este codigo para una layermask que use en un editor de plataformas:
 

//Layer
GUILayout.BeginHorizontal();
GUILayout.Label ("Layer");
m_target.wallsLayer = (int)EditorGUILayout.LayerField (m_target.wallsLayer);
GUILayout.EndHorizontal ();

 
y esto me quedo perfecto, use EditorGUILayout.LayerField , wallsLayer es un int (para el LayerMask)
 
Saludos

Enero 17, 2017, 09:55:38 PM #4 Ultima modificación: Enero 17, 2017, 10:06:19 PM por ftejada
Hola de nuevo!
 
La referencia de la claseB la hago dentro de de la funcion Start() de la ClaseA con getComponent<>(). Lo que pasa es que no lo he puesto para poner solo los puntos claves del código...
 
Lo del tema de que no aparecen marcadas en el inspector no se soluciona con el Repaint() pq ya lo probe. Y respecto al punto anterior no creo que el problema sea ese, ya que en el OnEnable() de la ClaseA_Inspector, obtengo la referencia de la ClaseA y esta a su vez tiene dentro la referencia a la ClaseB...
 

claseA = (ClaseA)target;


claseA.claseB.collisionLayer = EditorGUILayout.LayerField(claseA.claseB.collisionLayer, GUILayout.Width(119));

 
 
 
Creo que esto que hago es correcto pero no funciona como debería. Debe haber algo que desconozco, o simplemente hay que utilizarlo de otro modo... Por lo que creo el LayerField devuelve solo un int, con lo que creo que solo devuelve la layer que esté activa. Pero si hay varias activas y otras no, ¿Qué devuelve? ¿la primera que esté activa?... Estoy bastante confundido con este asunto la verdad
 
¿Como se utiliza la función MaskField? ya que parece que podría ser la solucion ya que supongo que trabaja con la mascara que le digas, (con todas sus layers dentro y la informacion de si cada layer esta activada o desactivada), y no con una capa individual  ... aunque no se.
 
 
 
¿¿¿Alguna idea acerca de como funciona todo esto??? o un buen tutorial de como va?
 
EDITO: otra cosa que se me habia olvidado y me dao cuenta... cuando intento modificar, abrir, etc el LayerField de la ClaseA_Inspector me sale esto algunas veces por consola como error:
 
Layer index out of bounds

Unit***itorInternal.InternalEditorUtility:GetLayerName(Int32)
 
Saludos




   
      La referencia de la claseB la hago dentro de de la funcion Start() de la ClaseA con getComponent<>(). Lo que pasa es que no lo he puesto para poner solo los puntos claves del código...
   

[/quote]
 
Si es asi la referencia se realiza cuando le das play solamente, por que el start funciona asi. Tenes que realizarla de otra manera, por eso te decia de realizarla en el inspector de claseA con el OnEnable. Si es asi no hay necesidad del "new", pensalo como cuando tenes un Rigidbody, directamente obtenes el componente, en esto es igual.
 
 
 



   
      Por lo que creo el LayerField devuelve solo un int, con lo que creo que solo devuelve la layer que esté activa. Pero si hay varias activas y otras no, ¿Qué devuelve? ¿la primera que esté activa?... Estoy bastante confundido con este asunto la verdad
   

[/quote]
 
El layermask es un numero solamente, funciona asi:
 
- ninguna marcada = 0  o en binario (8bits) = 00000000
 
- marcada la primera = 1 o en binario (8bits) = 00000001
 
- marcada la segunda = 2 o en binario (8bits) = 00000010
 
- marcada la 1ra y 2da = 3 o en binario (8bits) = 00000011
 
y asi...
 
--> entonces te devuelve un numero, unico que corresponde a las que estan o no marcadas dentro de las máscara.
 
La verdad el maskfield no se como funciona.
 
Como obtenes el "collisionLayer"?
 
Despues si tengo un tiempito hago un ejemplo en mi Unity a ver si funciona

Hola de nuevo, mira aca invente algo, quizas te sirva, la posta estaba en usar el MaskField, podes modificar o el de claseB o el de claseA, en definitiva el layerMask es uno solo (el de claseB):
 
claseB.cs
 

public class claseB : MonoBehaviour {
public LayerMask layerMask = 0;
}

 
claseA.cs
 

[RequireComponent(typeof(claseB))]
public class claseA : MonoBehaviour {
public claseB refClaseB;
}

 
claseA_Inspector.cs
 

[CustomEditor(typeof(claseA))]
public class claseA_Inspector : Editor {
claseA m_target;
static string[] layerNames = new string[32];
void OnEnable()
{
   m_target = (claseA)target;
   m_target.refClaseB = m_target.GetComponent<claseB> ();
   //layermask es de 32 bits o sea un bucle de 32 iteraciones
   for (int i = 0; i < 32; i++)
      layerNames = LayerMask.LayerToName (i);
}
void OnDisable()
{
}
public override void OnInspectorGUI ()
{
   //Layer
   GUILayout.BeginHorizontal ();
   GUILayout.Label ("Layer");
   m_target.refClaseB.layerMask = EditorGUILayout.MaskField (m_target.refClaseB.layerMask, layerNames );
   GUILayout.EndHorizontal ();
   Repaint ();
}
}

 
Aca la prueba:

Hola!! he estado liado hoy y no pude contestar hasta ahora....
 
He probado el código que pusiste y funciona a la perfección. Viendo tu código me doy cuenta de que la parte que yo no sabía, o no se me ocurria, como hacer para poder utilizar la función MaskField era esta:
 

//layermask es de 32 bits o sea un bucle de 32 iteraciones
   for (int i = 0; i < 32; i++)
      layerNames = LayerMask.LayerToName (i);

 
De nuevo muchas gracias por tu gran ayuda
 
 
 
Saludos

De nada! En esa parte tambien me lié un poco, despues lei que era un int32 y con un for funcionó bien.
 
Saludos

Etiquetas: