Implementación no trivial del tres en raya para Android. Otro tres en raya usando Java Swing

diosAlex 9 de diciembre de 2013 a las 23:09

Implementación no trivial de tres en raya para Android

  • Desarrollo de juegos

Este artículo le contará sobre el tres en raya en Java para Android en 30 líneas de código, cinco mil quinientas líneas de código, y por qué se necesitó tanto código para un juego tan simple hace exactamente un año, en diciembre. 9, publiqué mi juego en Google Play. Ahora quiero hablar sobre lo que pasó este año, cómo creció la popularidad del juego, qué funcionó y qué no. Verás estadísticas puras de descargas y eliminaciones para el año de existencia del juego, números. Descubrirás cuántos usuarios puedes reunir en Google Play, cómo se creó el juego y cómo se probó. Descubrirás si es posible crear un juego independiente con un presupuesto cero, qué dificultades puede tener un juego pequeño en un mundo grande. Si estás interesado, eres bienvenido.

Introducción

Llevo mucho tiempo intentando hacer juguetes, en su mayoría sencillos. Después de estudiar Java, decidí intentar hacer algo útil en este lenguaje. Después de saber que se pueden escribir juegos en Java para Android, decidí intentar hacer algo sencillo para aprender a crear aplicaciones para teléfonos inteligentes.

Idea

Ahora necesitamos una idea que sea demasiado simple para que no haga perder tiempo si falla, y demasiado única para que la aplicación no se quede como un nombre más en una lista multimillonaria de aplicaciones. Elegí Tic Tac Toe. ¿Demasiado fácil? ¿Hay muchos juegos de este tipo? Ahora teníamos que pensar en algo único. ¿Por qué todos los sistemas informáticos de tres en raya funcionan ahora según el principio de conjunto de clics? Entonces decidí que les dejaría dibujar las figuras, y el juego mismo determinará lo que se dibuja. Ahora, ¿cómo hacer la implementación? Opciones:
  • Cada celda es una imagen rasterizada. Desventaja: no se puede dibujar en el extranjero, de lo contrario parece torcido y desperdicia memoria. Ventajas: simplicidad. Un análogo de esto ya existe en Internet, pero no para dispositivos móviles.
  • Todo el campo es una imagen rasterizada. Desventaja: busque formas en toda la imagen, probablemente sea más fácil reconocer el texto impreso que encontrar formas torcidas, el problema de separar formas pegadas, alta carga de CPU. Ventaja: puedes dibujar una cruz corriendo hacia una celda adyacente.
  • Las formas son vectoriales. Desventajas: volver a dibujar puede llevar mucho tiempo (pero esto se puede solucionar con la ayuda del almacenamiento en búfer). Ventajas: puedes separar figuras que hayan chocado entre sí, puedes cambiar fácilmente la posición de figuras individuales.
Después de buscar en Google un poco, no pude encontrar nada parecido. Por qué la idea es buena: Dibujar figuras le da al juego libertad de elección; el propio jugador determina la forma de la figura. Por lo tanto, la idea del juego es brindar libertad para dibujar, libertad para "hacer trampa": dibujar las piezas del oponente y libertad en la configuración y las reglas del juego (tamaño del campo, etc.). También hay desventajas: dibujar. una cruz o un cero lleva mucho más tiempo que simplemente hacer clic en una celda. Por lo tanto, no a todos les gusta este juego. Antes de crearlo, busqué rápidamente juegos similares. No he encontrado ningún juego similar para Android. Pensé en llamar al juego “tic-tac-toe” + dibujo. Como hay tantos Tic Tac Toes en Android, decidí posponer esta etapa corta y difícil de crear el juego y pensar en el nombre final después de que el prototipo funcional estuviera listo. Después del prototipo funcional, comencé a escribir el nombre. el juego. Luego, al traducir "tic-tac-toe" al inglés y agregarle la palabra "Draw", encontré, quizás, el único competidor del tres en raya con el dibujo. Necesitaba incluir "dibujo" en el nombre del juego. Después de pensar en la palabra Draw y la necesidad de incluir una palabra sobre dibujo en el nombre del juego, se decidió llamar al juego “Tic-Tac-Toe Drawing”. Si dices que el nombre es muy similar a otros, entonces, ¿cómo se puede llamar al juego de tres en raya, si tan solo hay más de 250 juegos de tres en raya pagos en GooglePlay y más de 1000 gratuitos?

Implementaciones

ACTUALIZACIÓN: Características del juego que debían implementarse:
  • Dibujo de figuras por parte del jugador, reconocimiento de figuras dibujadas.
  • Diferentes tamaños del campo de juego: desde 3x3 hasta 12x12 (es posible más, pero los tamaños de pantalla de muchos dispositivos imponen limitaciones)
  • En consecuencia, el juego no es solo 3 seguidos, sino también 4, 5, 6 seguidos.
Aquí es donde surgieron los problemas: como puedes dibujar en cualquier lugar, puedes cambiar las piezas del oponente, tapar, dibujar, borrar, incluso moverte en una jugada que no es la tuya (sí, esto se puede hacer si dibujas dos cruces en el mismo tiempo). ¿Qué hacer en tales casos? ¿Prohibir o permitir? Se decidió permitirlo, dejando la posibilidad de ganar en cualquier opción, pero luego al ganar presentar una queja por trampa, por ejemplo: “Victoria injusta”.
Seleccionar un entorno de desarrollo y un plan general de creación.
Mientras buscaba algo para escribir en Android, descargué Eclipse, estudié la documentación y decidí comenzar con lo que tenía instalado: NetBeans. Pensé que en Android Java es lo mismo que Java simple, deberían ser casi iguales, lo que significa que la lógica (motor) se puede escribir por separado. Según mi plan, el juego debería haberse dividido en dos paquetes: Motor y GUI. . Motor: motor de juego, lógica de juego, este paquete es casi independiente de la implementación de la GUI y del sistema operativo, tiene una interfaz para interactuar con la GUI: recibir eventos entrantes y de salida. GUI: depende del sistema operativo. Para portar a Android, sólo necesitas reemplazar la GUI, sin tocar el Engine, o casi sin tocarlo. Ese era mi plan. La idea era buena, pero durante la traducción a Android surgió un problema de adaptación (que se describe a continuación).

