Noticias

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

[APORTE] Tutorial Objetos Interactivos (Usando Interfaces)

Iniciado por Luis_arg, Diciembre 13, 2016, 09:15:41 PM

Tema anterior - Siguiente tema


   
eature=oembed" width="480">

 
Crearemos objetos interactivos utilizando Interfaces.

Esto nos permitirá crear objetos con los que podamos interactuar y dotarlos de distintos comportamientos.
 
 

Ey muy bueno para aquellos principiantes, el uso de interfaces es una herramienta muy útil, pero la estas implementado de manera correcta, el problema es la interacción del Player en este caso no es correcto, no entiendo porque compruebas tag si el RayCast solo podría colisionar con "Objetos Interactivos" bastaría solo con utilizar Capas, de hecho no sería ni siquiera necesario el uso de Layer.
 

private void InteractivePlayer()
{
var ray = "Rayo desde el centro de la camara";
RayCastHit hit;
if(Physics.RayCast(ray, out hit, 2))
{
   var interactive = hit.transform.GetComponent<IInteractive>();
        if(interactive)
        {
           Ejecutar la Accion.. ..
        }
}
}

 
Aunque de todos modos esto es mejor realizarlo con funciones virtuales.
 
 
 
De todos modos buen comienzo.. 

Q linda herramientas las Interfaces, son muy utiles para encontrar objetos que tengan ciertas funciones o metodos definidos
 
yo me acuerdo que vi este hace un tiempito:
 
https://unity3d.com/es/learn/tutorials/topics/scripting/interfaces

Cita de: francoe1 date=1481666993Ey muy bueno para aquellos principiantes, el uso de interfaces es una herramienta muy útil, pero la estas implementado de manera correcta, el problema es la interacción del Player en este caso no es correcto, no entiendo porque compruebas tag si el RayCast solo podría colisionar con "Objetos Interactivos" bastaría solo con utilizar Capas, de hecho no sería ni siquiera necesario el uso de Layer.
   
   

private void InteractivePlayer()
{
var ray = "Rayo desde el centro de la camara";
RayCastHit hit;
if(Physics.RayCast(ray, out hit, 2))
{
   var interactive = hit.transform.GetComponent<IInteractive>();
        if(interactive)
        {
           Ejecutar la Accion.. ..
        }
}
}

   
      Aunque de todos modos esto es mejor realizarlo con funciones virtuales.
   
   
       
   
   
      De todos modos buen comienzo.. 
   


En realidad el Raycast podría colisionar con cualquier cosa (ej. una pared, enemigo,casa) exactamente por eso compruebo ...
 
Para usar métodos virtuales (Y hacer Overriding, sobrescribir la función) ... tendría que hacer una clase base (objeto interactivo) y hacer herencia ... crear una clase que herede de esta ... 
 
Es decir, las clases hijas serian objetos interactivos ... que extienden a el objeto interactivo base ... para mi esto no es así ... no son objetos interactivos extendidos ...
 
Son simplemente objetos (Armas, Puertas, Botones, etc.) ... que son interactivos ...
 
Entonces ... cada objeto interactivo debe implementar la interfaz ... (IInteractable ... o como quiera llamarle ... la interfaz para objetos interactivos) 
 
A mi me pareció mas practico usar Interfaces ...
 
Permite una solución general (Es valida para cualquier objeto) ... y permite dotar a lo objetos interactivos con distintos comportamientos (Al implementar la Interfaz se le puede poner cualquier comportamiento ej. Abrir una puerta, subir Vida, aumentar munición)
 
El tema del TAG ... se podría comprobar de otra forma ... quizás mas efectiva ... aunque encontré esa ... y me soluciono el problema (antes tenia otra forma de hacerlo y era menos buena) ... así como mejore esa parte ... se podría mejorar ... 
 
De que manera mas optima propones comprobar si es un Objeto Interactivo ? si implementa la Interfaz ...
 
Se podría mejorar esa parte ...
 
 
 
 
 
 
 


 


 
 

