~~NOTOC~~ ===== Interacción con Unity ===== [[https://unity.com|Unity]] es un motor de videojuego multiplataforma que puede funcionar dentro de una página web. Unity se basa en un entorno propio para desarrollo de videojuegos, lo que proporciona mucha flexibilidad a la hora de configurar ciertas tareas. Unity se basa en el lenguaje de programación [[https://docs.microsoft.com/es-es/dotnet/csharp/tour-of-csharp/|C#]], pero puede interactuar con código [[wpes>javascript]] para comunicarcíon entre la aplicación y la página web. A través de esta interactividad Unity-Javascript mediante [[https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html|WebGL]] se pueden integrar llamadas desde la pagina que contiene un ítem en Siette, y el juego, lo que permite construir una pregunta interactiva. A continuación se muestran ejemplos que como se ha resuelto en algunos casos concretos: ==== Ejemplo 1 ==== Se trata de la implementación del conocido juego [[wpes>Simon_(juego)|Simón]], en el que se emiten secuencias de sonidos, cada vez mas largas, asociadas a unos botones de colores, que el alumno debe reproducir en el mismo orden. Esta pensado para medir la capacidad de memoria visual y sonora. El juego es configurable, se puede modificar la imagen del fondo, el número máximo de secuencias que se presentan, la velocidad de exposición, etc. y presenta unas instrucciones breves al comenzar mediante un texto leído de forma automática. {{ es:manual:items:item-unity-simon-4.png?400 | }} Como resultado de la interacción el juego devuelve a Siette varios valores numéricos, correspondientes al número de secuencias correctas, incorrectas, tiempo de ejecució, etc. === Proceso de carga del juego === El juego esta implementado en Unity, aunque para Siette se trata de un ítem interactivo [[es:manual:items:interactivos:evaluacion|integrado mediante de javascript]] por lo que usará las mismas funciones. En este caso, el enunciado de la pregunta contiene el siguiente script:
La cabeceera de este código carga los ficheros auxiliares de Unity, y a continuación establece un ''div'' de dimensiones ''480x300'' sobre el que se cargará el juego. Unity resscala todas las imagenes para adaptarse al tamaño del ''div'' en el que se ejecuta. El juego propiamente dicho se carga mediante la sentencia: var unityInstance = UnityLoader.instantiate("unityContainer", "/siette.htdocs/techcat/unity/Simon/Build/Simon.json", {onProgress: UnityProgress}); lo que provoca que aparezca el logo de Unity y comience la carga: {{ es:manual:items:item-unity-simon-1.png?400 | }} El juego está programado para esperar a recibir los parámetros antes de comenzar propiamente su ejecución, presentando una vez concluida la carga, la siguiente pantalla {{ es:manual:items:item-unity-simon-2.png?400 | }} La carga del juego y de la pagina web que lo contiene se hace de forma asíncrona. Los parámetros se envian al juego tan pronto como se ha cargado la página y se invoca la ejecución de la función ''load()'' que pasa los parámetros de inicialización a la aplicación en Unity, que comienza su ejecución: {{ es:manual:items:item-unity-simon-3.png?400 | }} Un detalle importante a tener en cuenta es que el juego puede no haber terminado de cargarse al ejecutar la función ''load()'', por lo que esta función lo que hace es programar (mediante ''setTimeout'') diversas llamadas de forma iterativa para garantizar que la aplicación Unity recibe los mensajes.: function load() { for(conta=5000; conta<60000; conta+=2500) { setTimeout(function() { setParameters(2,3,6); }, conta ); } } === Final del juego === El final del juego se puede producir por dos circunstancias: * El alumno ha completado todas las secuencias que se le han propuesto. En este caso el juego ejecuta una llamada a la [[es:manual:items:interactivos:evaluacion#Función RespuestaActiva()|función javascript RespuestaActiva()]] (ya implementada en Siette, véase la [[es:manual:items:interactivos:evaluacion|integración con javascript]]). * El alumno pulsa el botón ''Siguiente pregunta'', indicando que ya no quiere seguir jugando. Esto provoca que Siette antes de terminar programe una llamada a la [[es:manual:items:interactivos:evaluacion#Función RespuestaPasiva()|función javascript RespuestaPasiva()]], que a su vez llamará a la [[es:manual:items:interactivos:evaluacion#Función evaluacion()|función evaluacion()]]. function evaluacion() { unityInstance .SendMessage('Log', 'Stop'); } Normalmente, esta función debería devolver un valor, pero en esta ocasión no lo hace, sino que simplemente emite un mensaje para la aplicación Unity, indicandole que pare el juego. Internamente al juego este mensaje desencadena una llamada a la [[es:manual:items:interactivos:evaluacion#Función RespuestaActiva()|función RespuestaActiva()]], y el proceso continúa como en el caso anterior. Las llamadas a a la [[es:manual:items:interactivos:evaluacion#Función RespuestaActiva()|función RespuestaActiva(respuesta)]], tiene como argumento una cadena de texto que contiene una secuencia de valores con el siguiente formato: ;#; '{' respuesta0 , respuesta1 , respuesta2 , .... '}' ;#; En este caso estos valores son indicadores del resultado obtenido con la interaccion del juego en concreto: * Número de secuencias correctas , * Número de secuencias incorrectas , * Máximo número de secuencias consecutivas correctas , * Máximo número de secuencias consecutivas incorrectas , * Tiempo de juego Siette almacena evaluará esta respuesta, por ejemplo mendiante [[es:manual:items:patron:funciones|patrones de funciones de evaluación]], y onbtendrá así una evaluación de la actividad realizada en el juego. === Presentación de resultados === Cuando Siette necesite presentar los resultados de esta evaluación, volverá a cargar el ítem que contiene el juego javascript y llamará a la [[es:manual:items:interactivos:evaluacion#Función resolver()|función resolver(respuesta,correcto)]], pasandole como argumentos la respuesta del alumno (es decir, la cadena de caracteres con los indicadores que recibió como respuesta); y la corrección de cada una de los [[es:manual:items:patron:funciones|patrones de funciones de evaluación]]. La [[es:manual:items:interactivos:evaluacion#función resolver()]] lo que hace es simplemente pasarle al juego esta respuesta que la muestra como resultado. function resolver(respuesta, correccion) { play = false; for(conta=2500; conta<60000; conta+=2500) { setTimeout(function() { unityInstance.SendMessage("Log", "Solve", respuesta); }, conta ); } } {{ es:manual:items:item-unity-simon-5.png?400 | }} En la imagen puede verse que Siette tambien muestra la respuesta del alumno, (la cadena de indicadores recibida). Esta duplicidad de información puede evitarse añadiendo ''return true'' al final de la [[es:manual:items:interactivos:evaluacion#función resolver()]], que inidica a Siette que la corrección de la pregunta ya se ha mostrado. Nótese que al igual que en la carga del juego para su ejecución, hay que esperar a que el juego se cargue para poder enviarle los mensajes. No hemos encontrado manera de obtener esta información desde la página javascript, por lo que una manera de sortear el problema es programar una secuencia de mensajes para estar seguros que el juego los recibe. En el [[#Ejemplo 2|siguiente ejemplo]] se describe en detalle como hay que programar en Unity la recepción y envío de todos estos mensajes. ==== Ejemplo 2 ==== Se trata de un minijuego denominado //EndlessRoad// con el que se quiere medir el tiempo y la constancia que un niño tiene en la realización de una tarea concreta. El objetvo final es medir la función ejecutiva de inhibición. Para ello se propone este juego en el que aparece un coche, una carretera y un regalo al final de la carretera. {{ es:manual:items:item_endlesroad-blue.png?400 | }} El regalo nunca llega a alcanzarse, pero el niño debe intentarlo procurando no salirse de la carretera. Si lo hace aparece un tono de aviso. {{ es:manual:items:item_endlesroad-blue-animation.png?400 | }} Finalmente el niño se aburre y pulsa el botón ''Siguiente'', o bien trascurren 10 minutos y el propio sistema fuerza la terminación, obteniendo la respuesta. {{ es:manual:items:item_endlesroad-blue-corrected.png?400 | }} Como resultado de la interacción el juego devuelve a Siette varios valores numéricos, correspondientes al tiempo que ha estado el coche dentro de la carretera, el tiempo que ha estado fuera, el número de veces que se ha salido, etc. Con estos datos Siette realiza la evaluación utilizando como patrones [[funciones de evaluación]] a las que asigna diversas puntuaciones dependiendo de la combinación de valores. === Ejecución del juego === El juego esta implementado en Unity, aunque para Siette se trata de un ítem interactivo [[es:manual:items:interactivos:evaluacion|integrado mediante de javascript]] por lo que usará las mismas funciones. En este caso, el enunciado de la pregunta contiene el siguiente script: Esta pagina carga el juego en unity en un [[https://developer.mozilla.org/es/docs/Web/HTML/Element/iframe|iframe]] que contiene el código de inicialización de Unity ((en este caso se usa un iframe para poder visualizar el juego de forma independiente y poder ejecutarlo en pantalla completa)), en concreto para este ejemplo este codigo es el siguiente: Unity WebGL Player | Endless Road
=== Paso de parámetros de configuración === Dentro de esta página se ha definido una funcion javascript ''setParameters(curvas, textura)'' a la que se llama desde el código que se ha escrito en el enunciado y que pasa dos parámetros que controlan algunos aspectos del juego, en este caso, si la carretera tiene curvas o es recta y la textura de la carretera. Modificando la llamada a esos parametros se obtiene una cambio en el funcionamiento del juego. Por ejemplo con la llamada ''setParameters(2,1)'' se obtiene este otro juego: {{ es:manual:items:item_endlesroad-red.png?400 | }} Como puede observarse en el código del enunciado, la llamada a esta función se repite varias veces a lo largo de la carga del juego, esto es debido a que hasta que el juego no termina de cargar las llamadas no tienenefecto, por lo que se repiten varias veces para conseguir este efecto. En el código del iframe, las llamadas a la funcion ''setParameters(curvas, textura)'' se traducen en dos llamadas a la funcion ''SendMesssage'' que es la que envia el mensaje a la instancia de Unity. (Véase la [[https://docs.unity3d.com/es/530/Manual/webgl-interactingwithbrowserscripting.html|documentación de Unity sobre la integración con javascript]]). En concreto en este caso dentro de Unity se ha implementado la clase ''MaterialSelector.cs'' con (entre otras) la funcion ''SelectScene'' que es la que recibe el mensaje de configuración. using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Runtime.InteropServices; using UnityEngine.SceneManagement; public class MaterialSelector : MonoBehaviour { .... string SceneName; public void SelectScene(int Scene) { if (Scene == 1) { SceneName = "Straight"; SceneManager.LoadScene(SceneName); } else if (Scene == 2) { SceneName = "Curve"; SceneManager.LoadScene(SceneName); } } } === Obtención de resultados === Para obtener los resultados de la evaluación se usa la [[es:manual:items:interactivos:evaluacion#función evaluación()]], que en este caso redirige a otra función del mismo nombre dentro del iframe. Esta función lo único que hace es enviar un mensaje al juego indicandole que debe finalizar, es decir envia el mensaje ''OnApplicationQuit'' a la clase ''Log''. Ésta lo recibe y genera una llamada a la [[es:manual:items:interactivos:evaluacion#función RespuestaActiva]] pasandole como parámetro la //respuesta virtual// del alumno, es decir los valores correspondientes al tiempo transcurrido dentro de la carretera, fuera, número de veces que se ha salido, etc. Esta //respuesta virtual// es la que usará Siette para evaluar aplicando las [[funciones de evaluación]]. En concreto dentro de Unity se ha implementado la clase ''Log.cs'' con el siguiente contenido: using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using System.Runtime.InteropServices; public class Log : MonoBehaviour { [DllImport("__Internal")] private static extern void ReportState(string str); float t; float clickcount=0; float clickout; float clickin; float timeout; float timein; ..... void OnApplicationQuit() { timeout = clickout / clickcount * t; timein = clickin / clickcount * t; ReportState( "{" + t.ToString("f2") + "," + timein.ToString("f2") + "," + timeout.ToString("f2") + "}"); } } La función ''ReportState'' se implementa en el fichero ''script.jslib'' que se incluye dentro del subdirectorio ''Plugins'' del directorio ''Assets'' de Unity, que es la que finalmente llama a la [[es:manual:items:interactivos:evaluacion#función RespuestaActiva()]] pasándole la cadena de caracetres que contiene la //respuesta virtual//. mergeInto(LibraryManager.library, { ReportState: function (str) { RespuestaActiva(Pointer_stringify(str)); } }); Cuando ha transcurrido el tiempo límite (en otros casos pueden darse otras circunstancias), la aplicación en Unity puede determinar que ya ha llegado el momento de finalizar. Para ello simplemente tendrá que hacer una llamada proactiva a la [[es:manual:items:interactivos:evaluacion#función RespuestaActiva()]] sin que sea necesaria una llamada previa por parte de la [[es:manual:items:interactivos:evaluacion#función evaluacion()]]