Desarrollo de juegos

Ciclo de desarrollo del programa
Para saber exactamente qué hacer, decidí anotar punto por punto lo que debería hacer mi juego, lo que hay que implementar. Primero, tomé una hoja de papel en la que anoté las secciones principales: Motor, IA, Gráficos, Errores, etc., luego, para cada categoría, anoté los problemas que debían resolverse. A medida que resolvía el problema, marcaba lo que se había hecho, mientras estaba inactivo, agregué nuevas ideas a las listas y, a medida que depuraba, amplié la sección de "errores" y, a veces, otras secciones. Luego se me acabó la segunda hoja, pero dos hojas fueron suficientes, ya que el desarrollo y el soporte no se planificaron en un par de días, sino durante un período largo, para la planificación y un mejor soporte no solo se utilizaron hojas con puntos, sino que también se utilizaron. También decidí comentar bien el código (ver Javadoc). El desarrollo del juego se dividió en 2 etapas: hacer una versión para PC, transferirla a Android. La primera versión (alfa) se hizo para PC. Se implementó por etapas:
  1. GUI simple, dibujando líneas con el mouse, celdas. Reconocer líneas como cruces y dedos de los pies. (Se coloreó las formas de acuerdo con la forma reconocida para depurar el reconocimiento de formas, luego, como quedó bien, se dejó en el juego)
  2. reglas de victoria, juego de hombre a hombre.
  3. IA para juegos, modo de juego de computadora.
El ciclo de vida de cada una de las etapas anteriores:
  1. compilar una lista de requisitos básicos
  2. programación
  3. comprobando el resultado y depurando. Si se identifican errores que son incompatibles con la vida del programa, vaya al paso 1)
En la siguiente etapa, los algoritmos se perfeccionaron. El ciclo de estas mejoras es similar al descrito anteriormente:
  1. elaborando una lista de problemas
  2. programación
  3. comprobando el resultado y depurando. Ahora buscábamos qué se podía mejorar.
Estas mejoras se relacionaron principalmente con el reconocimiento de figuras y el juego de bots, sobre lo cual hablaremos más adelante. En la segunda etapa, el juego fue portado a Eclipse y comenzó la creación de una GUI para Android. Luego surgieron problemas con la compatibilidad del motor. con Android. En Java normal, las coordenadas del lienzo (gráficos) son int, en Android son flotantes. Los nombres de muchas funciones y clases de dibujo también diferían (Canvas = Drawable, etc.) y la estructura de datos de drawLine. Tuve que escribir un "convertidor de formato" (una muleta) y reemplazar los nombres de los tipos de datos. Logré hacer todo esto en poco más de una hora, ya que principalmente había diferencias en los nombres, no en la lógica. Luego, se realizaron más mejoras y dibujos. Después de completar la integración del motor y la nueva GUI, comenzaron las pruebas y la depuración. El ciclo de vida de estas mejoras es similar al descrito anteriormente:
  1. Probar el juego: iniciar el juego en un emulador con diferentes tamaños de pantalla.
  2. Hacer una lista de problemas
  3. Solucionando problemas
Este proceso continuó hasta que se decidió que el juego no tenía fallas. Se lanzó la primera versión, la versión 1.0.

Cómo se escribió la IA

Desarrollar un bot (en el juego se utilizó el término IA para abreviar) para un simple tres en raya parece una tarea fácil. Pero quería que esta IA fuera universal: trabajar en un campo de cualquier tamaño, y no sólo en el juego 3 en fila, sino también en n-fila. Algunas implementaciones de IA funcionaron bien en un campo grande, pero se perdieron fácilmente en un campo pequeño, o viceversa. Dado que cada implementación de IA requería pruebas no solo para determinar algún tipo de rendimiento, sino también para determinar la capacidad de ganar el juego, primero tuve que hacerlo. prueba la IA manualmente, solo juega con ella. Pero era demasiado lento y medir el éxito era subjetivo, así que encontré otra forma de probar la IA. La IA se implementó íntegramente como una clase, por lo que se creó un programa separado para probar la IA: se lanzaron dos clases diferentes de IA, que jugaron en un campo de juego virtual, los resultados se enviaron a la consola (quién ganó, en cuántos movimientos). Además, si era necesario, los datos de cada movimiento se mostraban en formato de texto (situación del juego, qué piensa la IA sobre la rentabilidad de cada celda). El estándar para un oponente que jugaba bien era tomar la IA de código abierto del juego cinco veces seguidas, lo cual encontré en la plataforma de lanzamiento. Se realizaron pruebas varias veces para reducir la influencia de la aleatoriedad. Entonces se crearon 2 IA: normal y compleja. Eran muy diferentes en su implementación: la IA compleja funcionaba según el principio de rastrear posibles caminos, y la media, según un principio simple: clasificar las celdas dónde colocarlas para ganar, o no perder, o al menos algo parecido, en caso contrario. aleatorio. Por lo tanto, hasta que optimicé el algoritmo de análisis de victorias (versión 1.5), la IA compleja era muchas veces más rápida que la media y fácil en juegos con un campo mayor a 3x3. Después de eliminar gran parte de la IA promedio, apareció un nivel de dificultad fácil.

Código

Brevemente sobre el código del juego en Java: en total, el código fuente puro ocupa 275 kB, de los cuales el 75% es Engine. El código se divide en tres paquetes y se utilizan alrededor de 22 clases. Sólo hay 5670 líneas de código, pero hay muchos comentarios, partes comentadas y líneas vacías, por lo que hay alrededor de 4000 código puro, en promedio 250 líneas por clase (archivo). El motor principal se escribió en un par de semanas, fue posible acelerarlo hasta una semana. Al escribir el código, marqué los lugares peligrosos donde podría ocurrir un error (por ejemplo, el diseño resultó ser demasiado confuso). con comentarios // FIXME y // TODO. Concentración de comentarios: un FIXME por 400 líneas de código y aproximadamente 1 TODO por 60 líneas de código. Cuando se identificaron fallas durante el proceso de prueba, entendí inmediatamente qué corregir o los errores se ubicaron precisamente en estas líneas. Además, antes de cada lanzamiento, probé el juego jugando de 5 a 10 veces con diferentes configuraciones.