Claro pero en costo lo mas eficiente es verificar las capas (layermask) a lo primero o ya definidas en la matriz de Physics. Lo que sigue es directamente obtener la interfaz "IInteractable" como dice francoe1, osea el tag esta de mas me parece, aparte te perdes de colocar tags en objetos "interactable". Yo uso una capa "Usable" (items, puertas, etc), la meto en una layermask definida en codigo y en el raycast del player la uso. Esta bien que con un ray no notas jamas la diferencia. La ventaja de las interfaces es que podes poner implementar mas de una interfaz, con el polimorfismo dependes de una clase base.

Diciembre 14, 2016, 05:30:38 PM #5 Ultima modificación: Diciembre 14, 2016, 05:33:19 PM por francoe1
Una forma de optimizar sería sin utilizar raycast, podrias simplemente verificar la distancia entre el Objeto Interactivo y el Jugador si esta es menor a 2 (Esto varía a gusto y requerimientos del juego), luego tomamos el producto de las 2 posiciones.
 

foreach(var item in FindGameObjects<IInteractive>())
{
    Vector3 forward = transform.TransformDirection(Vector3.forward);
    Vector3 toItem = item.position - transform.position;
    if (Vector3.Dot(forward, toItem) > 0.6f) print("Look Object");    
}

 
Y si el resultado no te convence, es decir, existe un objeto entre el jugador y el objeto interactivo de esta manera no estaríamos comprobando, pero en este caso, si pasa las primeras 2 comprobaciones entonces podrias largar un rayo para comprobar que el jugador puede visualizarlo.
 
De esta manera lo hace BF3 y 4, muchas veces las soluciones son más simples de lo que pensamos.
 
Es cierto que el uso de interface se hace un poco más cómodo, práctico y organizado, el problema es cómo se implementa la interface.

Cita de: francoe1 date=1481733038Una forma de optimizar sería sin utilizar raycast, podrias simplemente verificar la distancia entre el Objeto Interactivo y el Jugador si esta es menor a 2 (Esto varía a gusto y requerimientos del juego), luego tomamos el producto de las 2 posiciones.
   
   

foreach(var item in FindGameObjects<IInteractive>())
{
    Vector3 forward = transform.TransformDirection(Vector3.forward);
    Vector3 toItem = item.position - transform.position;
    if (Vector3.Dot(forward, toItem) > 0.6f) print("Look Object");    
}

   
      Y si el resultado no te convence, es decir, existe un objeto entre el jugador y el objeto interactivo de esta manera no estaríamos comprobando, pero en este caso, si pasa las primeras 2 comprobaciones entonces podrias largar un rayo para comprobar que el jugador puede visualizarlo.
   
   
      De esta manera lo hace BF3 y 4, muchas veces las soluciones son más simples de lo que pensamos.
   
   
      Es cierto que el uso de interface se hace un poco más cómodo, práctico y organizado, el problema es cómo se implementa la interface.
   


En ese caso te conviene usar un OnTriggerEnter/Exit que es un callback y luego verificar si lo que entro es "interactable". Pones un BoxCollider adelante del player.
 
 
 

void OnTriggerEnter(Collider col)   
{
if(col.GetComponent<Interactable>())
      {
         //se ve un Interactable  
         col.GetComponent<Interactable>().Highlight();
     
         if(Input.GetKeyDown(KeyCode.E))
         col.GetComponent<Interactable>().Interact();
      }
}
     
void OnTriggerExit(Collider col)   
{
if(col.GetComponent<Interactable>())
      {
         //se ve un Interactable  
         col.GetComponent<Interactable>().Normal();      
         
      }
}

 
Si tenes muchos interactables y los tenes que buscar a todos y hacer el producto punto ya se empieza a notar, suponete que tengas 1000.

Cita de: francoe1 date=1481733038Una forma de optimizar sería sin utilizar raycast, podrias simplemente verificar la distancia entre el Objeto Interactivo y el Jugador si esta es menor a 2 (Esto varía a gusto y requerimientos del juego), luego tomamos el producto de las 2 posiciones.
   
   

