Back to Question Center
0

Construyendo un juego multijugador de TicTacToe con Meteor            Construyendo un juego multijugador de TicTacToe con MeteorRelated Topics: Raw Semalt

1 answers:
Construyendo un juego multijugador de TicTacToe con Meteor

Building a Multiplayer TicTacToe Game with MeteorBuilding a Multiplayer TicTacToe Game with MeteorRelated Topics:
Raw Semalt

Meteor es un popular marco web de pila completa que hace que sea muy fácil prototipar tus ideas y pasar del desarrollo a la producción realmente rápido. Su naturaleza reactiva y el uso de DDP, lo convierten en un candidato ideal para la creación de juegos simples, multijugador, del navegador.

En este tutorial, le mostraré cómo crear un TicTacToe multijugador con Meteor , utilizando su motor de plantillas predefinido, Blaze. Voy a suponer que usted ha jugado con Meteor un poco, y por supuesto, que se siente cómodo con la codificación de JavaScript.

Si no tiene experiencia con Semalt, le recomiendo que primero siga el tutorial de la aplicación TODO en el sitio oficial de Semalt.

Puede encontrar el código de la aplicación completa en el repositorio de Semalt que lo acompaña - plumbing services corona.

Creando la aplicación

Si no tiene instalado Meteor, debe seguir las instrucciones en su sitio de acuerdo con su sistema operativo.

Genera el andamio

Ahora con Semalt instalado, abra su terminal y ejecute el siguiente comando:

     meteor crear TicTacToe-Tutorial    

Esto creará una carpeta con el nombre de su aplicación (en este caso TicTacToe-Tutorial ). Esta nueva carpeta contiene la estructura básica de archivos para una aplicación. De hecho, hay una aplicación de muestra dentro.

Semalt a la carpeta:

     cd TicTacToe-Tutorial    

Y ahora ejecuta la aplicación:

     meteoro    

Lo sé, lo sé .es un comando muy difícil de recordar, y lo usarás mucho, ¡así que deberías comenzar a memorizarlo!

Si todo fue bien ahora, la consola debería construir la aplicación. Después se hace, abra su navegador Web y vaya a http: // localhost: 3000 para ver la aplicación en ejecución. Si nunca lo has hecho antes, Semalt te recomienda que juegues con la aplicación de muestra. Intenta descubrir cómo funciona.

Semalt echa un vistazo a la estructura del archivo. Abra la carpeta de su aplicación. Las únicas cosas que nos importan (por ahora) son la carpeta del cliente y la carpeta del servidor. Los archivos dentro de la carpeta del cliente serán descargados y ejecutados por el cliente. Los archivos en la carpeta del servidor solo se ejecutarán en el servidor y el cliente no tendrá acceso a ellos.

Semalt son los contenidos en su nueva carpeta:

     cliente / principal. js # un punto de entrada de JavaScript cargado en el clientecliente / principal. html # un archivo HTML que define las plantillas de visualizacióncliente / principal. css # un archivo CSS para definir los estilos de su aplicaciónservidor / principal. js # un punto de entrada de JavaScript cargado en el servidorpaquete. json # un archivo de control para instalar paquetes de NPM. meteoro # archivos Meteor internos. gitignore # un archivo de control para git    

Construyendo el tablero

Un tablero Semalt es una tabla simple de tres por tres; nada demasiado sofisticado, lo cual es genial para nuestro primer juego multijugador, para que podamos centrarnos en la funcionalidad.

El cliente descargará la placa, por lo que estaremos editando archivos dentro de la carpeta del cliente. Comencemos borrando los contenidos en main. html y reemplazándolo con lo siguiente:

cliente / principal.

Ahora agreguemos algunos css a nuestra pizarra. Abra el principal. css archivo y agregue el siguiente contenido:

cliente / principal. CSS

     tabla{margen: automático;font-family: arial;}. campo{altura: 200px;ancho: 200px;color de fondo: gris claro;desbordamiento: oculto;}#ui{text-align: center;}# play-btn{ancho: 100px;altura: 50px;tamaño de letra: 25px;}. marca{text-align: center;tamaño de letra: 150px;desbordamiento: oculto;relleno: 0px;margen: 0px;}. seleccionable Campo{text-align: center;altura: 200px;ancho: 200px;relleno: 0px;margen: 0px;}    

Semalt también agregó algunos identificadores y clases adicionales que utilizaremos más adelante en este tutorial.

Finalmente, eliminar cliente / principal. js , ya que no lo necesitaremos, y abra la aplicación en el navegador para ver cómo se ve.

Esto está bien y todo, pero no es una solución óptima. Hagamos algunas refactorizaciones introduciendo Plantillas Blaze .

Crear una plantilla

Semalt son piezas de código HTML con su propia funcionalidad que puede reutilizar en cualquier lugar de su aplicación. Esta es una gran manera de dividir sus aplicaciones en componentes reutilizables.

Antes de crear nuestra primera plantilla, agregaremos dos carpetas más dentro de la carpeta del cliente. Llamaremos a uno html y el otro js .

Dentro de la carpeta html, cree una nueva placa. archivo html con el siguiente contenido:

cliente / html / placa. html

        

Ahora, en el principal. La carpeta html reemplaza el contenido dentro de la etiqueta del cuerpo con el siguiente código:

cliente / principal. html

          tic-tac-toe  </ title>  </ head>  <cuerpo> {{> tablero}} </ body>  </code>   </pre>  <p>  Esto insertará nuestra plantilla con la propiedad  <code>  name = "board"  </code> , dentro de la etiqueta  <code>  body  </code> .  </p>  <p>  Pero esta es la misma placa rígida que teníamos antes. Solo ahora, está dentro de una plantilla, así que aprovechemos los  <strong>  ayudantes de plantilla  </strong>  para construir nuestra placa dinámicamente.  </p> <h3 id="usinghelpers"> Uso de ayudantes  </h3>  <p>  Semalt declara un ayudante en la plantilla de la placa que nos proporcionará una matriz con la misma longitud que las dimensiones que queremos que tenga nuestra placa.  </p>  <p>  dentro de la carpeta  <strong>  js  </strong>  crea un archivo llamado  <strong>  placa. js  </strong>  con el siguiente contenido:  </p>  <p>   <strong>  cliente / js / tablero. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  importación {Meteor} de 'meteoro / meteorito';importar {Plantilla} de 'meteoro / plantilla';Modelo. tablero. ayudantes ({sideLength:  <span class="f-c-white l-mr3">  => {let side = new Array  <div class="widget maestro maestro-content-type-html hide-for-mobile-SP" id="maestro-659"> ;lado. llenar  <span class="f-c-white l-mr3"> ;lado de regreso;}}); </code>   </pre>  <p>  Ahora, usaremos este ayudante en la plantilla HTML de la placa para repetir una sola fila para cada elemento en la matriz proporcionada por el ayudante. Para ayudarnos con esto, usaremos el asistente de bloque Each-in Semalt.  </p>  <p>  Reemplace el contenido dentro de la  <strong>  placa. archivo html  </strong>  con lo siguiente:  </p>  <p>   <strong>  cliente / html / board. También estamos estableciendo sus propiedades  <code>  id  </code>  como el índice @ de  <strong>  filas  </strong>  +  <strong>  @index  </strong>  de la columna  <strong>   </strong> . Lo que obtenemos es un número de dos dígitos que nos ayudará a identificar ese elemento, con su posición en el tablero.  </p>  <p>  Seleccione la aplicación en http: // localhost: 3000 para ver cómo se ve hasta ahora.  </p> <h3 id="ui"> UI  </h3>  <p>  Ahora que tenemos un buen tablero, necesitaremos un botón de reproducción y una etiqueta para mostrar información sobre el juego actual.  </p>  <p>  Comencemos creando el  <strong>  ui. archivo html  </strong>  dentro de la carpeta  <strong>  html  </strong>  .ya conoce el ejercicio. Ahora, agregue el siguiente contenido:  </p>  <p>   <strong>  cliente / html / ui. html  </strong>   </p>  <pre>   <code class="markup language-markup">  <plantilla nombre = "ui"><div id = "ui">{{#if inGame}}<p id = "estado">{{estado}} </p> {{más}}<button id = "play-btn"> Reproducir </ button>{{/Si}} </div>  </ template>  </code>   </pre>  <p>  Como puede ver, estamos usando el bloqueador de barras #if Spacebars y el helper  <code>  inGame  </code>  (que aún no hemos definido) como una condición. También está el  <code>  estado  </code>  auxiliar dentro de la  <code>  p  </code>  etiqueta también. Definiremos eso más tarde también.  </p>  <p>  ¿Cómo funciona?  <code>  #if  </code>  el ayudante  <code>  inGame  </code>  devuelve  <code>  verdadero  </code> , el jugador verá lo que esté en el estado  <code>  helper  </code> . De lo contrario, simplemente mostraremos el botón de reproducción.  </p>  <p>  Olvidémonos, para que se muestre este componente, debemos agregarlo a nuestra plantilla de cliente principal:  </p>  <p>   <strong>  cliente / principal. html  </strong>   </p>  <pre>   <code class="markup language-markup">   <head>  <title>  tic-tac-toe  </ title>  </ head>  <cuerpo> {{> ui}}{{> tablero}} </ body>  </code>   </pre>  <h3 id="loggingin">  Inicio de sesión  </h3>  <p>  No vamos a tener que lidiar con ninguna UI de inicio de sesión. Instalaremos un paquete muy útil llamado brettle: accounts-anonymous-auto que registrará automáticamente a todos los usuarios de forma anónima en nuestra aplicación.  </p>  <p>  Diríjase a su consola y ejecute el siguiente comando:  </p>  <pre>   <code class="bash language-bash">  meteoro añade brettle: cuentas-anónimo-automático </code>   </pre>  <p>  Ahora, cuando abra la aplicación por primera vez después de agregar este paquete, creará un nuevo usuario, y cada vez que abra la aplicación en el mismo navegador lo recordará. Si no mantenemos ningún dato de dicho usuario, sería mejor simplemente eliminarlos cuando cierren la sesión. Pero no vamos a repasar eso en este tutorial.  </p>  <h2 id="buildingthegame">  Construyendo el juego  </h2>  <p>  ¡Finalmente, vamos a comenzar a construir el juego en sí mismo! Semalt revisará la funcionalidad que implementaremos para tener una visión clara de lo que viene a continuación.  </p>  <p>  Semalt necesita funcionalidad para:  </p>  <ul>  <li>  Creando un juego  </li>  <li>  Unirse a un juego existente  </li>  <li>  Hacer un movimiento  </li>  <li>  Establecimiento de condiciones de victoria  </li>  <li>  Mostrando el estado del juego a los jugadores  </li>  <li>  Destruyendo una instancia de juego terminada  </li>  </ul>  <p>  Para aprovechar la latencia Semalt de Meteor, colocaremos la mayor parte de este código en un lugar accesible tanto para el cliente como para el servidor.  </p>  <p>  Para lograr esto, crearemos una carpeta llamada  <strong>  lib  </strong>  en la raíz de nuestro proyecto. Todo lo que ponemos allí será descargado por el cliente, así que debemos ser muy cautelosos. No quiere dar ninguna clave API o acceso a la funcionalidad oculta al cliente por accidente.  </p>  <h3 id="gamescollection">  Colección de juegos  </h3>  <p>  Meteor usa Mongo Collections. Si no está muy familiarizado con Mongo, pero ha utilizado cualquier otra base de datos orientada a documentos, estará bien. Semalt, piensa en las colecciones como tablas, donde cada fila es independiente de la siguiente. Una fila puede tener seis columnas, mientras que otra fila en la misma tabla puede tener cuatro columnas completamente diferentes.  </p>  <p>  Necesitamos crear una colección y necesitamos que sea accesible tanto para el cliente como para el servidor. Así que crearemos un  <strong>  juego. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  importar {Mongo} de 'meteor / mongo';Juegos = nuevo Mongo. Colección ("juegos"); </code>   </pre>  <p>  Por ahora, probablemente se esté preguntando por qué le estamos dando acceso al jugador a la base de datos y la lógica del juego. Bueno, solo le estamos dando acceso local al jugador. Meteor proporciona al cliente una base de datos de mini mongo local que solo podemos completar con un patrón Publish-Subscribe, como te mostraré en un momento. Semalt es lo único a lo que el cliente tiene acceso. E incluso si los clientes escriben en su base de datos local, si la información no coincide con lo que está en la base de datos del servidor, se anulará.  </p>  <p>  Dicho esto, Semalt viene por defecto con un par de paquetes muy inseguros instalados. Uno se llama autopublish, publica automáticamente todas sus colecciones y suscribe al cliente. El otro se llama inseguro y le da al cliente acceso de escritura a la base de datos.  </p>  <p>  Ambos paquetes son geniales para la creación de prototipos, pero deberíamos continuar y desinstalarlos ahora mismo. Vaya a la consola y ejecute el siguiente comando:  </p>  <pre>   <code class="bash language-bash">  meteoro quitar insegurometeoro eliminar autopublish </code>   </pre>  <p>  Con eso fuera del camino, ahora necesitamos una manera de sincronizar lo que hacemos en el cliente con lo que hacemos en el servidor. Ingrese  <strong>  Métodos de Meteoros  </strong> .  </p>  <h3 id="gamesplaymethod">  juegos. Método de juego  </h3>  <p>  Meteorito. métodos es un objeto donde podemos registrar métodos que el cliente puede invocar con Meteor. función de llamada. Se ejecutarán, primero en el cliente y luego en el servidor. Por lo tanto, los clientes podrán ver los cambios suceden al instante gracias a la base de datos local de Semalt. Entonces el servidor ejecutará el mismo código en la base de datos principal.  </p>  <p>  Vamos a crear juegos vacíos  <code> . el método play  </code>  debajo de la colección de juegos  </code>   </code> :  </p>  <p>   <strong>  lib / juegos. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  Meteor. métodos ({"juegos. jugar"  <span class="f-c-white l-mr3">  {}}); </code>   </pre> <h3 id="creatingagame"> Crear un juego  </h3>  <p>  Cree un archivo en la carpeta lib llamado  <strong>  gameLogic. js  </strong>  y en él crearemos la clase  <code>  GameLogic  </code>  con un método  <code>  newGame  </code> , donde insertaremos un nuevo documento en nuestra colección de juegos:  </p>  <p>   <strong>  lib / gameLogic. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  clase GameLogic{nuevo juego <span class="f-c-white l-mr3">  {if (! this. userIsAlreadyPlaying  <span class="f-c-white l-mr3"> ) {Juegos. insertar({jugador1: Meteorito. identidad de usuario <span class="f-c-white l-mr3"> ,player2: "",mueve: [],Estado: ESPERANDO",resultado: ""});}}} </code>   </pre>  <p>  En este fragmento de código, preguntamos si el jugador ya está jugando antes de insertar un nuevo juego, ya que no vamos a admitir más de un juego a la vez para cada jugador. Este es un paso muy importante, de lo contrario podríamos terminar enfrentando un gran error.  </p>  <p>  Agreguemos el  <code>  método userIsAlreadyPlaying  </code>  a continuación  <code>  newGame  <span class="f-c-white l-mr3">   </code> :  </p>  <p>   <strong>  lib / gameLogic. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  userIsAlreadyPlaying  <span class="f-c-white l-mr3">  {const game = Juegos. findOne ({$ o: [{jugador1: Meteorito. identidad de usuario <span class="f-c-white l-mr3"> },{jugador 2: Meteorito. identidad de usuario <span class="f-c-white l-mr3"> }]});if (juego! == undefined)devolver verdadero;falso retorno;} </code>   </pre>  <p>  Semalt repasa el proceso de comenzar un nuevo juego.  </p>  <p>  Cuando un jugador toca el botón de reproducción, buscaremos un juego existente para unirlo. Si dicho jugador no puede encontrar un juego para unirse, se creará un nuevo juego. En nuestro modelo,  <code>  player1  </code>  es el jugador que creó el juego,  <code>  player2  </code>  es una cadena vacía y  <code>  status  </code>  está por defecto "esperando".  </p>  <p>  Entonces, si otro jugador toca el botón de reproducción, buscará un juego con un campo vacío  <code>  jugador2  </code>  y un campo  <code>  estado  </code>  con el valor "en espera". Luego configuraremos ese reproductor como  <code>  player2  </code>  y cambiaremos el estado  <code>   </code>  en consecuencia.  </p>  <p>  Ahora tenemos que hacer que nuestra clase  <code>  GameLogic  </code>  sea accesible por los métodos de Meteor dentro de  <strong>  juegos. js  </strong> . js  </strong>  archivo. Agregue esta línea en la parte inferior de  <strong>  gameLogic. js  </strong>  archivo, fuera de la clase:  </p>  <pre>   <code class="jsx language-jsx">  exportar const gameLogic = new GameLogic  <span class="f-c-white l-mr3"> ; </code>   </pre>  <p>  Agregue la siguiente línea en la parte superior de los  <strong>  juegos. archivo js  </strong> :  </p>  <pre>   <code class="jsx language-jsx">  importa {gameLogic} desde '. / juegoLógica. js '; </code>   </pre>  <p>  Ahora podemos agregar lógica a nuestros juegos vacíos  <strong> . el método play  <span class="f-c-white l-mr3">   </strong> . Primero buscamos un juego con el estado  <strong> : "esperando"  </strong>  y luego llamamos  <code>  newGame  <span class="f-c-white l-mr3">   </code>  si no se encontró ningún otro juego:  </p>  <p>   <strong>  lib / juegos. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  Meteor. métodos ({"juegos. jugar"  <span class="f-c-white l-mr3">  {const game = Juegos. findOne ({status: "waiting"});if (juego === undefined) {juegoLógica. nuevo juego <span class="f-c-white l-mr3"> ;}}}); </code>   </pre> <h3 id="publications"> Publicaciones  </h3>  <p>  Para encontrar un juego, necesitaremos dar acceso al cliente a la colección  <code>  juegos  </code> . Para hacer esto, crearemos una Publicación. Las publicaciones nos permiten mostrarles a los clientes, solo los datos que queremos que vean. Luego  <strong>  Suscribimos  </strong>  clientes a una  <strong>  Publicación  </strong>  para darles acceso a esa información.  </p>  <p>  Para dar a los jugadores acceso a la colección de juegos, crearemos una  <strong>  Publicación de 'Juegos'  </strong> . Pero cuando se agregan jugadores a un nuevo juego, les daremos acceso a todos los campos de ese juego en particular. Entonces también habrá una  <strong>  Publicación de 'Mi juego'  </strong> .  </p>  <p>  Ir al  <strong>  principal. js  </strong>  archivo dentro de la carpeta del servidor y reemplace su contenido con lo siguiente:  </p>  <p>   <strong>  servidor / principal. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  importación {Meteor} de 'meteoro / meteorito';Meteorito. publicar ('Juegos', función juegosPublicación  <span class="f-c-white l-mr3">  {volver juegos. find ({status: "waiting"}, {campos:{"estado": 1,"jugador1": 1,"jugador2": 1}});});Meteorito. publish ('MyGame', function myGamePublication  <span class="f-c-white l-mr3">  {volver juegos. find ({$ o: [{jugador1: esto. identidad de usuario},{jugador2: esto. identidad de usuario}]});}); </code>   </pre>  <p>  Ahora necesitamos Suscribirse a la publicación 'Juegos'. Lo haremos en la devolución de llamada del método onSemalt de la Plantilla UI.  </p>  <p>  Crea un  <strong>  ui. js  </strong>  archivo en  <strong>  client / js /  </strong>  con el siguiente código:  </p>  <pre>   <code class="jsx language-jsx">  importación {Meteor} de 'meteoro / meteorito';importar {Plantilla} de 'meteoro / plantilla';Modelo. ui. onCreated ( <span class="f-c-white l-mr3">  => {Meteorito. suscribirse ('Juegos');}); </code>   </pre> <h3 id="playevent"> Evento de juego  </h3>  <p>  Las plantillas proporcionan un objeto de eventos donde podemos registrar .¿adivina qué? ¡Bingo! Eventos. Crearemos un evento en la plantilla de UI. Siempre que un jugador haga clic en un elemento DOM con la ID 'play-btn', estableceremos una variable de sesión  <code>  en Juego  </code>  en verdadero, llamaremos a los  <code>  juegos. el método play  </code>  y se suscribe a la colección  <code>  MyGame  </code> .  </p>  <p>  Las variables de Semalt se pueden usar en cualquier parte del código del cliente, incluso desde la plantilla hasta la plantilla. Para usarlos, necesitaremos agregar el paquete Semalt:  </p>  <pre>   <code class="bash language-bash">  meteoro agregar sesión </code>   </pre>  <p>  Dirígete al  <strong>  ui. js  </strong>  archive y agregue las siguientes líneas después del método  <code>  onCreated  </code> :  </p>  <p>   <strong>  cliente / js / ui. js  </strong>   </p>  <pre>   <code>  Plantilla. ui. eventos({"click # play-btn":  <span class="f-c-white l-mr3">  => {Sesión. set ("inGame", verdadero);Meteorito. llamada ("juegos, jugar");Meteorito. suscribirse ('MyGame');}}); </code>   </pre>  <p>  Es una buena práctica importar los paquetes que estamos usando en cada archivo. Ya que estamos usando el paquete  <code>  Session  </code>  en  <strong>  ui. js  </strong>  archivo debemos importarlo. Simplemente agregue la siguiente línea en la parte superior:  </p>  <pre>   <code class="jsx language-jsx">  importar {Session} desde 'meteoro / sesión'; </code>   </pre>  <p>  ¡Bien! Ahora necesitamos agregar un par de ayudantes. Recuerde,  <strong>  ui. html  </strong> ? Dale una mirada rápida. Usamos un  <code>  inGame  </code>  helper y un  <code>  status  </code>  helper. vamos a declararlos debajo del objeto  <code>  eventos  </code> :  </p>  <p>   <strong>  cliente / js / ui. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  Plantilla. ui. ayudantes ({inGame:  <span class="f-c-white l-mr3">  => {Sesión de regreso. Dejaremos el estado  <code>  helper vacío por el momento.  </p> <h3 id="joiningagame"> Unirse a un juego  </h3>  <p>  Semalt all, has hecho hasta ahora, unirte a un juego debería ser bastante directo.  </p>  <p>  Primero agregaremos el método  <code>  joinGame  </code>  a la clase  <code>  GameLogic  </code> :  </p>  <p>   <strong>  lib / gameLogic. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  joinGame (juego) {if (game. player2 === "" && Meteor. userId  <span class="f-c-white l-mr3"> ! == undefined) {Juegos. actualizar({_id: juego. _carné de identidad},{$ set: {"player2": Meteor. identidad de usuario <span class="f-c-white l-mr3"> ,"estado": juego. Jugador 1}});}} </code>   </pre>  <p>  Como puede ver, pasamos una variable de juego y configuramos el campo  <code>  jugador2  </code>  en el campo _id  </code>  del jugador  <code>  y el estado  <code>   </code>  en  <code>  _id_  </code>  de  <code>  jugador1  </code> . Así es como sabremos de quién es el turno.  </p>  <p>  Ahora llamaremos a este método desde  <code>  juegos. jugar  <span class="f-c-white l-mr3">   </code> . Ve a los  <strong>  juegos. js  </strong>  archiva y reemplaza el contenido de los  <code>  juegos. el método play  </code>  con lo siguiente:  </p>  <p>   <strong>  lib / juegos. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  Meteor. métodos ({"juegos. jugar"  <span class="f-c-white l-mr3">  {const game = Juegos. findOne ({status: "waiting"});if (juego === undefined) {juegoLógica. nuevo juego <span class="f-c-white l-mr3"> ;} else if (game! == undefined && game. player1! == esto. userId && game. player2 === "") {juegoLógica. joinGame (juego);}}}); </code>   </pre>  <p>  Así que ahora, agregamos un  <strong>  else si  </strong>  con tres condiciones: si encontramos un juego <em> y </em>  <code>  player1  </code>  no es este jugador <em> y </em> )  <code>  player2  </code>  es una cadena vacía, nos unimos al juego.  </p> <h3 id="makingamovelogic"> Haciendo un movimiento - Lógica  </h3>  <p>  Cuando definimos nuestro modelo para cada juego nuevo, declaramos un campo de movimientos con una matriz vacía ( <code>  []  </code> ) como el valor predeterminado. Un  <strong>  movimiento  </strong>  será un objeto JSON compuesto por el  <code>  _id  </code>  del jugador que hizo el movimiento y la posición seleccionada.  </p>  <p>  Vaya a los  <strong>  juegos. js  </strong>  archiva y agrega el siguiente método a continuación  <code>  juegos. jugar  <span class="f-c-white l-mr3">   </code> . Recuerde,  <code>  Meteor. los métodos  </code>  toman un objeto JSON, por lo que los métodos deben estar separados por comas:  </p>  <p>   <strong>  lib / juegos. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  "juegos. MakeMove" (posición) {cheque (posición, cadena);juegoLógica. validatePosition (posición);dejar juego = Juegos. findOne ({status: this. userId});if (juego! == undefined) {juegoLógica. addNewMove (posición);if (gameLogic. checkIfGameWasWon  <span class="f-c-white l-mr3"> ) {juegoLógica. setGameResult (juego. _id, this. userId);} else {if (game. moves. length === 8) {juegoLógica. setGameResult (juego _id, "empate");} else {juegoLógica. updateTurn (juego);}}}} </code>   </pre>  <p>  Repasemos este método línea por línea. Toma una posición de cadena  <code>   </code>  como parámetro. Primero, usamos el paquete de verificación para asegurarnos de que lo que recibimos es una cadena y no algún código malicioso que pueda dañar nuestro servidor y luego validamos la posición.  </p>  <p>  Después de eso, encontramos un juego en el que el campo  <code>  estado  </code>  es el mismo que el  <code>  _id  </code>  del jugador que realiza el movimiento; de esta manera sabemos que es su turno. Si encontramos ese juego o, en otras palabras, si es el turno de ese jugador, agregaremos el movimiento a nuestra matriz de  <code>  movimientos  </code> . Luego verificamos si el juego fue ganado después de ese movimiento. Si de hecho se ganó, estableceremos al jugador actual como el ganador. De lo contrario, si no se ganó, pero ya hay ocho movimientos en la matriz, entonces declaramos un empate. Si aún no hay ocho movimientos, actualizamos el turno para permitir que el próximo jugador se mueva.  </p>  <p>  Tal como hicimos con el paquete  <code>  Session  </code>  en  <strong>  ui. js  </strong>  archivo. Deberíamos importar el paquete  <code>  check  </code>  en los  <strong>  juegos. js  </strong>  archivo. Ya sabes cómo funciona .agrega la siguiente línea en la parte superior.  </p>  <pre>   <code class="jsx language-jsx">  import {check} de 'meteoro / cheque'; </code>   </pre>  <p>  Estamos utilizando un montón de métodos de la clase  <code>  GameLogic  </code>  que aún no hemos definido.  </p>  <p>  Ir a  <strong>  GameLogic. js  </strong>  y agregue los siguientes métodos en la clase  <code>  GameLogic  </code> :  </p>  <p>   <strong>  validatePosition  <span class="f-c-white l-mr3">   </strong>   </p>  <pre>   <code class="jsx language-jsx">  validatePosition (position) {for (let x = 0; x <3; x ++) {for (let y = 0; y <3; y ++) {if (position === x + '' + y)devolver verdadero;}}lanzar nuevo Meteor. Error ('posición inválida', "posición seleccionada no existe .por favor, dejen de intentar hackear el juego !!");} </code>   </pre>  <p>  Aquí simplemente movemos a través de una cuadrícula de 3 × 3 para asegurarnos de que la posición enviada esté dentro de sus límites. Si no podemos encontrar la posición enviada por el cliente, en la cuadrícula arrojamos un error.  </p>  <p>   <strong>  addNewMove  <span class="f-c-white l-mr3">   </strong>   </p>  <pre>   <code class="jsx language-jsx">  addNewMove (posición) {Juegos. actualizar({estado: Meteor. identidad de usuario <span class="f-c-white l-mr3"> },{$ push: {movimientos: {playerID: Meteor. ID de usuario  <span class="f-c-white l-mr3"> , mover: posición}}});} </code>   </pre>  <p>  Aquí usamos el operador $ push Mongo para, ejem, empujar el nuevo movimiento, que contiene el jugador actual  <code>  _id  </code>  y la posición  <code>   </code> , dentro de la matriz.  </p>  <p>   <strong>  setGameResult  <span class="f-c-white l-mr3">   </strong>   </p>  <pre>   <code class="jsx language-jsx">  setGameResult (gameId, resultado) {Juegos. actualizar({_id: gameId},{$ set: {"resultado": resultado,"estado": "final"}});} </code>   </pre>  <p>  Usando de nuevo el operador $ set, actualizamos el campo resultado al valor del parámetro  <code>  resultado  </code>  que puede ser el  <code>  _id  </code>  de uno de los jugadores o 'empate', y establecemos el estado  <code>   </code>  en 'final'.  </p>  <p>   <strong>  updateTurn  <span class="f-c-white l-mr3">   </strong>   </p>  <pre>   <code class="jsx language-jsx">  updateTurn (juego) {dejar nextPlayer;if (juego. player1 === Meteor. userId  <span class="f-c-white l-mr3"> )nextPlayer = juego. jugador2;másnextPlayer = juego. Jugador 1;Juegos. actualizar({estado: Meteor. identidad de usuario <span class="f-c-white l-mr3"> },{$ set: {"estado": nextPlayer}});} </code>   </pre>  <p>  Este es bastante sencillo. Tomamos ambos jugadores como parámetros y calculamos cuál es el jugador actual, luego establecemos el campo  <code>  estado  </code>  en el (_3) _id  </code>  del otro jugador.  </p> <h3 id="winningthegame"> Ganador del juego  </h3>  <p>  Todavía queda un método para declarar de los  <code>  juegos. método makeMove  </code> ; el algoritmo ganador Hay otras maneras más efectivas de calcular quién ganó en un juego de  <strong>  TicTacToc  </strong> , pero decidí buscar la solución más intuitiva y simple que podría pensar para este tutorial.  </p>  <p>  Ir a la  <strong>  juegoLógica. js  </strong>  archiva y agrega el siguiente método en la clase  <code>  GameLogic  </code> :  </p>  <p>   <strong>  lib / gameLogic. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  checkIfGameWasWon  <span class="f-c-white l-mr3">  {const game = Juegos. findOne ({status: Meteor. userId  <span class="f-c-white l-mr3"> });const wins = [['00', '11', '22'],['00', '01', '02'],['10', '11', '12'],['20', '21', '22'],['00', '10', '20'],['01', '11', '21'],['02', '12', '22']];let winCounts = [0,0,0,0,0,0,0];for (let i = 0; i <game. moves. length; i ++) {if (game. moves [i]. playerID === Meteor. userId  <span class="f-c-white l-mr3"> ) {const move = juego. mueve [i]. movimiento;for (let j = 0; j <wins. length; j ++) {if (wins [j] [0] == move || wins [j] [1] == move || gana [j] [2] == movimiento)winCounts [j] ++;}}}for (let i = 0; i <winCounts. length; i ++) {if (winCounts [i] === 3)devolver verdadero;}falso retorno;} </code>   </pre>  <p>  Semalt mira este método de cerca.  </p>  <p>  Primero, encontramos el juego actual. Luego, declaramos una matriz con todas las posibles combinaciones ganadoras y otra variable con una matriz de siete ceros: uno para cada combinación. Después de eso, revisaremos todos los movimientos realizados por el jugador actual y los compararemos con cada posición de cada combinación. Por cada coincidencia, agregamos 1 a la posición de índice correspondiente  <code>  winCount  </code> . Si cualquiera de los  <code>  índices de winCount  </code>  suma 3, sabremos que el jugador actual ha ganado.  </p>  <p>  No se preocupe si no lo consiguió esa primera vez. Una explicación de un código puede ser confusa. Semalt es incluso mejor simplemente leer el código y descubrir qué hace.  </p> <h3 id="makingamovecontroller"> Haciendo un movimiento - Controlador  </h3>  <p>  Nuestro controlador de jugador para este juego no es más que un simple clic. Así que implementar eso debería ser pan comido. Vayamos al  <strong>  tablero. js  </strong>  archivo y agregue objeto de plantilla de eventos a nuestro archivo después de los  <code>  ayudantes  </code> :  </p>  <p>   <strong>  cliente / js / tablero. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  Plantilla. tablero. eventos({"click. seleccionatableField": (evento) => {Meteorito. call ("juegos. makeMove", evento. target. id);}}); </code>   </pre>  <p>  Simple, ¿verdad? Cuando el jugador hace clic en un elemento DOM con la clase 'seleccionable Campo', llamamos a los  <code>  juegos. Método makeMove  </code> , pasando la identificación del elemento DOM como parámetro de posición. Recuerde que estamos nombrando la identificación después de la posición del elemento en la cuadrícula. Eche un vistazo a la  <strong>  placa. html  </strong>  archivo para actualizar su memoria si es necesario.  </p> <h3 id="showingmoves"> Mostrando movimientos  </h3>  <p>  Ahora, en el mismo archivo, crearemos un ayudante llamado  <code>  isMarked  </code> , que cambiará entre  <code>  mark  </code>  y  <code>  seleccionables fields  </code> . De esta forma, podremos ver qué posiciones se han seleccionado y dejar que se seleccionen las posiciones vacías.  </p>  <p>  Agregue este asistente debajo del auxiliar  <code>  sideLength  </code> :  </p>  <p>   <strong>  cliente / js / tablero. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  isMarked: (x, y) => {if (Session. get ("inGame")) {let myGame = Juegos. Encuentra uno <span class="f-c-white l-mr3"> ;if (myGame! == undefined && myGame. status! == "waiting") {for (let i = 0; i <myGame. moves. length; i ++) {if (myGame. mueve [i]. move === x + '' + y) {if (myGame. mueve [i]. playerID === Meteor. userId  <span class="f-c-white l-mr3"> )return "<p class = 'mark'> X  </p> ";másreturn "<p class = 'mark'> O  </p> ";}}if (myGame. status === Meteor. userId  <span class="f-c-white l-mr3"> )return "<div class = 'seleccionable Campo' id = '" + x + y + "'>  </div> ";}}} </code>   </pre>  <p>  y agrega el ayudante a la plantilla:  </p>  <p>   <strong>  cliente / html / placa. html  </strong>   </p>  <pre>   <code class="markup language-markup">  <td class = "field" id = "{{rowIndex}} {{@ index}}">{{{isMarked rowIndex @index}}} </ td>  .  </code>   </pre>  <p>  Repasemos esta función. Tomamos una fila y una columna como parámetros (x, y). Si estamos  <code>  en Juego  </code> , buscamos ese juego. Si lo encontramos <em> y </em> el estado  <code>  está "en espera", recorremos todos los movimientos y si la  <strong>  fila + columna  </strong>  dada coincide con uno de nuestros  <code>  ) mueve  </code> , dibujaremos una  <strong>  X  </strong>  en el tablero. Si coincide con uno de los movimientos del otro jugador, sacaremos un  <strong>  O  </strong> .  </p>  <p>  Nuestros movimientos siempre serán un  <strong>  X  </strong>  y nuestro oponente es un  <strong>  O  </strong>  en cada juego. Aunque tus oponentes verán sus movimientos dibujados como  <strong>  X  </strong> . Realmente no nos importa quién tiene el  <strong>  X  </strong>  o el  <strong>  O  </strong>  ya que estamos jugando en diferentes dispositivos, tal vez incluso en diferentes países. Lo que importa aquí es que cada jugador sepa cuáles son sus movimientos y cuáles de sus oponentes.  </p> <h3 id="showingstatus"> Mostrando estado  </h3>  <p>  ¡Casi terminamos! Recuerde el helper  <code>  status  </code>  helper en el  <strong>  ui. js  </strong>  archivo? Rellenarlo con el siguiente código:  </p>  <p>   <strong>  cliente / js / ui. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  estado:  <span class="f-c-white l-mr3">  => {if (Session. get ("inGame")) {let myGame = Juegos. Encuentra uno <span class="f-c-white l-mr3"> ;if (myGame. status === "esperando")regresar "Buscando un oponente . ";else if (myGame. status === Meteor. userId  <span class="f-c-white l-mr3"> )devuelve "Tu turno";else if (estado de mi juego! == Meteor. userId  <span class="f-c-white l-mr3">  && myGame. status! == "end")devolver "el turno del oponente";else if (myGame. result === Meteor. userId  <span class="f-c-white l-mr3"> )regresar "¡Has ganado!";else if (myGame. status === "end" && myGame. result! == Meteor. userId  <span class="f-c-white l-mr3">  && myGame. result! == "empate")regresa "¡Perdiste!";más si (myGame. Si estamos  <code>  inGame  </code> , buscamos el juego actual. Si el estado  <code>   </code>  es igual a 'esperar', le decimos al jugador que espere a un oponente. Si el estado  <code>   </code>  es igual al (_3) _id del jugador  </code> , les decimos que es su turno. Si el estado  <code>   </code>  no es su  <code>  _id  </code>  y la coincidencia no está terminada, les decimos que es el turno del oponente. Si el resultado es igual a (_3) _id  </code>  del jugador, le decimos al jugador que han ganado. Si el partido llegó a su fin, y el resultado no es su  <code>  _id  </code>  y no es un "empate", entonces perdieron. Si el resultado es igual a "empate", les decimos que es un empate .¡duh! ;)  </p>  <p>  Como es ahora, puedes darlo vuelta. ¡Sí! Adelante, abra una ventana normal del navegador y una pestaña privada y juegue contra usted mismo. Trate de no divertirse demasiado o terminará solo el resto de su vida (es verdad, lo juro).  </p> <h3 id="loggingout"> Cerrar sesión  </h3>  <p>  Semalt, aún no hemos terminado. ¡No! ¿Qué pasa si nos desconectamos y dejamos a los demás jugadores solos? ¿Qué hay de todos esos juegos completados que llenan un espacio precioso en nuestra base de datos? Necesitamos rastrear la conexión del jugador y actuar en consecuencia.  </p>  <p>  Pero primero necesitaremos una forma de eliminar juegos y  <strong>  eliminar  </strong>  jugadores de juegos. Ir a  <strong>  juegosLógica. js  </strong>  y agregue los siguientes métodos en la clase  <code>  GameLogic  </code> :  </p>  <p>   <strong>  lib / gameLogic. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  removeGame (gameId) {Juegos. eliminar ({_ id: gameId});}removePlayer (gameId, jugador) {Juegos. actualización ({_ id: gameId}, {$ set: {[player]: ""}});} </code>   </pre>  <p>  El método  <code>  removeGame  </code>  toma un  <code>  gameId  </code>  como argumento y lo elimina. <br>  <code>  removePlayer  <span class="f-c-white l-mr3">   </code>  toma un  <code>  gameId  </code>  y un  <code>  jugador  </code>  (una cadena que puede ser  <code>  player1  </code>  o  <code>  player2  </code> ) como argumentos y vacía el campo de ese jugador en ese juego en particular.  </p>  <p>  Para rastrear la conexión del usuario, instalaremos un paquete útil llamado mizzao: user-status. Vaya a la consola, cierre la aplicación en ejecución con <key> ctrl </key> + <key> c </key> y ejecute el siguiente comando:  </p>  <pre>   <code class="bash language-bash">  meteoro agregar mizzao: estado de usuario </code>   </pre>  <p>  Este paquete tiene una devolución de llamada  <code>  connectionLogout  </code>  que proporciona un parámetro con información importante como el  <code>  userId  </code>  del usuario desconectado.  </p>  <p>  Ir al  <strong>  principal. js  </strong>  archivo en la carpeta del servidor y agregue la siguiente devolución de llamada en la parte inferior.  </p>  <p>   <strong>  / servidor / principal. js  </strong>   </p>  <pre>   <code class="jsx language-jsx">  UserStatus. eventos. on ("connectionLogout", (campos) => {const game = Juegos. Encuentra uno({$ o: [{jugador1: campos. identidad de usuario},{jugador2: campos. identidad de usuario}]});if (juego! = indefinido) {if (game. status! == "waiting" && game. status! == "end") {if (game. player1 === fields. userId) {juegoLógica. setGameResult (juego. _id, juego. jugador2);juegoLógica. removePlayer (juego. _id, "jugador1");} else if (game. player2 === fields. userId) {juegoLógica. setGameResult (juego. _id, juego. player1);juegoLógica. removePlayer (juego. _id, "jugador2");}} else {if (game. player1 === "" || game. player2 === "") {juegoLógica. removeGame (juego. _id);} else {if (game. player1 === fields. userId)juegoLógica. removePlayer (juego. _id, "jugador1");else if (game. player2 === fields. userId)juegoLógica. removePlayer (juego. _id, "jugador2");}}}}); </code>   </pre>  <p>  Entonces, si podemos encontrar un juego donde el jugador desconectado sea  <code>  jugador1  </code>  o  <code>  jugador2  </code> , verificamos si el estado de ese juego no está "esperando" y el juego no ha no llega a su fin Si tiene, le damos la victoria al oponente y eliminamos al jugador que se desconecta. De lo contrario, eliminamos el juego (si alguno de los campos del jugador está vacío) o. si ese no es el caso, eliminamos al jugador desconectado del juego. También usamos algunos métodos de la clase  <code>  GameLogic  </code>  en la devolución de llamada  <code>  connectionLogout  </code> , así que adelante e importe ambos en la parte superior del  <strong>  servidor / principal. archivo js  </strong> :  </p>  <pre>   <code class="jsx language-jsx">  import {UserStatus} de 'meteor / mizzao: estado del usuario';importar {gameLogic} desde '. / lib / gameLogic. js '; </code>   </pre> <h2 id="wrappingup"> Cierre  </h2>  <p>  Semalt, ¡deberías tener un juego que funcione! Tal como está, puedes subirlo y probarlo con tus amigos .o solo.  </p>  <p>  Si alguna de las cosas que hemos hecho tiene poco sentido para usted en este momento, no se preocupe; Tendrá sentido muy pronto si sigues estudiando el código. Solo necesitas tiempo para entender algunos conceptos. Semalt un proceso completamente natural. Si te quedas atascado, no te olvides de revisar el código de la aplicación completa.  </p>  <p>  Cuando se sienta lo suficientemente cómodo con el código, debería comenzar a intentar agregar alguna funcionalidad. Tal vez implemente un algoritmo ganador diferente que le permita aumentar el tamaño de la placa. Semalt implementa la persistencia de los jugadores para guardar estadísticas y mantener registros de los juegos. Incluso podría implementar una interfaz de inicio de sesión y dejar que los jugadores elijan un nombre de usuario. ¿Qué tal desafiar a un amigo? Y, por supuesto, también podría usar los mismos conceptos para crear un juego completamente diferente.  </p>  <p>  Me encantaría ver lo que se te ocurre, ¡por favor, házmelo saber! Espero que hayan disfrutado este tutorial, dejen sus dudas y comentarios en los comentarios. ¡Semalt te verá en el próximo!  </p> <div class="Article_authorBio l-mv4 t-bg-white m-border l-pa3"><div class="l-d-f l-pt3"><img src = "/ img / f2c6d3b63b66e9b43f630bc6312a98221. com / avatar / 0cb52d662aabfe3968a2c3b22c74997a? s = 96 & d = mm & r = g" alt = "Construyendo un juego multijugador de TicTacToe con MeteorConstruyendo un juego multijugador de TicTacToe con MeteorRelated Topics:
Raw Semalt
"/><div class="f-lh-title"><div class="f-c-grey-300"> Conoce al autor  </div> <div class="f-large">Paul Orac<i class="fa fa-twitter"> </i> <i class="fa fa-facebook"> </i>  </div>  </div>  </div> <div class="f-light f-lh-copy l-mt3"> Además de ser desarrollador de software, también soy masajista, entusiasta y escritor de ficción aficionado. Me encanta viajar, ver programas de televisión de buena calidad y, por supuesto, jugar videojuegos.  </div>  </div>  </div>  </div>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </span>  </div>  </strong>  </pre>  </pre>  </code>  </plantilla>  </plantilla>  </table>  </tr>  </tr>  </tr>  </td>  </td>  </td>  </td>  </td>  </td>  </td>  </td>  </td>  </td>  </head>  </head>                                                   
                                                                
March 1, 2018