Spoiler sobre la prueba

Mientras probaba chamánicamente el modo multitáctil, en lugar de errores en el juego, encontré un error en Android: es posible repetirlo usando los 10 dedos, haciendo movimientos complejos en la pantalla. Para restaurar la funcionalidad del dispositivo después de esto, es necesario reiniciar.

El resultado es una aplicación confiable, como lo demuestra el informe Crashes y ANR.
Sin embargo, hay otras explicaciones para esto: se utilizaron bloques try-catch, lo cual no es muy bueno, pero en algunos casos, si ocurre una falla, la aplicación debería desactivar automáticamente las funciones "experimentales" y cambiar a sus versiones anteriores; o esta página no funciona para Google; o muy pocas personas están usando el juego.

Lanzamientos y traducciones

Traducir aplicaciones para Android es muy sencillo: todas las cadenas se almacenan en el archivo strings.xml, que se encuentra en la carpeta de valores. El idioma predeterminado es el inglés. Para agregar el idioma ruso (y otros), debe crear una carpeta valores-ru y crear una copia del archivo strings.xml, que contendrá los reemplazos de cadenas traducidas. Todas las cadenas de la aplicación se obtienen a través de la API. Para acelerar la traducción, puede utilizar traductores electrónicos, por ejemplo, yo utilicé Google Translator. Antes de traducir el juego, no confiaba en mi conocimiento del idioma inglés; después de la traducción, la incertidumbre en mi conocimiento del inglés se transfirió a Google Translator. Al traducir, como mínimo, intente traducir las oraciones resultantes al idioma de origen. Mejor aún, traduzca las palabras usted mismo y compárelas con cómo las tradujo el traductor. Ha habido casos en los que un traductor informático distorsionó demasiado el significado y aceptó que algunas palabras tuvieran significados diferentes. La aplicación fue traducida al inglés, por lo que admite dos idiomas. Hablaré más sobre el impacto de las traducciones en las descargas. El primer lanzamiento del juego fue la noche del 9 de diciembre de 2012. Al día siguiente vi en las estadísticas que el primer día hubo 1 descarga. Antes del lanzamiento, pensé qué número darle a la primera versión: ¿0,99 o 1,0? Como decidí que casi todo funcionaba en el emulador y podía jugar en él, tengo derecho a dar el número 1.0. Estaba muy equivocado entonces, porque muchos usuarios se quejaron de que no podían jugar a este juego, otros se quejaron de que tenían grandes frenos en el juego, otros de que los gráficos no se mostraban correctamente. Y tenían razón, era imposible jugar en el 90% de los dispositivos reales. El problema con los frenos se debía a que los gráficos se dibujaban volviendo a dibujar todo el lienzo Drawable, y se gastaban muchos recursos en esto. Tuve que buscar rápidamente una solución y la encontré. No, no a través de OpenGL. El redibujado se puede realizar como todo el lienzo (repaint(), onPaint()) o como fragmentos (repaintCliping(), onPaint(rect)). Tuve que enseñar el juego a determinar las áreas de redibujo requeridas y planificar las zonas de redibujo con anticipación (combinar zonas de intersección, zonas de redibujo separadas). Todas estas mejoras y consejos de algunas personas se recogieron en la nueva versión, que salió una semana después. Durante cada actualización, pensaba que todas las versiones anteriores eran malas y no se podían reproducir, pero la nueva es la mejor y no hay ninguna. Solo tiene un defecto. Las pruebas de lanzamiento se llevaron a cabo inicialmente solo en el emulador. Este dispositivo me fue útil sólo para dos propósitos: 1) darme cuenta de lo torcido que estaba el dibujo, que el emulador no mostró; 2) probar el modo multitáctil Mientras desarrollaba y probaba el juego durante tanto tiempo, estaba acostumbrado a dibujar figuras pares y no me di cuenta de que no todos los jugadores dibujaban figuras iguales, por lo que a menudo las figuras no eran reconocidas. Por eso, cuando dejé jugar a mis amigos, me sorprendió que el programa casi no entendía sus dibujos. Tuve que mejorar el reconocimiento nuevamente, ya que la lista de cambios está en la página sobre el juego, enumeraré los principales problemas que resolvieron las actualizaciones de la Versión 1.1: desde que la superficie se implementó como una Vista sobrecargada y se volvió a dibujar todo el Lienzo. , los gráficos eran lentos. Se implementó soporte para PaintClipping(), lo que hizo posible volver a dibujar no toda la pantalla, sino solo la parte cambiante, lo que aceleró significativamente el dibujo. Próximas actualizaciones: se solucionaron problemas con la animación de movimiento deshabilitándola; diseño; multitáctil; archivos. Primeras optimizaciones: noté que accidentalmente copié el renderizado dos veces y dibujé lo mismo dos veces; optimización mediante cachés.

¿Errores del compilador? ¿Existen?

En algún lugar leí un artículo sobre cómo los errores del compilador son en realidad errores del programador. Pero encontré errores reales, por supuesto, no errores del compilador en sí, sino errores del SDK y del complemento:
  • El emulador emula dispositivos "ideales". Durante las pruebas, los gráficos en el emulador parecían normales. También se veía bien en algunos dispositivos. Pero en la mayoría de los dispositivos reales, al animar figuras, los gráficos se veían terribles: las figuras se bifurcaban y se movían juntas (presumiblemente la razón fue el uso de matrices de transformación Matrix, para Java esto es AffineTransform).
  • ¿Error del compilador? Pasé bastantes horas depurando la salida del tiempo del juego: el tiempo del juego se mostraba dentro del botón, como su nombre, y no en un campo de texto. Solución: resulta que tenías que presionar el botón “limpiar proyecto” antes de compilar y luego todo quedó arreglado (en la documentación oficial de Google se recomienda presionar este botón cada vez antes de compilar el apk, pero ¿quién lo lee?) . Esto puede deberse a la actualización del SDK, pero la confusión de los nombres de los recursos puede atribuirse a errores del compilador.
  • También hubo errores de API, algunas funciones obsoletas, nuevamente, se comportaron como se esperaba en el emulador, pero devolvieron valores extraños en un dispositivo real.