foreach(var item in FindGameObjects<IInteractive>())
{
    Vector3 forward = transform.TransformDirection(Vector3.forward);
    Vector3 toItem = item.position - transform.position;
    if (Vector3.Dot(forward, toItem) > 0.6f) print("Look Object");    
}

   
      Y si el resultado no te convence, es decir, existe un objeto entre el jugador y el objeto interactivo de esta manera no estaríamos comprobando, pero en este caso, si pasa las primeras 2 comprobaciones entonces podrias largar un rayo para comprobar que el jugador puede visualizarlo.
   
   
      De esta manera lo hace BF3 y 4, muchas veces las soluciones son más simples de lo que pensamos.
   
   
      Es cierto que el uso de interface se hace un poco más cómodo, práctico y organizado, el problema es cómo se implementa la interface.
   


Buscar los Objetos Interactivos es mas costoso que solo tirar un RayCast (puede haber 50,100,1000 objetos interactivos en la escena)
 
Cita de: lightbug date=1481735648En ese caso te conviene usar un OnTriggerEnter/Exit que es un callback y luego verificar si lo que entro es "interactable". Pones un BoxCollider adelante del player.
   
   
       
   
   

void OnTriggerEnter(Collider col)   
{
if(col.GetComponent<Interactable>())
      {
         //se ve un Interactable  
         col.GetComponent<Interactable>().Highlight();
     
         if(Input.GetKeyDown(KeyCode.E))
         col.GetComponent<Interactable>().Interact();
      }
}
     
void OnTriggerExit(Collider col)   
{
if(col.GetComponent<Interactable>())
      {
         //se ve un Interactable  
         col.GetComponent<Interactable>().Normal();      
         
      }
}

   
      Si tenes muchos interactables y los tenes que buscar a todos y hacer el producto punto ya se empieza a notar, suponete que tengas 1000.
   


Esta buena esta buena esa solución aunque la veo mas para una estación de Crafteo (EJ: Un banco de creación de armas) o un juego en 3ra persona.
 
Mi idea es que mires al objeto ... y puedas interactuar con el ... solo cuando lo mires ... por eso uso un Raycast ...
 
Anecdota: Me paso con un MOD de Skyrim que tenias que hacer una misión en la que tenias que tocar 4 o 5 botones seguidos ... y el tiempo era muy justo ... entre el LAG (producido por las grandes imágenes/texturas del MOD) el tiempo muy justo ... y la imposibilidad de apuntar al boton para presionarlo (debido al LAG) ... era impolsible (de mas esta decir que desinstale el MOD)
 
En ese caso tu solución seria mas efectiva ... y evitaría el tener que apuntar al objeto para interactuar con el ... y seria mas sencillo realizar la misión ... 
 
Simplemente buscaba eso de mirar el objeto (y te de su nombre) por eso lo hice así ... hacer que te de el nombre es solo un método mas en la Interfaz ... así que para no hacerlo mas largo no lo agregue al tutorial ...
 
 
 
 
 
 
 
 
 

Jaja si el mod te andaba lento y no te permitia jugar bien ya estariamos hablando de otra cosa. A mi tambien ma pasaba eso con juegos y por alguna razon que jamas voy a entender los desarrolladores tambien usaban aceleracion de mouse para enpeorarlo mas, (de lo peor que se ha inventado). Era imposible si tenias lag tener exactitud.
 
El inconveniente que tenes con el metodo del "si lo ve" es si tenes varios items uno al lado del otro, terminas interactuando con todos.

Abre pasado muchos malos momentos intentando armar un sistema interactivo genérico cuando logre algo pasable lo intente meter en un proyecto multijugador LAN/Online y me di cuenta que era una mierda tuve que empezar todo de nuevo, estaba armado con interface pero no me sirvio, por esa experiencia digo que armar algo interactivo bueno es un problema gigante, a eso sumale el sistema de selección de armas que si bien al final meti mano y lo arme de 0 en el mismo proyecto me di cuenta que tener algo óptimo y funcional lleva muchísimo tiempo,planeamiento y conocimiento.
 
