de linares - trabajos academicos de la universidad de jaen...
TRANSCRIPT
Escuela
Polit
écnic
a S
uperi
or
de L
inare
s
UNIVERSIDAD DE JAÉN Escuela Politécnica Superior de Linares
Trabajo Fin de Grado
______
CARTA DE RESTAURANTE
VIRTUAL MEDIANTE REALIDAD
AUMENTADA AR
Alumno: Eva María Sánchez Lavi Tutor: Raúl Mata Campos
Depto.: Ingeniería de Telecomunicación
Febrero, 2019
1
Universidad de Jaén
Escuela Politécnica Superior de Linares
Trabajo Fin de Grado
Carta de restaurante virtual mediante realidad aumentada
AR
2
Agradecimientos:
Gracias a mi familia, por todo el apoyo y sacrificio. A Raúl por este proyecto tan
llamativo y que me ha aportado tanto. A Julián, por su apoyo incondicional y ayuda.
Gracias a mis compañeros, en especial a José Montoro Polo, que a pesar de la distancia
y el tiempo, somos y seremos siempre unos grandes compañeros y amigos.
3
Índice
1. INTRODUCCIÓN Y OBJETIVOS.................................................................................................... 5
Introducción .......................................................................................................................... 5
La Realidad Aumentada ............................................................................................................ 9
2.1. ¿Que es la Realidad Aumentada (AR)? ........................................................................... 9
2.2. Equipos de realidad aumentada ................................................................................ 9
2.3. Historia de la realidad Aumentada. .......................................................................... 10
Empresas que están invirtiendo en la realidad aumentada ................................................... 10
1.1. Microsoft ................................................................................................................. 11
1.2. Apple ....................................................................................................................... 12
1.3. Google ..................................................................................................................... 12
1.4. Ptc ............................................................................................................................ 14
Realidad Aumentada en la Restauración ................................................................................ 15
1.5. Restaurante Vida&Comida ...................................................................................... 16
1.6. Bareburger............................................................................................................... 16
1.7. Kabaq ....................................................................................................................... 17
2. OBJETIVOS Y METODOLOGÍA ................................................................................................. 19
Objetivos ............................................................................................................................. 19
Herramientas utilizadas .......................................................................................................... 20
Metodología ............................................................................................................................ 24
1.1. Diseño en Photoshop .............................................................................................. 25
1.2. Crear proyecto en Unity .......................................................................................... 28
1.3. Instalación SDK y JDK ............................................................................................... 31
1.4. Incorporar Vuforia ................................................................................................... 32
1.5. Importar Assets ....................................................................................................... 32
1.5.1. Obtener Licencia.................................................................................................. 32
1.5.2. Base de datos ...................................................................................................... 34
1.6. Importar contenido multimedia .............................................................................. 40
3. REALIZACIÓN DEL PROYECTO .................................................................................................. 41
Diseño Interfaz de Usuario de la aplicación. ....................................................................... 41
1.1. Creación de la primera escena que se ejecutara en la aplicación........................... 41
1.2. Creación escena encargada de realizar el pedido. .................................................. 54
1.3. Selección de Lenguaje ............................................................................................. 55
4
Script: Implementación de componentes. .......................................................................... 57
2.1. DeteccionTarget.cs y DeteccionTarget2.cs ............................................................. 61
2.2. CartaInicio.cs ........................................................................................................... 63
2.3. ComandosSQL.cs ..................................................................................................... 69
2.4. PlatoScript.cs ........................................................................................................... 76
2.5. Plato.cs .................................................................................................................... 80
2.6. CambiarFoto.cs ........................................................................................................ 81
2.7. CarritoControler.cs .................................................................................................. 83
2.8. CarritoScritpt.cs ....................................................................................................... 83
2.9. ErrorScript.cs ........................................................................................................... 86
2.10. MantelScript.cs .................................................................................................... 86
2.11. MostrarVideo.cs .................................................................................................. 87
2.12. NotaScript.cs ....................................................................................................... 88
2.13. OpiniónControler.cs ............................................................................................ 89
2.14. OpiniónDB.cs ....................................................................................................... 92
2.15. OpiniónScript.cs .................................................................................................. 95
2.16. PedidoCocina.cs .................................................................................................. 96
Resultados ......................................................................................................................... 102
CONCLUSION Y TRABAJOS FUTUROS. ............................................................................... 109
Conclusiones...................................................................................................................... 109
Líneas futuras ........................................................................................................................ 110
5. BIBLIOGRAFIA ........................................................................................................................ 111
ANEXO I. INSTALACION DE LOS PROGRAMAS NECESARIOS. .................................................... 113
ANEXO II. MANUAL DE UNITY. .................................................................................................. 116
Interfaz de Unity ................................................................................................................ 116
Assets .................................................................................................................................... 117
Sprite ..................................................................................................................................... 118
Escenas .................................................................................................................................. 119
Game Object.......................................................................................................................... 119
Scripts .................................................................................................................................... 119
ANEXO III. MYSQL Y PHP ........................................................................................................... 121
ANEXO IV. INDICE FIGURAS. ...................................................................................................... 124
ANEXO V. VÍDEO ........................................................................................................................ 128
5
1. INTRODUCCIÓN Y OBJETIVOS
Introducción
Las nuevas tecnologías, el desarrollo de dispositivos móviles y el acceso
continuo a internet, se han convertido en una parte integral en nuestra vida cotidiana. El
desarrollo de distintas aplicaciones nos permite compartir noticias, ideas, fotos de
manera instantánea con otros usuarios. Aplicaciones como Twitter, Facebook,
Instagram, por mencionar algunas, son un claro ejemplo de cómo las nuevas
tecnologías acercan de manera cotidiana el mundo virtual y el mundo real. Es obvio que
en el campo de la tecnología se anuncian muchos avances importantes, cada uno más
impresionante que el anterior. Muchos de estos anuncios tecnológicos hablan sobre el
diseño de nuevos auriculares de realidad virtual (HTC Vive, Oculus Rift) y auriculares
de realidad aumentada (Google Glass, HoloLens), y en un futuro muy cercano, podrían
constituir parte esencial de nuestra vida cotidiana, al igual que hoy lo representa el
teléfono móvil.
En los últimos años se ha desarrollado un nuevo tipo de tecnología capaz de
mejorar la percepción y la interacción de los usuarios con el mundo real, particularmente
a través del uso de la realidad aumentada (AR). De acuerdo con la definición de Azuma,
todo sistema de realidad aumentada debe cumplir con las siguientes tres características:
• Combinar el contenido real con el virtual.
• El sistema debe ser interactivo y debe funcionar el tiempo real.
• El contenido virtual está integrado en el mundo real (brillo, obstrucción,
recalibración).
Tanto en el plano material como en el software, esta última década ha sido rica
en nuevos productos de vanguardia. En el campo del software, debemos destacar:
• La disponibilidad de soluciones integradas de software profesional de manera
gratuita, lo que permite a cualquier persona con conocimientos técnicos
desarrollar sus propias soluciones.
• El lanzamiento de la primera versión gratuita de Unity 3D (2009), así como el
lanzamiento de ARKit de Apple (2017).
6
En el campo del hardware es determinante la democratización de la tecnología
y su evolución desde que Apple vendió su primer iPhone en 2007. El impacto que esto
provocó en el mercado de la telefonía móvil y de las aplicaciones móviles, generó una
rápida evolución en cuanto al acceso de los usuarios a tener un terminal equipado con
una pantalla de alta calidad, cámara y varios sensores (acelerómetros, pantallas
capacitivas…). Desde aquí el paso que debe dar un usuario para acceder a aplicaciones
de AR móviles, es corto. Otra revolución tecnológica de gran impacto es la introducida
con las tarjetas gráficas gracias al desarrollo de arquitecturas especializadas GPU
(Graphics Processing Unit) y coprocesadores en computación de alto rendimiento. Esto
permite incorporar imágenes de mayor calidad, mejorando la experiencia y la interacción
con el usuario, al requerir tiempos de ciclos más cortos (alta frecuencia de cálculo, bajo
retraso).
Sin embargo, a pesar de que la realidad aumentada es una tecnología que ha
provocado muchas fantasías, a fecha de hoy, ha dado muy poco. Muchos
comunicadores crearon videos sensacionales agregando lo virtual a lo real: esto permitió
que espectador creyera que era posible ver, por ejemplo, un automóvil que no existía.
Sin embargo, estas promesas superaron con creces las posibilidades tecnológicas que
hoy en día la AR puede ofrecer. La consiguiente reacción dio lugar a un fuerte rechazo
de la AR por parte del público en general, que hoy en día se contentan con los juegos
como Pokemon Go. Es difícil determinar las etapas en las que las empresas se han
apropiado de esta tecnología, esto es en parte debido a que las necesidades
expresadas y los usos previos resultaron en “pseudo-oferta”, que afirmaba responder a
sus demandas mediante el uso de trucos y artificios que no duraron mucho tiempo.
Merece la pena hacer mención de la curva de Gartner Hype para 2017 (ver Figura 1.1),
la cual sitúa a la AR en el abismo de la desilusión. Sin embargo, un análisis cuidadoso
nos muestra que en lo que respecta a las aplicaciones industriales, la realidad
aumentada ha comenzado en la fase 2, “pendiente de la ilustración”, con la estabilidad
del mercado que se espera en los próximos 2-5 años.
7
Figura 1.1. Courbe de Hype 2017.
La implementación de la AR se está consolidando en sectores económicamente
tan importantes como el comercio, el turismo, y el entretenimiento, y en menor medida
en los sectores industrial y médico. Las aplicaciones desarrolladas permiten integrar
información digital en tiempo real con el entorno en el que se encuentra el usuario. La
realidad aumentada se utiliza en el campo, incluso dentro de las unidades de producción
y en la construcción. Los equipos utilizados son tabletas, gafas de AR, y dispositivos
móviles. La consolidación de la AR en los sectores comerciales y turístico reside en la
capacidad que este tipo de tecnología presenta para ofrecer asistencia, bien a nivel
comercial (tecnología orientada al consumidor), o a nivel de información turística. La
información específica se vincula a distintos productos. Algunos ejemplos de la
implementación de AR, en el comercio los podemos encontrar en las aplicaciones
desarrolladas para IKEA o Wayfair, que permiten renderizar un mueble en relación con
el entorno real (ver Figura 1.2), o los “espejos mágicos” basados en AR de Rebecca
Minkoff o Charlotte Tilbury, por mencionar algunos. En el sector del turismo, la
información se asocia a diversos servicios turísticos basándose en la ubicación
determinada por GPS (Sistema de Posicionamiento Global). La superposición de la
información en el entorno inmediato de los clientes/turista no solo mejora la experiencia
sobre el restaurante/patrimonio urbano, además permite compartir la experiencia con
otros usuarios. Un claro ejemplo de la adopción de la AR en el sector del entretenimiento
8
lo representan aplicaciones como Pokémon Go y SnapChat. A nivel industrial, la AR
permite asistir a un operario sobre la guía o posicionamiento, ofreciendo asistencia
técnica local o remota. De esta manera se consiguen ganancias medibles en la
productividad de varias docenas de puntos porcentuales. En el plano industrial, destaca
MIRA, desarrollada por Airbus Group Innovation, la cual permite ayudar al ingeniero
aeronáutico verificar la integridad de la colocación correcta de miles de piezas instaladas
en aviones, como los soportes fijos que sujetan los cables, tuberías hidráulicas o
tuberías de aire acondicionado (ver Figura 1.2). En el sector médico, donde la
planificación quirúrgica es absolutamente esencial para el éxito de la cirugía, la
incorporación de los resultados correspondientes a los exámenes médicos del paciente
(resonancia magnética, rayos X…) con AR se puede conseguir mostrar el modelo en 3D
de la anatomía del paciente, mediante la superposición de imágenes reales de video
operativas. El paciente se vuelve virtualmente transparente a la vista del cirujano, lo que
permite visualizar estructuras dentro de los órganos (vasos, tumores…) que de otro
modo solo podría percibir a través del tacto. De esta manera se enriquece la visión real
del cirujano, lo que conduce a disminuir el tiempo de intervención, lo que también
reducirá el periodo de hospitalización. Sin embargo, los problemas asociados con la
calibración (puntos comunes entre los datos), imponen restricciones al desarrollo de
este tipo de técnicas.
Figura 1.2. Ejemplos de aplicaciones desarrolladas en realidad aumentada en distintos sectores: (a) comercial, (b) turísitico, (c) industrial y (d) médico.
9
La Realidad Aumentada
2.1. ¿Que es la Realidad Aumentada (AR)?
Es una tecnología que incorpora información digital a elementos físicos del
entorno, imágenes u objetos reales captados a través de algún dispositivo. Esta
información no solo es 3D. también pueden ser sonidos, imágenes, información, etc.
Todo aquello que se agregue a la realidad de nuestro entorno. La interacción del usuario
con la realidad natural es alta, ya que el mundo real es el entorno con el que se
interactúa a partir de la información digital que se agrega en él. El nivel de impresión en
una experiencia digital es medio. Dependiendo de la densidad digital que se agregue a
la realidad. Esta se desarrolla mediante aplicaciones diseñadas para teléfonos móviles.
Tendemos a confundir el termino realidad aumentada con realidad virtual. La
realidad virtual sumerge al usuario en un universo totalmente virtual. Donde hace creer
que esta en un entorno o situación que no existe en la realidad. Es decir, se añade algo
no existente, pero sin apartarnos de nuestra propia realidad y siempre usando nuestro
entorno.
2.2. Equipos de realidad aumentada
Los dispositivos que se pueden emplear en AR se pueden clasificar en las
siguientes categorías basándonos en su soporte físico:
• Dispositivos oculares: dependiendo de la complejidad del dispositivo
diferenciaremos entre los sistemas de “visualización óptica” que permiten la
visualización natural directa, y los sistemas de “visualización transparente”,
donde el entorno se percibe directamente a través de una cámara: google
glass, HoloLens, Magic Leap.
• Dispositivos de mano: teléfonos inteligentes y tabletas: google tango.
• Dispositivos móviles o fijos: video proyectores que proyectan una imagen
directamente sobre objetos en el entorno real, lo que provoca una
superposición natural.
10
2.3. Historia de la realidad Aumentada.
Haremos un breve recorrido del recorrido de la realidad aumentada en la historia:
• 1901. Antecedente Literario. Frank Baum, el escritor de relatos infantiles (El
Mago de Oz) Sus obras se anticiparon a cosas corrientes de finales de siglo,
como la televisión, la realidad aumentada, los ordenadores portátiles los
teléfonos inalámbricos las mujeres en ocupaciones de alto riesgo, cargadas de
acción y la omnipresencia de la publicidad en la ropa. En La Llave Maestra,
protagonizada por un niño que con sus anteojos podía ver la calidad moral de
las personas.
• 1957. Morton Heilig, máquina que agrega experiencia sensorial (sonido,
imágenes en 3D, perfume, aire) al espectador de una película.
• 1968. Primer dispositivo: Ivan Sutherland inventa el HMD, dispositivo que
proyecta gráficos geométricos en el entorno que le rodea.
• 1990. Origen del término: Es acuñado por Tom Caudell, ingeniero de la Boeing,
quien desarrolló unos lentes que proyectaban los planos del cableado, a modo
de guía virtual, para capacitar a los técnicos electricistas de la compañía aérea.
• 2000. ARQuake. Primer dispositivo de videojuego con RA.
• 2008. AR Wikitude. Dispositivo de RA con geolocalización.
• 2009. ARToolkit. Plataforma de acceso libre para generar contenidos de RA.
• 2012. Glass. Lentes de RA de Google.
• 2015. Proyecto Tango. Tecnología de RA en 3D para smartphones.
• 2016. Lanzamiento de Lenovo Phab Pro (Google). Primer dispositivo móvil con
soporte específico para RA. Niantic lanza Pokémon Go.
• 2017. Lanzamiento de Neuralink. Proyecto de Elon Musk para conectar RA en el
cerebro humano.
Empresas que están invirtiendo en la realidad aumentada
11
1.1. Microsoft
Microsoft HoloLens Development Edition es el primer equipo holográfico
totalmente autónomo que permite interactuar con hologramas de alta definición en su
entorno. Permiten ver el entorno físico que lo rodea mientras usa los auriculares,
combinando el mundo real con el contenido virtual. HoloLens es un dispositivo para
ejecutar aplicaciones de Realidad Aumentada y Realidad Mixta. HoloLens presenta
objetos virtuales en 3D en la vista de su dispositivo, creando la ilusión de que esos
objetos se encuentran en el entorno real del entorno. El usuario puede interactuar con
estos objetos mediante gestos, comandos de voz y mirada. Los objetos también pueden
interactuar con las superficies del mundo real en su entorno físico. Puede adjuntar audio,
animaciones y otros componentes a los hologramas, como con cualquier otro
GameObject en Unity.
Figura 1.3. HoloLens y captura del video que muestra Microsoft sobre el uso de estas.
Su precio es de 3.299€. Ha sido fabricado con componentes especializados que
al combinarse hacen posible la informática holográfica. El sistema óptico trabaja en
coordinación con sensores avanzados. La HPU hace que sea posible el procesamiento
de gran cantidad de datos por segundo. Gracias a todos estos componentes y otros
muchos, podrás desplazarte libremente entre hologramas e interactuar con ellos.
12
1.2. Apple
Apple esta invirtiendo en la realidad aumentada con su SDK Arkit. ARKit combina
el seguimiento de movimiento del dispositivo, la captura de la escena de la cámara, el
procesamiento avanzado de la escena y las funciones de visualización para simplificar
la tarea de crear una experiencia AR. Se puede usar estas tecnologías para crear
muchos tipos de experiencias AR usando la cámara trasera o la cámara frontal de un
dispositivo iOS.
Realidad aumentada con la cámara trasera
Los tipos más comunes de experiencia AR muestran una vista desde la cámara
trasera de un dispositivo iOS, aumentada por otro contenido visual, que brinda al usuario
una nueva forma de ver e interactuar con el mundo que los rodea.
ARWorldTrackingConfiguration proporciona este tipo de experiencia: ARKit
mapea y rastrea el espacio del mundo real que habita el usuario, y lo combina con un
espacio de coordenadas para que usted coloque contenido virtual. World tracking
también ofrece funciones para hacer que las experiencias de AR sean más envolventes,
como reconocer objetos e imágenes en el entorno del usuario y responder a las
condiciones de iluminación del mundo real.
Realidad aumentada con la cámara frontal
En el iPhone X, ARFaceTrackingConfiguration usa la cámara TrueDepth frontal
para proporcionar información en tiempo real sobre la postura y la expresión de la cara
del usuario para que la use en la representación de contenido virtual. Por ejemplo, puede
mostrar la cara del usuario en una vista de cámara y proporcionar máscaras virtuales
realistas. También puede omitir la vista de la cámara y usar los datos de expresión facial
de ARKit para animar personajes virtuales, como se ve en la aplicación Animoji para
iMessage.
1.3. Google
Google invierte en la realidad aumentada mediante su SDK ARCore y las gafas
Google Glass para realidad aumentada.
13
ARCore
ARCore es la plataforma de Google para crear experiencias de realidad
aumentada. Usa tres capacidades clave para integrar contenido virtual con el mundo
real como se ve a través de la cámara del teléfono:
• El seguimiento de movimiento permite que el teléfono entienda y rastree su
posición en relación con el mundo.
• La comprensión ambiental permite que el teléfono detecte el tamaño y la
ubicación de todo tipo de superficies: superficies horizontales, verticales y en
ángulo, como el suelo, una mesa de centro o paredes.
• La estimación de luz permite al teléfono estimar las condiciones de iluminación
actuales del entorno.
ARCore está diseñado para funcionar en una amplia variedad de teléfonos
Android calificados que ejecutan Android 7.0 (Nougat) y posteriores.
Google Glass
Figura 1.4. Glass de Google
Glass (Figura 1.4) es un dispositivo de visualización de tipo gafas de realidad
aumentada desarrollado por Google y presentado en el congreso I/O de junio de 2012.
Glass Explorer Edition fue puesto a la venta para los desarrolladores calificados el 15
de abril de 2013 por 1500 USD3 mientras que la versión para consumidores salió a la
venta el 15 de mayo de 2014 en Estados Unidos. El propósito de Glass era mostrar
información disponible para los usuarios de teléfonos inteligentes sin utilizar las manos,6
permitiendo también el acceso a Internet mediante órdenes de voz,7 de manera
comparable a lo que Google Now ofrece en dispositivos Android. El sistema operativo
será Android.
14
1.4. Ptc
Ptc invierte en la realidad aumentada mediante su SDK de desarrollo Vuforia.
Vuforia
La integración de Unity en Vuforia nos permite crear aplicaciones y juegos de
visión para Android e iOS utilizando un flujo de trabajo de creación de arrastrar y soltar.
Podremos ver varios ejemplos de RA en Assets Store, para poder entender así todas
las prestaciones de Vuforia.
El seguimiento de Vuforia es mediante marcadores, siendo imágenes u objetos.
Cuando la cámara del dispositivo detecta estos marcadores en la realidad (mientras se
ejecuta la aplicación) se ejecutará y mostrará el contenido 2D o 3D que hemos generado
mediante ordenador. Tiene una variedad de seguimientos de marcadores, como los
códigos QR, imágenes y las etiquetas 2D. El más común es el Image Targets que es el
que utilizaremos en este proyecto.
Prestaciones de Vuforia:
• Model Targets permite reconocer objetos utilizando modelos 3D preexistentes.
Coloca el contenido AR en elementos como equipo industrial, vehículos,
juguetes y electrodomésticos.
• Image Targets es la forma más sencilla de poner contenido AR en objetos
planos, como páginas de revistas, figuritas coleccionables y fotografías.
• Multi Targets. Se aplica a los objetos con superficies planas y múltiples lados, o
a los que contienen imágenes múltiples. Los envases de productos, afiches y
murales son elementos fantásticos para trabajar con Multi Targets.
• Cylinder Targets. Permiten colocar contenido AR en objetos con formas
cilíndricas y cónicas. Latas de bebidas gaseosas, botellas y tubos con diseños
impresos son grandes candidatos para Cylinder Targets.
• Object Targets. se crean escaneando un objeto. Constituyen una buena
alternativa para los juguetes y otros productos ricos en detalles superficiales y
una forma constante.
15
• VuMarks. Permite identificar y agregar contenido a una serie de objetos.
Constituye una fantástica manera de agregar información y contenido a las
líneas de productos, inventario y maquinaria.
También podemos realizar seguimiento sin marcado. Estos comúnmente se
basan en la ubicación o posición de la cámara. Esta forma de rastreo se basa en
tecnologías tales como GPS, acelerómetro, giroscopio y algoritmos de procesamiento
de imágenes más complejos, para colocar objetos virtuales o información en el entorno.
El hardware y el software de RV luego tratan estos objetos como si estuvieran anclados
o conectados a ubicaciones u objetos específicos del mundo real.
Realidad Aumentada en la Restauración
La realidad aumentada, que estaba inicialmente diseñada para distintas áreas
comerciales, ha ido ganando terreno en marketing y publicidad. Aporta al consumidor
una experiencia distinta al combinar la realidad con imágenes y objetos virtuales con los
que interactuar en tiempo real. Esta nueva forma de interacción con el usuario permite
tener una mayor experiencia con la marca. Además, el hecho de que el usuario
interactúe con su entorno y con elementos virtuales, fomentan el factor sorpresa que
hace que recuerden la acción y, por consiguiente, la marca.
Esta nueva técnica aplicada al marketing no solo sirve para llamar la atención
del consumidor, sino que también aporta contenido personalizado, de valor y con una
mayor creatividad. Nuestros clientes reales o potenciales pueden ver un producto antes
de comprarlo y usarlo como si fuera real. Lo más importante es que el usuario puede
conocer los valores, características y beneficios de nuestros productos en lugares donde
físicamente sería imposible.
Empresas de restauración que utilizan la realidad aumentada:
16
1.5. Restaurante Vida&Comida
Figura 1.5. Aplicación de realidad aumentada del restaurante Vida&Comida.
http://vidaycomida.com/
Primer restaurante de España que presenta su carta en realidad aumentada.
Con su aplicación podrás ver sus platos del menú, para así poder elegir que plato
quieres andes de hacerle el pedido al camarero. Los clientes del restaurante
Vida&Comida ya pueden descargarse gratuitamente la App VidayComida, disponible
para iOS/ Android, utilizando el WiFi que ofrecen en el propio establecimiento, y
comenzar a disfrutar de su comida o cena desde el minuto cero.
1.6. Bareburger
Es una cadena de hamburgueserías estadounidense que se convertirá en la
primera cadena que permitirá a los clientes ver la comida con realidad aumentada antes
de realizar el pedido. Los clientes recibirán un Snapcodigo que deberán escanear con
el smartphone o la tableta, automáticamente aparecen los distintos elementos del menú
sobre la mesa con la ayuda de la realidad aumentada, una hamburguesa, unas patatas
fritas, unos aros de cebolla, etc.
Las imágenes virtuales han sido creadas a partir de las imágenes reales de la
comida, de ahí que lo que se vea a través de la pantalla del teléfono tenga tanto
realismo. Se pueden utilizar diferentes filtros de lentes personalizados de Snapchat para
colocar sobre la mesa un plato virtual, cambiar el tamaño de la hamburguesa, simular
que se tiene en las manos, etc. Se puede obtener una vista de la comida desde cualquier
17
ángulo, hacerse fotografías con la hamburguesa y enviarlas a los amigos, todo ello
mientras los clientes esperan el auténtico menú que se prepara en la cocina del
establecimiento.
Figura 1.6. Captura de la aplicación de AR de la hamburguesería Bareburger.
https://www.bareburger.com/
1.7. Kabaq
Aplicación de realidad aumentada que permite que los clientes puedan ver la
oferta gastronómica a través del teléfono y conocer el tamaño real de lo que han pedido,
algo que comentan que contribuye a aumentar la participación y satisfacción de los
clientes. Con esta app se puede presentar el menú en 3D, ver la comida a escala real,
realizar combinaciones de diferentes guarniciones con los platos principales, crear
campañas de marketing a través de Snapchat o Facebook, presentar menús de cáterin,
crear anuncios 3D interactivos, etc.
Kabaq fue creada por Alper Guler y Caner Soyer en 2016, que anteriormente
habían trabajado en la realidad aumentada como herramienta para la presentación de
mobiliario y diseño de interiores, la idea de adaptarla a las presentaciones de comida
vino al tratar de explicar a un amigo los platos que habían probado en un restaurante
turco.
19
2. OBJETIVOS Y METODOLOGÍA
Objetivos
El objetivo del presente trabajo fin de grado consiste en diseñar y realizar una
aplicación de realidad aumentada orientada al sector de la restauración. En el diseño de
la aplicación se debe mostrar el contenido digital asociado a cada plato una vez que se
detecte la carta y el mantel físico propio del restaurante.
Los contenidos incorporados será principalmente información y contenido
multimedia. Los enumeramos a continuación:
• Platos disponibles en el restaurante, clasificados por categorías.
• Nombre, precio, fotos y/o videos, ingredientes, calorías, alérgenos y
opiniones de usuario correspondientes a cada plato.
• Enlaces a Facebook y web del restaurante.
• Posibilidad de añadir platos al carro. Poder modificar eliminando y
añadiendo platos mediante la ejecución de la aplicación.
• Añadir notas a cada plato añadido al pedido, para que lo vea cocina y
tenga en cuenta al realizar el plato.
• Envió de pedido a cocina, para que puedan realizar el pedido de platos y
con la información de la mesa a la que corresponde.
Para la realización del trabajo haremos uso del motor de videojuegos Unity, para
la interfaz visual del usuario, sobre el que acoplaremos los programas y códigos
aplicados. Emplearemos Vuforia, plataforma sobre la que implementaremos la realidad
aumentada. Es preciso diseñar una base de datos SQLite donde registraremos todos
los platos e información asociada a cada uno, así como el carrito mediante el cual se
efectuarán los pedidos a la cocina de cada uno de los platos del menú del restaurante.
Toda la programación se desarrollará con C#, código compatible con Unity y utilizado
para desarrollar videojuegos. Para la parte de diseño del proyecto emplearemos
Photoshop para hacer las plantillas correspondientes del menú y del mantel que
usaremos de marcador para nuestra aplicación, y para la modificación visual de los
platos.
20
Herramientas utilizadas
Para la realización del proyecto hemos utilizado el motor de videojuegos
Unity. Con el no solo puedes hacer videojuegos, si no también aplicaciones. Es una gran
ayuda para las creaciones de videojuegos, y con el puedes crear juegos en las
siguientes plataformas: aplicación Mac OSX, ejecutable de Windows, navegadores web
(con Unity WebPlayer), iPhone, iPad, teléfonos y tabletas con Android, Wii, PS3 y Xbox
360. Unity es un motor de videojuego multiplataforma creado por Unity Technologies.
Unity está disponible como plataforma de desarrollo para Microsoft Windows, OS X,
Linux.
Otras de las razones para usarlo es su descarga gratuita, ideal para comenzar o
estudiantes. La versión gratuita tiene todas las herramientas de Unity, las versiones de
pago, dependiendo de los ingresos obtenidos al año, ofrecen mas servicios en la nube
y también para eliminar la marca de agua de Unity que se ejecuta al inicio de la
aplicación.
Acerca de Unity Technologies
Unity Technologies está revolucionando la industria de los juegos con Unity, su
plataforma de desarrollo innovadora galardonada. Unity Technologies tiene más de
500,000 usuarios registrados en todo el mundo, incluyendo Bigpoint, Cartoon Network,
Coca-Cola, Disney, Electronic Arts, LEGO, Microsoft, NASA, Nickelodeon, Ubisoft,
Warner Bros., estudios grandes y pequeños, indies, estudiantes y aficionados. Todos
utilizan Unity para crear juegos y juegos 3D interactivos, así como simulaciones de
entrenamiento y visualizaciones médicas y arquitectónicas, en la web, dispositivos
móviles, consolas, por mencionar algunos. Unity Technologies está innovando
agresivamente para ampliar la capacidad de uso, la potencia y el alcance de la
plataforma, junto con su mercado de contenido digital Asset Store y el servicio de
distribución de juegos Unión para que pueda cumplir con su visión de democratizar la
tecnología 3D interactiva. Unity Technologies tiene su sede en San Francisco y oficinas
de desarrollo en todo el mundo.
21
Vuforia es una plataforma de desarrollo de aplicaciones de Realidad Aumentada
(AR) y Realidad Mixta (MR) multiplataforma, con seguimiento robusto y rendimiento en
una variedad de hardware (incluyendo dispositivos móviles y monitores de realidad
mixta montados en la cabeza (HMD) como Microsoft HoloLens).
Se ha realizado la parte de realidad aumentada con Vuforia ya que está ya
implementada en Unity, es gratuita e intuitiva de usar. Para ello se ha utilizado un
marcado de imagen, Image Target. El cual usara como marcador cualquier imagen
seleccionada, rica en detalle para su fácil detección. En nuestro caso será la carta del
restaurante y un mantel, el cual se ha realizado su diseño.
SQLite es una biblioteca en proceso que implementa un motor de base de datos
transaccional de SQL autocontenido, sin servidor, de configuración cero. El código para
SQLite está en el dominio público y, por lo tanto, es de uso gratuito para cualquier
propósito, comercial o privado. SQLite es la base de datos más implementada en el
mundo con más aplicaciones de las que podemos contar, incluidos varios proyectos de
alto perfil.
SQLite es un motor de base de datos SQL incorporado. A diferencia de la
mayoría de las otras bases de datos SQL, SQLite no tiene un proceso de servidor
separado. SQLite lee y escribe directamente en archivos de disco ordinarios. Una base
de datos SQL completa con múltiples tablas, índices, activadores y vistas está contenida
en un solo archivo de disco.
Usaremos esta base de datos de forma local en nuestro proyecto, para tener una
base de datos de los platos que se encuentran en nuestro menú. En ella se encontrarán
las tablas correspondientes según sea un aperitivo, tapa o ensalada, y en cada registro
se encontrarán los platos con su información correspondiente: nombre, precio,
ingredientes, alérgenos etc. También se encontrarán el nombre e ingredientes en inglés
para mostrarlos si se selecciona este lenguaje.
22
MySQL
Base de datos remota para guardar información dentro de un servidor. MySQL
es un sistema de gestión de bases de datos relacional desarrollado bajo licencia dual:
Licencia pública general/Licencia comercial por Oracle Corporation y está considerada
como la base datos de código abierto más popular del mundo y una de las más
populares en general junto a Oracle y Microsoft SQL Server, sobre todo para entornos
de desarrollo web.
Usaremos esta base de datos para tenerla en el servidor y conectar a ella
mediante PHP. En ella guardaremos las opiniones de los usuarios, para así una vez
agregada la opinión de un usuario poder verla el resto de los usuarios. Puesto que no
tenemos servidor de pago, usaremos XAMPP como servidor local para realizar la
programación.
PHP
PHP, acrónimo recursivo en inglés de PHP: Hypertext Preprocessor
(preprocesador de hipertexto), es un lenguaje de programación de propósito general de
código del lado del servidor originalmente diseñado para el desarrollo web de contenido
dinámico. Fue uno de los primeros lenguajes de programación del lado del servidor que
se podían incorporar directamente en un documento HTML en lugar de llamar a un
archivo externo que procese los datos. El código es interpretado por un servidor web
con un módulo de procesador de PHP que genera el HTML resultante.
*Tanto el uso de MySQL y PHP lo dejaremos planteado y con sus códigos
correspondientes. En la aplicación de muestra final dejaremos las opiniones en la base
de datos SQLite, para poder acceder a ella una vez ejecutado en el móvil, ya que no
tenemos host de pago. Su contenido se mostrará en el ANEXO III.
23
C# es un lenguaje presentado en .Net Famework, procede de C++. Sin
embargo, C# es un lenguaje orientado a objetos, moderno y seguro. Características
del lenguaje:
• Clases
Todo el código y los datos en C# deben estar incluidos en una clase. Las clases
admiten herencias simples y todas las clases derivan al final de una clase base
llamada objeto.
• Tipos de datos
C# permite trabajar con dos tipos de datos: de valor y de referencia. Los de valor
contienen valores reales. Los de referencia contienen referencias a valores
almacenados en algún lugar de la memoria.
• Funciones
Una función es un fragmento de código que puede ser invocado y que puede o no
devolver un valor al código que lo invoco en un principio. Las funciones pueden tener
cuatro tipos de parámetros:
• Parámetros de entrada: tienen valores que son enviados a la función, pero la
función no puede cambiar esos valores.
• Parámetros de salida: no tienen valor cuando son enviados a la función, pero la
función puede darles un valor y enviar el valor de vuelta al invocador.
• Parámetros de referencia: introducen una referencia en otro valor. Tienen un
valor de entrada para la función y ese valor puede ser cambiado dentro de la
función.
• Parámetros Params: definen un número variable de argumentos en una lista.
• Variables
Las variables pueden ser definidas como constantes. Las constantes tienen valores
que no pueden cambiar durante la ejecución del código. C# incorpora un mecanismo
para definir y procesar eventos. Un controlador de eventos es un procedimiento en el
24
código que determina las acciones que deben llevarse a cabo cuando ocurra un
evento, como que el usuario pulse un botón.
• Interfaces
C# admite interfaces, que son grupos de propiedades, métodos y eventos que
especifican un conjunto de funcionalidad.
• Atributos
Los atributos aportan información adicional sobre su clase al CLR. Antes, si quería qu
la clase fuera autodescriptiva, tenia que seguir un enfoque sin conexión, en el que la
documentación fuera almacenada en archivos externos como un archivo IDL o incluso
archivos HTML. Los atributos solucionan este problema permitiendo al programador
vincular información a las clases.
Metodología
En este apartado se mostrará la metodología que se ha seguido para realizar la
aplicación. Comenzaremos diseñando la interfaz que se utilizara en nuestro proyecto.
Cada plato que se mostrara, como las interfaces utilizadas para mostrar botones
intuitivos y llamativos al usuario. Para ello se hará uso del Photoshop, para preparar
todo este contenido multimedia que será mostrado.
A continuación, crearemos un proyecto en Unity, en el cual prepararemos toda
la configuración necesaria para la utilización de la plataforma Android y de la realidad
aumentada, así como la implementación a Unity de los SDK y JDK necesarios, como la
implementación de Vuforia.
Importaremos la base de datos obtenida en Vuforia para obtener los marcadores
y códigos de seguimiento de realidad aumentada. Para ello tendremos que dirigirnos a
Vuforia, registrarnos y pedir las licencias y base de datos correspondientes a nuestro
proyecto.
Una vez completado, tendremos todo nuestro proyecto listo para comenzar su
realización, explicado con detenimiento en el capítulo 3.
25
1.1. Diseño en Photoshop
La aplicación, para mostrará el contenido multimedia creado, tendrá que detectar
los marcadores asignados. Estos como hemos dicho anteriormente serán Image Target,
que son marcadores de Imagen implementados mediante Vuforia. Estos marcadores
pueden ser cualquier imagen rica en detalle y no repetitiva.
Para ello, se diseñarán dos imágenes por Photoshop que estarán presentes en
el restaurante (menú y mantel) de forma habitual como presentación de menús. Y que
su vez, emplearemos como marcador. Aquellos clientes que quieran hacer uso de la
aplicación encontrarán disponible información adicional asociada a cada uno de los
platos. Mediante el uso de guías hemos realizado un diseño uniforme. Nótese que la
información necesaria se ha incluido mediante cuadros de textos combinada con
distintas imágenes, para hacerlo visualmente más llamativo. El resultado se muestra en
la Figura 2.1.
Figura 2.1. Plantilla correspondiente al menú y mantel presentes en el restaurante que
emplearemos como marcador.
Se han procesado las imágenes correspondientes a los distintos platos
empleados como modelo por Photoshop y archivadas con extensión .png sin fondo (ver
Figura 2.2).
26
Original Tratadas
Entrantes
Ensaladas
Carnes
Figura 2.2. Imágenes empleadas en el proyecto antes y después de ser tratadas por
Photoshop.
De esta manera se consigue reproducir los platos que se mostrarán en el mantel,
haciendo ver al cliente como es el plato real que encontrará en la mesa una vez que sea
pedido. Es importante dar la sensación de realidad al producto final, por esta razón
intentaremos ajustar las imágenes de cada plato al tamaño que el cliente encontrará en
la realidad. De esta manera se evitarán preguntas dirigidas al camarero asociadas al
contenido del plato, los ingredientes que lo componen, y al tamaño de este. Gracias a
la aplicación el cliente decidirá lo que quiere y si el tamaño o ingredientes son de su
agrado. También haremos fotos de cada plato desde distintas perspectivas, con el
objetivo de mostrar al cliente distintos detalles asociados al mismo plato (ver Figura 2.3).
27
Figura 2.3. Fotos utilizadas en el proyecto.
Nótese que en este caso no es necesario eliminar el fondo, ya que la finalidad
asociadas a las mismas está orientada a proporcionar información adicional a la
generada con la imagen en realidad aumentada.
Las imágenes empleadas en la interfaz de usuario, esto es carrito, pagina web,
enlace a la página asociada a Facebook, alérgenos, etc. que sirven para mostrar
información adicional al usuario, las mostramos en la Figura 2.4.
Figura 2.4. Botones de interfaz de usuario y alérgenos.
28
1.2. Crear proyecto en Unity
Para crear la aplicación se hará uso de Unity. Para ellos necesitamos crear un
proyecto nuevo y configurarlo para la plataforma de Android. En esta configuración
indicaremos el nombre que se mostrara en la aplicación una vez instalada en el móvil,
así como su imagen. También tendremos que configurara en el proyecto el uso de
realidad aumentada mediante Vuforia.
Comenzaremos abriendo Unity. Se mostrará una ventana que contienen
proyectos ya creados (si los hubiera). En nuestro caso comenzamos uno nuevo. Para
ello, pulsamos Crear Proyecto, y automáticamente abrirá la ventana mostrada en la
Figura 2.5. A partir de aquí se cumplimenta la información asociada al proyecto (nombre,
donde se guardará dentro de nuestro ordenador y el Template que utilizaremos, que en
este proyecto será 3D).
Figura 2.5. Creando proyecto en Unity.
Nótese que Unity abre por defecto un proyecto con una escena de ejemplo, ya
que Unity siempre tiene que trabajar con al menos una escena abierta.
El proyecto está enfocado a realizar una aplicación de realidad aumentada para
la plataforma Android. Por lo tanto, antes de empezar a crear nuestro proyecto, haremos
las configuraciones necesarias para que Unity configure nuestra aplicación orientado a
esta plataforma.
29
Para ello, iremos a la barra de Herramientas y le daremos a FileBuild→Settings…
Donde se nos abrirá la ventana mostrada en la Figura 2.6.
Figura 2.6. Build Settings en Unity.
Como se puede observar, en esta ventana se nos muestran distintos campos.
En la parte superior las escenas incluidas en el proyecto. Cada vez que generemos una
escena y deseemos que se ejecute en nuestro proyecto tendremos que incluirlas aquí.
De esta manera, Unity, mostrará las escenas que hemos creado y su tipo de
numeración. Podemos acceder a cada una de ellas desde una escena a otra indicando
su nombre o numeración.
En la ventana Platform se pueden ver las distintas plataformas a las que
podemos crear nuestra aplicación. En nuestro caso es Android, por lo que lo
seleccionaremos y pulsaremos el botón Switch Platform. De esta forma ya nuestro
proyecto estará configurado para esa plataforma, mostrándonos Unity las distintas
resoluciones y opciones de dicha plataforma.
En la parte derecha, se muestra la configuración seleccionada según elegimos
una u otra plataforma. De aquí no tocaremos nada y lo dejaremos tal como viene por
defecto.
En la parte inferior muestra una serie de botones: (i) para seleccionar Android,
(ii) Player Settings, que nos permite seguir la configuración de nuestro proyecto. Al
pulsarlo abrirá unos parámetros en la ventana del inspector (parte derecha de la ventana
30
de Unity si viene con la configuración por defecto). En la parte superior mostrará el
nombre de la compañía y del producto (ver Figura 2.7).
Para este proyecto, hemos seleccionado como nombre de compañía Ujaen. En
Producto name definimos el nombre asociado a esta aplicación. Este nombre es el que
aparecerá al tener la aplicación instalada en nuestro móvil o tableta. Incluimos el logo
de la universidad. Esta imagen es la que se mostrará en nuestro recuadro de aplicación.
De no incluir ningún logo, mostrará el icono de Unity.
Figura 2.7. Players Settings.
Una vez modificados estos dos campos, completamos el apartado OtherSettings
para modificar el Package Name del Inspector. Lo definiremos
com.Ujaen.CartaRestaurante. Nótese que deben coincidir con el nombre de producto y
con la compañía que hemos definido.
Ya que nuestra aplicación será de realidad aumentada tendremos que dirigirnos
a XR Settings (Figura 2.8), marcaremos la casilla de Vuforia, para poder así trabajar con
las cámaras de realidad aumentada.
Figura 2.8. XR Settings
31
1.3. Instalación SDK y JDK
Para poder trabajar con plataformas Android es necesario instalar el SDK y JDK.
El kit de desarrollo de software (SDK) es generalmente un conjunto de herramientas de
desarrollo de software que le permite al programador o desarrollador de software crear
una aplicación informática para un sistema concreto. Ir a la búsqueda. Java
Development Kit (JDK) es un software que provee herramientas de desarrollo para la
creación de programas en Java.
En la barra de herramientas Edit→Preferences se abrirá la siguiente la ventana
Unity Preferences (ver Figura 2.9). En el apartado External Tools, pedirá la ruta del SDK
y JSDK. Si están instalados basta con buscarlos en nuestro ordenador. Si no es así,
pulsaremos el botón Download, este nos redireccionara directamente a la página donde
descargaremos el SDK y JDK. Una vez instalados Unity los reconoce inmediatamente,
si no fuese así le damos al botón Browse e indicamos en que carpeta se encuentran.
Figura 2.9. Unity Preferences.
De esta manera conseguimos configurar el programa antes de desarrollar el
proyecto. Merece la pena destacar que como este proyecto está orientado a realidad
aumentada, es preciso redirigirnos a la página de Vuforia para descargar nuestra propia
base de datos y pedir licencia.
32
1.4. Incorporar Vuforia
Para implementar la realidad aumentada en nuestro proyecto, haremos uso de
Vuforia. Para ello, este SDK nos proporciona la base de datos correspondiente a
nuestros marcadores y los códigos de seguimiento, que serán importados en el Assets
de nuestro proyecto. Para obtener esta base de datos, nos dirigiremos a Vuforia y
realizaremos lo pasos correspondientes para asignarnos las licencias y obtener la
base de datos correspondiente.
1.5. Importar Assets
Al empezar el proyecto, Unity creará una escena en la cual se dispone de cámara
y luz. Ambas son necesarias en todas las escenas, pero en nuestro caso no
necesitaremos configurar ninguna de ellas. En cambio, para poder trabajar con realidad
aumentada, es preciso importar a nuestra jerarquía la cámara de realidad aumentada,
que no es la que lleva inicialmente cada escena. Para ello nos dirigimos a jerarquía y le
damos al botón derecho del ratón. También se puede conseguir de manera alternativa
a partir de la barra de herramientas GameObjects→Vuforia→ARCamara.
De esta manera se importarán las carpetas de Vuforia en el Asset de nuestro
proyecto. Podemos verlo en nuestra Ventana de Proyecto, que se encuentra en la parte
inferior de Unity. Y veremos que se han creado las distintas carpetas.
1.5.1. Obtener Licencia
Para poder utilizar la ARCamara necesitaremos disponer de una licencia. De lo
contrario, aparecerá un error en Consola que impedirá iniciar la aplicación. Para ello
tendremos que dirigirnos a la página de Vuforia (https://developer.vuforia.com/).
Para continuar es preciso registrarnos en la página, o iniciar cuenta si ya estamos
registrados. Nos dirigimos al apartado Develop, donde aparecerán las distintas licencias
33
que disponemos, ordenadas por orden alfabético si están activas y fecha. En nuestro
caso solicitamos una licencia con el nombre CartaRestaurante (ver Figura 2.10).
Figura 2.10. Licencias en Vuforia.
Figura 2.11. Licencia de CartaRestaurante Vuforia.
Una vez creada, nos facilitará una ventana con un código (Figura 2.11), el cuál
tendremos que copiar. Nos dirigirnos al proyecto en Unity y lo incorporamos en la
cámara. Seleccionamos la cámara y en el inspector mostrará sus parámetros. En el
apartado Vuforia Behaviour, le daremos al botón Open Vuforia Configuración (Figura
2.12).
34
Figura 2.12. Parametros ARCamara.
Esto abrirá la configuración de Vuforia, y en el apartado App License Key (Figura
2.13) copiaremos la licencia que nos ha proporcionado en la página de Vuforia. Con
esto ya nuestra cámara de realidad aumentada está preparada para trabajar.
Figura 2.13. Copiando Licencia de ARCamara en sus parámetros.
1.5.2. Base de datos
En este proyecto trabajaremos con Image Target. Los Image Target son un tipo
específico de marcador utilizado en el seguimiento basado en marcadores de imagen,
pero estos no necesitan ser códigos en blanco y negro con una forma especiar para ser
35
reconocidos como los códigos QR. En este caso, son imágenes compatibles y ricas en
detalles, que la cámara detecta y al reconocerla ejecuta el contenido digital.
De nuevo en Vuforia Develop, dentro del apartado Target Manager. Subimos a
Vuforia nuestras bases de datos y de esta manera el programa nos proporciona los
Assets asociados que tendremos que incorporar a nuestro proyecto. Pulsando al botón
Add Database para crear así nuestra primera base de datos, o si tenemos ya una
podemos seleccionar si queremos una ya existente. Se nos abrirá una ventana que nos
pedirá que indiquemos el nombre de nuestra base de datos y el tipo (Figura 2.14).
Señalaremos Develop.
Figura 2.14. Creando base de datos Vuforia.
Creada nuestra base de datos, pulsamos el botón añadir, con el cual se nos
abrirá la ventana de Add Target (Figura 2.15). En ella se mostrará los distintos tipos de
marcadores que dispone Vuforia. EN nuestro caso como dijimos anterior mente será
una Image Target, por lo que seleccionaremos el tipo single image. En el apartado File
subiremos la imagen que deseamos utilizar como marcador. Nótese que el formato de
las imágenes debe ser jpg o png, tanto en blanco y negro o en color. El tamaño que nos
permite es como máximo de 2 MB. En el apartado Width indicaremos el ancho de
nuestra imagen. Y por último nos pedirá el nombre de la Image Target. Esto es
necesario, ya que como en nuestro caso, usaremos varias imágenes en la misma base
de dato.
36
Figura 2.15. Añadiendo marcador.
Vuforia nos evaluara mediante estrellas de 0 a 5 la calidad de nuestra imagen. L
imagen debe ser rica en detalles para que la cámara la detecte sin problemas, por lo
que de 1 o 2 estrellas la calidad es baja, para obtener mejores resultados se recomienda
una calificación de 4 o 5 estrellas. Si el objetivo no está bien enfocado, el contenido no
se verá adecuadamente y será difícil de detectar el objetivo. Mostrando así el contenido
virtual con mala calidad o vibrante. La luz también es importante, debe de haber
bastante luz para la detección de la imagen y no haber reflejos que dificulte la cámara
detectar parte de la imagen.
El detectado de Vuforia, representa un mapeado de estrella por cada detalle que
encuentra en la imagen, por eso la necesidad de que esta este rico en detalles, pero no
tenga unos patrones repetitivos. También a mayor contraste mejor diferenciación de
colores o detalles, por lo tanto, mas puntos de detección.
• Imágenes con baja calificación:
No obtendría niguna estrella. A penas tiene detalles.
37
Tb tendría ninguna estrella al ser un patron repetitivo
• Imagen con buena calificación:
Obtendria una clasificación de 5 estrellas. Es rica en detalles no repetitivos y con buen
contraste de color.
Inicialmente comenzamos diseñando un prototipo de mantel con pocos detalles.
En él se presentaba un diseño simple y con poco texto. Al incorporarlo a la base de
datos de Vuforia, la calificación obtenida fue de una estrella (Figura 2.16). De esta forma
se detectaría el target, pero este podría perderse fácilmente. Para solucionar
contratiempos futuros, modificamos el diseño, rellenando algunos espacios con
imágenes. De esta forma conseguimos que Vuforia le concediese una clasificación de
4 estrellas (Figura 2.17), que ya es una clasificación bastante buena y nos asegura su
detección y buena calidad.
38
Figura 2.16. Primer mantel con baja clasificación.
Figura 2.17. Mantel final, con una buena clasificación.
En este proyecto emplearemos dos Image Target. Una que representa al mantel
en papel, del estilo al que podemos encontrar en múltiples restaurantes. El segundo
será la correspondiente carta del restaurante. Esta última proporciona, no solo el
contenido real para todos los clientes sin necesidad de usar móvil. Además, puede ser
usada por los clientes que desean utilizar la aplicación y aprovechar su aumento de
información y comodidad de pedido.
39
Figura 2.18. Marcadores subidos a la base de datos de Vuforia.
Una vez subido todo el contenido de nuestro proyecto le daremos a Download
Database y descargaremos la base de datos para Unity. Esta base de datos es la que
nos proporciona Vuforia para importarla a nuestro proyecto. Una vez que la cámara,
detecte el marcador, buscará en la base de datos para identificarlo.
Para importarlo a nuestro proyecto, iremos de nuevo a Unity. Nos dirigiremos a
la ventana de jerarquía y le daremos al botón derecho, o desde la ventana de
herramientas Assets→Import Package, y lo seleccionaremos en la carpeta donde lo
tengamos archivado en nuestro ordenador. Se mostrará una ventana (Figura 2.19) que
incluirá el contenido del paquete, de manera que podremos seleccionar el contenido que
queremos importar.
Figura 2.19. Base de datos a importar.
Importada la base de datos, estaremos en condiciones de añadir los dos Image
Target que hemos subido previamente. Para ello nos dirigiremos a la ventada
herramientas GameObject→Vuforia→Image. Como en este caso tenemos dos
cargadas, seleccionaremos la más adecuada en cada caso seleccionando el Image
(Figura 2.20).
Figura 2.20. Selección del Image Target.
40
1.6. Importar contenido multimedia
En Unity, todo el contenido relacionado con el proyecto tiene que estar dentro de
la carpeta Assets de este mismo. Comenzaremos creando las distintas carpetas para
organizar nuestro proyecto, en las cuales se encontrarán los Scripts (códigos),
Animaciones, Scenas, Resources (contendrán las imágenes, videos, prefabs, etc, que
son llamados mediante código), Librerías, etc.
Para importar las imágenes, tendremos que configurara cada una de ellas como
Sprite 2D. Estas imágenes se han agrupado en Atlas para mejorar el rendimiento de la
aplicación. Esto es así, ya que disminuye el número de llamadas. También
importaremos las bases de datos del menú (más información en el Anexo II).
41
3. REALIZACIÓN DEL PROYECTO
Diseño Interfaz de Usuario de la aplicación.
A continuación, mediante la interfaz de Unity (para más información ver Anexo
II) haremos todo el diseño de la interfaz de nuestra aplicación, para diseñar el contenido
digital que se mostrara mediante realidad aumentada y poder ver el usuario. Para ello
se irán creando las distintas escenas que cumplimentarán el proyecto. Dentro de cada
escena se encontrarán los distintos GameObject que, según sus componentes
añadidas, tendrán funciones como imagen, botón, etc. Estos se irán agrupando en
jerarquía de forma organizada dependiendo de su puesta en escena. También se
diseñarán los distintos prefabs que se irán mostrando en escena durante la ejecución
de la aplicación.
1.1. Creación de la primera escena que se ejecutara en la aplicación.
Scena1 – CartaAR
Esta escena es la que se ejecutará nada más iniciar la aplicación. En nuestro
programa, al detectarse la carta del restaurante, se ejecutará Vuforia. De esta manera
se ejecuta todo el contenido que se incluye en esta image Target. Es importante notar
que el contenido que queremos que sea ejecutado al detectar cada image target, es
necesario asociarlo como hijo de este en la jerarquía.
Al detectar la carta, se mostrarán dos páginas virtuales. En una de ella se
encontrarán los grupos de botones que representan los platos de los que dispone la
carta del restaurante, y para la segunda página, se mostrará la información y el
contenido asociado al plato para el que se ejecute.
Para ello hemos crearemos una interfaz de usuario sencilla e intuitiva, capaz de
ser aplicada en Unity, de modo que tendremos que crear un canvas para visualizar el
contenido.
42
Canvas, es un rectángulo puesto en escena. Es un Game Object con un
componente canvas en él. Todos los elementos de interfaz de usuario deben ser
asociados como hijo de un canvas. Podemos tener más de un canvas en escena. En
nuestro proyecto de canvas no queremos que este se vea en la propia pantalla,
queremos que muestre el contenido paralelo a la carta. Por lo tanto, seleccionaremos el
canvas e iremos a la ventana de inspector. En Render Mode, usaremos el modo World
Space (Figura 3.1). En este modo de renderizado, el Canvas se comportará como
cualquier otro objeto propio de la escena. El tamaño de este Canvas puede ser
configurado manualmente utilizando su Rect Transform, y los elementos UI van a
generarse al frente o detrás de otros objetos dentro de la escena atendiendo a su
colocación 3D. En evento cámara arrastraremos la cámara AR. Dentro de nuestra
ventana de escena iremos cambiando el tamaño y posición de nuestro Canvas, hasta
ponerlo en paralelo a nuestra carta y con el tamaño deseado.
Figura 3.1. Canvas
Empezaremos creando la primera página virtual. Esta dispondrá de cuatro
grupos de botones: aperitivos, tapas, ensaladas y botones de enlace a la página de
Facebook, web y carrito. Crearemos tres grupos de Game Object vacíos, que servirán
para agrupar estos contenidos. Para cada uno de ellos, nos dirigiremos a
GameObjects→UI→Button. De este modo añadiremos un botón a nuestra escena.
Haremos el diseño de un botón y lo iremos duplicando para facilitar el trabajo. Button
reacciona al click de un usuario ejecutando una acción, que esta será añadida
posteriormente por código. Dentro de nuestro código añadiremos dos Game Object más,
de Image y Text. El texto es otro game object de interfaz de usuario de componente
visual no interactiva. Se mostrará un texto previamente añadido en cada componente o
accediendo desde código. En sus parámetros podemos modificar el tamaño, fuente,
alineación, etc. Image al igual que Text, pero en este caso se muestra una imagen,
añadida ya en componentes o asignada por código. Una vez aplicado el diseño y
duplicado como botones necesarios, le iremos añadiendo en texto el nombre del plato y
en imagen la foto del plato correspondiente (ver Figura 3.2). Esto proceso lo repetiremos
para cada uno de los tres grupos de botones, aperitivos, tapas y ensaladas.
43
Figura 3.2. Seleccionando Sprite en los parámetros de los botones.
Todos los botones los incluiremos juntos en un GameObject vacío llamado
Botones para disponer de ellos de manera más organizada. Seleccionamos el
GameObject Botones y añadimos el componente Horizontal Layout Group. Este un
componente que organiza a sus hijos de forma horizontal uno al lado del otro. En los
parámetros se puede modificar el espacio entre ellos y otras formas de diseño. En el
caso de los botones de web, Facebook y carrito, nos iremos a la componente imagen y
seleccionaremos los Sprites que ya habíamos diseñado para estos botones (estos tres
se encuentran en el png de interfaz de usuario ya añadido a los Assest). También los
añadiremos a un GameObject con la componente Horizontal Layout Group.
Dado que el número de botones es amplio y no todos se podrán ver a la vez,
aplicaremos una máscara. Mask es un componente que se le añade a un Game Object,
este no es de interacción, si no que modifica la forma visual de ver los componentes
hijos de este. La máscara tendrá un tamaño determinado, y los GameObject que sean
hijos de este y sean más grandes, solo se mostrará el contenido que este dentro del
tamaño de la máscara. Para ello crearemos una Mask para cada grupo de botones, de
manera que cada uno sea independiente. Creamos GameObject Image, y en el
inspector pulsamos el botón Add Component y buscaremos Mask, finalmente
aceptamos. Seleccionamos el tamaño deseado que queremos mostrar y haremos hijo
al grupo de botones. Para que no se vea el componente Image de nuestra macara (se
vería un recuadro blanco) iremos a la propiedad del componente Mask, Show Mask
44
Graphic y lo deseleccionaremos (Figura 3.3). Así ya solo se mostrará el contenido que
hay sobre ella.
Figura 3.3. Show Mask Graphic.
Aun así, no es posible ver todo el contenido que hay bajo la máscara, por lo que
es preciso añadir un Scroll Rect. Este componente, combinado con Mask, permite
desplazar el contenido de un lado a otro y poder ver todo el contenido que este dentro
del scroll. Añadiremos este componente, y nos pedirá el contenido, que en nuestro caso
es el GameObject Botones donde se encuentran todos los botones. Para poder añadirlo,
es preciso añadir a Botones el componente Content Size Filter, que es un componente
que indicará el tamaño del contenido. Seguidamente, trasladamos el GameObject
Botones dentro del parámetro Content de la máscara (ver Figura 3.4).
Figura 3.4. Content Size Filter y Scroll Rect.
Repetimos este proceso para cada grupo de botones, y como resultado
obtenemos un canvas con 4 mascaras (Figura 3.5) llamadas:
• Mask1, para los aperitivos. En la cual se encuentra el GameObject
Botones, con 7 botónes AperitivoN, correspondiente a cada plato.
• Mask2, para las tapas. En la cual se encuentra el GameObject Botones,
con 7 botones TapaN, correspondiente a cada plato.
• Mask3, para las ensaladas. En la cual se encuentra el GameObject
Botones, con 7 botones EnsaladaN, correspondiente a cada plato.
• Mask4, en la que se encuentra los tres botones Facebook, web y carrito.
45
Figura 3.5. Resultado de las cuatro mascaras.
GameObject: PaginaDos
Al igual que hemos descrito anteriormente, diseñaremos la interfaz de la segunda
página. En esta se mostrará una máscara más grande, donde se mostrará la información
según el plato seleccionado.
Para ello creamos un GameObjec PaginaDos, de manera que todos los
contenidos quedan estructuralmente organizados. Añadiremos como ya hicimos
anteriormente el componente Layout Group, pero en este caso vertical. Según la
posición que tengan los Layout en jerarquía es el orden con el que se verá el contenido
visualmente. Para ello crearemos un GameObject Image, para usarlo de fondo blanco y
poder visualizar el nombre del plato de manera clara. Para ello es preciso crear un Text
hijo, sobre el que se incluirá el nombre del plato. En este caso este texto se modificará
mediante código, ya que según el plato que seleccionemos este cambiará. Repetimos
este proceso para indicar el precio del plato y el símbolo €.
El siguiente Layout incluirá la opinión correspondiente a los usuarios. Crearemos
dos botones horizontales, hijos de otro GameObject y le daremos componente Layout
Group Horizontal. Nótese que de no lo hacerlo así, obtendremos como resultado uno
colocado debajo del otro. De esta manera ambos botones estarán situados al mismo
nivel. Para el primer botón se elimina la componente texto que viene por defecto, y la
imagen la modificaremos añadiendo el Sprite correspondiente a las estrellas de
calificación en su componente de imagen. Este Sprite se irá modificando en función a la
opinión que expongan los clientes. El segundo botón modificaremos el texto a Opinión,
46
y ejecutaremos una función que permitirá al usuario añadir su opinión al plato
seleccionado.
Ahora toca el papel más importante, la foto del plato. Crearemos un GameObject
Image, para el cual el Sprite precisa ser modificado mediante código, atendiendo al plato
elegido o según los botones posteriores, que mostrarán más fotos de cada plato
seleccionado.
El botón play, permite reproducir el video correspondiente a cada plato. Al
pulsarlo se activará un plano, sobre el que se reproduce el video correspondiente. Este
se desactivará pulsando otro botón o al terminar el video. El plano lo crearemos
GameObject→3DObject→Plane. Una vez creado, en el inspector, le añadiremos la
componente video. Mediante código accederemos al parámetro Video clip que es el
video que se reproducirá sobre el plano.
Los ingredientes y el botón pedir plato, se crearán como hemos descrito
anteriormente. En el caso de alérgenos, es preciso crear un Layout Group para que,
atendiendo a los alérgenos que contenga cada plato, las imágenes se activaran
correspondientemente indicando dichos alérgenos, este se organizara dependiendo del
tamaño necesario automáticamente.
Figura 3.6. Resultado MaskGrande.
De esta manera hemos concluido con los componentes correspondientes al
Image Target carta. Repetiremos el mismo proceso para los componentes
correspondientes a Image Target mantel. El contenido que pretendemos se ejecute al
detectar el mantel y sobre este serán los platos en realidad aumentada. Para ello
debemos diseñarlos de tal manera que debe producir la sensación de que el plato se
encuentra sobre la mesa. También se mostrará sobre el carrito, como si de una pantalla
47
se tratase, manteniendo el diseño del propio mantel y mostrándonos cada uno de los
platos que hemos seleccionado, su precio total, número de platos total y enviar a cocina
una vez estemos de acuerdo. También nos dará la posibilidad de eliminar los platos, por
si queremos corregir, y también añadir nota a cada uno de ellos, para enviar a cocina.
Para este image target es preciso diseñar otro canvas, que denominaremos
Canvas2, y que tendrá las mismas características que el Canvas1. En este caso el
tamaño será igual al mantel. Se crearán dos GameObject con función de agrupar. Uno
llamado CarritoM, que será el que contenga todos los game Object encargados de
mostrar el carrito. El segundo PlatoM, en el cual se mostrará el plato en realidad
aumentada.
PlatoM será un GameObject con componente Imagen, el cual se modificará su
Sprite en función del plato seleccionado. Este será modificado mediante código.
CarritoM tendrá varios gameObject. Comenzamos con un GameObject text, en
el cual indicaremos un texto que indique al usuario el contenido de su pedido, que
denotaremos como PEDIDO. Dos Image, que cada una de ellas tendrán dos hijos Text.
La primera que no se modificará e indicará el título correspondiente al: Precio total o
Número platos totales. El segundo nos indicará el precio y el número de platos. Este
será modificado mediante código. En el caso de Image de precio, tendremos un tercer
texto con el símbolo €.
Crearemos un botón con el texto Pedir Cocina. El usuario al pulsar este botón se
abrirá otra escena. En la cual nos pedirá el número de mesa en la que nos encontramos,
una vez introducido se hará nuestro pedido a cocina.
Por último, crearemos el diseño que tendrá cada plato que añadamos al carro.
En este caso, como el número de platos que se muestran es variable, crearemos el
diseño y lo importamos en nuestros assets para así crear un prefabs. De esta forma
accederemos a él mediante código, y en función del número de platos que tengamos,
será el número de prefabs que añada.
Un prefabs es un Game Object completo, almacenado con todos sus
componentes, valores y Game Objects secundarios. Es como una plantilla que se
almacena en nuestros assets y es llamada desde código para incorporarla a escena.
Para ello este Pregaf lo denominaremos plato y lo diseñaremos del siguiente
modo: (i) Crear una imagen con texto encima, el cual indique el nombre del plato. (ii) A
continuación, se encontrará un Image el cual el Sprite se modificará según el plato que
corresponda. Otra Image con dos componentes de texto, que indicarán el símbolo € y
48
el precio que corresponde a ese plato. (iii) Por último, dos botones, los cuales tienen
Sprite de interfaz de usuario, uno la de eliminar y otro tomar nota. Estas funciones las
añadiremos a continuación mediante código. Cada plato añadido al carro seguirá esta
plantilla, y modificará los valores correspondientes (Figura 3.7).
Figura 3.7. Prefab Plato.
Desconocemos la cantidad de platos que se añadirán al carrito. Nótese que se
nos puede presentar el problema asociado a que, si se agrupan muchos platos, estos
se saldrán de nuestro tamaño deseado. Con el fin de solucionar este problema, se
añadirá una Mask. El proceso consiste en crear un GameObject con la componente
Mask y ScrollRect. Otro GameObject llamado Platos que tendrá como hijos los prefabs
que se añadan. Este incluirá el componente de content size filter y Layout Group ya
explicados anteriormente. Y, en este caso, también añadiremos un Scrollbar, para que
el usuario sepa que puede arrastrar hacia abajo para ver todo su contenido, aunque
pueda verlo sin la necesidad de este. Una vez creado tendremos que arrastrarlo a la
Mask sobre su componente Vertival Scrollbar.
49
Figura 3.8. Resultado jerarquía de ImageTarget2.
Para finalizar con toda la interfaz visual correspondiente a esta escena, queda
por incluir dos prefabs más. Cada una se ejecutará en la escena al querer el usuario
añadir una nota, comentario o bien incluir su opinión asociada al plato. Para ello se crea
un canvas. De esta manera la interfaz será ejecutada sobre la pantalla, no sobre el
espacio, y al pedir introducir valores y escrito será más fácil y cómodo a la hora de
hacerlo para el usuario. Crearemos dos canvas llamados CanvasOpinión y CanvasNota
respectivamente.
CanvasOpinión, será un canvas como los creados anteriormente. Sin embargo,
los parámetros de canvas son diferentes. El parámetro Render Move del componente
canvas, seleccionar modo Screen Space – Overlay (con este modo el canvas se pondrá
justo en nuestra pantalla, a diferencia del caso anterior donde era otro componente mas
de la escena). También modificaremos el parámetro Canvas Scaler. Este parámetro es
importante para el diseño en si. El canvas según la resolución de la pantalla se verá de
una u otra forma, ajustándose a la resolución que tenga el terminal con el que se ejecute
la aplicación. Para que el diseño se mantenga lo mas parecido al diseño que pongamos
en un principio, modificaremos estos parámetros para que tome referencia a la
resolución en la que estamos diseñando y así escale a la que tenga el usuario. En UI
Scale Mode se ha de seleccionar el modo Scale Width Screen Size. En nuestro caso
50
hemos diseñado con la resolución 1280x720, así que la añadiremos en el parámetro
Reference Resolution (ver Figura 3.9).
Figura 3.9. Parámetros de Canvas.
Este canvas tiene GameObjects secundarios distintos. Un Image que servirá de
fondo, ya que nuestra pantalla será la imagen captada por la cámara al estar ejecutando
la cámara de realidad aumentada. Por lo tanto, para ver mejor el contenido, añadiremos
un fondo negro con un tamaño determinado. Tendremos después dos GameObjects
importantes, AddOpinión y Opiniones. Uno se ejecutará para añadir una opinión y el otro
para muestras las opiniones que ya se encuentran en nuestra base de datos
respectivamente. El primero compuesto por varios Text, que indican el titulo Nombre,
Opinión y Comentarios. A su vez tendremos tres Input Field. Este Game Object permite
que el Text sea modificado por el usuario, así podrá escribir sobre el dato
correspondiente. Para ello añadiremos tres, uno para el nombre de usuario, otro para el
valor dado al plato y otro para el comentario. Estos valores son tratados mediante
código.
El segundo GameObject llamado Opiniones, tendrá la función de mostrarnos
visualmente todos los comentarios que ya se encuentre en nuestra base de datos.
Tendremos dos botones con la función de volver, para volver a nuestra carta. Y otro con
la función de opinar, para así llevarnos al otro GameObject AddOpinión y poder dejar
una opinión. En este también tendremos otro prefab, llamado opinión. En este caso,
como no sabemos cuántas opiniones tendremos, también tendremos que hacer uso de
una plantilla. Este, como en el caso anterior y de igual forma, estará compuesto por una
mask y un scrollbar.
51
El prefab Opinión (Figura 3.10) estará formado por Text que indicará el nombre,
Image que indicará el número de estrellas según el valor dado, Text fijo con el texto
Comentario: y un scrollbar y Mask para poder mostrar todo el comentario añadido por el
usuario.
Figura 3.10. Prefab Opinión
El resultado total de todos los contenidos será el de la Figura 3.11.
Figura 3.11. Jerarquía prefab CanvasOpinión.
52
De forma similar realizaremos el CanvasNotas. En este caso es de menor
tamaño, ya que solo se pedirá un dato, que es la nota que se agregará al plato que este
en el carrito y no a los platos que ya se encuentran en el menú. Tendrá igual que el
anterior un Image de fondo negro, para ver correctamente el texto. Text con texto fijo
“Añadir nota al plato:”, Input Field donde el usuario añadirá la nota correspondiente, y
dos botones para Enviar la nota y para Volver al inicio si no se desea añadir la nota al
final. El resultado será el de la Figura 3.12.
Figura 3.12. Prefab CanvasNotas.
Por último, antes de crear la scena2, es preciso generar las animaciones. Estas
animaciones irán mostrando lateralmente el contenido de uno en uno. Esto hará que se
vea el contenido visualmente más atractivo, y no se muestre el contenido tal cual.
Unity tiene un sistema de animación excelente y sofisticado llamado Mecanim.
Este se basa en crear clips de animación, controlados por Animal Controler. Para poder
crear estas animaciones, tendremos que añadir la ventana animación a nuestro editor
de Unity. Si este no se muestra, le daremos a Window→Animation→Animation. Una vez
abierta esta ventana, para poder crear la animación tendremos que tener seleccionado
el GameObject al que queremos que se ejecute la animación. Empezaremos con Mask1,
y esta animación será la misma para Mask2, Mask3 y Mask4. Una vez seleccionado se
nos mostrará en la ventana Animation un botón Create. Una vez seleccionada, nos
pedirá donde guardar la animación. En nuestro caso lo guardaremos en la carpeta
Animacion. La guardaremos con el nombre Mostrar1, al guardarla automáticamente nos
creará un controlador de animación con el nombre del GameObject donde se ejecutará
esta, en este caso Mask1. Volviendo a la ventana Animation, le daremos al botón Add
Property, en el cual tendremos que seleccionar sobre que propiedad de ese
GameObject queremos que se ejecute la animación. En nuestro caso queremos que el
tamaño de la máscara se modifique, sea en un principio casi 0, hasta recuperar su
53
tamaño normal y mostrar todo el contenido, así dará esa sensación de que el contenido
se desplaza. Para ello le daremos al botón Rec, y modificaremos manualmente el
tamaño de nuestra cámara hasta el deseado, cuando terminemos daremos de nuevo al
botón Rec (Figura 3.13). Automaticamente Unity nos ha creado la animación sobre
nuestro contenido. Si le damos a Play podremos ver nuestra animación. Unity por
defecto nos creará una animación que se repite una y otra vez, como solo queremos
que se ejecute una vez nos iremos a nuestra carpeta de animación y haremos clip sobre
la animación creada Mostrar1. En el inspector se nos mostrará su parámetro, y
seleccionaremos Loop Time, así nuestra animación solo se ejecutará una vez.
Figura 3.13. Creación de animaciones en Unity.
Al crear la animación sobre el GameObject, a este se les añadirá
automáticamente el componente Animator. Como queremos usar la misma animación
para las otras mascaras ya que son idénticas una de otras, añadiremos en cada una de
ellas el componente Animator en la ventana inspector. En el parámetro Controler
añadiremos el controlador que se creó automáticamente Mask1, de esta forma todas
tendrán la misma animación.
En MaskGrande, es donde se encuentra la pagina dos. Al tener un tamaño mayor
crearemos una animación adicional sobre ella, al igual que hicimos con Mask1.
Tendremos una vez hecho esto, dos animaciones y dos controladores respectivamente.
54
1.2. Creación escena encargada de realizar el pedido.
Scene 2 – EnviarPedido
En esta escena también añadiremos una AR Camara, para que el fondo sea el
contenido de la cámara del terminal. En ella crearemos un canvas, que en este caso se
ejecutará en la pantalla del terminal, por lo que tendrá las mismas propiedades que el
CanvasOpinión de la otra escena. Este tendrá tres contenidos importantes. El primero
es el que se encarga de pedir al cliente en la mesa en que se encuentra, el segundo
mostrará su pedido, con todos los platos y notas añadidas, tal y como lo verían en
cocina, y el tercero mostrará un texto al usuario que se indicará que su pedido se ha
enviado correctamente.
El primer contenido, lo agruparemos en un GameObject llamado PedidoMesa.
Estará formado por un Image que hará de fondo, componente texto que indicará al
usuario que introduzca la mesa, InputField en la cual el usuario introducirá el número de
mesa correspondiente y un botón, que al hacer click el usuario, se enviará el valor
introducido en el Input y a su vez desactivará este grupo y activará TextCarrito. El
InputField en el cual introducirá el cliente el número de mesa en la que se encuentra,
será modificado en el Inspector su componente InputField el parámetro Content Type.
En este indicaremos que el valor que se puede ingresar en este Imput es Integer
Number, impidiendo así que el usuario no introduzca otros caracteres que no sean
numéricos. También limitaremos el número de caracteres a dos en el parámetro
Character Limit, ya que las mesas no superaran la cantidad de dos números.
EL segundo contenido lo agruparemos en el GameObject llamado TextCarrito.
En él se mostrará el texto que recibirá cocina, donde se encuentran los platos y las
notas. Estará formado por componente Image que hará de fondo, Text que será
modificado por código y mostrará los platos y dos botones, botón Ok y botón Volver.
Botón Volver le llevará de nuevo a la escena CartaAR, por si quiere modificar el carrito.
Botón OK es cuando está de acuerdo con lo pedido y quiere enviar a cocina. Al hacer
pulsar el usuario sobre este, se desactivará este grupo y activará el grupo final TexFinal.
El último contenido lo agruparemos en el GameObject llamado TextFinal. Estará
formado por componente Image que hará de fondo, componente Text que dirá que el
pedido se ha enviado a cocina correctamente y un botón OK, que al pulsarlo el usuario
se encontrará de nuevo a la escena CartaAR.
55
Figura 3.14. Jerarquía escena EnviarPedido.
1.3. Selección de Lenguaje
Añadiremos la posibilidad de seleccionar el lenguaje en el cual se representará
la carta. Esta podrá ser mostrada de igual forma en inglés o español. Para ello
duplicaremos las escenas CartaAR y EnviarPedido llamándolo CartaIng y
EnviarPedidoIng. Estas serán exactamente igual que sus escenas duplicadas, pero
cambiaremos los textos que se encuentran en la interfaz de usuario en inglés. Así si el
usuario selecciona lengua inglesa, se ejecutarán estas escenas con la lengua
correspondiente.
De igual forma haremos con los prefabs Opinion, CanvasNotas, CanvasOpinion
y Error, llamándolos OpinionIng, CanvasNotasIng, CanvasOpinionIng y ErrorIng. Si el
usuario selecciona la lengua inglesa, se ejecutarán estos prefabs.
56
Para poder seleccionar en que lenguaje queremos que se muestre la carta,
crearemos dos botones con un Sprite de la bandera correspondiente a la lengua que
representa. Estos estarán dentro de un canvas con parámetro Screen Space – Overlay
para que se muestre en la pantalla del dispositivo. Estos botones se encontrarán en la
escena CartaAR y CartaIng.
Fig. 3.15. Botones lenguaje.
57
Script: Implementación de componentes.
Por último y más importante, describamos los scripts incluidos en el proyecto.
Con ellos definiremos qué parámetros se ejecutarán primero en cada escena, el
comportamiento de cada objeto o cómo se modificarán durante la ejecución de la
aplicación. Todos estos se encontrarán en la carpeta Scripts en los Assest del proyecto.
Los scripts para ser llamados tienen que estar asociados a los gameobject activos en la
escena, o bien, deben ser llamados desde otro script.
Para tener una idea más clara de los scripts que se incluyen en la apliación, se
mostraran en el siguiente diagrama de flujo. Veamos cómo se comporta cada uno de
forma general.
58
59
Los primeros scripts que se ejecutan al iniciar la aplicación son
DeteccionTarget.cs y DeteccionTarget2.cs. Ambos scripts están asociados a los
componentes de cada Image target, esto es, la carta y el mantel respectivamente. En
ellos definiremos que, hasta que el marcador no sea detectado, no se debe ejecute el
GameObject Controlador. Este incluye en sus componentes el script CartaInicio.cs. Una
vez detectados los marcadores y activado el GameObject, se ejecutará su script
correspondiente. En estos scripts se precisa que las animaciones se ejecuten mediante
co-rutinas, cada x tiempo (se irán mostrando el contenido digital que hemos asociado a
cada objeto). Nótese que definimos estos parámetros para que las animaciones no se
ejecuten instantáneamente al iniciar la aplicación, de lo contrario, si la aplicación no
detecta el marcador, cuando lo reconozca, es posible que la animación haya finalizado
Una vez la animación ha mostrado todo el contenido, el usuario tiene la
posibilidad (pulsando en la pantalla) de seleccionar el plato que quiere que se muestre
en la app. Estas llamadas estarán conectadas mediante el On Click de cada botón al
script CartaInicio.cs. En base al menú seleccionado (tapa, ensalada o aperitivo) y el
numero indicado de entrada, las funciones MostrarAp(), MostrarEnsalada() y
MostrarTapa() llamaran al ComandoSelect() del script ComandosSQL.cs. Esta función
leerá los datos almacenados en la base SQLite, y se los añadirá al GameObject
PaginaDos, el cual tiene asociado a sus componentes el script PlatoScript.cs. Esta
función se encarga de indicar que valores tiene que mostrar en sus parámetros: foto,
nombre, precio, opiniones, ingredientes y alérgenos, dentro de este GameObject. Al
pulsar cada plato, el script CartaInicio.cs llamará a la función MostrarP() que conecta
con script MantelScript.cs asociado al GameObject PlatoM hijo del segundo Image
Target Mantel, y mostrará al cliente en 2D, la imagen del plato seleccionado sobre el
Mantel.
Los botones Web y Facebook, tienen sus funciones incluidas al On Click en sus
componentes; ambas funciones se encuentran en el script CartaInicio.cs. Al pulsar los
iconos asociados, la app conectará directamente con la página web del restaurante y/o
con el perfil en Facebook correspondiente.
El cliente también dispone de la posibilidad de añadir una opinión sobre el plato,
o bien de puntuar según su gusto al mismo, señalando las estrellas mostradas o bien
pulsando el botón Opinar. Ambas opciones disponen en su On Click la función Opinar()
dentro del script.cs CartaInicio.cs. Mediante esta función se consigue añadir a la escena
el prefab CanvasOpinion, el cual dispone el script OpinionControler.cs. Este último
controla el contenido y el orden en el que se debe mostrar. Se mostrará las distintas
60
opiniones emitidas por otros usuarios en relación con el plato seleccionado. El script
OpinionDB.cs, se encarga de recoger de la base de datos las opiniones expuestas por
otros clientes, y las añade mediante la función GetComponent() al Script
OpinionScript.cs añadido a la componente del prefab Opinión. Esta función se encarga
de añadir un prefab Opinión y modifica sus componentes en base a la información
obtenida. Cargará un numero de prefab igual a opiniones que haya. Si se quiere aportar
una opinión, hará click en el botón Dejar Opinión, el cual invocará la función de
OpinionControler.cs, encargada de mostrar el contenido necesario y los inputs para que
el usuario pueda ingresar su opinión al respecto. Al pulsar el botón Enviar, se llamará
de nuevo al script OpinionDB.cs, para almacenar en la base de datos la nueva opinión
del usuario. Si a esta opinión no se introdujo ningún nombre, se almacenará con el valor
“Sin nombre”. Si el valor de opinión ingresado en la opinión no está entre 0 y 5 se
mostrará un Error, este error es un prefab que se cargara en escena. Tendrá adjunto en
sus componentes el script Error.cs, que añadirá la función de autodestruirse cuando el
cliente pulse el botón Ok, para así dejar de mostrarse en pantalla. Cada vez que se
añada una opinión, se hará media y en función del resultado se mostrará las estrellas
según su valor correspondiente.
Para añadir un plato al carrito, se hará click al botón Añadir Plato. El cual tendrá
la función AddCarrito() del script CartaInicio.cs en su On Click. Esta ejecutara la función
AddPlato() de ComandosSQL.cs, almacenando el plato mostrado en la tabla
correspondiente al carrito dentro de la base de datos.
Para mostrar los platos que se han seleccionado en el pedido, se pulsará el botón
con imagen de carrito. Este botón en su On Click tendrá añadida la función
MostrarCarrito() del script CartaInicio.cs. Se encargara de invocar la función
MostrarCarrito() de ComandosSQL.cs. Esta leerá los platos que se encuentran en la
tabla Carrito e ira creando copias del prefab Plato. Se le ira modificando sus parámetros
mediante GetComponent(), accediendo a su script CarritoScript.cs, para mostrar la foto,
nombre y precio de cada plato. Se les añadirá dos botones, Eliminar y Nota. Al pulsar el
botón Eliminar, se ejecutara la función Eliminar() de CarritoScript.cs, que este llamara a
la función DELETE() del script ComandosSQL.cs, la cual elimina de la tabla Carrito, el
plato indicado mediante su Id. Al pulsa Nota, se ejecutara la función AddNota() de
CarritoScript.cs, que añadirá el prefab CanvasNotas. Este canvas dispone del script
NotaScript.cs, encargado de leer la nota que añade el usuario en su input. Al pulsar el
botón Ok, se llamara la función AddNota() de ComandosSQL.cs, que se encargara de
añadir esta nota en el registro correspondiente de la tabla, según su Id indicada.
61
Hacer Pedido es el botón encargado de realizar el pedido a cocina. La función
HacerPedido() estará añadido en su On Click de CartaInicio.cs, la cual cargara la
escena EnviarPedido.
En la escena EnviarPedido, se encontrará otro GameObject Controlador, con su
script PedidoCocina.cs. Mostrará un texto solicitando el número de mesa. Este dato se
introducirá en el input y al hacer click en el botón Enviar, se mostrara un texto con los
platos que fuesen añadidos a la tabla Carrito y sus notas correspondientes si las
hubiese. Si se envía el pedido, se duplicará la tabla Carrito, pero con nombre “PedidoN”
(siendo N el número de mesa). Esta tabla es la que luego se mostraría a cocina, para
realizar los platos solicitados.
Los botones de cambiar idioma se encargara de llamar a las funciones
CambiarIdiomaEsp() y CambiarIdiomaIng() que se encuentran en el script
CartaInicio.cs. Estas funciones cargaran las escenas CartaAR para español y CartaIng
para inglés.
2.1. DeteccionTarget.cs y DeteccionTarget2.cs
Estos scripts son una copia del script que viene por defecto en cada
ImageTarget, el cual se encarga de la detección del marcador,
DefaultTrackableEventHandler.cs. Este escript indicara mediante Debug.Log() los
estados de nuestra detección: cuando comienza la aplicación, cuando detecta el
marcador y cuando lo pierde.
Se realizará un script igual a este, activara el GameObject Controlador de
nuestra escena al detectar el marcador ImageTarget Carta. Este GameObject es
declarado con el nombre Comienzo, el cueal es inicializado mediante la función de
búsqueda FindGameObject() indicando entre los parametros de entrada su nombre en
jerarquía. Si el marcador es detectado, este GameObject será activado mediante la
función SetActive(): indicando true para activarla o false para desactivarla. Si un
GameObject no se encuentra activo en escena, aunque se encuentre en jerarquía este
no será mostrado.
62
using System.Collections; using System.Collections.Generic; using UnityEngine; using Vuforia; public class DeteccionTarget: MonoBehaviour, ITrackableEventHandler { TrackableBehaviour mTrackableBehaviour; //Componente de Vuforia (Image Target en este caso) GameObject Comienzo; void Start() { mTrackableBehaviour = GetComponent<TrackableBehaviour>(); if (mTrackableBehaviour) mTrackableBehaviour.RegisterTrackableEventHandler(this); } public void OnTrackableStateChanged(TrackableBehaviour.Status previousStatus, TrackableBehaviour.Status newStatus) { if (newStatus == TrackableBehaviour.Status.DETECTED || newStatus == TrackableBehaviour.Status.TRACKED || newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED) { Debug.Log("Detecta Carta"); Comienzo.SetActive(true); ; } else if (previousStatus == TrackableBehaviour.Status.TRACKED && newStatus == TrackableBehaviour.Status.NO_POSE) { Debug.Log("Pierde Carta"); } else { Debug.Log("Comienza Carta"); Comienzo = GameObject.Find("Controlador"); Comienzo.SetActive(false); } } }
En el caso del ImageTarget Mantel, se creará un script igual a este, con la función
de dejarlo inactivo en el comienzo de la aplicación, hasta que no se detecte el
ImageTarget Carta.
using System.Collections; using System.Collections.Generic; using UnityEngine; using Vuforia; public class DeteccionTarget2 : MonoBehaviour, ITrackableEventHandler {
63
TrackableBehaviour mTrackableBehaviour; //Componente de Vuforia (Image Target en este caso) public GameObject GOPlato, GOCarrito; void Start() { mTrackableBehaviour = GetComponent<TrackableBehaviour>(); if (mTrackableBehaviour) mTrackableBehaviour.RegisterTrackableEventHandler(this); } public void OnTrackableStateChanged(TrackableBehaviour.Status previousStatus, TrackableBehaviour.Status newStatus) { if (newStatus == TrackableBehaviour.Status.DETECTED || newStatus == TrackableBehaviour.Status.TRACKED || newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED) { Debug.Log("Detecta mantel"); } else if (previousStatus == TrackableBehaviour.Status.TRACKED && newStatus == TrackableBehaviour.Status.NO_POSE) { Debug.Log("Pierde Mantel"); } else { Debug.Log("Comienza Mantel"); GOCarrito.SetActive(false); GOPlato.SetActive(false); } } }
2.2. CartaInicio.cs
Es el encargado de indicar como se tiene que comportar la aplicación al detectar
el target. Estará añadida a los componentes del GameObject Controlador.
Necesitamos añadir los espacios de nombre Unityengine.UI, ya que haremos
uso de clases referentes a la interfaz de usuario como Button o Image. Y también
UnityEngine.SceneManagement, para poder cambiar de una escena a otra. Al comienzo
declararemos todas las variables que necesitamos, tantos las básicas de C# como string
int, etc, como las de Unity, GameObject, Text, Image, etc. Podemos declarar el
GameObject en su totalidad y luego llamar sus componentes, o simplemente llamar un
componente determinado de un GameObject, así mejoraremos el rendimiento del
programa. Si necesitamos el componente texto con Text, el componente imagen con
64
Image, el componente botón con Button, así sucesivamente. Estos los podremos iniciar
mediante código o haciéndolos públicos. Si indicamos que esa variable es pública
podemos acceder a ella mediante el inspector, arrastrando el componente deseado, y
se iniciara automáticamente.
Se hará uso del script ComandosSQL.cs, por lo que declararemos una variable
de ese tipo para así poder acceder a sus funciones públicas mediante este.
En la función Star() nos llamará a las funciones Inicializar() e Inicio(). En la
primera función se encargará de inicializar las variables que no hemos hecho públicas,
y las cargaremos con la función Resource.Load(). Para que puedan ser ejecutadas, los
archivos tienen que estar en la carpeta Resources.. En Inicio () nos desactivará todos
los gameObject que están en escena, para así ir activándolos según corresponda. En
ella haremos llamada a una co-rutina cada x tiempo y sobre un contenido cada vez. El
contenido será la Mask1, Mask2, Mask3, Mask4 y MaskGrande. De este modo, cada
vez que sea activada una mascará, se ejecutará su animación, y se verán mostradas en
cadena.
Las co-rutinas se utilizan para que la función sea llamada según el tiempo
indicado en sus parámetros de entrada. Primero tiene que ser creada una función
IEnumerator Nombre (variables). Para ser llamada StartCoroutine(Nombre(variables)).
En la función de la co-rutina tendremos que indicar mediante el siguiente código el
tiempo que tiene que esperar para que esta se ejecute yield return new
WaitForSeconds((tiempo en float );
La función MostrarAp() será la encargada de mostrar el plato que se seleccione.
Esta función será asociada a cada botón de Aperitivos en el On click de cada uno de
ellos, e indicando a su vez el número de parámetro de entrada. Esta hará llamamiento
al código ComandosSQL, que accederá a la base de datos SQLite para devolver los
parámetros asociados a ese plato. En caso de ser el Aperitivo número 1 (es decir
Provolone) se activará el botón de video, ya que a este plato dispone de un video.
También se llamará a la función MostrarP(). La función MostrarTapa() y
MostrarEnsalada() estará formada de la misma forma.
65
Figura 3.16. Añadir función a On Click() de un botón.
MostrarP() será la función encargada de acceder al contenido del Mantel, es
decir, el otro ImageTarget. Según el botón de plato que seleccionemos, esta función
llamará al mantel e indicará que plato tiene que mostrar en 2D. De esta forma activa el
GameObject y llama al script MantelScript.
AddCarrito() esta función será asignada al On Click() del botón Pedir Plato. Así
el usuario al hacer seleccionar este botón, se le llamará al script ComandosSQL y se
accederá a la base de datos, donde se añadirá el plato seleccionado al carrito.
AbrirWEB() y AbrirFace() serán las funciones asignadas al On Click() de los
botones WEB y Facebook. Estas mediante la función Application.OpenURL() nos abrirá
la dirección indicada, que en este caso será la web del restaurante y el enlace asociado
a la página de facebook del mismo.
Opinar() será la función añadida a los botones estrellas y al botón Opinar, para
así poder dejar una opinión respecto al plato que esté mostrado. Creará un GameObject
siguiendo la plantilla del prefab CanvasOpinión, que estará guardado en la carpeta
Resource en Prefabs.
HacerPedido() función asignada al On Click() del botón Hacer Pedido. Cargará
la escena EnviarPedido. Hará uso de la función SceneManager.LoadScene() indicando
el nombre de la escena a la cual queremos llamar. Tenemos que tener estas escenas
añadidas en el Build Settings, si no, no podrán ser invocadas.
MostrarCarrito() función asignada al On Click() del botón carrito. Hará uso del
script ComandosSQL y nos mostrará los registros que contiene la tabla Carrito en
nuestra base de datos.
CambiarIdiomaIng() y CambiarIdiomaEsp() ambas funciones serán añadidas al
On Click de los botones para cambiar el idioma de la aplicación, inglés o español
respectivamente. Estos cargaran las escenas correspondientes al idioma, en la cual los
botones y textos se encuentran en el idioma seleccionado. En este y otro códigos
siempre se preguntará en que escena se está activa m_Scene =
SceneManager.GetActiveScene(). De esta forma, si la aplicación se encuentra en la
escena CartaAR, el lenguaje que se mostrara es en español, y se ejecutara los
contenidos indicados para este idioma. En cambio, si se encuentra en la escena
CartaIng, se ejecutarán los contenidos correspondientes en inglés.
66
Update() es una función que está en continuo llamamiento. La usaremos para
que siempre se actualice el número de platos que tenemos en el carrito. Para ellos
accederá al texto que esta sobre el carro, mostrando así siempre esta cantidad
actualizada.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class CartaInicio : MonoBehaviour { public GameObject[] Contenido; public GameObject GOOpinion, BVideo, MantelPlato, MantelCarrito; public Text carritoN; public int numeroplato; public string menuplato; public Image ImagePlato; Sprite[] Faperitivos, Ftapas, Fensaladas; GameObject pref; Scene m_Scene; ComandosSQL sql; void Start() { Inicializar(); Inicio(); } public void Inicializar() { m_Scene = SceneManager.GetActiveScene(); Faperitivos = Resources.LoadAll<Sprite>("Image/FotoAperitivos"); Fensaladas = Resources.LoadAll<Sprite>("Image/FotoEnsaladas"); Ftapas = Resources.LoadAll<Sprite>("Image/FotosTapas"); sql = GetComponent<ComandosSQL>(); } public void Inicio() { MantelPlato.SetActive(false); MantelCarrito.SetActive(false); for (int i = 0; i < Contenido.Length; i++) { Contenido[i].SetActive(false); } for (int i = 0; i < Contenido.Length; i++) { int tiempo = i + 1; StartCoroutine(AnimacionesInicio(Contenido[i], tiempo)); }
67
} public void MostrarAp(int num) { if (num == 1) BVideo.SetActive(true); else BVideo.SetActive(false); numeroplato = num; menuplato = "Aperitivos"; if (m_Scene.name.ToString() == "CartaIng") sql.ComandoSelectIng(num.ToString(), menuplato); else sql.ComandoSelect(num.ToString(), menuplato); MostrarP(num, menuplato); ImagePlato.sprite = Faperitivos[num - 1]; } public void MostrarTapa(int num) { numeroplato = num; menuplato = "Tapear"; BVideo.SetActive(false); if (m_Scene.name.ToString() == "CartaIng") sql.ComandoSelectIng(num.ToString(), menuplato); else sql.ComandoSelect(num.ToString(), menuplato); MostrarP(num, menuplato); ImagePlato.sprite = Ftapas[num - 1]; } public void MostrarEnsalada(int num) { numeroplato = num; menuplato = "Ensaladas"; BVideo.SetActive(false); if (m_Scene.name.ToString() == "CartaIng") sql.ComandoSelectIng(num.ToString(), menuplato); else sql.ComandoSelect(num.ToString(),menuplato); MostrarP(num, menuplato); ImagePlato.sprite = Fensaladas[num - 1]; } public void MostrarP(int num,string menu) { MantelCarrito.SetActive(false); MantelPlato.SetActive(true); MantelPlato.GetComponent<MantelScript>().PonerImagen(num, menu); } public void AddCarrito() { if (m_Scene.name.ToString() == "CartaIng") sql.AddPlatoIng(numeroplato, menuplato); else sql.AddPlato(numeroplato, menuplato); } public void AbrirWeb() { Application.OpenURL("http://www.pizzeriaalgusto.es/"); } public void AbrirFace() { Application.OpenURL("https://www.facebook.com/pizzeriaalgustotapas/"); }
68
public void Opinar() { if (m_Scene.name.ToString() == "CartaIng") { pref = Resources.Load("Prefabs/Ing/CanvasOpinionIng") as GameObject; } else { pref = Resources.Load("Prefabs/CanvasOpinion") as GameObject; } GameObject PrefabOpinion = (GameObject)Instantiate(pref); } public void HacerPedido() { if (m_Scene.name.ToString() == "CartaIng") SceneManager.LoadScene("EnviarPedidoIng"); else SceneManager.LoadScene("EnviarPedido"); } IEnumerator AnimacionesInicio(GameObject TempGO,int num) //corrutina { yield return new WaitForSeconds((float)num); TempGO.SetActive(true); MostrarAp(1); } public void MostrarCarrito() { MantelCarrito.SetActive(true); sql.MostrarCarrito(); } public void CambiarIdiomaIng() { SceneManager.LoadScene("CartaIng"); } public void CambiarIdiomaEsp() { SceneManager.LoadScene("CartaAR"); } void Update() { carritoN.text = sql.NumeroPlatos().ToString(); } }
El script estará añadido como una componente al GameObject Controlador, y
este se verá en el inspector como en la Figura 3.16. Donde se ven las variables que
hemos declarado públicas, y los GameObject o componentes que hemos introducido en
ellas.
69
Figura 3.17. Componente Script CartaInicio.
2.3. ComandosSQL.cs
Este script se encarga del acceso a la base de datos SQLite, en el cual se leerá
los registros de las tablas Aperitivos, Tapas y Ensaladas, que contendrán la información
de cada plato. Y añadirá nuevos registros a las tablas Carrito y Opinión.
Se hará uso de nuevos espacios de nombre. System.Data, que contiene clases
de arquitectura acceso a datos de ADO.NET. La arquitectura ADO.NET permite
construir componentes que pueden gestionar datos de varias fuentes en modo
desconectado o conectado. System.IO contiene clases que pueden leer y escribir flujos
de datos y archivos de disco. Las clases contenidas en este espacio de nombre pueden
gestionar la entrada y salida de archivos sincrónica y asincrónica. Mono.Data.Sqlite
contiene un proveedor de datos ADO.NET para el motor de base de datos SQLite.
Primero se declararán las variables. Entre estas también se encontrarán las
encargadas de la lectura de la base de datos. Crearemos tres string para almacenar el
nombre de la base de datos, la ruta de conexión y ruta de la base de datos. El string del
nombre de la base es inicializado indicando el nombre de la base de datos a acceder
con su extensión. A continuación, se declaran otras tres variables de la clase
System.Data: cadena de conexión, la conexión y las cadenas de texto que
implementarán las cuatro operaciones a realizar sobre la tabla. Nótese que lanzaremos
consultas directamente sobre la base de datos a través de ADO.NET.
AbrirDB() Crea y abre la conexión, para la lectura de la base de datos. Se crea
la ruta de DB indicando que la base de datos se encuentra en StreamingAssets. URI-
70
file se encargará de los archivos mediante la ejecución de la aplicación, en este caso la
base de datas. Tendremos una serie de condiciones de ruta, ya que dependiendo en
que plataforma nos encontremos esta será de una u otra forma. CerrarDB() cierra estas
conexiones.
ComandoSelect() y ComandoSelectIng() Funciones que se encargaran de
seleccionar los datos solicitados. Estos datos serán introducidos en los parametros de
PaginaDos. Para ello crearemos el comando SQLite para acceder a la tabla, en este
caso el comando SELECT, y todo ello lo pasaremos a string para hacer la consulta. Con
el asterisco indicamos que queremos ver todos los datos. Con FROM indicamos el
nombre de la tabla, que en este caso puede ser tanto Ensaladas, Aperitivos o Tapas,
siendo así un valor de entrada en la función. El comando WHERE seleccionara el
registro con el ID indicada. En nuestra base de datos cada plato tiene una Id para
identificarlo, así sabremos que plato se tiene que mostrar. Una vez encontrado el plato
deseado, con while (leerdatos.Read()), se hará un recorrido de todos los registros de la
fila con la id indicada. Dentro de éste accederemos a PaginaDos, que tendrá un script
llamado PlatoScript en sus componentes. Mediante GetComponent accederemos a este
script y le asignaremos los valores leídos, este ya se encargará de acceder a la interfaz
de usuario y modificarlos, según los valores enviados. Try y catch se encargará de
imprimir por consola los posibles errores que haya mientras la consulta, para que así no
se nos pare la aplicación. La función ComandoSelectIng() cumple con la misma función
descrita, pero en este caso mostrará los nombres e ingredientes en inglés (si se ejecuta
desde la escena CartaIng, que es la seleccionada para mostrar todo en inglés). Esta
información se encontrará en las dos últimas columnas de la tabla.
ObtenerCarrito() éste al igual que la función anterior, accederá a la base de
datos, pero en este caso a la tabla Carrito leyendo todos los registros que la compone,
donde se encuentran los platos que el usuario ha ido añadiendo. Estos la ira añadiendo
a una lista que creamos al inicio de la clase llamada miCarrito. Los valores que se
añaden a esta lista, Plato, es una clase creada para poder acceder a los valores de cada
uno de los platos añadidos.
DimePrecio() devuelve el precio total de los platos, consultando a la lista.
Mostrarcarrito() es el encargado de mostrar el carro en el Mantel. Primero
eliminamos todos los posibles platos ya creados, para que así al consultarlo no nos
aparezcan platos duplicados. Estos GameObject los hemos llamado con un Tag Platos,
para así poder identificarlos. En los siguientes comandos, se buscará el Prefab Plato
que ya hicimos con anterioridad, y se le añadirá sus componentes en función del plato
71
que sea. Para añadir los componentes estos tendrán que tener el script CarritoScript.
También tendremos que saber dónde se copiaran las copias del prefab, por ello
hacemos la búsqueda de su transform, así se le asignara esa transform a cada una de
las copias.
NúmeroPlatos() devuelve el número de platos que tiene el carrito, consultando a
la lista.
AddCarrito() accede a la base de datos, y añade un valor a la tabla carrito, con
los valores de entrada indicados.
AddPlato() y AddPlatoIng() este en función del plato indicado, accederá a la base
de datos y leerá los datos correspondientes para introducir a la función AddCarrito() y
de esta manera poder añadir el plato indicado con estos valores pedidos.
AddCarritoIng() cumplirá la misma función, pero en este caso añadirá el nombre en
inglés.
DELETE() accede a la base de datos, a la tabla Carrito, pero en este caso se
eliminara el registro indicado según su ID con el comando DELETE.
AddNota() es el encargado de acceder a la base de datos, a la tabla Carrito y
añadir una nota al registro indicado. Se usará el comando UPDATE para actualizar ya
datos asignados. Este modificara el valodel registro Nota.
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using System.Data; using System.IO; using Mono.Data.Sqlite; public class ComandosSQL : MonoBehaviour { string rutaDB; string conexion; string DBFileName = "Menu.db"; IDbConnection conexionDB; IDbCommand comandosDB; IDataReader leerDatos; public GameObject producto; public GameObject[] productos; string nombre; int IdCarrito;
72
double precio; Transform productopadre; GameObject plato; List<Plato> miCarrito = new List<Plato>(); public void AbrirDB() { //creamos la conexion if (Application.platform == RuntimePlatform.WindowsEditor) { rutaDB = Application.dataPath + "/StreamingAssets/" + DBFileName; } else if (Application.platform == RuntimePlatform.Android) { rutaDB = Application.persistentDataPath + "/" + DBFileName; // ruta para plataforma android } if (!File.Exists(rutaDB)) { WWW loadDB = new WWW("jar:file://" + Application.dataPath + "!/assets/" + DBFileName); while(!loadDB.isDone) { } File.WriteAllBytes(rutaDB, loadDB.bytes); } conexion = "URI=file:" + rutaDB; //ruta para cargar la base de datos conexionDB = new SqliteConnection(conexion); conexionDB.Open(); } public void ComandoSelect(string num, string menu) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from " + menu + " where Id = " + num; comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); plato = GameObject.Find("PaginaDos"); while (leerDatos.Read()) { try { plato.GetComponent<PlatoScript>().PonerPlato(leerDatos.GetString(1), leerDatos.GetString(2), leerDatos.GetDouble(3), Convert.ToString((leerDatos["Alergeno1"])), Convert.ToString((leerDatos["Alergeno2"])), Convert.ToString((leerDatos["Alergeno3"]))); PlayerPrefs.SetString("PlatoN",Convert.ToString(leerDatos["Nombre"])); } catch (FormatException fe) { Debug.Log(fe.Message); continue; } catch (Exception e)
73
{ Debug.Log(e.Message); continue; } } CerrarDB(); } public void ComandoSelectIng(string num, string menu) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from " + menu + " where Id = " + num; comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); plato = GameObject.Find("PaginaDos"); while (leerDatos.Read()) { try { plato.GetComponent<PlatoScript>().PonerPlato(leerDatos.GetString(1), leerDatos.GetString(8), leerDatos.GetDouble(3), Convert.ToString((leerDatos["Alergeno1"])), Convert.ToString((leerDatos["Alergeno2"])), Convert.ToString((leerDatos["Alergeno3"])), leerDatos.GetString(7)); PlayerPrefs.SetString("PlatoN", Convert.ToString(leerDatos["Nombre"])); } catch (FormatException fe) { Debug.Log(fe.Message); continue; } catch (Exception e) { Debug.Log(e.Message); continue; } } CerrarDB(); } public void ObtenerCarrito() { miCarrito.Clear(); AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from Carrito"; comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); while (leerDatos.Read()) { miCarrito.Add(new Plato(leerDatos.GetInt32(0),leerDatos.GetInt32(1), leerDatos.GetString(2), leerDatos.GetString(3), leerDatos.GetDouble(4))); } CerrarDB(); } public double DimePrecio() {
74
ObtenerCarrito(); double precio = 0.00; for (int i = 0; i < miCarrito.Count; i++) { precio += miCarrito[i].DimePrecio(); } return precio; } public void MostrarCarrito() { GameObject[] productos = GameObject.FindGameObjectsWithTag("Platos"); if (productos.Length != 0) { for (int i = 0; i < productos.Length; i++) { Destroy(productos[i]); } } producto = Resources.Load("Prefabs/Plato") as GameObject; productopadre = GameObject.Find("Platos").GetComponent<Transform>(); ObtenerCarrito(); for (int i = 0; i < miCarrito.Count; i++) { GameObject tempPrefab = (GameObject) Instantiate(producto,productopadre); tempPrefab.GetComponent<CarritoScript>().Carrito(miCarrito[i].DimeId(), miCarrito[i].DimeNumero(), miCarrito[i].DimeTipo(), miCarrito[i].DimeNombre(), miCarrito[i].DimePrecio()); tempPrefab.SetActive(true); } } public int NumeroPlatos() { ObtenerCarrito(); return miCarrito.Count; } void CerrarDB() { leerDatos.Close(); leerDatos = null; comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } public void AddCarrito(int num,string tipo, string nombre, double precio) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = String.Format("insert into Carrito(Numero,Tipo,Nombre,Precio) values(\"{0}\",\"{1}\",\"{2}\",\"{3}\")", num,tipo,nombre,precio); comandosDB.CommandText = sqlQuery; comandosDB.ExecuteScalar(); comandosDB.Dispose();
75
comandosDB = null; conexionDB.Close(); conexionDB = null; } public void AddPlato(int num,string menu) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from " + menu + " where Id = " + num.ToString(); comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); while (leerDatos.Read()) { IdCarrito = leerDatos.GetInt32(0); nombre = leerDatos.GetString(1); precio = leerDatos.GetDouble(3); } AddCarrito(IdCarrito, menu, nombre, precio); CerrarDB(); } public void AddPlatoIng(int num, string menu) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from " + menu + " where Id = " + num.ToString(); comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); while (leerDatos.Read()) { IdCarrito = leerDatos.GetInt32(0); nombre = leerDatos.GetString(7); precio = leerDatos.GetDouble(3); } AddCarrito(IdCarrito, menu, nombre, precio); CerrarDB(); } public void DELETE(string dato) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "delete from Carrito where Id = "+ dato; comandosDB.CommandText = sqlQuery; comandosDB.ExecuteScalar(); comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } public void AddNota(string num, string nota) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "update Carrito set Nota = '" + nota + "' where Id =" + num; comandosDB.CommandText = sqlQuery;
76
comandosDB.ExecuteScalar(); comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } }
2.4. PlatoScript.cs
Este script tendrá la función de ser un componente de PaginaDos. Para ello
tendrá que estar añadido en sus componentes en este GameObject. Sera el encargado
de indicar que componentes serán modificados y mostrados en la interfaz según los
datos con los que le sea llamado.
PonerPlato() es la función principal desde la que se accede mediante el anterior
código (ComandosSQL.cs), con ella se modificara los parámetros de las variables
declaradas en función de los valores introducidos con los que se llama la función.
PonerAlergeno() indicará según el alérgeno de entrada, que Sprite le
corresponde, para que así la función principal sepa que soporte tiene que asignar a las
imágenes de alérgenos.
PonerEstrellas() actualizará las estrellas que se mostrarán en función del plato
seleccionado. Para ello accederá a otro script que abrirá la base de datos y la tabla
Opiniones, y con ello calculará el valor correspondiente a cada plato, mostrando el
correspondiente en cada caso.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class PlatoScript : MonoBehaviour { public GameObject[] ImagenAlergenos; Sprite imagenA; public Image FotoPlato; Image spriteAlergeno1, spriteAlergeno2, spriteAlergeno3; public Text textNombre, textIngr, textPrecio; Sprite[] alergenos; double valorTOp;
77
public Image estrellasI; Sprite[] estrellasCL; public void PonerPlato(string nombre, string ingredientes, double precio, string alergeno1, string alergeno2, string alergeno3) { if ((alergeno1 == "")) { ImagenAlergenos[0].SetActive(false); ImagenAlergenos[1].SetActive(false); ImagenAlergenos[2].SetActive(false); Debug.Log("Sin alergeno"); } if ((alergeno1 != "") && (alergeno2 == "")) { ImagenAlergenos[0].SetActive(true); ImagenAlergenos[1].SetActive(false); ImagenAlergenos[2].SetActive(false); spriteAlergeno1 = ImagenAlergenos[0].GetComponent<Image>(); PonerAlergenos(alergeno1); this.spriteAlergeno1.sprite = imagenA; } if ((alergeno2 != "") && (alergeno3 == "")) { ImagenAlergenos[0].SetActive(true); ImagenAlergenos[1].SetActive(true); ImagenAlergenos[2].SetActive(false); spriteAlergeno1 = ImagenAlergenos[0].GetComponent<Image>(); spriteAlergeno2 = ImagenAlergenos[1].GetComponent<Image>(); PonerAlergenos(alergeno1); this.spriteAlergeno1.sprite = imagenA; PonerAlergenos(alergeno2); this.spriteAlergeno2.sprite = imagenA; } if (alergeno3 != "") { ImagenAlergenos[0].SetActive(true); ImagenAlergenos[1].SetActive(true); ImagenAlergenos[2].SetActive(true); spriteAlergeno1 = ImagenAlergenos[0].GetComponent<Image>(); spriteAlergeno2 = ImagenAlergenos[1].GetComponent<Image>(); spriteAlergeno3 = ImagenAlergenos[2].GetComponent<Image>(); PonerAlergenos(alergeno1); this.spriteAlergeno1.sprite = imagenA; PonerAlergenos(alergeno2); this.spriteAlergeno2.sprite = imagenA; PonerAlergenos(alergeno3); this.spriteAlergeno3.sprite = imagenA; } this.textNombre.text = nombre; this.textIngr.text = ingredientes; this.textPrecio.text = precio.ToString(); PonerEstrellas(nombre); PlayerPrefs.SetString("PlatoN", nombre); }
78
public void PonerPlato(string nombre, string ingredientes, double precio, string alergeno1, string alergeno2, string alergeno3, string nombre2) { if ((alergeno1 == "")) { ImagenAlergenos[0].SetActive(false); ImagenAlergenos[1].SetActive(false); ImagenAlergenos[2].SetActive(false); Debug.Log("Sin alergeno"); } if ((alergeno1 != "") && (alergeno2 == "")) { ImagenAlergenos[0].SetActive(true); ImagenAlergenos[1].SetActive(false); ImagenAlergenos[2].SetActive(false); spriteAlergeno1 = ImagenAlergenos[0].GetComponent<Image>(); PonerAlergenos(alergeno1); this.spriteAlergeno1.sprite = imagenA; } if ((alergeno2 != "") && (alergeno3 == "")) { ImagenAlergenos[0].SetActive(true); ImagenAlergenos[1].SetActive(true); ImagenAlergenos[2].SetActive(false); spriteAlergeno1 = ImagenAlergenos[0].GetComponent<Image>(); spriteAlergeno2 = ImagenAlergenos[1].GetComponent<Image>(); PonerAlergenos(alergeno1); this.spriteAlergeno1.sprite = imagenA; PonerAlergenos(alergeno2); this.spriteAlergeno2.sprite = imagenA; } if (alergeno3 != "") { ImagenAlergenos[0].SetActive(true); ImagenAlergenos[1].SetActive(true); ImagenAlergenos[2].SetActive(true); spriteAlergeno1 = ImagenAlergenos[0].GetComponent<Image>(); spriteAlergeno2 = ImagenAlergenos[1].GetComponent<Image>(); spriteAlergeno3 = ImagenAlergenos[2].GetComponent<Image>(); PonerAlergenos(alergeno1); this.spriteAlergeno1.sprite = imagenA; PonerAlergenos(alergeno2); this.spriteAlergeno2.sprite = imagenA; PonerAlergenos(alergeno3); this.spriteAlergeno3.sprite = imagenA; } this.textNombre.text = nombre2; this.textIngr.text = ingredientes; this.textPrecio.text = precio.ToString(); PonerEstrellas(nombre); PlayerPrefs.SetString("PlatoN", nombre); } private void PonerAlergenos(string alergeno) { alergenos = Resources.LoadAll<Sprite>("UI/AlergenosImagen");
79
switch (alergeno) { case "Leche": imagenA = alergenos[10]; break; case "Huevo": imagenA = alergenos[6]; break; case "Gluten": imagenA = alergenos[2]; break; case "Crustaceos": imagenA = alergenos[8]; break; case "Pescado": imagenA = alergenos[7]; break; case "Cacahuetes": imagenA = alergenos[5]; break; case "Soja": imagenA = alergenos[11]; break; case "Frutos Secos": imagenA = alergenos[4]; break; case "Apio": imagenA = alergenos[12]; break; case "Mostaza": imagenA = alergenos[0]; break; case "Sesamo": imagenA = alergenos[1]; break; case "Sulfitos": imagenA = alergenos[13]; break; case "Altramuces": imagenA = alergenos[3]; break; case "Moluscos": imagenA = alergenos[9]; break; default: imagenA = alergenos[0]; break; } } public void PonerEstrellas(string nombre) { OpinionDB DBOpinion = new OpinionDB(); valorTOp = DBOpinion.CalcularOpinionT(nombre); estrellasCL = Resources.LoadAll<Sprite>("UI/calif"); Debug.Log(valorTOp); if (valorTOp > 4.5) estrellasI.sprite = estrellasCL[0]; // cinco estrellas else if ((valorTOp <= 4.5) && (valorTOp > 3.5)) estrellasI.sprite = estrellasCL[1]; // cuatro estrellas else if ((valorTOp <= 3.5) && (valorTOp > 2.5))
80
estrellasI.sprite = estrellasCL[2]; else if ((valorTOp <= 2.5) && (valorTOp > 1.5)) estrellasI.sprite = estrellasCL[3]; else if ((valorTOp <= 1.5) && (valorTOp > 0.5)) estrellasI.sprite = estrellasCL[4]; else estrellasI.sprite = estrellasCL[5]; } }
Estes script estará añadido como una componente del GameObject PaginaDos.
Esta componente se verá en el inspector como en la Figura 3.17.
Figura 3.18. Componente PlatoScript
2.5. Plato.cs
Este script no derivará de la clase MonoBehaviour. Sera creado para crear una
clase añadida a la lista y acceder a los valores correspondientes de cada una.
using System.Collections; using System.Collections.Generic; public class Plato { public int Id { get; set; } public int Número { get; set; } public string Tipo { get; set; } public string Nombre { get; set; } public double Precio { get; set; } public Plato(int id,int número, string tipo,string nombre, double precio) { this.Nombre = nombre; this.Precio = precio; this.Id = id; this.Número = número; this.Tipo = tipo; }
81
public int DimeId() { return Id; } public int DimeNúmero() { return Número; } public string DimeTipo() { return Tipo; } public string DimeNombre() { return Nombre; } public double DimePrecio() { return Precio; } }
2.6. CambiarFoto.cs
Este script se encargará de añadir las funciones a los botones encargados de
mostrar más fotos al usuario del mismo plato.
MostrarSig() y Mostraránt() serán las funciones que añadiremos al On Click() de
los botónes encargados de mostrar estas fotos.
CargarSprite() se encarga de buscar en Resources estas fotos e iniciarlas en los
valores declarados al inicio. En función de las fotos que tengamos este las cargará en
un array de Sprite.
DeclararVariables() lo usaremos para tener acceso a CartaInicio y poder obtener
el valor correspondiente a las variables indicadas. Así podemos saber que plato está
seleccionado en ese momento y poder seleccionar sus fotos en concreto.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class CambiarFoto : MonoBehaviour { public Image ImagePlato; public GameObject botónSig, botónAnt; string MenuPlato; int NumPlato;
82
Sprite[] Faperitivos, Ftapas, Fensaladas, Faperitivos2, Ftapas2, Fensaladas2; public void MostrarSig() { CargarSprite(); DeclararVariables(); switch (MenuPlato) { case "Aperitivos": ImagePlato.sprite = Faperitivos2[NumPlato - 1]; break; case "Tapear": ImagePlato.sprite = Ftapas2[NumPlato - 1]; break; case "Ensaladas": ImagePlato.sprite = Fensaladas2[NumPlato - 1]; break; } botónAnt.SetActive(true); botónSig.SetActive(false); } public void Mostraránt() { CargarSprite(); DeclararVariables(); switch (MenuPlato) { case "Aperitivos": ImagePlato.sprite = Faperitivos[NumPlato - 1]; break; case "Tapear": ImagePlato.sprite = Ftapas[NumPlato - 1]; break; case "Ensaladas": ImagePlato.sprite = Fensaladas[NumPlato - 1]; break; } botónAnt.SetActive(false); botónSig.SetActive(true); } public void CargarSprite() { Faperitivos2 = Resources.LoadAll<Sprite>("Image/FotosAperitivos2"); Fensaladas2 = Resources.LoadAll<Sprite>("Image/FotosEnsaladas2"); Ftapas2 = Resources.LoadAll<Sprite>("Image/FotosTapas2"); Faperitivos = Resources.LoadAll<Sprite>("Image/FotoAperitivos"); Fensaladas = Resources.LoadAll<Sprite>("Image/FotoEnsaladas"); Ftapas = Resources.LoadAll<Sprite>("Image/FotosTapas"); } public void DeclararVariables() { CartaInicio Variable = GetComponent<CartaInicio>(); NumPlato = Variable.númeroplato; MenuPlato = Variable.menuplato; } }
Este script estará añadido como una componente del GameObject Controlador,
y su vista en el inspector será como la Figura 3.18.
83
Figura 3.19. Componente CambiarFoto.
2.7. CarritoControler.cs
Este script se encargará indicar como se mostraran los valores de los platos
añadidos al pedido. Este accederá a las interfaces texto para mostrar el número de
platos y precio actualizado en todo momento.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class CarritoControler : MonoBehaviour { public Text textNúmeroT, textPrecio; ComandosSQL sql = new ComandosSQL(); void Update() { textPrecio.text = sql.DimePrecio().ToString(); textNúmeroT.text = sql.NúmeroPlatos().ToString(); } }
Este script estará añadido como una componente del GameObject CarritoM, se
verá en el inspector como indica la Figura 3.19
Figura 3.20. Componente Carrito Controler.
2.8. CarritoScritpt.cs
84
Cuando se muestra el carrito, se carga cada prefab Plato, y desde
ComandosSQL llama a CarritoScript para modificar sus valores.
Carrito() modifica las variables declaradas, y en función de las variables con las
que se llama la función, esta las modifica. Como el nombre, la imagen y precio.
PonerImagen() indica a Carrito que imagen es la que le corresponde a ese plato,
para que esta la inicialice.
Eliminar() y AddNota() añaden la función correspondiente a los botones eliminar
y añadir nota que vienen con cada plato del carro. Para que así el usuario al hacer click
sobre ello se ejecute la función correspondiente. En AddNota se creará un prefab ya
guardado en Resources, llamado CanvasNotas. El cuál se encargará de proporcionar al
usuario que añada una nota sobre el plato correspondiente. Eliminar() eliminara el plato
seleccionado en el carrito.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class CarritoScript : MonoBehaviour { public Text Nombre, Precio; public Image Imgen; public Button Boton, Boton2; public GameObject[] productos; Sprite imagenP; Sprite[] aperitivos, ensaladas, tapas; GameObject PrefabNota, pref; ComandosSQL sql; NotaScript MiNota; int IdCarrito; Scene m_Scene; public void Carrito(int id, int num, string tipo, string nombre,double precio) { aperitivos = Resources.LoadAll<Sprite>("Image/Aperitivos"); ensaladas = Resources.LoadAll<Sprite>("Image/Ensaladas"); tapas = Resources.LoadAll<Sprite>("Image/Tapas"); m_Scene = SceneManager.GetActiveScene(); this.IdCarrito = id; this.Nombre.text = nombre; this.Precio.text = precio.ToString();
85
PonerImagen(num, tipo); this.Imgen.sprite = imagenP; Boton.onClick.AddListener(Eliminar); if (m_Scene.name.ToString() == "CartaIng") { Boton2.onClick.AddListener(AddNotaIng); } else { Boton2.onClick.AddListener(AddNota); } PlayerPrefs.SetInt("Id", IdCarrito); } public void PonerImagen(int num, string tipo) { switch (tipo) { case "Aperitivos": imagenP = aperitivos[num - 1]; break; case "Tapear": imagenP = tapas[num - 1]; break; case "Ensaladas": imagenP = ensaladas[num - 1]; break; } } public void Eliminar() { sql = new ComandosSQL(); sql.DELETE(IdCarrito.ToString()); sql.MostrarCarrito(); } public void AddNota() { pref = Resources.Load("Prefabs/CanvasNotas") as GameObject; PrefabNota = (GameObject)Instantiate(pref); } public void AddNotaIng() { pref = Resources.Load("Prefabs/Ing/CanvasNotasIng") as GameObject; PrefabNota = (GameObject)Instantiate(pref); } }
Este script estará añadido como una componente del prefab Plato, se verá en el
inspector como muestra la siguiente Figura 3.20.
86
Figura 3.21. Componente CarritoScript.
2.9. ErrorScript.cs
Cuando en Opiniones intentamos introducir un valor no indicado, se ejecutará
este error. Este script controla el comportamiento de esta ventana de error
BotónOK() es la función que se le asignará al On Click() del botón OK del error
que se creará. Así cuando el usuario da a Ok, este gameobject se eliminará.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript : MonoBehaviour { public GameObject Error; public void BotónOK() { Destroy(Error); } }
Este script estará añadido como una componente del prefab error, se verá en el
inspector como muestra la Figura 3.21.
Figura 3.22. Componente ErrorScript.
2.10. MantelScript.cs
Se encargará del comportamiento del Mantel. Este según el plato seleccionado,
nos mostrará el plato en 2D, simulando que ya se encuentra en mesa.
PonerImagen() esta será llamada desde CartaInicio, y según los valores
introducidos se mostrará una u otra imagen. Las imágenes serán cargadas desde
Resources, con su correspondiente Sprite.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class MantelScript : MonoBehaviour
87
{ public Image Plato; Sprite[] aperitivos, tapas, ensaladas; public void PonerImagen(int num, string tipo) { aperitivos = Resources.LoadAll<Sprite>("Image/Aperitivos"); ensaladas = Resources.LoadAll<Sprite>("Image/Ensaladas"); tapas = Resources.LoadAll<Sprite>("Image/Tapas"); switch (tipo) { case "Aperitivos": Plato.sprite = aperitivos[num - 1]; break; case "Tapear": Plato.sprite = tapas[num - 1]; break; case "Ensaladas": Plato.sprite = ensaladas[num - 1]; break; } } }
Este script estará añadido como una componente del GameObject PlatoM, y se
verá en el inspector como muestra la Figura 3.22.
Figura 3.23. Componente Mantel Script.
2.11. MostrarVideo.cs
Se encarga del comportamiento del video cuando éste es mostrado. Una vez
que se llama se mostrará el video indicado. Los videos los añadiremos desde el
inspector, y el script será añadido al plano donde se mostrará el video.
OnDisable() es una función que ejecutará los comandos contenidos cuando el
GameObject está desactivado. En este caso este script estará añadido al botón Play. Si
el botón está desactivado, el plano se desactivará automáticamente. Si es activado, el
video se verá en la ventana de inspector tal y como se muestra en la Figura 3.23. Asi se
indicara a que platos tienen videos, activandole al selecionarlos este botón.
using System.Collections; using System.Collections.Generic;
88
using UnityEngine; using UnityEngine.Video; using UnityEngine.UI; public class MostrarVideo : MonoBehaviour { public VideoClip Video; public GameObject Plano; VideoPlayer miVideo; public void Mostrar() { Plano.SetActive(true); miVideo = Plano.GetComponent<VideoPlayer>(); miVideo.clip = Video; miVideo.Play(); } void OnDisable() { Plano.SetActive(false); } }
Figura 3.24. Componente Mostrar video.
2.12. NotaScript.cs
Indicará como se comportará el CanvasNota. La función EnviarNota() se le
añadirá al On Click() del botón Enviar Nota. De esta forma al pulsar el usuario el botón,
añadirá el valor del InputField al registro Nota de la ID correspondiente. Para acceder a
este valor tendrá que ir seguido con la extensión text. La función Volver() será añadido
al On Click() del botón Volver, para que en el caso de que el usuario no quiera añadir la
nota, pueda volver al inicio. Esta eliminara este GameObject, para que no se visualice
en pantalla.
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class NotaScript : MonoBehaviour {
89
int IdCarrito; public InputField miNota; public GameObject GONota; public void EnviarNota() { IdCarrito = PlayerPrefs.GetInt("Id"); ComandosSQL sql = new ComandosSQL(); sql.AddNota(IdCarrito.ToString(), miNota.text); Destroy(GONota); } public void Volver() { Destroy(GONota); } }
Este script estará añadido como una componente del prefab CanvasNota, y se
verá en el inspector como muestra la figura 3.24.
Figura 3.25. Componente Nota Script.
2.13. OpiniónControler.cs
Se encarga de decir cómo tiene que comportarse el CanvasOpinión. Este será
un prefab cargado a la escena cuando el usuario quiere añadir una opinión al plato
seleccionado. Este script será añadido como una componente a este gameObject.
InicioOp() indicará que gameobject se mostrarán al iniciarse este prefab.
También se obtendrá el nombre del plato ejecutado mediante la función PlayerPrefs. Se
llamará a la función MonstrarOpinión().
MostrarOpinión() accederá al script OpiniónDB para obtener de la tabla de datos,
de la tabla de Opiniones, todas las opiniones correspondientes a ese plato, y mostrarlas
así por pantalla.
EnviarOpinión() es una función que se le añadirá al On click() del botón Enviar
Opinión. Este se encargará de llamar al script OpiniónDB y añadir la opinión que el
usuario haya introducido mediante Input field. En caso de no introducirse los valores
correctos, se ejecutará el error indicado anteriormente.
90
Volver() función que se le añadirá al On Click() del botón Volver, para que el
usuario pueda volver al inicio. Esta función destruirá el gamebobject para que deje de
verse en escena. A su vez acedera al componente del plato y actualizara las estrellas
con la opinión añadida.
DejarOp() función añadida al On Click() del botón Dejar Opinión, que se mostrará
al inicio cuando se muestran todas las opiniones que se encuentran ya sobre ese plato.
Al hacer click el usuario en este botón, se abrirá la ventana correspondiente para que el
usuario pueda dejar su opinión.
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class OpinionControler : MonoBehaviour { public GameObject MostrarOP, AddOP, GOOpinion; public InputField usuario, valorOp, comentarioOp; OpinionDB DBOpinion; GameObject Error, pref,plato; int Mivalor; public string NombrePlato; string Miusuario, Micomentario; void Start() { InicioOp(); } public void InicioOp() { DBOpinion = GetComponent<OpinionDB>(); plato = GameObject.Find("PaginaDos"); MostrarOP.SetActive(true); AddOP.SetActive(false); NombrePlato = PlayerPrefs.GetString("PlatoN"); MostrarOpinion(); } public void MostrarOpinion() { MostrarOP.SetActive(true); AddOP.SetActive(false); DBOpinion.ObtenerOpinion(NombrePlato); } public void EnviarOp() { int num = Convert.ToInt32(valorOp.text); if (usuario.text == "") usuario.text = "Sin nombre";
91
if ((num != 0) && (num != 1) && (num != 2) && (num != 3) && (num != 4) && (num != 5)) { pref = Resources.Load("Prefabs/Error") as GameObject; Error = (GameObject)Instantiate(pref); GOOpinion.SetActive(false); } else { Miusuario = usuario.text; Micomentario = comentarioOp.text; Mivalor = Convert.ToInt32(valorOp.text); Debug.Log(Micomentario); DBOpinion.AddOpinion(NombrePlato, Miusuario, Mivalor, Micomentario); MostrarOpinion(); } } public void EnviarOpIng() { int num = Convert.ToInt32(valorOp.text); if (usuario.text == "") usuario.text = "Unnamed"; if ((num != 0) && (num != 1) && (num != 2) && (num != 3) && (num != 4) && (num != 5)) { pref = Resources.Load("Prefabs/Ing/ErrorIng") as GameObject; Error = (GameObject)Instantiate(pref); GOOpinion.SetActive(false); } else { Miusuario = usuario.text; Micomentario = comentarioOp.text; Mivalor = Convert.ToInt32(valorOp.text); Debug.Log(Micomentario); DBOpinion.AddOpinion(NombrePlato, Miusuario, Mivalor, Micomentario); MostrarOpinion(); } } public void Volver() { Destroy(GOOpinion); plato.GetComponent<PlatoScript>().PonerEstrellas(NombrePlato); } public void DejarOp() { MostrarOP.SetActive(false); AddOP.SetActive(true); } }
Este script estará añadido como una componente del prefab CanvasOpinión y
se verá en el inspector como indica la Figura 3.26.
92
Figura 3.26. Componente Opinión Controler.
2.14. OpiniónDB.cs
Este script abrirá la base de datos igual que el script ComandosSQL. En este
caso solo irán orientados a la tabla opiniones, para no saturar el otro script y mejorar el
rendimiento.
AddOpinión() se encargará de introducir la opinión del usuario en la tabla
Opinión. Indicando el valor introducido, nombre y la opinión correspondiente.
MostrarOpinión() abrirá de nuevo la base de datos, e introducirá cada uno de las
opiniónes que se encuentre respecto a ese plato en un prefab Opinión. Este sus datos
serán modificados y mostrados al usuario mediante el script OpiniónScript.
CalcularOpinión() calcula el valor sobre un plato indicado. Busca en la tabla
Opinión los valores introducidos sobre ese plato y los añade sobre una lista, para si
después poder hacer las operaciones matemáticas correspondientes para calcular su
valor sobre 5.
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using System.Data; using System.IO; using Mono.Data.Sqlite; using UnityEngine.SceneManagement; public class OpinionDB : MonoBehaviour { string rutaDB; string conexion; string DBFileName = "Menu.db"; IDbConnection conexionDB;
93
IDbCommand comandosDB; IDataReader leerDatos; GameObject GOopinion; Transform GOpadre; Scene m_Scene; List<int> valoresT = new List<int>(); public void AbrirDB() { //creamos la conexion if (Application.platform == RuntimePlatform.WindowsEditor) { rutaDB = Application.dataPath + "/StreamingAssets/" + DBFileName; } else if (Application.platform == RuntimePlatform.Android) { rutaDB = Application.persistentDataPath + "/" + DBFileName; // ruta para plataforma android } if (!File.Exists(rutaDB)) { WWW loadDB = new WWW("jar:file://" + Application.dataPath + "!/assets/" + DBFileName); while (!loadDB.isDone) //controlar errores, rellenarlo mas tarde { } File.WriteAllBytes(rutaDB, loadDB.bytes); } conexion = "URI=file:" + rutaDB; //ruta para cargar la base de datos conexionDB = new SqliteConnection(conexion); conexionDB.Open(); } void CerrarDB() { leerDatos.Close(); leerDatos = null; comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } public void AddOpinion(string plato, string usuario, int valor, string opinion) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = String.Format("insert into Opinion(Plato,Nombre,Valor,Opinion) values(\"{0}\",\"{1}\",\"{2}\",\"{3}\")", plato, usuario, valor, opinion); comandosDB.CommandText = sqlQuery; comandosDB.ExecuteScalar(); comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null;
94
} public void ObtenerOpinion(string plato) { m_Scene = SceneManager.GetActiveScene(); GameObject[] GOopiniones = GameObject.FindGameObjectsWithTag("Opiniones"); if (GOopiniones.Length != 0) { for (int i = 0; i < GOopiniones.Length; i++) { Destroy(GOopiniones[i]); } } if (m_Scene.name.ToString() == "CartaIng") GOopinion = Resources.Load("Prefabs/Ing/OpinionIng") as GameObject; else GOopinion = Resources.Load("Prefabs/Opinion") as GameObject; GOpadre = GameObject.Find("MostrarOpinion").GetComponent<Transform>(); AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from Opinion where Plato = "+ "'" + plato +"'"; comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); while (leerDatos.Read()) { try { GameObject tempPrefab = (GameObject)Instantiate(GOopinion, GOpadre); tempPrefab.GetComponent<OpinionScript>().Opinion(leerDatos.GetString(1), Convert.ToString(leerDatos["Opinion"]), leerDatos.GetInt32(2)); tempPrefab.SetActive(true); } catch (FormatException fe) { Debug.Log(fe.Message); continue; } catch (Exception e) { Debug.Log(e.Message); continue; } } CerrarDB(); } public double CalcularOpinionT(string plato) { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from Opinion where Plato = " + "'" + plato + "'"; comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); while (leerDatos.Read()) { try
95
{ valoresT.Add(leerDatos.GetInt32(2)); } catch (FormatException fe) { Debug.Log(fe.Message); continue; } catch (Exception e) { Debug.Log(e.Message); continue; } } CerrarDB(); double opinionTOTAL = 0.00; for (int i = 0; i < valoresT.Count; i++) { opinionTOTAL += valoresT[i]; } opinionTOTAL = opinionTOTAL / valoresT.Count; return opinionTOTAL; } }
2.15. OpiniónScript.cs
Script que va añadido a las componentes del prefab opinión. Este indicará cuales
son los valores para modificar, y según los valores introducidos los modificara en sus
parámetros, mostrándolos así en la interfaz de usuario.
Opinión() según los valores introducidos mediante el script OpiniónDB este los
modificara en cada componente. Llamará también a la función PonerImagenOp() que
indicará cuantas estrellas se mostrarán en función del valor introducido.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class OpinionScript : MonoBehaviour { public Text Usuario, Comentario; public Image imageOp; public GameObject GOComentario, GOTextCom; public void Opinion(string usuario, string comentario, int valorop) { this.Usuario.text = usuario; this.Comentario.text = comentario; PonerImagenOp(valorop);
96
if (comentario == "") { GOComentario.SetActive(false); GOTextCom.SetActive(false); } } public void PonerImagenOp(int num) { Sprite[] valores = Resources.LoadAll<Sprite>("UI/calif"); switch (num) { case 1: imageOp.sprite = valores[4]; break; case 2: imageOp.sprite = valores[3]; break; case 3: imageOp.sprite = valores[2]; break; case 4: imageOp.sprite = valores[1]; break; case 5: imageOp.sprite = valores[0]; break; } } }
Este script estará añadido como una componente del prefab Opinión y se verá
en el inspector como muestra la Figura 3.27.
Figura 3.27. Componente OpiniónScript.
2.16. PedidoCocina.cs
Este script estará añadido al Gameobject Controlador de la escena
EnviarCocina. Se encargará de decir qué contenido se mostrará, en función a lo
declarado en el inspector. Primero se mostrará una ventana que pedirá al usuario el
número de mesa en el que se encuentra.
97
MostrarPedido() esta función se añadirá al On Click() del botón Enviar. Cuando
el usuario hace click, se cogerá el valor añadido en el Input Field indicando la mesa en
la que se encuentra el usuario. Con este valor se creará una lista de string, que se irán
añadiendo los platos que se encuentran en el carrito y las notas correspondientes si lo
hubiera. Todo este contenido se mostrará en tun texto. También se mostrarán dos
botones de volver y enviar Pedido.
Volver() se añadiría al On Click() del botón Volver, en caso de que el usuario
quiera volver de nuevo a la carta del restaurante para modificar su pedido. También se
le añadirá al botón OK que se encontrara en la ventana final cuando se muestra el texto
de que se ha enviado el pedido correctamente.
EnviarPedido() esta función se añadirá al On Click() del botón Enviar Pedido.
Este creará una tabla nueva con el número de mesa y platos correspondientes para que
en una aplicación simultanea cocina podrá tener acceso a esta base de datos que se
encontrara en la nube y acceder a los platos pedidos y sus correspondientes notas. A
su vez se abrirá otra ventana ocultando la actual. En esta nueva ventana se mostrará el
texto en el que indicará que el pedido se ha enviado correctamente y que esperen para
que le sirvan los platos. También se mostrará un botón de OK, con la función Volver
añadida. Para finalizar, llamara a las funciones BorrarCarrito() y CarritoNuevo() para
tener el carrito preparado para el siguiente pedido.
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using System.Data; using System.IO; using Mono.Data.Sqlite; using UnityEngine.SceneManagement; public class PedidoCocina : MonoBehaviour { public InputField mesa; public GameObject[] contenido; public Text TextPedido; string rutaDB; string conexion; string DBFileName = "Menu.db"; IDbConnection conexionDB; IDbCommand comandosDB; IDataReader leerDatos; string Textmesa; string NumeroMesa; Scene m_Scene;
98
List<String> Pedido = new List<String>(); void Start() { Inicio(); } public void Inicio() { for ( int i=0; i< contenido.Length; i++) { contenido[i].SetActive(false); } contenido[0].SetActive(true); m_Scene = SceneManager.GetActiveScene(); } public void MostrarPedido() { contenido[0].SetActive(false); contenido[1].SetActive(true); NumeroMesa = mesa.text; if (m_Scene.name.ToString() == "EnviarPedidoIng") ObtenerPedidoIng(); else ObtenerPedido(); TextPedido.text = ""; for (int i = 0; i < Pedido.Count; i++) TextPedido.text += Pedido[i]; } public void EnviarPedido() { CrearTabla(NumeroMesa); contenido[1].SetActive(false); contenido[2].SetActive(true); BorrarCarrito(); CarritoNuevo(); } public string DecirMesa() { Textmesa = "Mesa " + NumeroMesa; return Textmesa; } public string DecirMesaIng() { Textmesa = " Table " + NumeroMesa; return Textmesa; } public void ObtenerPedido() { Pedido.Clear(); Pedido.Add("~~~~~~ "+ DecirMesa() + " ~~~~~~"); Pedido.Add("\n\n"); Pedido.Add(" Platos:"); Pedido.Add("\n"); int i = 1;
99
AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from Carrito"; comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); while (leerDatos.Read()) { Pedido.Add(" " + i.ToString() + ". Plato: " + leerDatos.GetString(3)+"\n"); if (leerDatos["Nota"] != DBNull.Value) Pedido.Add(" *Nota: " + leerDatos.GetString(5)); Pedido.Add("\n\n"); i++; } CerrarDB(); } public void ObtenerPedidoIng() { Pedido.Clear(); Pedido.Add("~~~~~~ " + DecirMesaIng() + " ~~~~~~"); Pedido.Add("\n\n"); Pedido.Add(" Plates:"); Pedido.Add("\n"); int i = 1; AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "select * from Carrito"; comandosDB.CommandText = sqlQuery; leerDatos = comandosDB.ExecuteReader(); while (leerDatos.Read()) { Pedido.Add(" " + i.ToString() + ". Plate: " + leerDatos.GetString(3) + "\n"); if (leerDatos["Nota"] != DBNull.Value) Pedido.Add(" *Note: " + leerDatos.GetString(5)); Pedido.Add("\n\n"); i++; } CerrarDB(); } public void EscribirText() { string mydocpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); using (StreamWriter outputFile = new StreamWriter(Path.Combine(mydocpath, "TiquetCocina.txt"))) { for (int i = 0; i < Pedido.Count; i++) outputFile.WriteLine(Pedido[i]); } } public void CrearTabla(string nom) { AbrirDB();
100
comandosDB = conexionDB.CreateCommand(); string nombre = "Pedido" + nom; string sqlQuery = "CREATE TABLE '" + nombre + "' as select Id, Nombre, Nota from Carrito "; comandosDB.CommandText = sqlQuery; comandosDB.ExecuteScalar(); comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } public void CarritoNuevo() { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "CREATE TABLE 'Carrito' ('Id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE," + "'Numero' INTEGER NOT NULL,'Tipo' TEXT NOT NULL,'Nombre' TEXT NOT NULL, 'Precio' REAL NOT NULL,'Nota' TEXT); "; comandosDB.CommandText = sqlQuery; comandosDB.ExecuteScalar(); comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } public void BorrarCarrito() { AbrirDB(); comandosDB = conexionDB.CreateCommand(); string sqlQuery = "DROP TABLE 'Carrito'"; comandosDB.CommandText = sqlQuery; comandosDB.ExecuteScalar(); comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } public void AbrirDB() { //creamos la conexion if (Application.platform == RuntimePlatform.WindowsEditor) { rutaDB = Application.dataPath + "/StreamingAssets/" + DBFileName; } else if (Application.platform == RuntimePlatform.Android) { rutaDB = Application.persistentDataPath + "/" + DBFileName; // ruta para plataforma android } if (!File.Exists(rutaDB)) { WWW loadDB = new WWW("jar:file://" + Application.dataPath + "!/assets/" + DBFileName); while (!loadDB.isDone) //controlar errores, rellenarlo mas tarde { }
101
File.WriteAllBytes(rutaDB, loadDB.bytes); } conexion = "URI=file:" + rutaDB; //ruta para cargar la base de datos conexionDB = new SqliteConnection(conexion); conexionDB.Open(); } void CerrarDB() { leerDatos.Close(); leerDatos = null; comandosDB.Dispose(); comandosDB = null; conexionDB.Close(); conexionDB = null; } public void Volver() { if (m_Scene.name.ToString() == "EnviarPedidoIng") SceneManager.LoadScene("CartaIng"); else SceneManager.LoadScene("CartaAR"); } }
Este script esta añadido como una componente del GameObject Controlador
de la escena EnviarPedido. Se verá en el inspector como muestra la figura 3.28.
Figura 3.28. Componente Pedido Cocina.
102
Resultados
Para mostrar los resultados, crearemos nuestra aplicación. Para ello nos
dirigiremos al Build Setting, y le daremos al botón Build. Si nuestro proyecto no tiene
ningún tipo de error, se nos creará una apk, la cual tendremos que instalar en nuestro
dispositivo móvil. Una vez ejecutada la aplicación en el dispositivo, se mostrarán las
capturas de pantalla de este. Vemos que nos aparece la marca de agua de Vuforia, ya
que hemos realizado nuestra aplicación con la versión gratuita.
Al iniciar la aplicación, no se ejecutará el contenido digital hasta no detectar el
marcador Image Target Carta. Cuando este es detectado, se mostrarán las animaciones
correspondientes Figura 3.29. Se visualizarán los distintos platos indicando el nombre y
la foto de cada uno. El usuario podrá acceder a cada uno de ellos seleccionándolos en
la pantalla. Figura 3.30. Con los botones de anterior y siguiente, encontrados en la parte
inferior de la foto, se podrán acceder a mas fotos de cada uno de los platos.
Figura 3.29. Animaciones
Figura 3.30. Información plato seleccionado.
Plato Provolone, contiene un video, el cual, si pulsamos el botón play que se
muestra sobre él, se ejecutara el video correspondiente. Figura 3.31.
103
Figura 3.31. Video Provolone
Cuando seleccionamos un plato, se mostrará una imagen 2D en el Mantel, dando
la sensación al usuario de que ya se encuentra el plato sobre su mesa. Figura 3.32. Esto
dará una idea de cómo será su plato y que contendrá antes de realizar su pedido.
Figura 3.32. Platos 2D
Cada plato dispone un botón Opinar y unas estrellas que muestran el valor que
tiene cada plato según los clientes. Si pulsamos a alguno de estos, podremos ver las
opiniones del resto de cliente, así como los comentarios que hayan añadido. Figura 3.33.
Figura 3.33. Comentarios de usuarios
Si hacemos clic en el botón Dejar Opinión, se nos abrirá otra ventana en la que
nos pedirán tres valores, nombre de usuario, puntuación que le damos al plato y la
posibilidad de añadir un comentario al respecto Figura 3.34. Tanto el nombre como el
104
comentario podemos dejarlo sin ningún texto añadido, pero en valor tendremos que
añadir un entero del 0 al 5, si no es así, nos mostrara un error y nos enviará de nuevo a
la carta. Figura 3.35.
Figura 3.34. Añadir opinion
Figura 3.35. Error valor introducido.
El botón Pedir Plato, que se encuentra debajo de la información de cada plato
seleccionado, nos da la opción de poder añadirlo a nuestro pedido. Figura 3.36. Si
hacemos click en ese botón, iremos agrupando los correspondientes platos. Sobre este
botón se mostrará un número, el cual indicara los platos que dispone nuestro pedido.
Figura 3.37.
Figura 3.36. Botón pedir plato.
105
Figura 3.37. Numero platos.
Para visualizar nuestro pedido, pulsaremos sobre el botón imagen del carrito.
Una vez pulsado, en el ImageTarget mantel se nos mostraran los platos que hemos ido
añadiendo, así como su nombre precio, precio total, numero de platos, etc. Figura 3.38.
También se visualizará dos botones: eliminar y añadir nota. Si se hace click sobre el
botón Eliminar, se eliminará ese plato del pedido. Figura 3.39. Seleccionando el botón
con la imagen de un lápiz, que representa añadir nota, abrirá una ventana, en la cual
nos pedirá que introduzcamos la nota que queremos almacenar al plato
correspondiente. Figura 3.40.
Figura 3.38. Mantel carrito
Figura 3.39. Función eliminar plato.
106
Figura 3.40. Añadir nota.
Una vez seguros del pedido deseado, se pulsará el botón Hacer Pedido. Este
nos enviará a la nueva escena, que solicita al usuario el número de mesa en la que se
encuentra. Este valor será añadido en el InputField. Figura 3.41. Al seleccionar el botón
Enviar, se mostrará un texto con el número de mesa que se encuentra, los platos
seleccionados y las notas introducidas. Figura 3. 42. Este texto es el que recibirá cocina
para realizar el pedido. Si este todo correcto el cliente le dará al botón Ok, enviándosele
de forma inmediata a cocina el pedido, y mostrando por pantalla al usuario que el pedido
se ha realizado de forma correcta. Figura 3.43.
Figura 3.41. Introducir número de mesa
107
Figura 3.42. Texto del pedido.
Figura 3.43. Texto final.
En esta carta de realidad aumentada también se mostrarán dos botones, en los
cuales si el usuario los selecciona se le redirigirá a internet mostrando la página del
restaurante o el Facebook de este. De esta forma podrán tener más información sobre
el restaurante o los platos, al igual que las opiniones de los usuarios respecto al
restaurante. Figura 3.44.
Figura 3.44. Botones Facebook y Web.
En toda la aplicación, en la parte superior izquierda, se mostrarán dos botones
para seleccionar el lenguaje. Si seleccionamos el botón para representar la aplicación
en inglés, se mostrarán los mismos contenidos ya indicados anteriormente, pero
mostrara todo el contenido en inglés. En la siguiente figura 3.45, podremos ver la misma
carta ya descrita, pero en este caso con lengua inglesa.
108
Figura 3.45. Contenido en inglés.
Estos resultados se han realizado con marcadores impresos en blanco y negro
y papel normal que se han ido utilizando durante la realización de la aplicación. Como
producto final se deberán imprimir en color (serán visualmente más llamativos para los
clientes) y en un material más duro y de mejor calidad para que se mantengan en mejor
estado. Se deben evitar papeles que emitan brillo para que la cámara los detecte con
mejor claridad.
109
CONCLUSION Y TRABAJOS FUTUROS.
Conclusiones
En el presente trabajo fin de grado hemos descrito el protocolo seguido para
diseñar y crear una carta de restaurante en realidad aumentada.
✓ En este proyecto se ha incluido información asociada a cada uno de los platos
del menú del restaurante, destacando ingredientes, precio, alérgenos y
opiniones de usuario.
✓ Se ha incluido la opción de confeccionar, por parte del cliente, su pedido
directamente a cocina, dándole la posibilidad de añadir
modificaciones/comentarios al chef para los platos seleccionados.
✓ En el desarrollo de este proyecto se han aplicado los conocimientos de
programación adquiridos durante la carrera, lo cual ha permitido no solo poner
en práctica los mismos, si no aprender nuevos lenguajes asociados que
desconocía hasta el momento de realizar el proyecto.
✓ El desarrollo de esta ampliación se ha basado en la carta ya existente, con la
aprobación del propietario. “Pizzeria al Gusto Tapas”
(http://www.pizzeriaalgusto.es/).
Este proyecto demuestra la versatilidad que ofrece la implementación de realidad
aumentada en aplicaciones orientadas al sector gastronómico, y al sector del
entretenimiento gracias a los SDK´s de desarrollo.
La implementación de este tipo de nuevas tecnologías basada en AR sobre el
sector de la restauración puede suponer una pequeña gran revolución en cuanto a
modernización e innovación sobre la información proporcionada de los productos
disponibles hacia los clientes.
Esta aplicación está preparada para poder ser comercializada por el propio
restaurante. A su vez, también es posible modificar su diseño como sus bases de
información para ser aplicada a distintos restaurantes.
110
Líneas futuras
La aplicación de este tipo de tecnología al sector industrial y en especial al
médico, puede constituir el gran avance que se presume que experimentará la AR
durante los próximos años. Colaboraciones con otros departamentos puede ser un más.
Para mejorar la aceptación de la aplicación, se adjuntaría contenido 3D, en el
cual se representes los platos realizados con software grafico tipo Blender 3D. Así podrá
ver el cliente el plato lo más real posible.
Se implementaría también, un registro de usuarios. De esta forma se tendrá un
control de los clientes a la hora de dejar comentarios sobre los platos, comprobando que
en realidad han sido clientes y han probado ese plato.
En un futuro, cuando las HooloLens estén más al alcance del usuario, esta carta
podría interactuarse con ella mediante el cursor con seguimiento de mirada de esta y
combinado con comandos de voz. Así, no se necesitaría el móvil ni hacer los click a
pantalla, dando una mayor sensación de realidad aumentada.
111
5. BIBLIOGRAFIA
[1] JAVORNIK, A. Augmented reality: Research agenda for studying the impact of its
media characteristics on consumer behaviour. Journal of Retailing and Consumer
Services, 2016, 30, 252– 261.
[2] AZUMA, R., A survey of augmented reality. Presence: Teleoperation and Virtual
Environments, 2001, 6 (4), 355-385.
[3] AZUMA, R. et al. Recent advances in augmented reality, IEEE Computer Graphics
and applications, 2001, 21 (6), 34-47.
[4] HENRYSSON A. et al. “Mobile Phone based Augmented Reality”, in Emerging
Technologies of Augmented reality, Interfaces and Design, M. Haller, M. Billinghurst, B.
Thomas Ed., Idea Group Publishing London, UK.,2007, Cap 5.
[5] ARNALDI, B., et al. Virtual Reality and Augmented reality, Arnaldi, B., Guitton, P.,
Moreau, G., Eds., ISTE Ltd, London, UK, 2018.
[6] MCCORMICK H., et al. Fashion retailing—past, present and future. Textile Progress,
2017, 46(3), 227–321.
[7] BRUNS E., et al. Enabling mobile phones to support large-scale museum guidance.
Multimedia, IEEE, 2014, 14(2), 16–25.
[8] LEUE M. C., et al. Google glass augmented reality: Generic learning outcomes for
art galleries. In I. Tussyadiah & A. Inversini (Eds.), Information and communication
technologies in tourism. New York: Springer, 2015.
[9] CHUNG N., et al. Tourists’ intention to visit a destination: The role of augmented
reality (AR) application for a heritage site. Computers in Human Behaviour, 2015, 50,
588–599.
[10] TEBER D., et al. Augmented reality: a new tool to improve surgical accuracy during
laparoscopic partial nephrectomy? preliminary in vitro and in vivo results”, European
Urology, 2009, vol. 56, no. 2, pp. 332–338.
[11] KREVELEN D.W.F., et al. A survey of augmented reality technologies, applications
and limitations, International Journal of Virtual Reality, 2010, vol. 9, no. 2, pp. 1–20.
[12] HAOCHINE N., et al. Image-guided simulation of heterogeneous tissue deformation
for augmented reality during hepatic surgery, IEEE International Symposium on Mixed
and Augmented Reality (ISMAR), 2013, pp. 199–208.
112
[13] https://gastronomiaycia.republica.com/2018/04/19/ver-la-comida-con-realidad-
aumentada-antes-de-realizar-el-pedido/
[14] https://unity.com/es/solutions/mobile-ar
[15] https://docs.unity3d.com/Manual/index.html
[16] https://www.ptc.com/en/why-ptc
[17] https://developers.google.com/ar/discover/
[18] https://developer.apple.com/arkit/
[19] https://sqlite.org/index.html
[20] J. FERGUSON et al. La biblia de C#. Analla. 2003.
[21] Observatorio de Innovación Educativa del Tecnológico de Monterrey, Realidad Aumentada y Realidad Virtual, 2017.
113
ANEXO I. INSTALACION DE LOS PROGRAMAS NECESARIOS.
1. Instalacion Unity
Para instalar Unity iremos a su página oficial: https://unity3d.com/es
Unity tiene una página muy completa, en la que nos mostrará mucha información
sobre ella, juegos que se han realizado con Unity, blogs, etc. Para poder instalarla
iremos al apartado Obtener. En este nos muestra las distintas versiones a poder
descargar en función de lo que facture la empresa al año (Figura13). En este caso
descargaremos la versión personal de Unity que es gratuita. Podemos utilizar todas las
prestaciones de Unity al igual que las de pago, solo que no tendremos servicios externos
que ofrecen. Pero lo más importante es, que en las versiones gratuitas siempre se nos
ejecutara una ventana inicial de Unity al comenzar nuestra aplicación. Si queremos que
esto no aparezca tenemos que obtener la de pago.
Figura AI.1. Versiones de Unity en función de los ingresos obtenidos.
Nos pedirá registrarnos. Esto también será necesario luego en el programa, ya
que tendremos que tener la sección iniciada. Una vez descargado iniciaremos su
instalación. En la ventana de selección de componentes nos preguntara que contenidos
114
queremos que instalemos (Figura 14). En este es necesario que seleccionemos, aparte
de Unity, Android Build Support y Vuforia Augmented Really Support.
Figura AI.2. Ventana de instalación Unity.
2. Instalación de Vuforia
A partir de la versión de Unity 2017.2 Vuforia ya viene integrado en Unity. Lo hemos
marcado en la ventana de instalación de componentes.
3. Base de datos SQLite
Por último, crearemos nuestra base de datos para guardar los platos de los que
disponemos y la información de cada uno de ellos. También la usaremos para ir
almacenando cada uno de los platos que el usuario va añadiendo a su carrito para hacer
pedido a cocina. Y a su vez, para cada uno de ellos almacenaremos la opinión y
comentarios de cada plato correspondiente. Esta base de datos puede crearse mediante
los códigos de SQLite. Pero por agilizarlo usare un software DB Browser for SQLite.
Este nos ayudara de forma mas visual y sencilla poder crear nuestra base de datos con
cada una de sus tablas correspondientes. En nuestro caso la base de datos se llamará
menú.db.
115
Figura AI.3. Base de datos menú.db.
Para poder llamar la base de datos en Unity, tendremos que crear una carpeta
en Assets de nuestro proyecto llamada StreaminAssets. En ella importaremos nuestra
base de datos para que pueda ser llamada. También para poder hacer uso de esta,
necesitamos una serie de librerías que tendremos que almacenar en una carpeta
llamada Plugins. Para obtener estas librerías nos iremos a nuestro explorador de
archivos y nos dirigiremos donde tenemos instalado Unity, (Archivos de Programa)
>Data>Mono>lib>mono>2.0. En esta carpeta se encuentran las librerías que
necesitamos. Arrastraremos a nuestro proyecto los siguientes:
• Mono.Data.dll
• Mono.Data.Sqlite.dll
• Mono.Data.SqliteClient.dll
• System.Configuretion.dll
• System.Data.dll
• System.Security.dll
• System.EnterpriseServices.dll
116
ANEXO II. MANUAL DE UNITY.
Interfaz de Unity
Comenzaremos hablando de la interfaz del editor de Unity, para el mejor
entendimiento de aquí en adelante. En Unity nos encontramos varias ventadas
dispuesta predeterminadamente como en la siguiente figura. La barra de herramientas
proporciona un acceso a las características más esenciales para trabajar. En la
izquierda contiene las herramientas básicas para manipular la ventana de escena y los
objetos dentro de esta. En el centro están los controles de reproducción, pausa, y pasos.
Los botones a la derecha le dan acceso a sus servicios de Unity Cloud y su cuenta de
Unity, seguido por un menú de visibilidad de capas, y finalmente el menú del layout del
editor. La ventana de jerarquía es una representación de texto jerárquico de cada objeto
en la escena. Cada elemento en la escena tiene una entrada en la jerarquía, por lo que
las dos ventanas están inherentemente vinculadas. La jerarquía revela la estructura de
cómo los objetos están agrupados el uno al otro. La ventana de escena proporciona una
navegación visual y el poder editar la escena. Puede mostrar una perspectiva 2D o 3D
dependiendo en el tipo de proyecto en el que esté trabajando, que en nuestro caso es
3D principalmente. La ventana del proyecto muestra los assets de librería que tienen
nuestro proyecto. Como ya anunciamos anteriormente, es donde se encuentran
nuestras carpetas y datos necesarios para nuestro proyecto.
117
Figura AII.1. Editor de Unity.
Assets
En Unity a los archivos, carpetas o código se les llama Assets. Estos son los que
se muestran en la ventana de proyecto. Estos son importados o creados en el mismo
Unity. En la carpeta Assets de nuestro proyecto es donde se deben guardar todos estos
archivos. Ya sea mediante nuestro explorador en el pc o importados dentro de Unity.
Incluso podemos arrastrar estos archivos desde su ventana correspondiente a la
ventana de proyecto dentro de la carpeta deseada. Unity detectará archivos
automáticamente a medida que son agregados a la carpeta Assets, o si son
modificados. Cuando coloque cualquier asset en la carpeta Assets, verás al asset
aparecer en la ventana de proyecto.
Figura AII.2. Assets.
Siempre intentaremos tener estas carpetas bien organizadas (Figura 32), en
algunos casos trabajásemos con más personas, al compañero se le hará más fácil
localizar cada una de las cosas que le sea necesaria para seguir trabajando con el
proyecto. Para ello crearemos carpetas donde se encontrarán las Animaciones,
Imágenes, Scripts, etc. Así como muestra la figura. En el caso de Imágenes o Prefabs
que serán llamados mediante código, deberán estar dentro de la carpeta Resource para
que puedan ser encontrados.
118
Figura AII.3. Carpetas del proyecto.
Sprite
Los Sprite son objetos gráficos 2D. Estos serán las fotos, platos e interfaces que se
incluirán en este proyecto. En la carpeta Resources, crearemos una serie de carpetas
donde importaremos nuestras imaginas ya creadas con Photoshop. Nótese que en Unity
haremos uso de atlas para que la aplicación haga menos llamadas de imágenes, y así
mejoremos el rendimiento de nuestra aplicación. Para ello se agrupan las imágenes
atendiendo a su función (aperitivos, carnes…) sin fondo y los guardaremos con
extensión png.
Es preciso indicarle a Unity que es un Sprite. Para ello seleccionamos la imagen
y se nos abrirá el inspector. En Texture Type es preciso seleccionar Sprite (2D and UI).
En nuestro caso al ser un atlas, también tendremos que seleccionar en el parámetro
Sprite Mode que es Múltiple. Aplicaremos los cambios y le daremos al botón Sprite
Editor. Se nos habría la ventana de editor de Sprite (Figura 33) donde podremos ver la
imagen seleccionada. Es preciso recortar e identificar cada Sprite y renombrarlo, para
que así Unity pueda diferenciar uno de otros. Este proceso lo repetiremos con cada una
de las imágenes incluidas en el proyecto.
119
Figura AII.4. Sprite Editor.
Figura AII.5. Sprites ya editados.
Escenas
Unity siempre tiene que trabajar con una escena abierta. Las escenas contienen
todo lo que se reproducirá en la aplicación. Por tanto, todas las escenas serán
guardadas en la carpeta Scenes. Nótese que en su interior se encontrarán las escenas
CartaAR y EnviarCocina.
Game Object
Los Game Object son los objetos más importantes de Unity. Estos contenedores
que según los componentes que le vayamos indicando por inspector o mediante código,
haremos que el game object tenga un tipo de características específicas o que se
comporte de una forma en concreto. Estos pueden ser imágenes, mascaras, botones,
etc. Estos se pueden añadir en jerarquía con unas características ya añadidas o crear
un Game Object vacío e ir añadiendo componentes para indicar su funcionalidad.
Scripts
Un script hace sus conexiones con el funcionamiento interno de Unity al
implementar una clase que deriva desde la clase integrada llamada MonoBehaviour. La
función Update es el lugar para colocar el código que se encargará de la actualización
por frame para el GameObject. Este puede incluir movimiento, acciones de trigger y
responder al input del usuario, básicamente cualquier cosa que necesite ser manejado
120
en el tiempo durante el gameplay. Para que la función Update haga su trabajo, a veces
es útil configurar variables, leer. La función Start va a ser llamada por Unity antes de
que el gameplay comience y es el lugar donde inicializaremos.
En el comienzo de cada script haremos uso de los espacios de nombre. Con la
palabra clave using se usará para como una directica del espacio de nombre, así avisará
al compilador C# que el código va a hacer referencias a un espacio de nombre específico
y de que antepondrá el identificador de espacio de nombre a las clases de ese espacio
de nombre.
El espacio de nombre System contiene clases que implementan funcionalidades
básicas como conversiones de tipos de datos, operaciones matemáticas, invocación a
programas y gestión del encorno de procesos. El espacio de nombre System es el mayor
de los proporcionados por .NET.
El espacio de nombre System.Collections contiene clases que implementan
colecciones de objetos, como listas, colas, matrices, tablas, hash y diccionarios.
El espacio de nombre UnityEngine contiene las clases básicas de Unity para su
funcionamiento.
Debug.Log() es un comando simple para imprimir en consola.
El script al ser añadido a un GameObject funcionara como cualquier componente
que se le añada a este ya establecido por Unity. Si hacemos una variable publica
podremos acceder a ella desde el inspector.
121
ANEXO III. MYSQL Y PHP
En caso de disponer de un servidor de pago con vistas a líneas futuras,
tendremos que incorporar mysql y php a nuestro proyecto. Para poder ver las opiniones
de los usuarios, estas tendrán que guardarse en una base de datos remota. Así, todos
los usuarios podrán ver las opiniones del resto de usuarios, y también añadir opiniones
nuevas. Para acceder a esta base de datos MySQL, se accederá a ella mediante PHP,
que conectará a ella usando el método PDO. La extensión Objetos de Datos de PHP
(PDO por sus siglas en inglés) define una interfaz ligera para poder acceder a bases de
datos en PHP. Mediante el método, se le indicara la ruta del host y el nombre de la base
de datos, nombre de usuario y contraseña. En este caso se ha usado XAMPP para crear
un host local en el terminar, por lo tanto, el usuario será root y no hay contraseña.
Al inicio indicaremos que los valores que el usuario tiene que introducir, es decir,
mediante Unity, mediante GET. El método prepare() enviara el comando indicado a la
base de datos MySQL, en el cual nos encargaremos tanto de leer los datos
correspondientes por el comando SELECT o introduciremos datos por el comando
INTRO. Por el método execute() indicaremos que valores queremos modificar dentro de
prepare, para así indicar que plato queremos obtener las opiniones, al igual a que plato
queremos indicar la opinión y sus valores correspondientes. A continuación, se
mostrarán los dos códigos PHP tanto para leer los datos de la tabla pdo.php como para
introducir nuevos datos pdo2.php.
Pdo.php
<?php $plato = $_GET['plato']; try{ $conexion = new PDO('mysql:host=localhost;dbname=usuariosmenu','root',''); //echo "Conexion OK".'<br/>'; $statement = $conexion->prepare('SELECT * FROM opinion WHERE Plato = :plato'); $statement->execute( array(':plato' => $plato) ); $resultados = $statement->fetchAll(); foreach($resultados as $fila) { echo $fila['Nombre']."\n"; echo $fila['Valor']."\n";
122
echo $fila['Opinion']."\n"; } }catch(PDOException $e){ echo "Error: " . $e->getMessage(); } ?>
Pdo2.php
<?php $plato = $_GET['plato']; $nombre = $_GET['nombre']; $valor = $_GET['valor']; $opinion = $_GET['opinion']; try{ $conexion = new PDO('mysql:host=localhost;dbname=usuariosmenu','root',''); //echo "Conexion OK".'<br/>'; $statement = $conexion->prepare('INSERT INTO opinion (Plato, Nombre, Valor, Opinion) values (:plato,:nombre,:valor,:opinion)'); $statement->execute( array(':plato' => $plato,':nombre' => $nombre,':valor' => $valor,':opinion' => $opinion) ); }catch(PDOException $e){ echo "Error: " . $e->getMessage(); } ?>
Para acceder a estos códigos php, crearemos otro script en Unity, en el cual
indicaremos la url donde se encuentran almacenados estos php en el servidor. Para leer
datos indicaremos en la ruta pdo.php, y para introducirlos pdo2.php. Para llamar a estas
url tendremos que hacerlo por corutinas. Mediante las clases www enviaremos los datos
correspondientes, al igual que así lo recibimos.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class BaseRemota : MonoBehaviour { string URL, URL2; string nombrePlato; public Text miTexto; public InputField nombreUs, valorUs, opinionUs;
123
void Start() { URL = "http://localhost/cursoPHP/pdo.php"; URL2 = "http://localhost/cursoPHP/pdo2.php"; nombrePlato = PlayerPrefs.GetString("PlatoN"); MostrarOpinion(); } public void MostrarOpinion() { StartCoroutine("CargarOpiniones"); } public void Enviar() { StartCoroutine("EnviarOpinion"); } IEnumerator CargarOpiniones() { string URLString = URL + "?plato=" + WWW.EscapeURL(nombrePlato); WWW getOpinion = new WWW(URLString); yield return getOpinion; Debug.Log(getOpinion.text); } IEnumerator EnviarOpinion() { string URLString = URL2 + "?plato=" + WWW.EscapeURL(nombrePlato)+ "?nombre=" + WWW.EscapeURL(nombreUs.text) + "?valor=" + WWW.EscapeURL(valorUs.text) + "?opinion=" + WWW.EscapeURL(opinionUs.text); WWW sendOpinion = new WWW(URLString); yield return sendOpinion; Debug.Log("Cargado"); } }
124
ANEXO IV. INDICE FIGURAS.
Figuras Capitulo 1:
Figura 1.1. Courbe de Hype 2017.
Figura 1.2 Ejemplos de aplicaciones en RA.
Figura 1.3. HoloLens y captura del video de muestra Microsoft.
Figura 1.4. Glass de Google.
Figura 1.5. Aplicación de realidad aumentada del restaurante Vida&Comida.
Figura 1.6. Captura de la aplicación de AR de la hamburguesería Bareburger.
Figura 1.7. Imagen de la aplicación de Kabaq
Figuras Capitulo 2:
Figura 2.1. Plantilla correspondiente al menú y mantel presentes en el restaurante que
emplearemos como marcador.
Figura 2.2. Imágenes empleadas en el proyecto antes y después de ser tratadas por
Photoshop.
Figura 2.3. Fotos utilizadas en el proyecto.
Figura 2.4. Botones de interfaz de usuario y alérgenos.
Figura 2.5. Creando proyecto en Unity.
Figura 2.6. Build Settings en Unity.
Figura 2.7. Players Settings.
Figura 2.8. XR Settings
Figura 2.9. Unity Preferences.
Figura 2.10. Licencias en Vuforia.
Figura 2.11. Licencia de CartaRestaurante Vuforia.
125
Figura 2.12. Parámetros ARCamara.
Figura 2.13. Copiando Licencia de ARCamara en sus parámetros.
Figura 2.14. Creando base de datos Vuforia.
Figura 2.15. Añadiendo marcador.
Figura 2.16. Primer mantel con baja clasificación.
Figura 2.17. Mantel final, con una buena clasificación.
Figura 2.18. Marcadores subidos a la base de datos de Vuforia.
Figura 2.19. Base de datos a importar.
Figura 2.20. Selección del Image Target.
Figuras Capitulo 3:
Figura 3.1. Canvas
Figura 3.2. Seleccionando Sprite en los parámetros de los botones.
Figura 3.3. Show Mask Graphic.
Figura 3.4. Content Size Filter y Scroll Rect.
Figura 3.5. Resultado de las cuatro mascaras.
Figura 3.6. Resultado MaskGrande.
Figura 3.7. Prefab Plato.
Figura 3.8. Resultado jerarquía de ImageTarget2.
Figura 3.9. Parámetros de Canvas.
Figura 3.10. Prefab Opinión
Figura 3.11. Jerarquía prefab CanvasOpinión.
Figura 3.12. Prefab CanvasNotas.
Figura 3.13. Creación de animaciones en Unity.
Figura 3.14. Jerarquía escena EnviarPedido.
Figura 3.15. Botones lenguaje.
126
Figura 3.16. Añadir función a On Click() de un botón.
Figura 3.17. Componente Script CartaInicio.
Figura 3.18. Componente PlatoScript
Figura 3.19. Componente CambiarFoto.
Figura 3.20. Componente Carrito Controler.
Figura 3.21. Componente CarritoScript.
Figura 3.22. Componente ErrorScript.
Figura 3.23. Componente Mantel Script.
Figura 3.24. Componente Mostrar video.
Figura 3.25. Componente Nota Script.
Figura 3.26. Componente Opinión Controler.
Figura 3.27. Componente OpiniónScript.
Figura 3.28. Componente Pedido Cocina.
Figura 3.29. Animaciones
Figura 3.30. Información plato seleccionado.
Figura 3.31. Video Provolone
Figura 3.32. Platos 2D
Figura 3.33. Comentarios de usuarios
Figura 3.34. Añadir opinión
Figura 3.35. Error valor introducido.
Figura 3.36. Botón pedir plato.
Figura 3.37. Numero platos.
Figura 3.38. Mantel carrito
Figura 3.39. Función eliminar plato.
Figura 3.40. Añadir nota.
Figura 3.41. Introducir número de mesa
127
Figura 3.42. Texto del pedido.
Figura 3.43. Texto final.
Figura 3.44. Botones Facebook y Web.
Figura 3.45. Contenido en inglés.
Figuras ANEXOS:
Figura AI.1. Versiones de Unity en función de los ingresos obtenidos.
Figura AI.2. Ventana de instalación Unity.
Figura AI.3. Base de datos menú.db.
Figura AII.1. Editor de Unity.
Figura AII.2. Assets.
Figura AII.3. Carpetas del proyecto.
Figura AII.4. Sprite Editor.
Figura AII.5. Sprites ya editados.
128
ANEXO V. VÍDEO
Se enviará un vídeo demostrativo de la ejecución de la aplicación, en el cual se
visualizará todo el contenido de este y sus funciones.
https://drive.google.com/open?id=1y5oXOhdwTCquzph2rfk9jRc7exHJq_jC