También me gustaría hablar del ProGuard integrado. Al contrario de lo que la mayoría piensa, funciona, pero con condiciones: no debe haber espacios ni caracteres cirílicos en la ruta al SDK, carpeta de inicio, carpeta con el proyecto y su nombre. Entonces funciona (no olvides descomentar las líneas necesarias en project.properties. Puedes hacer la ofuscación manualmente (tuve que hacer esto hasta que configuré ProGuard). Para hacer esto, primero haga una copia del proyecto. Luego vamos a la configuración del proyecto (donde agregar) y desmarcamos las casillas de verificación innecesarias en la sección "Compilador Java" en el grupo "Generación de Classfile". A continuación, utilizando las herramientas “Refractor>Mover...” y “Refractor>Rename...”, convertimos el código en algo incomprensible. A medida que avanza en este proceso, recuerde verificar la funcionalidad residual del código.

Gráficos

Para dibujar gráficos utilizamos: GIMP, un editor de trama, e Inkscape, un editor de vectores. A primera vista, GIMP me pareció mucho peor que Photoshop, no tiene algunas funciones (o no las encontré). Al principio parece muy incómodo y parece como si ni siquiera pudieras trazar una línea. Pero con el tiempo te acostumbras y te das cuenta de que es como abrir Photoshop por primera vez, cuando antes solo habías visto Paint. Puedes dibujar en GIMP, Inkscape también es más simple que CorelDraw, pero no necesitaba nada más. Un editor bastante simple y comprensible. El diseño exitoso no resultó de inmediato.

Más fotos

Estadística y Marketing

No esperaba ganar dinero con el juego, así que no lo publicité en ningún lado. Además, decidí recopilar estadísticas reales en ausencia de publicidad, por lo que a veces ni siquiera lo promocionaba, pero todavía había algunos intentos de promoción.

Promoción

La primera semana intenté escribir sobre el juego en el sitio web de 4PDA. Escribí en algunos hilos del foro (aumento de descargas +15 - +40) sin leer sus reglas, por lo que me banearon durante un par de días. Luego escribí en los hilos de su foro para desarrolladores, de donde recibí valiosos consejos, un par de instalaciones extra (+10) y varias buenas críticas en Google Play (+2). Tenían un proyecto para apoyar a los desarrolladores de juegos de los países de la CEI, consistía en que escribías una reseña del juego y lo hospedaban gratis, dicen que ahora han cerrado este proyecto. Pero por alguna razón no me aceptaron en este proyecto, o no les gustó el sitio con ".com" o no encontraron mi correo electrónico en el sitio.

Reseñas en Google Play

Dicen que no necesitas preocuparte por reseñas y calificaciones individuales en Google Play. Esto es cierto: las personas son diferentes, tienen opiniones diferentes, algunos esperan algo completamente diferente de lo que piensan. No leen la descripción del juego y, por lo tanto, descargan algo que alguien no necesita, o se pierden un juego interesante.
Sin embargo, si varias personas escriben que "es imposible jugar", o incluso describen con más detalle errores "inexistentes" ("se dibuja una cruz 2 cm más arriba"), entonces no es así. Es posible que el emulador te esté engañando, es posible que tu dispositivo también actúe de manera diferente a otros. En este caso, no debes perderte este tipo de revisiones, pero trata de averiguar qué está sucediendo en los dispositivos de los usuarios y lidiar con ello. Me gustaría señalar que en un año, con más de 5.000 descargas y 1.000 usuarios, solo 5 letras. fueron recibidos por correo electrónico: 4 de ellos - spam (me ofrecieron anunciar mi juego y obtener una buena calificación, por esto me pidieron entre $ 100 y $ 60 000). Y una carta de Opera Mobile Store (apps.opera.com), que decía que puedes colocar aplicaciones en su tienda de forma gratuita, pero por ahora decidí quedarme solo en Google Play.

Competencia y SEO

La competencia por las primeras posiciones es alta, especialmente en un género de juegos como el tres en raya. Hay miles, miles de tres en raya idénticos con nombres casi idénticos. Hay tantos porque son un poco más difíciles de escribir que “Hola mundo”. Está claro que los usuarios no se molestarán en desplazarse más allá de la vigésimo segunda página en busca del mejor juego, y hay más de (3000/20=100...) páginas en total competencia entre miles de tic-tac. -dedo del pie... Mi juego está en las primeras veinte páginas (en 20 páginas), esto no es tan malo como más del 70% de otros competidores. Vale la pena señalar que, como en los motores de búsqueda, la posición del juego es. determinado no solo por la categoría seleccionada, sino también en las búsquedas de varias consultas clave. Entonces, para la consulta "tic-tac-toe", mi juego está en la página 20, y para la consulta "dibujar tres en raya" está en la primera página, entre una docena de competidores, pero pocas personas escriben de esa manera. Sin embargo, para la consulta "tic-tac-toe para dos", que aparece en la información sobre herramientas al escribir la frase "tic-tac-toe", solo hay dos páginas de competencia, y esto se escribe con frecuencia. El resultado de la suma. La palabra clave "para dos" en la descripción del juego supuso un aumento en la carga de usuarios de habla rusa de 3.636 veces (según las descargas de la parte de usuarios de habla inglesa). enviar spam con todas las palabras clave posibles, porque si escribes algo que los jugadores esperan ver, pero no ven en el juego, no solo eliminarán el juego, sino que también agregarán una mala reseña, como algunos habrán adivinado. la búsqueda está tan influenciada por frases específicas que tres en raya se llamará de manera diferente en diferentes idiomas. Surge la pregunta: ¿a cuántos idiomas es necesario traducir la aplicación? La mayoría de los usuarios instalaron el juego en los siguientes idiomas: ruso (58%), inglés (EE.UU. 20% + Reino Unido 11% + otros ingleses 2% = 33%), español (3%), el resto 6%. Por tanto, podemos concluir que debe existir el inglés; un idioma que hablas bien; A continuación se muestran los idiomas por popularidad que conocen tus amigos.
En el gráfico, los usuarios de habla rusa se muestran en verde, las otras dos líneas son los de habla inglesa. El fuerte aumento para los usuarios de habla rusa es la adición de "para dos" en la descripción del juego Estadísticas promedio sobre el uso del idioma en la sección "Rompecabezas" (proporcionada en la consola de desarrollador de Google Play): ¿Cómo llega la gente a la página del juego, quién la descarga y quién abandona el juego? La siguiente imagen mostrará esto.
Video
Dado que algunos votantes negativos escribieron comentarios como "puedes jugar en papel", llegué a la conclusión de que muchos de los que instalan el juego no leen su descripción. Las imágenes (capturas de pantalla) no explican bien la característica de este juego: dibujar. Por eso decidí hacer un vídeo. No para atraer nuevos jugadores, sino para mostrar la jugabilidad y eliminar a aquellos a quienes no les gustará el juego (para no estropear la calificación del juego). Como se sugiere en la documentación de Google Play, hice un video que muestra la característica de. el juego. Intenté hacer un vídeo sin efectos especiales, ya que supuse que no mucha gente lo vería. Pero no pude cortar 15 minutos de video capturado desde la pantalla usando ffmpeg en 1,5 minutos. Pero fue en vano. El resultado del vídeo fue ambiguo. Aproximadamente 2 visualizaciones por día, 1 visualización duró 48 segundos (no supervisada). De esto podemos concluir que, teniendo en cuenta el tiempo invertido en rebobinar el vídeo, la mejor duración del vídeo es 1 minuto. Los cambios en el número de desinstalaciones/instalaciones fueron insignificantes.

Descargar estadísticas

Estadísticas generales del año: 5113 instalaciones, 4017 eliminaciones (78%), 1096 usuarios se fueron. Durante los primeros 10 días (primera versión): 56 descargas, 22 eliminaciones, 34 se fueron (+3,4 usuarios por día). Estas buenas estadísticas se deben al hecho de que en las primeras semanas el juego fue discutido en w3bsit3-dns.com. Durante diferentes períodos, las descargas y eliminaciones fluctuaron en proporción uniforme. Las descargas y eliminaciones en verano son menos de 10 por día, desde enero más de 20 por día. El efecto habra se medirá y publicará. Es de suponer que habrá un fuerte aumento en las descargas y luego un aumento similar en las eliminaciones. ACTUALIZACIÓN: Después de 2 días, el artículo obtuvo 13.000 visitas, luego les hablaré sobre el “efecto habra”. En resumen: de las 13.000 visitas, solo menos del 1% de las personas se interesaron por el juego y lo descargaron, pero lo hicieron. agregó muchas buenas críticas al juego Leer más: El número de descargas del juego en dos días aumentó en +180 (1,38% de las vistas del artículo). En su mayoría había dispositivos con Android 4.1 y 4.2. Las eliminaciones también aumentaron, fueron 50 los días de publicación del artículo. En promedio, estos días se eliminaron el 43% de todas las instalaciones (el 40% fueron eliminados por los lectores del artículo, el 70% por otros usuarios. El número de usuarios del juego en un par de días aumentó de 1110 a 1230, un aumento de 120). (de los cuales la proporción de personas que leyeron el artículo resultó ser el 0,8% de las vistas del artículo). A modo de comparación, el crecimiento habitual en un par de días es de 3 a 4 usuarios. Un par de días después de la publicación del artículo, todos los indicadores comenzaron a acercarse a la normalidad.

Cómo los lectores influyeron en las descargas, en gráficos

Ajustes
Eliminaciones
Crecimiento de usuarios

Me di cuenta de que si algunos usuarios se sienten atraídos por el producto a través de comunicaciones personales (hilos de foros, artículos), la cantidad de comentarios y calificaciones aumenta. Debido a la publicación de este artículo, aparecieron muchas reseñas del juego en Google Play y las reseñas fueron positivas, a pesar de las críticas sobre las deficiencias de la vida real que deben corregirse.

La calificación promedio de la aplicación aumentó de 2,13 a 3,81.

Las reproducciones de videos aumentaron solo en 7 por día, todas vistas desde dispositivos móviles. Curiosamente, más personas vieron la página de mi sitio web sobre el juego que el vídeo. Las dos primeras cartas de quienes intentaron instalar el juego llegaron por correo electrónico. Antes de esto, sólo se recibía spam.

Lista de programas utilizados para crear el juego y otros materiales.

Entornos de desarrollo:
  • NetBean
  • Eclipse (+Android SKD)
Imágenes y vídeos:
  • GIMP - editor de gráficos rasterizados
  • Inkscape - editor de vectores
  • OpenShot - editor de vídeo
  • ffmpeg: usado para capturar video desde la pantalla
El desarrollo se realizó en Linux. Todos estos programas son gratuitos.

Costo

0 (cero) para desarrollo, gráficos, software Más de ~$30 para soporte del sitio, transacciones bancarias y pago de una cuenta de desarrollador de GooglePlay. Lo que recibí: 0 rublos, experiencia en el desarrollo de una aplicación para Android, estadísticas sobre instalaciones de juegos en Google. Juega, un buen artículo.

En conclusión

Puedes, con una computadora, $30 y tiempo, hacer un juego simple y bueno. Para un buen juego necesitas tanto una jugabilidad (idea) como una hermosa implementación (gráficos) al mismo tiempo. Para ganar dinero y conseguir muchos usuarios, es necesario hacer lo que los usuarios necesitan, incluso si ni siquiera sospechan que existe, y hacer lo que aún no existe, en un área menos competitiva que miles de tres en raya. . Es un juego ahora gratis, sin anuncios, no requiere ningún permiso adicional (por ejemplo, recopilar datos personales, enviar SMS pagos, etc.). (se puede hacer clic en la página del juego en Google Play)

Etiquetas:

  • desarrollador de juegos
  • google play
  • Java
  • independiente
  • juegos independientes
Agregar etiquetas

Creamos un juego de tres en raya multijugador en red utilizando tecnologías PHP, XML y un conjunto de herramientas para desarrollar aplicaciones en la plataforma Android.

Tres en raya multijugador en línea

Abreviaturas de uso común
  • API: interfaz de programación de aplicaciones
  • HTTP: protocolo de transferencia de hipertexto
  • IP: protocolo de Internet
  • SDK: kit de desarrollo de software
  • SQL: lenguaje de consulta estructurado
  • Interfaz de usuario: interfaz de usuario
  • XML: lenguaje de marcado extensible

Los juegos de ordenador casuales (juegos para una amplia gama de usuarios que se juegan ocasionalmente) son extremadamente populares y muy rentables, lo cual es fácil de entender. No todo el mundo, independientemente de su edad, está interesado en los shooters interactivos en primera persona contra hordas de enemigos que requieren reacciones ultrarrápidas. A veces es más divertido jugar juegos que te dan tiempo para pensar y elaborar estrategias, o que requieren una estrategia cooperativa para ganar.

Desde la perspectiva de un desarrollador, los juegos casuales son mucho más fáciles de desarrollar que los juegos de disparos gráficos en primera persona o los juegos deportivos. Por lo tanto, es más fácil para un desarrollador o grupo de desarrolladores crear un nuevo juego original.

Este artículo cubre los conceptos básicos de la creación de un juego informal de tres en raya multijugador en línea. El servidor del juego es una aplicación web con una interfaz XML que utiliza MySQL y PHP. El cliente es una aplicación estándar de Android que se ejecuta en teléfonos Android.

Creando una parte del servidor

El backend utiliza una base de datos MySQL simple con dos tablas. B muestra el esquema de la base de datos.

Listado 1. db.sql
DROP TABLE SI EXISTE juegos; CREAR TABLA de juegos (id INT NOT NULL AUTO_INCREMENT, clave principal (id)); SOLTAR TABLA SI EXISTE se mueve; CREAR TABLA de movimientos (id INT NOT NULL AUTO_INCREMENT, juego INT NOT NULL, x INT NOT NULL, y INT NOT NULL, color INT NOT NULL, clave principal (id));

En una aplicación real, probablemente se usaría una tabla de usuarios y la tabla de juegos contendría los ID de ambos jugadores. Por simplicidad, abandoné este enfoque y me concentré en almacenar datos del juego, en la interacción entre el cliente y el servidor y en crear el lado del cliente.

La segunda tabla, movimientos, contiene los movimientos individuales del juego y consta de cinco columnas. La primera columna es el identificador de movimiento único. La segunda columna es el identificador del juego al que pertenece la jugada. Luego están las posiciones xey del movimiento. Estos valores deben estar entre 0 y 2 porque el tablero de juego es de tres en tres. El último campo es el "color" del movimiento, que es un número entero que indica X (cruz) u O (dedo del pie).

Para crear una base de datos, primero inicie el programa mysqladmin y use el comando mysql para ejecutar el script db.sql como se muestra a continuación:

% mysqladmin --user=root --password=foo crear ttt % mysql --user=root --contraseña=foo ttt< db.sql

Esta acción crea una nueva base de datos ttt que contiene el patrón del juego tres en raya.

Después de crear el esquema, debes crear una forma de ejecutar el juego. Para hacer esto, use un script llamado start.php, que se proporciona en .

Listado 2. start.php
preparar($sql); $algo->ejecutar(array()); $qid = $dd->lastInsertId(); $doc = nuevo DOMDocumento(); $r = $doc->createElement("juego"); $r->setAttribute("id", $qid); $doc->appendChild($r); imprimir $doc->saveXML(); ?>

El script comienza con una conexión a la base de datos. Luego se ejecuta la instrucción INSERT en la mesa de juegos y se devuelve el ID generado. Después de esto, se crea un documento XML, se agrega un identificador a la etiqueta del juego y se exporta el XML.

Este script debe ejecutarse para crear un juego en la base de datos porque nuestra sencilla aplicación de Android no tiene una interfaz para crear juegos. Aquí está el código:

$php inicio.php $

Entonces tenemos nuestro primer juego. Para ver la lista de juegos, utilice el script games.php que se proporciona en .

Listado 3. juegos.php
preparar($sql); $q->ejecutar(matriz()); $doc = nuevo DOMDocumento(); $r = $doc->createElement("juegos"); $doc->appendChild($r); foreach ($q->fetchAll() as $fila) ( $e = $doc->createElement("juego"); $e->setAttribute("id", $fila["id"]); $r- >appendChild($e); imprimir $doc->saveXML(); ?>

Este script, al igual que el script start.php, comienza con una conexión a la base de datos. Luego se consulta la tabla de juegos para ver los juegos disponibles. Después de esto, se crea un nuevo documento XML, se agrega una etiqueta de juego y luego se agregan etiquetas de juego para cada juego disponible.

Después de ejecutar el script desde la línea de comando, debería ver la siguiente información:

$ php juegos.php $

También puede ejecutar este script desde un navegador web.

¡Maravilloso! Ahora que tenemos la interfaz del juego, podemos empezar a escribir código de servidor para procesar movimientos. Este código comienza creando un script auxiliar show_moves que obtiene los movimientos actuales para un juego determinado y los exporta a XML. B muestra el código PHP para esta función auxiliar.

Listado 4. show_moves.php
preparar($sql);

El script toma un identificador de base de datos y una identificación del juego. Luego ejecuta una consulta SQL para obtener la lista de movimientos. Luego crea un documento XML con los movimientos de ese juego.

Dos scripts utilizan esta función auxiliar. El primero es movimientos.php, que devuelve los movimientos actuales del juego especificado. Este script se proporciona en .

Listado 5. movimientos.php

Este sencillo script conecta el código auxiliar, se conecta a la base de datos e invoca la función show_moves con el ID del juego especificado. Para probar este código, use el comando curl para ejecutar el script en el servidor desde la línea de comando:

$ curl "http://localhost/ttt/moves.php?game=1" $

Aún no se han realizado movimientos, por lo que no se muestra nada interesante. Para solucionar la situación, debe agregar el último script a la interfaz del servidor. A continuación se muestra el script move.php.

Listado 6. move.php
preparar($sql); $sth->execute(array($_REQUEST["juego"], $_REQUEST["x"], $_REQUEST["y"])); $sql = "INSERT INTO mueve VALORES (0, ?, ?, ?, ?)"; $algo = $dbh->preparar($sql); $sth->execute(array($_REQUEST["juego"], $_REQUEST["x"], $_REQUEST["y"], $_REQUEST["color"])); show_moves($dbh, $_REQUEST["juego"]); ?>

Este script comienza conectando una función auxiliar y conectándose a la base de datos. Luego se ejecutan dos sentencias SQL. El primero elimina todos los movimientos que puedan entrar en conflicto con los movimientos enviados en la segunda solicitud. La segunda instrucción SQL inserta una nueva fila en la tabla de movimientos para el movimiento especificado. Luego se devuelve una lista de movimientos al cliente. Esta acción elimina la necesidad de que el cliente realice dos solicitudes en cada turno. El ancho de banda no es barato, por lo que debes agregar solicitudes siempre que sea posible.

Para realizar pruebas, puede realizar el siguiente paso:

$ curl "http://localhost/ttt/move.php?game=1&x=1&y=2&color=1"

Una vez que hayas terminado de crear el código del servidor, podrás comenzar a crear la interfaz de Android para este juego multijugador masivo en línea.

Creando una interfaz de Android

En primer lugar, instale el SDK de Android, varias versiones de la plataforma Android y finalmente Eclipse y el complemento Android Eclipse. Afortunadamente, todo esto está bien descrito en el sitio web de Android (los enlaces se proporcionan en la sección). Una descripción detallada de la configuración del entorno de desarrollo ocuparía un artículo completo, y probablemente más de uno.

Después de configurar su entorno de desarrollo, inicie Eclipse e inicie un nuevo proyecto de Android. La pantalla debe mostrar la información que se muestra en.

Listado 11. BoardView.java
paquete com.jherrington.tictactoe; importar android.content.Context; importar android.graphics.Canvas; importar android.graphics.Color; importar android.graphics.Paint; importar android.graphics.Rect; importar android.util.AttributeSet; importar android.view.MotionEvent; importar android.view.View; la clase pública BoardView extiende la Vista ( private int _color = 1; public void setColor(int c) ( _color = c; ) public BoardView(Context context) ( super(context); GameService.getInstance().startGame(0); ) public BoardView(Contexto contexto, AttributeSet atributos) ( super(contexto,attrs); GameService.getInstance().startGame(0); ) public BoardView(Contexto contexto, AttributeSet atributos, int defStyle) ( super(contexto,attrs, defStyle); GameService.getInstance().startGame(0); onTouchEvent público booleano (evento MotionEvent) ( if (event.getAction() != MotionEvent.ACTION_UP) devuelve verdadero; int offsetX = getOffsetX(); int offsetY = getOffsetY(); int tamaño de línea = getTamaño de línea(); para(int x = 0; x< 3; x++) { for(int y = 0; y < 3; y++) { Rect r = new Rect((offsetX + (x * lineSize)), (offsetY + (y * lineSize)), ((offsetX + (x * lineSize)) + lineSize), ((offsetY + (y * lineSize)) + lineSize)); if (r.contains((int)event.getX(), (int)event.getY())) { GameService.getInstance().setPosition(0, x, y, _color); invalidate(); return true; } } } return true; } private int getSize() { return (int) ((float) ((getWidth() < getHeight()) ? getWidth() : getHeight()) * 0.8); } private int getOffsetX() { return (getWidth() / 2) - (getSize() / 2); } private int getOffsetY() { return (getHeight() / 2) - (getSize() / 2); } private int getLineSize() { return (getSize() / 3); } protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.BLACK); canvas.drawRect(0,0,canvas.getWidth(),canvas.getHeight(), paint); int size = getSize(); int offsetX = getOffsetX(); int offsetY = getOffsetY(); int lineSize = getLineSize(); paint.setColor(Color.DKGRAY); paint.setStrokeWidth(5); for(int col = 0; col < 2; col++) { int cx = offsetX + ((col + 1) * lineSize); canvas.drawLine(cx, offsetY, cx, offsetY + size, paint); } for(int row = 0; row < 2; row++) { int cy = offsetY + ((row + 1) * lineSize); canvas.drawLine(offsetX, cy, offsetX + size, cy, paint); } int inset = (int) ((float)lineSize * 0.1); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10); for(int x = 0; x < 3; x++) { for(int y = 0; y < 3; y++) { Rect r = new Rect((offsetX + (x * lineSize)) + inset, (offsetY + (y * lineSize)) + inset, ((offsetX + (x * lineSize)) + lineSize) - inset, ((offsetY + (y * lineSize)) + lineSize) - inset); if (GameService.getInstance().positions[ x ][ y ] == 1) { canvas.drawCircle((r.right + r.left) / 2, (r.bottom + r.top) / 2, (r.right - r.left) / 2, paint); } if (GameService.getInstance().positions[ x ][ y ] == 2) { canvas.drawLine(r.left, r.top, r.right, r.bottom, paint); canvas.drawLine(r.left, r.bottom, r.right, r.top, paint); } } } } }

La mayor parte del trabajo se realiza mediante el método onTouch, que responde cuando el usuario presiona una celda específica del campo de juego, y el método onDraw, que dibuja el campo de juego utilizando el motor de dibujo de Android.

El método onTouch utiliza las funciones de tamaño para calcular la posición del rectángulo de cada celda. Luego llama al método contiene en el rectángulo para determinar si el usuario ha hecho clic en la celda. En caso afirmativo, se activa una solicitud al servicio del juego para ejecutar el movimiento.

La función onDraw utiliza las funciones de dimensionamiento tanto para dibujar las líneas del campo de juego como para dibujar todas las X y O colocadas. El singleton de GameServer se utiliza para la matriz de posiciones, que almacena el estado actual de cada casilla del campo de juego.

La última clase que necesitamos es UpdateTimer, que utiliza el servicio del juego para actualizar las posiciones en el campo de juego con los datos más recientes. El código fuente del temporizador se muestra a continuación.

Listado 12. UpdateTimer.java
paquete com.jherrington.tictactoe; importar java.util.TimerTask; la clase pública UpdateTimer extiende TimerTask ( public BoardView boardView; @Override public void run() ( GameService.getInstance().startGame(0); boardView.post(new Runnable())( public void run())( boardView.invalidate( ) ) ) );