Una de las cosas que me sirvió mucho es una función de tipo genérica con un Call Back Genérico dentro del componente PlayerCamera, la idea de esto era a través de un método, el objetivo tomar cualquier objeto que el jugador estuviera viendo. La función cuenta de 3 parámetros más el tipo genérico. 
 

public GetObjectLook<T> (Action<T> CallBackLookObject, Action CallBackExit, int Distance);

 
 El resultado fue bastante bueno, me olvide de RayCast por un largo tiempo al trabajar con CallBack las cosas se hicieron muchisimo mas simples, de momento este método lo utilizo para muchas partes del proyecto, la utilización de CallBack y Eventos es algo muy potente.
 
Por el momento C# .NET lo manejo muy bien tengo varios cursos en cuanto a esto y muchísimo de C++ , UML y otros extras favorables al desarrollo, pero me gusta tomar ideas como esta, siempre voy viendo en internet a ver si alguien encuentra la solución a X problemas de manera más eficiente.

Usar callbacks es buenisimo, el update habria que usarlo solo para polling, movimientos o reconocer entradas. En unreal engine creo que utilizan callbacks para cada entrada, funciona algo asi como una interrupcion de CPU, algo asi como el OnMouseDown pero para cualquier entrada registrada en el proyecto. Unity estaba haciendo algo asi hace un tiempo pero no se en que quedó.
 
Yo lo que estoy haciendo ahora es cambiar mis temporizadores a corutinas y todo lo que no requiera accion cuadro a cuadro a un subrutina y se nota el cambio (en los numeros por lo menos).
 
Igual realizar un raycast con un solo ray cuadro a cuadro en el hardware de hoy en dia es practicamente gratis, mucho de las ineficiencias surgen de condicionales que estan de mas probocando saltos innecesarios, y un salto dentro de las intrucciones esta bien penalizado, salvo que el compilador sea muy bueno, caso de c++, no se en c#

Cita de: francoe1 date=1481733038Una forma de optimizar sería sin utilizar raycast, podrias simplemente verificar la distancia entre el Objeto Interactivo y el Jugador si esta es menor a 2 (Esto varía a gusto y requerimientos del juego), luego tomamos el producto de las 2 posiciones.
   
   

foreach(var item in FindGameObjects<IInteractive>())
{
    Vector3 forward = transform.TransformDirection(Vector3.forward);
    Vector3 toItem = item.position - transform.position;
    if (Vector3.Dot(forward, toItem) > 0.6f) print("Look Object");    
}

   
      Y si el resultado no te convence, es decir, existe un objeto entre el jugador y el objeto interactivo de esta manera no estaríamos comprobando, pero en este caso, si pasa las primeras 2 comprobaciones entonces podrias largar un rayo para comprobar que el jugador puede visualizarlo.
   
   
      De esta manera lo hace BF3 y 4, muchas veces las soluciones son más simples de lo que pensamos.
   
   
      Es cierto que el uso de interface se hace un poco más cómodo, práctico y organizado, el problema es cómo se implementa la interface.
   


Esa manera es mas costosa ... primero tengo que hacer una búsqueda de todos los objetos y luego trabajar con una matriz de 50-1000 elementos ... (es decir recorrerla y luego calcular su distancia)
 
Mantener una matriz asi en cada Update del juego es muy costoso ... es preferible solo lanzar un ray ...

pero el raycast depende de otros factores que tambien afectan el rendimiento del juego, es decir el ítem tiene que cumplir parte de la física para que RayCast funcione, deberias hacer tus propias pruebas, tomar la distancia de objetos no es algo costoso para el procesador, no es algo que tengas que hacerlo constantemente podrias realizarlo cada .5s. Deberías probarlo verás que es más óptimo.
 
1º Tomar todos los objetos Interactivos.
 
2º Medir su distancia.
 
3º Si la distancia es menor que X, verificar si está a la vista con el producto.

Etiquetas: