1 00:00:00,000 --> 00:00:04,620 Hola, somos Álvaro Ilzarbe, Jorge Navarro y Andrés Inche, y vamos a presentar nuestro 2 00:00:04,620 --> 00:00:08,259 proyecto intermodular correspondiente al grado superior de Desarrollo de Aplicaciones Multipletaforma 3 00:00:08,259 --> 00:00:09,880 en el IES Francisco de Quevedo. 4 00:00:10,119 --> 00:00:13,339 Nuestro proyecto consiste en una aplicación multipletaforma relacionada con el ámbito 5 00:00:13,339 --> 00:00:16,100 competitivo de Pokémon, desarrollada con Kotlin Multiplatform. 6 00:00:16,519 --> 00:00:20,579 La aplicación permite crear, consultar y analizar equipos Pokémon utilizando datos 7 00:00:20,579 --> 00:00:25,359 reales sobre Pokémon, movimientos, habilidades, objetos y también equipos utilizados en torneos 8 00:00:25,359 --> 00:00:25,899 oficiales. 9 00:00:25,980 --> 00:00:28,699 Para entender el contexto del proyecto, hay que tener en cuenta que en el competitivo 10 00:00:28,699 --> 00:00:32,579 de Pokémon no basta con elegir Pokémon al azar. Los jugadores necesitan construir equipos 11 00:00:32,579 --> 00:00:36,979 equilibrados, revisar estadísticas, comprobar eficacias de tipos, calcular daños aproximados 12 00:00:36,979 --> 00:00:41,179 y analizar qué estrategias funcionan en partidas reales o torneos. Actualmente, una de las 13 00:00:41,179 --> 00:00:44,679 herramientas más conocidas para esto es Pokémon Showdown, que se considera prácticamente 14 00:00:44,679 --> 00:00:49,119 el estándar para crear equipos, probar estrategias y simular combates. Sin embargo, aunque es 15 00:00:49,119 --> 00:00:53,060 una herramienta muy completa, no cuenta con una aplicación móvil específica. En móviles 16 00:00:53,060 --> 00:00:56,520 lo habitual es acceder desde el navegador web, lo que puede resultar menos cómodo para 17 00:00:56,520 --> 00:00:59,520 consultar datos, revisar equipos o trabajar con la interfaz durante mucho tiempo. 18 00:00:59,700 --> 00:01:03,020 A partir de esa necesidad, decidimos crear una aplicación que reuniera varias funciones 19 00:01:03,020 --> 00:01:05,620 útiles en un formato más adaptado a una experiencia multiplatforma. 20 00:01:05,719 --> 00:01:09,780 La idea no era hacer solamente una Pokédex o una base de datos estática, sino una herramienta 21 00:01:09,780 --> 00:01:13,719 que ayudara al usuario a crear equipos, consultar información relevante y apoyarse en ejemplos 22 00:01:13,719 --> 00:01:14,859 reales de entorno competitivo. 23 00:01:15,200 --> 00:01:18,540 La justificación del proyecto está precisamente en ofrecer una alternativa más cómoda y 24 00:01:18,540 --> 00:01:22,579 organizada para este tipo de consultas, especialmente en dispositivos donde el uso de una web no 25 00:01:22,579 --> 00:01:23,560 siempre es más práctico. 26 00:01:23,560 --> 00:01:27,659 Además, el proyecto nos permitía aplicar muchos contenidos del ciclo 27 00:01:27,659 --> 00:01:31,980 Como el desarrollo de interfaces en plataforma, el consumo de APIs externas, la organización cliente-servidor 28 00:01:31,980 --> 00:01:36,519 La gestión de datos, el trabajo con backend y frontend y el uso de control de versiones en equipo 29 00:01:36,519 --> 00:01:40,920 En cuanto a los datos, la aplicación obtiene información relacionada con Pokémon, movimientos, habilidades y objetos 30 00:01:40,920 --> 00:01:44,599 Además, también incorpora la consulta de equipos presentados en torneos competitivos 31 00:01:44,599 --> 00:01:49,079 Lo que permite al usuario tomar referencias reales, comparar estrategias y ver tendencias dentro del metajuego 32 00:01:49,079 --> 00:01:50,560 Los objetivos principales del proyecto son 33 00:01:50,560 --> 00:01:52,680 Desarrollar una aplicación multiplataforma funcional 34 00:01:52,680 --> 00:01:55,340 Permitir la creación y consulta de equipos Pokémon 35 00:01:55,340 --> 00:01:59,439 Implementar herramientas de análisis, como el cálculo de daño y eficacia 36 00:01:59,439 --> 00:02:02,079 Integrar datos externos mediante APIs 37 00:02:02,079 --> 00:02:07,260 Y organizar el proyecto con una estructura realista, separando responsabilidades entre las distintas partes de la aplicación 38 00:02:07,260 --> 00:02:11,780 En resumen, nuestro proyecto combina una temática cercana y motivadora con un desarrollo técnico completo 39 00:02:11,780 --> 00:02:18,780 No queremos hacer únicamente una aplicación de consulta, sino una herramienta que conectara datos, análisis y creación de equipos dentro de una experiencia multiplataforma 40 00:02:18,780 --> 00:02:24,319 A continuación explicaremos con más detalle las tecnologías utilizadas, la estructura del proyecto y las principales funcionalidades desarrolladas. 41 00:02:24,479 --> 00:02:30,560 Para el desarrollo del proyecto, lo primero que hicimos fue crear un repositorio compartido en GitHub, donde centralizamos todo el código, la documentación y el control de versiones. 42 00:02:30,639 --> 00:02:35,740 Esto nos permitió trabajar de forma simultánea, diseñar cambios de manera ordenada y mantener un seguimiento claro de la evolución del proyecto. 43 00:02:35,879 --> 00:02:39,620 Una vez preparado el entorno de trabajo, repartimos las distintas partes del proyecto entre los miembros del equipo. 44 00:02:39,780 --> 00:02:46,479 Para coordinar las tareas utilizamos Taiga como herramienta de organización, lo que nos ayuda a distribuir el trabajo, planificar objetivos y llevar un control del progreso del desarrollo. 45 00:02:46,479 --> 00:02:51,319 Como el objetivo era crear una aplicación móvil multiplataforma, utilizamos Android Studio como entorno principal de desarrollo. 46 00:02:51,439 --> 00:02:59,139 Antes de empezar con el código diseñamos el diseño visual mediante Figma, lo que nos permitió definir la experiencia del usuario y organizar las diferentes pantallas antes de pasar al código. 47 00:02:59,319 --> 00:03:05,400 Durante las prácticas y procesos de planificación se nos recomendó utilizar Kotlin Multiplatform, ya que se ajustaba especialmente bien a la idea del proyecto. 48 00:03:05,780 --> 00:03:13,259 Esta tecnología nos permitía compartir lógica de negocio entre distintas plataformas, manteniendo una arquitectura más eficiente, modular y preparada para un desarrollo multiplataforma realista. 49 00:03:13,259 --> 00:03:18,159 Además, encajaba con uno de los principales objetivos del proyecto, aplicar tecnologías actuales relacionadas con el desarrollo multiplataforma. 50 00:03:18,180 --> 00:03:20,879 En cuanto a la parte del servidor y gestión de datos, optamos por utilizar Kator, 51 00:03:21,139 --> 00:03:24,120 que es el backend, encargándose de la comunicación cliente-servidor. 52 00:03:24,379 --> 00:03:28,240 Para la persistencia de datos, empleamos MySQL como sistema de base de datos, 53 00:03:28,439 --> 00:03:34,080 donde almacenamos información relacionada con los usuarios, equipos y distintos elementos necesarios para el funcionamiento interno del sistema. 54 00:03:34,400 --> 00:03:38,199 Respecto a la seguridad de la aplicación, implementamos autenticación basada en JWT, 55 00:03:38,500 --> 00:03:39,199 JSON Web Token. 56 00:03:39,199 --> 00:03:45,479 Esto nos permitió gestionar el acceso de usuarios de forma segura, controlar las sesiones y validar las peticiones autenticadas entre cliente y servidor. 57 00:03:45,639 --> 00:03:52,080 De esta forma conseguimos una estructura más cercana a un entorno de desarrollo profesional incorporando mecanismos reales de autenticación y protección de datos. 58 00:03:52,219 --> 00:03:55,900 A continuación mi compañero Jorge explicará cuáles fueron los problemas que nos encontramos durante el desarrollo. 59 00:03:56,240 --> 00:04:01,580 En esta parte de la defensa vamos a explicar los principales problemas que tuvimos durante el desarrollo de la aplicación, 60 00:04:01,860 --> 00:04:07,680 de qué forma conseguimos solucionarlos y especialmente qué aprendizaje sacamos de todos y cada uno de ellos. 61 00:04:07,680 --> 00:04:26,759 Uno de nuestros principales problemas fue que, dado que ninguno de los tres integrantes del grupo tenemos experiencia realmente en Kotlin multiplatform, pues carecíamos realmente de la experiencia para entrar de lleno en el desarrollo y, sobre todo, en cómo funcionan los módulos en una aplicación de este tipo, qué responsabilidad tiene cada módulo. 62 00:04:26,759 --> 00:04:52,300 Al final, un poco también por prueba y error, casi acabamos decidiendo que nuestros tres principales módulos, que iban a ser el server, el módulo server, que es el que tiene el backend independiente, hecho con Cator, el de JadeBrainz, y el tendríamos el módulo shared, que tendrían estructuras redutilizables, pero sobre todo tendríamos ComposeApp como módulo en el que se va a compartir casi todo el código común a todas las versiones de la aplicación. 63 00:04:52,300 --> 00:04:54,360 porque, claro, a su vez 64 00:04:54,360 --> 00:04:56,240 una aplicación multiplataforma tenemos que 65 00:04:56,240 --> 00:04:57,860 organizar cómo funcionan los targets 66 00:04:57,860 --> 00:05:00,040 y problemas que nacen de la compatibilidad 67 00:05:00,040 --> 00:05:02,120 entre versiones de librerías, de según 68 00:05:02,120 --> 00:05:04,339 dónde se vaya a ejecutar y este tipo de 69 00:05:04,339 --> 00:05:06,160 proyecto, pues eso es fundamental. La solución 70 00:05:06,160 --> 00:05:07,540 fue ir estabilizando poco a poco 71 00:05:07,540 --> 00:05:09,420 la configuración del Gradle 72 00:05:09,420 --> 00:05:12,000 separando responsabilidades y 73 00:05:12,000 --> 00:05:14,000 aprendiendo la arquitectura 74 00:05:14,000 --> 00:05:15,660 que es necesaria para una aplicación 75 00:05:15,660 --> 00:05:17,980 de estas características. Otro problema 76 00:05:17,980 --> 00:05:19,939 que tuvimos durante el desarrollo 77 00:05:19,939 --> 00:05:21,540 de la aplicación fue poner en práctica 78 00:05:21,540 --> 00:05:23,660 lo que hemos aprendido durante todo el año en la clase 79 00:05:23,660 --> 00:05:25,560 de acceso a datos. Una vez 80 00:05:25,560 --> 00:05:27,639 las tres tablas principales que utilizamos, que son 81 00:05:27,639 --> 00:05:29,139 usuarios, equipos y equipos lots, 82 00:05:29,740 --> 00:05:31,540 estaban disponibles y funcionaban 83 00:05:31,540 --> 00:05:33,680 perfectamente, nuestra primera decisión 84 00:05:33,680 --> 00:05:35,660 en realidad importante 85 00:05:35,660 --> 00:05:37,500 al respecto de cómo íbamos a realizar 86 00:05:37,500 --> 00:05:39,420 a trabajar con la database 87 00:05:39,420 --> 00:05:41,620 fue crear una clase en la que 88 00:05:41,620 --> 00:05:43,620 teníamos lógica centralizada de las operaciones 89 00:05:43,620 --> 00:05:45,699 que realizamos sobre la database para que 90 00:05:45,699 --> 00:05:47,680 cualquiera pudiera, cualquiera miembro del equipo 91 00:05:47,680 --> 00:05:49,100 pudiera mirarlo en cualquier momento, que es 92 00:05:49,100 --> 00:05:51,120 la database operations.kt 93 00:05:51,120 --> 00:05:52,980 y esto nos permitió gestionar 94 00:05:52,980 --> 00:05:54,879 de forma clara las operaciones que íbamos a hacer 95 00:05:54,879 --> 00:05:56,680 y el aprendizaje aquí fue básicamente 96 00:05:56,680 --> 00:05:59,120 separar rutas y lógica 97 00:05:59,120 --> 00:06:00,579 de datos y 98 00:06:00,579 --> 00:06:02,240 realizar al final 99 00:06:02,240 --> 00:06:05,019 operaciones complejas en 100 00:06:05,019 --> 00:06:07,000 una sola clase para que el código sea 101 00:06:07,000 --> 00:06:08,860 mantenible y menos propenso a errores 102 00:06:08,860 --> 00:06:11,240 uno de los problemas más importantes que tuvimos 103 00:06:11,240 --> 00:06:13,199 al final fue decidir cómo íbamos 104 00:06:13,199 --> 00:06:14,959 a enfrentar la seguridad 105 00:06:14,959 --> 00:06:16,540 de la aplicación, evidentemente al principio 106 00:06:16,540 --> 00:06:18,560 lo más básico era 107 00:06:18,560 --> 00:06:21,339 leer usuario y contraseña 108 00:06:21,339 --> 00:06:22,699 y buscar a ver que hiciera un match 109 00:06:22,699 --> 00:06:25,079 evidentemente tuvimos que 110 00:06:25,079 --> 00:06:27,000 decidir pues que seguridad 111 00:06:27,000 --> 00:06:28,279 íbamos a implementar 112 00:06:28,279 --> 00:06:30,120 en nuestra aplicación 113 00:06:30,120 --> 00:06:32,279 lo primero al final lo hicimos 114 00:06:32,279 --> 00:06:34,220 utilizando la librería Bcrypt 115 00:06:34,220 --> 00:06:36,620 para jasear las contraseñas 116 00:06:36,620 --> 00:06:38,959 y que compruebe la contraseña 117 00:06:38,959 --> 00:06:39,920 del usuario durante el login 118 00:06:39,920 --> 00:06:43,120 nosotros en nuestra base de datos 119 00:06:43,120 --> 00:06:45,120 si otra persona se hace un usuario 120 00:06:45,120 --> 00:06:47,620 realmente crea un usuario, nosotros no podemos ver 121 00:06:47,620 --> 00:06:51,819 realmente la contraseña como texto plano, evidentemente, 122 00:06:51,959 --> 00:06:54,060 porque para eso estaba Bacrit, para hashearla. 123 00:06:54,740 --> 00:06:58,779 Otra cosa que implementamos también fue realizar una protección 124 00:06:58,779 --> 00:07:01,899 un poco más robusta de las rutas privadas utilizando Authenticate 125 00:07:01,899 --> 00:07:04,899 y sobre todo lo más importante en cuanto a la seguridad 126 00:07:04,899 --> 00:07:07,819 fue implementar una autenticación mediante JSON Web Token. 127 00:07:08,379 --> 00:07:11,079 Básicamente el servidor genera un token 128 00:07:11,079 --> 00:07:13,220 y cuando el usuario inicia sesión correctamente 129 00:07:13,220 --> 00:07:16,779 y es ese token el que se utiliza para acceder a todas las rutas protegidas. 130 00:07:16,779 --> 00:07:19,060 nuestra implementación al final 131 00:07:19,060 --> 00:07:21,060 nos permite tener una autenticación 132 00:07:21,060 --> 00:07:22,980 stateless, que 133 00:07:22,980 --> 00:07:23,980 significa que al final 134 00:07:23,980 --> 00:07:27,220 no dependemos de que se guarde 135 00:07:27,220 --> 00:07:28,259 una sesión 136 00:07:28,259 --> 00:07:30,779 por así decirlo en el servidor 137 00:07:30,779 --> 00:07:32,879 y al final 138 00:07:32,879 --> 00:07:35,180 una de las mejoras más importantes 139 00:07:35,180 --> 00:07:37,120 fue que decidí que 140 00:07:37,120 --> 00:07:38,860 no íbamos a depender de enviar un 141 00:07:38,860 --> 00:07:41,480 user ID desde el cliente 142 00:07:41,480 --> 00:07:42,519 ya que el backend 143 00:07:42,519 --> 00:07:44,759 obtiene el usuario autenticado directamente 144 00:07:44,759 --> 00:07:46,980 desde el token. Esto al final es clave 145 00:07:46,980 --> 00:07:48,439 porque si no, evidentemente, pues 146 00:07:48,439 --> 00:07:50,980 un cliente podría mandar cualquier 147 00:07:50,980 --> 00:07:52,779 user ID cambiando 148 00:07:52,779 --> 00:07:55,040 un parámetro. El aprendizaje 149 00:07:55,040 --> 00:07:56,899 principal que sacamos de 150 00:07:56,899 --> 00:07:58,740 este problema en específico 151 00:07:58,740 --> 00:08:00,759 fue aprender a hacer un login 152 00:08:00,759 --> 00:08:02,639 en condiciones como 153 00:08:02,639 --> 00:08:04,920 almacenar contraseñas en una base de datos, 154 00:08:05,300 --> 00:08:06,660 implementar un hasheo 155 00:08:06,660 --> 00:08:09,019 y sobre todo 156 00:08:09,019 --> 00:08:10,699 aprender a validar la identidad 157 00:08:10,699 --> 00:08:11,740 desde el backend 158 00:08:11,740 --> 00:08:14,740 y garantizar que cada usuario 159 00:08:14,759 --> 00:08:16,519 y solo puede acceder a sus propios datos. 160 00:08:16,899 --> 00:08:20,100 El problema que tuvimos fue aprender a conectar correctamente 161 00:08:20,100 --> 00:08:21,879 el frontend con el backend de la aplicación. 162 00:08:22,279 --> 00:08:24,500 Dependiendo además de si se va a ejecutar en Android, 163 00:08:24,600 --> 00:08:26,899 en el emulador, en el escritorio, el host puede cambiar. 164 00:08:27,259 --> 00:08:30,259 Para ello, bueno, centralizamos host y ruta dentro de constante 165 00:08:30,259 --> 00:08:33,080 para no tener las URLs repartidas por toda la aplicación 166 00:08:33,080 --> 00:08:35,759 y garantizar, pues al final, una mantenibilidad y una legibilidad. 167 00:08:36,340 --> 00:08:40,320 Separamos llamadas de autenticación en una clase específica. 168 00:08:40,820 --> 00:08:43,899 Era importante manejar correctamente los errores 169 00:08:43,899 --> 00:08:49,940 Para que, bueno, si el servidor no está disponible, que la aplicación mostrara un mensaje elegible. 170 00:08:50,360 --> 00:08:52,480 Y para los equipos hicimos algo parecido también. 171 00:08:52,620 --> 00:08:55,899 Separamos las llamadas relacionadas con equipos en su propia API. 172 00:08:56,159 --> 00:09:01,879 Tomamos la decisión de que enviamos el token en la cabecera authorization como un better token. 173 00:09:02,320 --> 00:09:07,120 También implementamos un fallback en posibles hosts o llamadas. 174 00:09:07,620 --> 00:09:12,120 Tuvimos el problema de actualizar los equipos después de iniciar sesión o después de modificar datos. 175 00:09:12,120 --> 00:09:16,559 y añadimos un estado para mostrar errores relacionados con los equipos 176 00:09:16,559 --> 00:09:19,559 en vez de dejar un poco que el usuario no supiera que había pasado. 177 00:09:19,879 --> 00:09:23,919 Como extra, en la versión en Android tuvimos que declarar permisos como Internet 178 00:09:23,919 --> 00:09:26,139 y permitir ciertas conexiones durante el desarrollo. 179 00:09:26,600 --> 00:09:30,980 Aquí nuestro principal aprendizaje fue conectar frontend y backend 180 00:09:30,980 --> 00:09:36,820 yendo más allá de hacer simplemente peticiones HTTP y contemplar los distintos entornos, 181 00:09:37,220 --> 00:09:41,059 la sesión, los tokens, los errores de red, el feedback visual que le estamos dando al usuario en general. 182 00:09:41,059 --> 00:09:53,220 Uno de los bloques más complejos, a su vez, de todo el proyecto fue el motor del combate. La mejor idea era separar la lógica del combate en varias clases que tengan distintas responsabilidades. 183 00:09:53,960 --> 00:10:09,279 Este problema lo solucionamos a base de tener la clase Battle Engine, que actúa como el coordinador principal del combate y que básicamente resuelve turno, decide qué va a ocurrir y se apoya para cosas específicas en clases específicas. 184 00:10:09,279 --> 00:10:22,159 Como por ejemplo la Damage Calculator, que es un que hace el cálculo de daño, una parte matemática muy específica con muchos modificadores posibles y que lo mantuvimos en su clase individual. 185 00:10:22,159 --> 00:10:42,539 Otra de las clases en las que asignamos una responsabilidad es muy específica, la de Ability Effect Resolver, que básicamente lo que hace es resolver el efecto de una habilidad concreta, dado que las habilidades interactúan con cualquier aspecto de la batalla, tanto con estadísticas, con el porcentaje de daño, efectos variopintos. 186 00:10:42,539 --> 00:10:59,259 Tenemos PokeAPI Battle Data Source. Lo que hacemos en esta clase es asignarle la responsabilidad específica de cargar datos relacionados con Pokémon y los movimientos para que pueda trabajar con la información oficial de la PokeAPI. 187 00:10:59,960 --> 00:11:10,639 Y finalmente, para una lógica de este calibre, decidimos hacer una clase que era Battle Engine Test para hacer test y comprobar que el motor se comporta como esperamos. 188 00:11:10,639 --> 00:11:30,899 El aprendizaje principal que sacamos de aquí fue al final entender que cuando una lógica de dominio es muy compleja, la mejor forma de lidiar con ella es irla dividiendo en piezas muy pequeñas y así el código es más fácil de entender, de explicar, de probar y de ampliar. 189 00:11:31,120 --> 00:11:37,279 En este apartado ahora lo que vamos a hacer va a ser hablar de las diferentes partes que consideramos más importantes dentro de nuestro proyecto. 190 00:11:37,279 --> 00:11:45,039 El objetivo de haber creado una aplicación con una estructura multiplatform consiste en poder llegar a reutilizar la mayor parte de código posible. 191 00:11:45,399 --> 00:11:57,059 Durante las prácticas recibimos la recomendación de utilizar este tipo de arquitectura porque nos permitía poder utilizar el mismo código para poder crear una aplicación web, una aplicación móvil, una aplicación iOS y cosas similares. 192 00:11:57,059 --> 00:12:00,639 En esta ocasión en el proyecto se encuentran divididos en tres módulos principales 193 00:12:00,639 --> 00:12:02,100 Pero vamos a explicar un poquito 194 00:12:02,100 --> 00:12:06,919 El módulo Shared, que consiste en un módulo pequeño transversal 195 00:12:06,919 --> 00:12:08,679 Que tiene unas utilidades básicas 196 00:12:08,679 --> 00:12:12,500 Tenemos aquí cosas como constantes, clases básicamente de demo 197 00:12:12,500 --> 00:12:14,379 Y un poquito de información sobre la plataforma 198 00:12:14,379 --> 00:12:16,620 Una clase un poquito más secundaria 199 00:12:16,620 --> 00:12:20,659 Porque la principal clase que tenemos dentro de la estructura de CodeMultiplatform 200 00:12:20,659 --> 00:12:23,200 Consiste en el módulo ComposeUp 201 00:12:23,200 --> 00:12:36,159 En esta clase podemos ver dentro del despliegue del SRC las diferentes estructuras, que aquí es donde se encuentran las pantallas del Android, del apartado web, del apartado aplicación pura y del apartado iOS. 202 00:12:36,159 --> 00:12:38,700 que todas estas aplicaciones, todos estos módulos 203 00:12:38,700 --> 00:12:40,539 lo que hacen es tirar del propio módulo 204 00:12:40,539 --> 00:12:42,519 del Common Main, que es básicamente donde se 205 00:12:42,519 --> 00:12:44,620 encuentra la interfaz visual 206 00:12:44,620 --> 00:12:46,440 de todas las clases con toda 207 00:12:46,440 --> 00:12:48,620 la estructura que se ha ido utilizando 208 00:12:48,620 --> 00:12:50,639 para poder generar todas las diferentes 209 00:12:50,639 --> 00:12:52,139 funcionalidades 210 00:12:52,139 --> 00:12:54,799 dentro de lo que viene siendo la propia aplicación 211 00:12:54,799 --> 00:12:56,480 y luego por último tenemos el módulo 212 00:12:56,480 --> 00:12:58,480 Server, que básicamente es un backend independiente 213 00:12:58,480 --> 00:13:00,379 que utiliza Cator, que expone 214 00:13:00,379 --> 00:13:02,139 el API REST que estamos utilizando 215 00:13:02,139 --> 00:13:03,740 o en este caso más de un API REST 216 00:13:03,740 --> 00:13:05,960 se encarga de dar persistencia a los datos 217 00:13:05,960 --> 00:13:12,559 autentificar usuarios y servir la información que consumen o que utiliza la aplicación a los 218 00:13:12,559 --> 00:13:18,200 clientes el resultado final consiste en crear una única aplicación que se desarrolla en diferentes 219 00:13:18,200 --> 00:13:23,899 plataformas a la vez como hemos podido ver en plataformas como el propio ordenador web android 220 00:13:23,899 --> 00:13:28,279 ios de acuerdo la lógica es común ya que se escribe solamente una única vez como hemos podido 221 00:13:28,279 --> 00:13:33,759 ver en la parte del común main esto es mucho más práctico mucho más sencillo a la larga porque si 222 00:13:33,759 --> 00:13:38,000 Si se encuentra un bug en la lógica, se corrige una única vez para todas las plataformas 223 00:13:38,000 --> 00:13:43,200 y no hay que ir modificando cada plataforma de forma unitaria. 224 00:13:43,419 --> 00:13:46,679 La base de datos que hemos creado para este proyecto es una base de datos de MySQL 225 00:13:46,679 --> 00:13:51,559 con 10 tablas principales que lo que hacen es modelar el sistema completo de nuestra aplicación. 226 00:13:52,000 --> 00:13:54,980 Por un lado tenemos las datos más principales, los datos maestros, 227 00:13:55,299 --> 00:13:59,500 que son las tablas de los tipos, donde se almacenan los 18 tipos principales que hay de Pokémon, 228 00:13:59,860 --> 00:14:03,220 la tabla de habilidades, donde se almacenan todas las habilidades existentes en los Pokémon, 229 00:14:03,220 --> 00:14:06,980 la tabla de los objetos que te permite almacenar todos los objetos de forma individual 230 00:14:06,980 --> 00:14:10,419 la tabla de movimientos donde te permite almacenar todos los ataques que existen 231 00:14:10,419 --> 00:14:12,120 en la versión de la franquicia de los juegos de Pokémon 232 00:14:12,120 --> 00:14:14,340 por otro lado también tenemos lo que es la tabla de las especies 233 00:14:14,340 --> 00:14:18,179 que consiste básicamente en uno de los puntos más importantes que podemos definir 234 00:14:18,179 --> 00:14:22,320 de la aplicación porque es la que te permite tener localizado un Pokémon 235 00:14:22,320 --> 00:14:24,220 con sus características individuales 236 00:14:24,220 --> 00:14:27,379 ya que cada Pokémon tiene un valor de 6 características diferentes 237 00:14:27,379 --> 00:14:30,460 que son vida, ataque, defensa, ataque especial, defensa especial y velocidad 238 00:14:30,460 --> 00:14:39,240 Por otro lado, la tabla de usuarios, donde tenemos la información de cada usuario que utiliza la aplicación con su contraseña y su correo electrónico identificatorio. 239 00:14:39,419 --> 00:14:52,419 También tenemos lo que vienen siendo los equipos de cada usuario, que están asociados por el ID de cada usuario que se genera, a lo que viene siendo el equipo, de tal forma que equipo 1 tenga el ID de X jugador, equipo 2 puede tener el de X jugador o el de Y jugador. 240 00:14:52,419 --> 00:14:59,019 Por otro lado, tenemos la tabla de lo que son los equipos slots, que son datos persistidos de cada Pokémon en el equipo, ¿vale? 241 00:14:59,179 --> 00:15:07,159 De tal forma que son los movimientos propios, habilidades propias con los que estén usando, el objeto que haya equipado la persona, los puntos individuales y cosas similares. 242 00:15:07,279 --> 00:15:10,480 Datos de información que el usuario le ha metido a su propio equipo. 243 00:15:10,600 --> 00:15:20,539 Y luego tenemos lo que viene siendo, por último, la tabla de Pokémon movimientos, que básicamente lo que te hacen es una correlación entre la tabla de Pokémon y la tabla de ataques, 244 00:15:20,539 --> 00:15:24,360 permitiéndote decir que X Pokémon tiene acceso a estos determinados ataques 245 00:15:24,360 --> 00:15:31,019 porque si no, cada Pokémon podría tener acceso a los 800.000 ataques que existen dentro de la Database 246 00:15:31,019 --> 00:15:35,480 con lo cual, esta tabla es bastante, bastante necesaria, todavía sé 247 00:15:35,480 --> 00:15:38,820 La base de datos indica también a normalizar lo que me he mencionado, los datos al máximo 248 00:15:38,820 --> 00:15:40,860 Voy a poner un ejemplo de a lo que me refiero 249 00:15:40,860 --> 00:15:46,759 No tendría sentido que si tenemos un Pikachu y un Raichu, que son ambos del mismo tipo 250 00:15:46,759 --> 00:15:50,179 se duplicara el dato de que son de tipo eléctrico 251 00:15:50,179 --> 00:15:56,279 Con lo cual, lo que se hace es usar una referencia a ese tipo y solamente se utiliza una vez ese dato y no dos veces. 252 00:15:56,539 --> 00:15:58,080 ¿Esto qué es lo que nos permite tener? 253 00:15:58,279 --> 00:16:04,080 Nos permite tener por un lado una integridad referencial, es decir, no hay Pokémon con tipos inválidos, 254 00:16:04,259 --> 00:16:11,559 una mayor eficiencia, ya que permite que si a futuro se produjera el cambio de un Pokémon o de un nombre de un tipo, 255 00:16:11,960 --> 00:16:19,179 se podrá cambiar solamente una referencia y no tendrías que ir mil Pokémon revisando cuál sería la referencia correspondiente. 256 00:16:19,179 --> 00:16:26,039 y una mayor escalabilidad, ya que permite poder estar preparado para poder hacer millones de registros 257 00:16:26,039 --> 00:16:31,139 en vez de solamente uno al momento y da mayor facilidad a la hora de funcionar. 258 00:16:31,559 --> 00:16:34,799 También hay que mencionar que el servidor se conecta utilizando Exposed, 259 00:16:34,960 --> 00:16:38,399 que es básicamente para unas operaciones de tipo de type 6, ¿vale? 260 00:16:38,500 --> 00:16:42,799 Que se ejecutan directamente contra la base de datos, que esto es una funcionalidad propia de Kotlin. 261 00:16:43,000 --> 00:16:46,539 Vamos a hablar ahora de una cuestión de lo que viene siendo el corazón principal del proyecto, 262 00:16:46,539 --> 00:16:48,039 que es el motor interno de peleas, ¿vale? 263 00:16:48,039 --> 00:16:51,659 Es un motor que simula las batallas de los Pokémon 264 00:16:51,659 --> 00:16:53,639 Lo que viene siendo de la misma forma que en el juego 265 00:16:53,639 --> 00:16:55,919 Y se encuentra dividido principalmente en cuatro capas 266 00:16:55,919 --> 00:16:58,320 La primera de ellas es el Battle Engine 267 00:16:58,320 --> 00:17:00,600 Que es el director de orquesta, por así definirlo 268 00:17:00,600 --> 00:17:02,279 Es el que maneja los hilos 269 00:17:02,279 --> 00:17:05,640 Es la clase principal en la cual se dedica a gestionar el combate 270 00:17:05,640 --> 00:17:07,920 Ya que recibe la información de la pelea 271 00:17:07,920 --> 00:17:11,000 De, por un lado, los equipos de ambos jugadores 272 00:17:11,000 --> 00:17:12,680 Y, por otro lado, los datos de la batalla 273 00:17:12,680 --> 00:17:15,019 Es decir, los datos internos que tiene cada Pokémon 274 00:17:15,019 --> 00:17:17,039 Las estadísticas, unidades, etcétera 275 00:17:17,039 --> 00:17:27,099 ¿De acuerdo? ¿Cómo funciona? Pues lo que se dedica a hacer esta clase es Tom, que genera un estado inicial, que es lo que se denomina en los juegos como Battle Preview, ¿vale? 276 00:17:27,099 --> 00:17:39,279 Que ves a todos los Pokémon entrando y visualmente quedan ahí registrados, recibe las acciones de los jugadores y determina el orden de actuación, ya que aquí hay que tener en cuenta que los Pokémon van por velocidades, ¿vale? 277 00:17:39,279 --> 00:17:43,980 Luego pasa el tema de los valores de los daños al Damage Calculator 278 00:17:43,980 --> 00:17:44,859 Que ahora hablaremos de ello 279 00:17:44,859 --> 00:17:51,480 Y el tema de aplicar los efectos de las habilidades al Ability Effect Resolver 280 00:17:51,480 --> 00:17:55,259 Luego gestiona, lo que viene siendo los cambios de los Pokémon y las acciones entre turnos 281 00:17:55,259 --> 00:17:58,839 Pero básicamente se dedica a dirigir todo este tipo de acciones 282 00:17:58,839 --> 00:18:02,700 Y pasar a las clases siguientes los datos correspondientes 283 00:18:02,700 --> 00:18:04,039 ¿Qué cuáles son las clases siguientes? 284 00:18:04,160 --> 00:18:06,420 Pues mira, vamos a empezar primero con el Damage Calculator 285 00:18:06,420 --> 00:18:08,779 Que es básicamente el cerebro matemático 286 00:18:09,279 --> 00:18:25,400 En Pokémon cada golpe no genera el mismo valor de daño al oponente, genera un baremo de A a B y entre A y B hay 16 valores diferentes que puede llegar a dar ese ataque. 287 00:18:25,400 --> 00:18:34,619 Con lo cual hemos visto bastante necesario crear una calculadora interna, vamos a decirlo así, para poder llevar a cabo ese valor y dar fidelidad a los resultados. 288 00:18:34,619 --> 00:18:36,859 Por eso hemos visto la necesidad de crear esta clase 289 00:18:36,859 --> 00:18:38,859 Ya que a fin de cuentas es la que aplica 290 00:18:38,859 --> 00:18:40,099 Esa fórmula, ¿vale? 291 00:18:40,359 --> 00:18:42,859 Porque a fin de cuentas es una fórmula bastante compleja 292 00:18:42,859 --> 00:18:45,079 Porque tiene en cuenta diferentes baremos 293 00:18:45,079 --> 00:18:46,180 Diferentes multiplicadores 294 00:18:46,180 --> 00:18:49,400 Diferentes multiplicadores en negativo 295 00:18:49,400 --> 00:18:51,119 Es bastante complejo 296 00:18:51,119 --> 00:18:53,240 Y era bastante, bastante necesario esta parte 297 00:18:53,240 --> 00:18:55,240 Pero hay una forma que es bastante necesario 298 00:18:55,240 --> 00:18:57,099 El apartado del Ability Effect Resort 299 00:18:57,099 --> 00:18:59,319 Porque hay muchos Pokémon que sus habilidades 300 00:18:59,319 --> 00:19:01,599 Interactúan directamente con el cálculo de daño 301 00:19:01,599 --> 00:19:02,420 De las peleas 302 00:19:02,420 --> 00:19:14,039 Por ejemplo, hay algunos que tienen habilidades que bajan las estadísticas del rival. Hay otros que se dedican a modificar el clima de la batalla haciendo que haya otros ataques que sean más fuertes o menos fuertes, que permitan cambiar el terreno. 303 00:19:14,440 --> 00:19:21,960 Vale, todo esto tenemos que gestionar de alguna forma y hemos creado una clase que se dedica a gestionarlo para saber qué efecto se genera dependiendo de la situación. 304 00:19:21,960 --> 00:19:24,799 Y por último, sucede lo mismo con los objetos 305 00:19:24,799 --> 00:19:30,900 Hay objetos que se dedican a subir las características o bajar las características de los ataques del propio Pokémon 306 00:19:30,900 --> 00:19:37,279 De tal forma que era bastante necesario crear una clase que se dedicara a gestionar de forma interna 307 00:19:37,279 --> 00:19:42,319 Ese tipo de objetos para saber luego en el cálculo de daño cómo poder llegar a manejarlo 308 00:19:42,319 --> 00:19:44,680 El siguiente punto que vamos a tratar va a ser el tema de las API REST 309 00:19:44,680 --> 00:19:51,420 Para explicar y para partir de un punto inicial vamos a decir que los API REST son un conjunto de URLs 310 00:19:51,420 --> 00:19:53,859 que el cliente, dependiendo 311 00:19:53,859 --> 00:19:55,700 de donde sea, que en este caso 312 00:19:55,700 --> 00:19:57,720 como tenemos holding multiplatform, va a ser de diferentes 313 00:19:57,720 --> 00:19:59,839 sitios, consulta para poder 314 00:19:59,839 --> 00:20:01,380 obtener o guardar los datos de forma 315 00:20:01,380 --> 00:20:03,660 propia. Para poder llevar a cabo 316 00:20:03,660 --> 00:20:05,640 esto, hemos creado una serie de endpoints 317 00:20:05,640 --> 00:20:07,599 organizados en cinco categorías diferentes 318 00:20:07,599 --> 00:20:09,539 que cada uno va hacia 319 00:20:09,539 --> 00:20:11,660 un punto diferente. El primero 320 00:20:11,660 --> 00:20:13,660 de ellos es la autentificación, lo que es el login 321 00:20:13,660 --> 00:20:14,940 que hemos hecho lo que es básicamente 322 00:20:14,940 --> 00:20:17,579 un post, un post 323 00:20:17,579 --> 00:20:19,720 tirando los datos 324 00:20:19,720 --> 00:20:21,440 hacia el login con el JWT 325 00:20:21,440 --> 00:20:23,680 para poder hacer que el usuario 326 00:20:23,680 --> 00:20:25,680 utilizando el token que se genera 327 00:20:25,680 --> 00:20:27,299 pueda loguearse, eso por un lado 328 00:20:27,299 --> 00:20:29,779 luego, una de las principales 329 00:20:29,779 --> 00:20:31,119 puntos que 330 00:20:31,119 --> 00:20:33,680 más información nos proporciona 331 00:20:33,680 --> 00:20:35,859 es utilizar el API REST de la PokeAPI 332 00:20:35,859 --> 00:20:38,119 que es una de las dos APIs principales 333 00:20:38,119 --> 00:20:39,279 que hemos utilizado 334 00:20:39,279 --> 00:20:41,400 ¿por qué? porque de la PokeAPI hemos hecho 335 00:20:41,400 --> 00:20:43,759 diferentes GET para poder obtener tanto tipos 336 00:20:43,759 --> 00:20:45,579 como habilidades, como movimientos, como las especies 337 00:20:45,579 --> 00:20:47,559 de los Pokémon, porque si no, íbamos a tener 338 00:20:47,559 --> 00:20:49,319 que haber metido a mano una cantidad de datos que era 339 00:20:49,319 --> 00:20:51,240 inhumana, vamos a ser honestos 340 00:20:51,240 --> 00:20:53,740 luego el siguiente punto del cual vamos a hablar 341 00:20:53,740 --> 00:20:55,619 es la otra API REST, es la API REST de 342 00:20:55,619 --> 00:20:57,480 Limitless, que es una página 343 00:20:57,480 --> 00:20:59,559 donde puedes obtener la información de 344 00:20:59,559 --> 00:21:01,759 los equipos que se han estado jugando 345 00:21:01,759 --> 00:21:03,599 de tal forma que es lo que genera 346 00:21:03,599 --> 00:21:05,680 una database interna dentro de la 347 00:21:05,680 --> 00:21:07,700 aplicación del móvil de los datos 348 00:21:07,700 --> 00:21:09,900 de cada torneo, pudiendo consultar 349 00:21:09,900 --> 00:21:11,900 la información de que jugador 350 00:21:11,900 --> 00:21:13,759 ha jugado, que y que equipo ha quedado 351 00:21:13,759 --> 00:21:16,059 en que posición en un determinado 352 00:21:16,059 --> 00:21:17,559 torneo, luego por otro lado 353 00:21:17,559 --> 00:21:19,500 para poder mostrar la información de lo que viene siendo 354 00:21:19,500 --> 00:21:21,460 el propio usuario, hemos creado un endpoint 355 00:21:21,460 --> 00:21:23,400 que apunta al usuario utilizando el propio 356 00:21:23,400 --> 00:21:24,880 ID del usuario por medio 357 00:21:24,880 --> 00:21:27,140 del uso de un GET, ¿vale? 358 00:21:27,599 --> 00:21:29,259 Y luego, por último, pero no menos importante 359 00:21:29,259 --> 00:21:30,980 un GET para saber 360 00:21:30,980 --> 00:21:33,519 qué equipos tiene qué usuario 361 00:21:33,519 --> 00:21:34,980 como hemos dicho, hemos utilizado 362 00:21:34,980 --> 00:21:37,440 el propio ID del usuario para poder 363 00:21:37,440 --> 00:21:39,559 asignárselo a unos equipos, como he 364 00:21:39,559 --> 00:21:41,619 dicho antes cuando explicaba la parte de la database 365 00:21:41,619 --> 00:21:43,200 de tal forma que haciendo un GET 366 00:21:43,200 --> 00:21:45,220 podemos hacer que ese GET nos dé 367 00:21:45,220 --> 00:21:47,460 el equipo del usuario determinado 368 00:21:47,460 --> 00:21:51,220 que estamos usando ese id como digo apartado queremos hablar del tema de la seguridad vale 369 00:21:51,220 --> 00:21:55,920 que consideramos que es un tema muy importante a la hora de poder crear una aplicación ya no sea 370 00:21:55,920 --> 00:22:00,779 móvil sino multiplatform como es nuestro caso que hemos basado la seguridad principalmente en 371 00:22:00,779 --> 00:22:06,839 dos elementos importantes por un lado es el paseo de las contraseñas vale para poder dar esa 372 00:22:06,839 --> 00:22:15,339 seguridad a los usuarios de que las contraseñas no van a ser liqueadas con una facilidad alta y 373 00:22:15,339 --> 00:22:24,720 Y por otro lado, lo que viene siendo el logueo de los usuarios, que lo hemos hecho mediante un JWT, es decir, un JSON Web Token. 374 00:22:25,359 --> 00:22:29,140 Vamos a explicar un poquito rápidamente el flujo de cómo funciona este sistema, que básicamente sería, 375 00:22:29,880 --> 00:22:35,259 el usuario hace el logueo con lo que viene siendo tanto su nombre de usuario como la contraseña. 376 00:22:35,259 --> 00:22:40,319 Se mandan al servidor. Primero, se hace una comprobación de si este usuario está en la base de datos. 377 00:22:40,319 --> 00:22:42,299 ¿Existe? Sí 378 00:22:42,299 --> 00:22:44,019 ¿Tiene la contraseña correcta? Sí 379 00:22:44,019 --> 00:22:46,259 Perfecto, se genera el JWT 380 00:22:46,259 --> 00:22:48,700 Para poder utilizar las diferentes acciones 381 00:22:48,700 --> 00:22:50,400 Posteriores, ya sea 382 00:22:50,400 --> 00:22:52,720 Del equipo, del IDE 383 00:22:52,720 --> 00:22:53,740 Etcétera, etcétera 384 00:22:53,740 --> 00:22:56,140 ¿Cómo funciona esto a la hora de crear un usuario? 385 00:22:56,140 --> 00:22:57,240 Pues a la hora de crear un usuario 386 00:22:57,240 --> 00:23:00,240 Se hace primero la comprobación de si ese usuario 387 00:23:00,240 --> 00:23:01,579 Está en la base de datos 388 00:23:01,579 --> 00:23:04,279 Si no está en la base de datos, se va a generar un usuario 389 00:23:04,279 --> 00:23:06,400 Pidiendo al usuario que introduzca la contraseña 390 00:23:06,400 --> 00:23:07,980 La cual va a almacenarse 391 00:23:07,980 --> 00:23:10,059 Por medio de un raseo de contraseñas 392 00:23:10,059 --> 00:23:13,579 no se van a guardar en ningún momento en texto plano ni de forma similar 393 00:23:13,579 --> 00:23:16,960 porque eso no es una práctica segura y eso lo hemos podido ver también en clase 394 00:23:16,960 --> 00:23:20,099 de que es mucho más seguro aplicar el hashable 395 00:23:20,099 --> 00:23:23,299 y el funcionamiento ha sido similar al que hemos hecho en clase 396 00:23:23,299 --> 00:23:29,359 con lo cual nos parecía mucho más apropiado poder optar por ese tipo de almacenamiento 397 00:23:29,359 --> 00:23:31,799 para poder dar esa seguridad al usuario 398 00:23:31,799 --> 00:23:35,460 y una vez se ha almacenado la base de datos se genera el usuario y ya el usuario puede lograrse 399 00:23:35,460 --> 00:23:38,480 A continuación os haré una demostración de lo que es capaz de hacer nuestra aplicación 400 00:23:38,480 --> 00:23:42,500 Lo primero que vemos al iniciar es el formulario de inicio de sesión con el botón de crear cuenta. 401 00:23:43,039 --> 00:23:45,559 Al darle, se abrirá el formulario para crear una nueva cuenta. 402 00:23:46,079 --> 00:23:51,859 Al rellenar el formulario y darle al botón de crear cuenta, nuestra cuenta será creada y nos dejará acceder. 403 00:23:52,259 --> 00:23:56,500 Una vez acceda a la aplicación, llegas al menú principal, donde hay 7 opciones disponibles. 404 00:23:56,960 --> 00:24:00,819 La primera opción es el Team Builder, que como su nombre indica, nos permite crear nuestro propio equipo. 405 00:24:01,259 --> 00:24:05,799 Esta de aquí es una función que tiene la aplicación y facilita mucho el trabajo al momento de crear tu equipo, el autocompletado. 406 00:24:05,799 --> 00:24:09,319 No solo lo tiene Pokémon, sino también el objeto que puede llevar 407 00:24:09,319 --> 00:24:13,200 Otra función que tiene la aplicación es mostrarte la cantidad de habilidades que tiene un Pokémon 408 00:24:13,200 --> 00:24:15,299 Si bajamos un poco más abajo podemos ver los movimientos 409 00:24:15,299 --> 00:24:17,960 Que al igual que el nombre y los objetos, también tiene autocompletado 410 00:24:17,960 --> 00:24:20,799 Si bajamos más abajo tenemos los puntos de esfuerzo 411 00:24:20,799 --> 00:24:23,740 Los cuales nos permiten darle ciertas características a nuestros Pokémon 412 00:24:23,740 --> 00:24:25,740 Si seguimos bajando podemos ver los botones 413 00:24:25,740 --> 00:24:27,940 El de guardar equipo y el de cambiar el meta 414 00:24:27,940 --> 00:24:31,779 Si le damos a guardar equipo podemos ver cómo se ha creado el equipo y también se ha guardado 415 00:24:31,779 --> 00:24:33,299 La siguiente opción sería equipos propios 416 00:24:33,299 --> 00:24:34,700 Nos permite ver los equipos que tenemos 417 00:24:34,700 --> 00:24:37,859 Eso sí, el Team Builder nos da una forma de añadir equipos a esta aplicación 418 00:24:37,859 --> 00:24:41,700 Si la damos aquí, podemos importar un Pokémon siguiendo el mismo estilo de importación de Shodown 419 00:24:41,700 --> 00:24:44,039 Y después podemos pegar el texto de Shodown abajo 420 00:24:44,039 --> 00:24:50,190 Al darle a importar, se nos crea justo debajo el equipo que acabamos de importar 421 00:24:50,190 --> 00:24:52,650 Pasando con la tercera opción, tenemos el simulador de batallas 422 00:24:52,650 --> 00:24:54,630 Nos permite seleccionar uno de los equipos que tengamos 423 00:24:54,630 --> 00:24:56,730 El botón de simulador de batallas nos lleva al simulador de batallas 424 00:24:56,730 --> 00:25:00,029 Aunque el nombre diga jugador 1 y jugador 2, realmente es el usuario que controla los dos equipos 425 00:25:00,029 --> 00:25:02,730 Esto le permite crear situaciones que de otro modo no se podrían dar 426 00:25:02,730 --> 00:25:05,029 El simulador de combate funciona como los juegos originales 427 00:25:05,029 --> 00:25:10,210 Tras elegir las acciones contra el primer equipo, puedes darle al botón Cambiar Jugador para tomar las acciones del segundo jugador 428 00:25:10,210 --> 00:25:14,750 Tras seleccionar las acciones de los dos equipos, podrás darle al botón de Jugar Turno, el cual las ejecutará 429 00:25:14,750 --> 00:25:18,930 En el recuadro de encima, se puede ver los movimientos que se han realizado y en el orden en el que se ha hecho 430 00:25:18,930 --> 00:25:23,509 El botón de calculadora te permite ir a la calculadora de daño y guardar el replay te permite guardar la repetición del combate 431 00:25:23,509 --> 00:25:25,130 Nuestra cuarta opción es la tabla de tipos 432 00:25:25,130 --> 00:25:31,029 Una vez aquí podrás seleccionar cualquiera de los 18 tipos y te dirá sus debilidades, sus naturalidades, sus resistencias y sus inmunidades 433 00:25:31,029 --> 00:25:33,349 Como en Pokémon, los Pokémon pueden tener hasta dos tipos 434 00:25:33,349 --> 00:25:36,390 Puedes seleccionar tus equipos y verás la combinación de sus debilidades y resistencias 435 00:25:36,390 --> 00:25:38,130 La quinta opción es la base de datos de los torneos 436 00:25:38,130 --> 00:25:40,990 Aquí puedes filtrar por meta y seleccionar cualquier torneo 437 00:25:40,990 --> 00:25:42,970 Donde verás los jugadores que participaron en este 438 00:25:42,970 --> 00:25:46,309 Puedes guardarte sus equipos y utilizarlos en la aplicación 439 00:25:46,309 --> 00:25:48,210 Nuestra quinta opción es la calculadora de daño 440 00:25:48,210 --> 00:25:52,490 Utilizan todos los datos de los Pokémon para calcular el daño que realizarían en una situación concreta 441 00:25:52,490 --> 00:25:54,910 Nos dirigimos abajo para seleccionar el ataque que queremos hacer 442 00:25:54,910 --> 00:25:58,529 Podemos elegir las condiciones del campo y cuando ya esté todo listo le damos a calcular daño 443 00:25:58,529 --> 00:26:00,450 Al dar a calcular daño nos aparece una ventana emergente 444 00:26:00,450 --> 00:26:09,609 Con el nombre del ataque, el daño mínimo que se podría haber hecho, el daño máximo que se podría haber hecho, la media del daño, la cantidad de DPS que le podrías haber quitado al Pokémon y cuántos golpes habría que darle para derrotarlo. 445 00:26:09,730 --> 00:26:14,690 Por último, tenemos el Replace, donde podemos ver la repetición de los combates que hayamos guardado en el simulador de batallas. 446 00:26:15,849 --> 00:26:19,569 Darle, podemos ir turno por turno y ver lo que fue ocurriendo. 447 00:26:19,950 --> 00:26:20,829 Vayamos con las conclusiones. 448 00:26:21,150 --> 00:26:28,329 En conclusión, el desarrollo de este proyecto nos ha permitido aplicar de forma práctica gran parte de los conocimientos adquiridos durante el ciclo de desarrollo de aplicaciones múltiples de forma, 449 00:26:28,329 --> 00:26:32,849 integrando tantas tecnologías de frontend como de backend dentro de una arquitectura completa y funcional. 450 00:26:33,190 --> 00:26:36,529 Podemos afirmar que se han cumplido los objetivos principales que se han planteado desde el inicio del proyecto. 451 00:26:36,809 --> 00:26:40,109 Hemos conseguido desarrollar una aplicación multiplataforma orientada al competitivo de Pokémon 452 00:26:40,109 --> 00:26:43,250 capaz de gestionar equipos, consultar información relevante 453 00:26:43,250 --> 00:26:48,109 y ofrecer una experiencia más cómoda y adaptada al entorno móvil que las soluciones actualmente disponibles. 454 00:26:48,390 --> 00:26:51,990 Además del resultado funcional obtenido, el proyecto ha supuesto una experiencia muy importante 455 00:26:51,990 --> 00:26:54,150 de aprendizaje técnico y organizativo. 456 00:26:54,150 --> 00:27:05,990 El uso de Kotlin Multiplatform nos permitió comprender mejor los beneficios y desafíos del entorno multiplataforma, especialmente en aspectos relacionados con la reutilización de lógica de negocio, la modularidad del código y la compatibilidad de tecnologías. 457 00:27:06,190 --> 00:27:13,769 Desde el punto de vista técnico, tecnologías como Kator, MySQL y JWT han sido fundamentales para construir una herramienta robusta, segura y organizada. 458 00:27:13,869 --> 00:27:23,490 La implementación de autenticación mediante tokens, la gestión estructurada de la base de datos y la separación cliente y servidor nos han permitido acercarnos a un modelo de desarrollo similar al utilizado en aplicaciones reales del sector. 459 00:27:23,890 --> 00:27:26,230 A lo largo del proceso, también hemos encontrado diferentes dificultades 460 00:27:26,230 --> 00:27:28,150 relacionadas principalmente con la configuración del entorno 461 00:27:28,150 --> 00:27:30,230 de la plataforma, la integración del frontend y backend 462 00:27:30,230 --> 00:27:32,029 y la adaptación de ciertas dependencias y librerías. 463 00:27:32,670 --> 00:27:34,150 Sin embargo, la resolución de estos problemas 464 00:27:34,869 --> 00:27:36,309 ha sido precisamente una de las partes 465 00:27:36,309 --> 00:27:38,269 más enriquecedoras del proyecto, ya que nos ha obligado 466 00:27:38,269 --> 00:27:40,289 a investigar, experimentar y mejorar nuestra capacidad 467 00:27:40,289 --> 00:27:42,049 de análisis de los usos y resolución de problemas. 468 00:27:42,369 --> 00:27:44,170 Finalmente, consideramos que este proyecto no sólo 469 00:27:44,170 --> 00:27:46,210 cumple los requisitos académicos planteados, sino que 470 00:27:46,210 --> 00:27:48,029 también demuestra el potencial que puede tener una aplicación 471 00:27:48,029 --> 00:27:50,089 de este tipo dentro de un nicho concreto, como el competitivo 472 00:27:50,089 --> 00:27:52,269 de Pokémon. Además, dejará abiertas las posibles 473 00:27:52,269 --> 00:27:55,829 líneas de mejora futuras como la ampliación de funcionalidades, la integración de nuevas APIs 474 00:27:55,829 --> 00:27:59,609 o la expansión completa hacia otras plataformas soportadas por Kotlin Bully Platform. 475 00:28:00,150 --> 00:28:02,930 En definitiva, este trabajo supuso una experiencia completa de desarrollo de software, 476 00:28:03,289 --> 00:28:07,670 combinando planificación, diseño, programación, trabajo en equipo y resolución de problemas 477 00:28:07,670 --> 00:28:11,250 dentro de un proyecto técnico realista y cercano al ámbito profesional.