El temporizador lo inicializa la clase TicTacToeActivity durante el inicio de la aplicación. El temporizador implementa un mecanismo de sondeo. Esta no es la forma más eficaz de comunicación entre el cliente y el servidor, pero sí la más sencilla y fiable. La forma más eficaz es utilizar la versión 1.1 del protocolo HTTP para mantener la conexión activa y hacer que el servidor envíe actualizaciones al cliente cuando se realiza un movimiento. Este enfoque es mucho más complejo; requiere soporte para el protocolo 1.1 tanto por parte del cliente como del servidor y tiene restricciones en la cantidad de conexiones. La consideración de este enfoque está más allá del alcance de este artículo. Para juegos de demostración sencillos como el nuestro, el mecanismo de votación es fantástico.

Después de escribir el código fuente, puedes probar la aplicación. Para hacer esto necesitas ejecutar el emulador. Después del inicio, la pantalla debería mostrar la información que se muestra en la Figura 3.

Figura 3. Lanzamiento del emulador de Android
Figura 5.

Dependiendo del estado de tu servidor, verás movimientos o no. En nuestro caso, el juego aún no ha comenzado. Los botones Play X y Play O están ubicados encima del campo de juego, ubicado en el centro de la pantalla. Haga clic en el botón Jugar X, y luego la plaza central. Aparecerá una pantalla similar a la que se muestra en.

Puedes jugar tanto con cruces como con ceros. La aplicación se conecta al servidor para guardar el estado del juego en una ubicación de acceso público. Y gracias al temporizador de actualización, cualquier jugador puede ver los movimientos realizados por otro jugador.

Conclusión

¿Este juego ha terminado? Por supuesto que no. No hay verificación de la condición de victoria, los jugadores pueden ocupar los mismos campos repetidamente y no hay control sobre el orden de los movimientos. Pero los principales bloques tecnológicos están presentes: un servidor de juego con un estado de juego guardado disponible para todos los jugadores y una aplicación gráfica estándar en un dispositivo móvil que se conecta al servidor de juego para proporcionar la interfaz del juego. Puedes utilizar este juego como punto de partida para tu propia aplicación de juegos y convertirlo en lo que quieras. Solo recuerda mantener el juego interesante y divertido, y podrías terminar creando un competidor de Words With Friends o una versión multijugador de Angry Birds.

Cuando me pidieron que ayudara a crear/encontrar un juego de tres en raya en Java con una interfaz de usuario gráfica (para dos jugadores), lo primero que hice fue buscar en Google para ver si había un tema listo para usar y bien implementado. Quizás me veía mal, pero todo lo que encontré estaba mal hecho. En algún lugar el tamaño del tablero de juego está establecido rígidamente, en la mayoría de los ejemplos encontrados en Java Swing: toda la lógica del juego está mezclada de manera inapropiada con la interfaz de usuario. Hay muchas implementaciones de este juego publicadas en línea, pero ¿por qué no agregar una más?

Entonces este ejemplo usa Swing para crear la interfaz de usuario. Tamaño del tablero de juego 5x5:

Para ganar, debes hacer una línea continua de cruces o ceros, de 5 elementos de largo. Sin embargo, el tamaño del campo y la longitud de la línea que se debe trazar para ganar se pueden cambiar fácilmente. Como puede ver en la captura de pantalla, utiliza el tema Nimbus, que está incluido en JRE desde Java SE 6 Update 10. La estructura del proyecto se ve así:

en el paquete ru.spbstu.tictactoe- clases necesarias para crear una interfaz gráfica; en el paquete ru.spbstu.tictactoe.common- clases necesarias para crear un juego de tres en raya que no atado a la vista (UI). La clase GameBoard es una representación del campo de juego para jugar al tres en raya y también contiene todos los métodos necesarios para implementar el juego:

Usando el constructor GameBoard(int), puedes crear un campo de juego de cualquier tamaño. Puede cambiar el tamaño de un campo de juego ya creado, establecer un elemento (X u O) en una posición determinada y también obtener un elemento en una posición determinada del campo. Aquí debes prestar atención al método getNextWinner, que se llama cada vez que cambia el estado del campo de juego para encontrar un ganador:

/** * Calcula la siguiente combinación ganadora dado el estado actual del tablero de juego.

*La combinación que se encuentra en el tablero de juego está marcada como calculada.

// tamaño del tablero de juego private final int BOARD_SIZE = 5; // tamaño de celda del tablero de juego, px private final int FIELD_WIDTH = 40; // el número de cruces o ceros en una línea continua, en los que // se cuenta una ganancia private final int WIN_COUNT = 5; // dimensiones de la ventana, px private final int FRAME_DEFAULT_WIDH = 250; int final privado FRAME_DEFAULT_HEIGTH = 220;




Arriba