Crear un juego de tres en línea en red para Android

Construir un juego de tres en línea de multijugador en red con PHP, XML y el kit de desarrollo de Android

Construya el fondo de un juego de tres en línea de multijugador y habilitado para la red con una aplicación frontal de Android en este artículo.

Jack D. Herrington, Senior Software Engineer, Leverage Software Inc.

Photo of Jack HerringtonJack Herrington es un ingeniero, autor y presentador que vive y trabaja en el Área de la Bahía. Puede mantenerse al tanto de su trabajo y sus escritos en http://jackherrington.com.



09-04-2012

Juego de tres en línea de multijugador en red

Acrónimos de uso frecuente

  • API: Interfaz de programación de aplicaciones
  • HTTP: Protocolo de Transferencia de Hipertexto
  • IP: Protocolo Internet
  • SDK: Kit de desarrollo de software
  • SQL: Lenguaje de Consulta Estructurado
  • IU: Interfaz de Usuario
  • XML: Extensible Markup Language

Los juegos casuales son extremadamente populares y muy lucrativos, y es fácil saber por qué. No todas las personas de todos los grupos de edades están interesadas en jugar online videojuegos de disparos en primera persona contra hordas de adolescentes con reflejos tan rápidos como la velocidad de la luz. Algunas veces, es más interesante jugar videojuegos donde tiene tiempo para pensar y realizar una estrategia o donde la meta es cooperar unos con otros para ganar el juego.

Lo genial sobre los videojuegos casuales desde la perspectiva de los desarrolladores es que son mucho más fáciles de construir que los videojuegos intensivos en gráficos de disparos en primera persona o de deportes. Así que es más fácil para un solo desarrollador, o para un grupo de desarrolladores, producir una primera versión de un nuevo videojuego original.

En este artículo, pasamos a través de las bases para crear un juego de línea de tres casual de multijugador en red. El servidor del videojuego es una aplicación web basada en MySQL y PHP con una interfaz XML. La parte frontal es una aplicación nativa de Android que funciona en teléfonos de Android.


Construyendo el fondo

El fondo inicia con una base de datos simple de MySQL que tiene dos tablas. El Listado 1 muestra el esquema para la base de datos.

Listado 1. db.sql
DROP TABLE IF EXISTS games;
CREATE TABLE games(
     id INT NOT NULL AUTO_INCREMENT,
     primary key ( id ) );

DROP TABLE IF EXISTS moves;
CREATE TABLE moves(
     id INT NOT NULL AUTO_INCREMENT,
     game INT NOT NULL,
     x INT NOT NULL,
     y INT NOT NULL,
     color INT NOT NULL,
     primary key ( id ) );

La primera de las dos tablas es la tabla de los juegos, que sólo tiene el ID exclusivo del juego. En una aplicación de producción, probablemente tenga una tabla de usuarios, y la tabla de juegos incluye los IDs de usuario de ambos jugadores. Para hacerlo simple, sin embargo, renunciaré a este enfoque para concentrarme en las bases de almacenar los datos del juego, la comunicación entre el cliente y el servidor y la construcción de la parte frontal.

La segunda tabla es la tabla de movimientos, que incluye los movimientos individuales para el juego dado, así que tiene cinco columnas. La primera columna es el ID exclusivo del movimiento. La segunda columna es el ID del juego al que aplica este movimiento. Después vienen las posiciones 'x' y 'y' del movimiento. Estos valores deben estar entre 0 y 2 para 'x' y 'y', ya que cuenta con una red de tres por tres. El último campo es el "color" del movimiento, que es un entero que indica X u O.

Para construir la base de datos, primero use mysqladmin para crearla y después use el comando mysql para ejecutar el script db.sql como se muestra aquí:

% mysqladmin --user=root --password=foo create ttt
% mysql --user=root --password=foo ttt < db.sql

Esta etapa crea una nueva base de datos llamada "ttt", que tiene el esquema del juego de tres en línea.

ahora que tiene el esquema, necesita crear una forma de iniciar un juego. Para esto, usted cuenta con un script llamado start.php, como en el Listado 2.

Listado 2. start.php
<?php
header( 'Content-Type:text/xml' );

$dd = new PDO('mysql:host=localhost;dbname=ttt', 'root', '');
$sql = 'INSERT INTO games VALUES ( 0 )';
$sth = $dd->prepare($sql);
$sth->execute( array() );
$qid = $dd->lastInsertId();

$doc = new DOMDocument();
$r = $doc->createElement( "game" );
$r->setAttribute( 'id', $qid );
$doc->appendChild( $r );

print $doc->saveXML();
?>

El script comienza por conectarse a la base de datos. Después ejecuta una sentencia INSERT en la tabla de juegos y recupera el ID que fue generado. Desde ahí crea un documento XML, añade el ID a una etiqueta de juego y exporta el XML.

Necesita ejecutar este script para poner un juego en la base de datos, ya que la simple aplicación de Android no tiene una interfaz para crear juegos. Este es el código:

$ php start.php
<?xml version="1.0"?>
<game id="1"/>
$

Ahora ya tiene su primer juego. Para ver la lista de juegos, use el script games.php que está en el Listado 3.

Listado 3. games.php
<?php
header( 'Content-Type:text/xml' );

$dbh = new PDO('mysql:host=localhost;dbname=ttt', 'root', '');

$sql = 'SELECT * FROM games';

$q = $dbh->prepare( $sql );
$q->execute( array() );

$doc = new DOMDocument();
$r = $doc->createElement( "games" );
$doc->appendChild( $r );

foreach ( $q->fetchAll() as $row) {
  $e = $doc->createElement( "game" );
  $e->setAttribute( 'id', $row['id'] );
  $r->appendChild( $e );
}

print $doc->saveXML();
?>

Este script, igual que el script.php, comienza por conectarse a la base de datos. Después de eso, consulta la tabla de juegos para ver qué está disponible. Y desde ahí crea un nuevo documento XML, añade una etiqueta de juegos, después añade etiquetas de juego para cada uno de los juegos disponibles.

Cuando ejecuta este script desde la línea de comandos, ve algo como esto:

$ php games.php
<?xml version="1.0"?>
<games><game id="1"/></games>
$

También puede ejecutar este script desde el navegador web para ver la misma salida.

¡Excelente! Con la API de juegos fuera del camino, es momento de escribir el código de servidor para manejar los movimientos. Este código inicia con la construcción de un script ayudante llamado show_moves que obtiene los movimientos actuales para un juego dado y los exporta como XML. El Listado 4 muestra el código PHP para esta función de ayudante.

Listado 4. show_moves.php
<?php
function show_moves( $dbh, $game ) {
  $sql = 'SELECT * FROM moves WHERE game=?';

  $q = $dbh->prepare( $sql );
  $q->execute( array( $game ) );

  $doc = new DOMDocument();
  $r = $doc->createElement( "moves" );
  $doc->appendChild( $r );

  foreach ( $q->fetchAll() as $row) {
    $e = $doc->createElement( "move" );
    $e->setAttribute( 'x', $row['x'] );
    $e->setAttribute( 'y', $row['y'] );
    $e->setAttribute( 'color', $row['color'] );
    $r->appendChild( $e );
  }

  print $doc->saveXML();
}
?>

El script toma un manejador de base de datos y el ID de juego. Desde ahí ejecuta el SQL para obtener la lista de movimientos. Después crea un documento XML con los movimientos para el juego dado.

Usted creó esta función de ayudante porque hay dos scripts que la usan; el primero es un script moves.php que retorna los movimientos actuales para el juego especificado. El Listado 5 muestra este script.

Listado 5. moves.php
<?php
require_once( 'show_moves.php' );

header( 'Content-Type:text/xml' );

$dbh = new PDO('mysql:host=localhost;dbname=ttt', 'root', '');

show_moves( $dbh, $_REQUEST['game'] );
?>

Este simple script incluye el código de la función de ayudante, se conecta a la base de datos y después invoca la función show_moves con el ID del juego especificado. Para probar este código, use el comando curl para invocar el script en el servidor desde la línea de comandos:

$ curl "http://localhost/ttt/moves.php?game=1"
<?xml version="1.0"?>
<moves/>
$

Desgraciadamente, aún no ha hecho ningún movimiento, así que no es una salida particularmente interesante. Para remediar eso necesita añadir el script final a la API del servidor. El Listado 6 muestra el script move.php.

Listado 6. move.php
<?php
require_once( 'show_moves.php' );

header( 'Content-Type:text/xml' );

$dbh = new PDO('mysql:host=localhost;dbname=ttt', 'root', '');
$sql = 'DELETE FROM moves WHERE game=? AND x=? AND y=?';
$sth = $dbh->prepare($sql);
$sth->execute( array(
  $_REQUEST['game'],
  $_REQUEST['x'],
  $_REQUEST['y']
) );

$sql = 'INSERT INTO moves VALUES ( 0, ?, ?, ?, ? )';
$sth = $dbh->prepare($sql);
$sth->execute( array(
  $_REQUEST['game'],
  $_REQUEST['x'],
  $_REQUEST['y'],
  $_REQUEST['color']
) );

show_moves( $dbh, $_REQUEST['game'] );
?>

Este script comienza por incluir la función de ayudante y conectarse a la base de datos. Después ejecuta dos sentencias SQL. La primera elimina cualquier movimiento que pueda chocar con la que está siendo enviada. La segunda inserta una nueva fila en la tabla de movimientos para el movimiento especificado. El script después retorna la lista de movimientos al cliente. Esta etapa salva al cliente de tener que hacer dos solicitudes cada vez que hace un movimiento. El ancho de banda no es barato, así que siempre que pueda conglomerar solicitudes debe hacerlo.

Para probar que todo esto funciona, puede hacer un movimiento:

$ curl "http://localhost/ttt/move.php?game=1&x=1&y=2&color=1"
<?xml version="1.0"?>
<moves><move x="1" y="2" color="1"/></moves>

Con el código del servidor del juego completo, puede construir la parte frontal de Android para este juego en red de multijugador.


Construyendo la parte frontal de Android

Primero, instale el SDK de Android, así como algunas versiones de la plataforma de Android, y después finalmente Eclipse y el plug-in de Eclipse de Android. Para nuestra suerte, todo esto está bien documentado en el sitio de Android (vea Recursos para obtener enlaces). Una cobertura a profundidad sobre cómo configurar su entorno de desarrollo requeriría todo este artículo y más.

Después de que configure el entorno de desarrollo, lance Eclipse e inicie un nuevo proyecto de Android. Debe ver algo similar a la Figura 1.

Figura 1. Creando la aplicación de Android en Eclipse
Creando la aplicación de Android en Eclipse

La Figura 1 muestra el asistente de proyecto para aplicaciones de Android. Ingrese el nombre de un proyecto, seleccione el botón de selección Create new project in workspace y especifique la ubicación para el código con los elementos de UI. En la lista de comprobación Build Target, seleccione una plataforma de Android. Para este código, yo uso Android 2.3.1. El código es muy simple, así que puede usar cualquier versión que prefiera. Si no ve ninguna plataforma listada, entonces necesita descargar e instalar las plataformas como se indica en las instrucciones de configuración del SDK de Android. Tenga en cuenta que descargar todas estas plataformas requiere mucho, mucho tiempo.

En la sección Properties , llene el nombre de la aplicación y el nombre del paquete. Yo usé "Tic Tac Toe" y "com.jherrington.tictactoe" en los campos respectivos. Después, marque el recuadro de selección Create Activity e ingrese un nombre para la actividad. Yo usé "TicTacToeActivity" como el nombre de la actividad.

Haga clic en Finish para ver un nuevo proyecto que se parece a la Figura 2.

Figura 2. Los archivos del proyecto TicTacToe
Los archivos del proyecto TicTacToe

La Figura 2 muestra los directorios de alto nivel y los archivos para una aplicación de Android (los directorios son src, gen, Android 2.3.1 y res y los archivos son assets, AndroidManifest.xml, default.properties y proguard.cfg). Los elementos importantes son:

  • El directorio res, que contiene recursos
  • El directorio src, que tiene el origen de Java™
  • El archivo manifest, que contiene la información biográfica sobre la aplicación

Su primera edición es en el archivo manifest. La mayoría del archivo ya está correcto, pero necesita añadir el permiso de Internet, de forma que la aplicación pueda hacer solicitudes en Internet. El Listado 7 muestra el archivo manifest completado.

Listado 7. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:versionCode="1"
      android:versionName="1.0" package="com.jherrington.tictactoe">

    <uses-permission
        android:name="android.permission.INTERNET" />

    <uses-sdk android:minSdkVersion="5" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name="TicTacToeActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

El único cambio fue añadir la etiqueta uses-permission en la parte superior del archivo.

Su siguiente tarea es diseñar la IU. Para esto, ajuste el archivo layout.xml, que está contenido en el directorio res/layout. El Listado 8 muestra el nuevo contenido para este archivo.

Listado 8. layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <LinearLayout android:layout_height="wrap_content" 
                android:layout_width="match_parent" android:id="@+id/linearLayout1">
        <Button android:text="Play X" android:id="@+id/playx" 
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content"></Button>
        <Button android:text="Play O" android:id="@+id/playo" 
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content"></Button>
    </LinearLayout> 
    <com.jherrington.tictactoe.BoardView android:id="@+id/bview" 
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content"
    ></com.jherrington.tictactoe.BoardView>
</LinearLayout>

Este es un diseño sencillo. La parte superior es un conjunto de dos botones envueltos en un diseño lineal con una orientación horizontal. Estos dos botones son los botones X y O que el usuario usa para especificar qué color está jugando.

El resto del código está llenado con una clase BoardView, que muestra el tablero de tres en línea con el juego actual. El código para la clase BoardView está en el Listado 11.

Con el diseño a la mano, es momento de escribir algo de código de Java para la aplicación. Esta codificación comienza con la clase TicTacToeActivity en el Listado 9. Las actividades son los componentes básicos de aplicaciones de Android. Cada aplicación tiene una o más actividades que representan los diversos estados de la aplicación. A medida que navega a través de la aplicación construye una pila de actividades que puede después sacar al usar el botón de retroceso en el teléfono. La aplicación TicTacToe tiene una sola actividad.

Listado 9. TicTacToeActivity.java
package com.jherrington.tictactoe;

import java.util.Timer;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.LinearLayout;

public class TicTacToeActivity extends Activity implements OnClickListener {
  @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      Button playx = (Button)this.findViewById(R.id.playx);
      playx.setOnClickListener( this );

      Button playo = (Button)this.findViewById(R.id.playo);
      playo.setOnClickListener( this );

      Timer timer = new Timer();
      UpdateTimer ut = new UpdateTimer();
      ut.boardView = (BoardView)this.findViewById(R.id.bview);
      timer.schedule( ut, 200, 200 );
    }

    public void onClick(View v) {
      BoardView board = (BoardView)this.findViewById(R.id.bview);
      if ( v.getId() == R.id.playx ) {
        board.setColor( 2 );
      }
      if ( v.getId() == R.id.playo ) {
        board.setColor( 1 );
      }
    }
}

La actividad tiene dos métodos. El primero es el método onCreate, que construye la interfaz de usuario, conecta al manejador onClick a los botones X y O e inicia el temporizador de actualización. El temporizador de actualización es usado para renovar el estado del juego cada 200 milisegundos. Este dispositivo permite a ambos jugadores ver cuando el otro jugador se mueve.

El manejador onClick establece el color actual del tablero con base en si el usuario hace clic en el botón X o en el botón O.

La clase GameService, en el Listado 10, es una clase singleton que representa el servidor del juego y el estado actual del juego dado.

Listado 10. GameService.java
package com.jherrington.tictactoe;

import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import android.util.Log;

public class GameService {
  private static GameService _instance = new GameService();

  public int[][] positions = new int[][] { 
      { 0, 0, 0 },
      { 0, 0, 0 },
      { 0, 0, 0 }
  };

  public static GameService getInstance() {
    return _instance;
  }

  private void updatePositions( Document doc ) {
    for( int x = 0; x < 3; x++ ) {
      for( int y = 0; y < 3; y++ ) {
        positions[x][y] = 0;
      }
    }
    doc.getDocumentElement().normalize();
    NodeList items = doc.getElementsByTagName("move");
    for (int i=0;i<items.getLength();i++){
      Element me = (Element)items.item(i);
      int x = Integer.parseInt( me.getAttribute("x") );
      int y = Integer.parseInt( me.getAttribute("y") );
      int color = Integer.parseInt( me.getAttribute("color") );
      positions[x][y] = color;
    }
  }

  public void startGame( int game ) {
    HttpClient httpclient = new DefaultHttpClient();
    HttpPost httppost = new HttpPost("http://10.0.2.2/ttt/moves.php");

    try {
      List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
      nameValuePairs.add(new BasicNameValuePair("game", Integer.toString(game)));
      httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

      HttpResponse response = httpclient.execute(httppost);
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      updatePositions( db.parse(response.getEntity().getContent()) );
    } catch (Exception e) {
      Log.v("ioexception", e.toString());
    }
  }

  public void setPosition( int game, int x, int y, int color ) {
    HttpClient httpclient = new DefaultHttpClient();
    HttpPost httppost = new HttpPost("http://10.0.2.2/ttt/move.php");

    positions[x][y] = color;

    try {
      List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
      nameValuePairs.add(new BasicNameValuePair("game", Integer.toString(game)));
      nameValuePairs.add(new BasicNameValuePair("x", Integer.toString(x)));
      nameValuePairs.add(new BasicNameValuePair("y", Integer.toString(y)));
      nameValuePairs.add(new BasicNameValuePair("color", Integer.toString(color)));
      httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

      HttpResponse response = httpclient.execute(httppost);
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      updatePositions( db.parse(response.getEntity().getContent()) );
    } catch (Exception e) {
      Log.v("ioexception", e.toString());
    }
  }
}

Este código es algo del código más interesante en la aplicación. Primero, tiene el método updatePositions, que toma el XML retornado del servidor y busca los elementos de movimiento, después actualiza la matriz de posiciones con el conjunto de movimientos actual. La matriz de posiciones tiene un valor para cada posición en el tablero; cero indica un espacio vacío, 1 representa "O" y 2 es para "X".

Las otras dos funciones, startGame y setPosition, son la forma en que se comunica con el servidor. El método startGame solicita el conjunto de movimientos actual del servidor y actualiza la lista de posiciones. El método setPosition publica el movimiento en el servidor al crear una solicitud de publicación HTTP y configurar los datos para la publicación usando una matriz de pares de valores de nombre, los cuales son después son codificados para transporte. Después analiza el XML de respuesta para actualizar la lista de posiciones.

Si observa detenidamente, el IP usado para conectarse al servidor es realmente interesante. No es "localhost" o "127.0.0.1"; es "10.0.2.2", que es un alias para la máquina en la que se ejecuta el emulador. Ya que el teléfono de Android es en sí mismo un sistema de UNIX® , tiene sus propios servicios en localhost. Fascinante, ¿verdad? No es frecuente que sea tan claro que el teléfono no es en realidad un teléfono per se, sino una computadora en toda regla que se ajusta a la palma de su mano y sucede que tiene un teléfono integrado en él.

Entonces, ¿dónde estamos? Usted tiene la actividad, que es el componente principal para la aplicación; tiene la configuración del diseño de IU; tiene el código de Java para conectarse al servidor. Ahora necesita dibujar el tablero del juego. Esto es realizado por la clase BoardView en el Listado 11.

Listado 11. BoardView.java
package com.jherrington.tictactoe;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class BoardView extends View {
  private int _color = 1;
  
  public void setColor( int c ) {
    _color = c;
  }
  
  public BoardView(Context context) {
      super(context);
      GameService.getInstance().startGame(0);
  }
  
  public BoardView(Context context, AttributeSet attrs) {
      super(context,attrs);
      GameService.getInstance().startGame(0);
  }
  
  public BoardView(Context context, AttributeSet attrs, int defStyle) {
      super(context,attrs,defStyle);
      GameService.getInstance().startGame(0);
  }

  public boolean onTouchEvent( MotionEvent event ) {
    if ( event.getAction() != MotionEvent.ACTION_UP )
      return true;
    int offsetX = getOffsetX();
    int offsetY = getOffsetY();
    int lineSize = getLineSize();
    for( 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ía del trabajo aquí es realizado en el método onTouch, que responde al usuario tocando una célula particular en el tablero del juego, y el método onDraw, que pinta el tablero del juego usando el mecanismo de pintura de Android.

El método onTouch usa las funciones de tamaño para definir un rectángulo para cada posición de célula. Después usa el método contains en el rectángulo para ver si el usuario hizo clic dentro de la célula. Si lo hizo, ejecuta una solicitud al servicio del juego para que haga un movimiento.

La función onDraw usa las funciones de tamaño para dibujar las líneas del tablero y para dibujar cualquier X u O jugada. El singleton GameServer es usado para su matriz de posiciones, que tiene el estado actual de cada cuadro en el tablero del juego.

La última clase que necesita es UpdateTimer, que usa el servicio del juego para actualizar las posiciones del tablero con sus últimos valores. El Listado 12 muestra el código para el temporizador.

Listado 12. UpdateTimer.java
package com.jherrington.tictactoe;

import java.util.TimerTask;

public class UpdateTimer extends TimerTask {
  public BoardView boardView;
  
  @Override
  public void run() {
    GameService.getInstance().startGame( 0 );
    boardView.post(new Runnable(){ public void run(){ boardView.invalidate(); } });
  }
}

El temporizador es iniciado por la clase TicTacToeActivity cuando la aplicación se inicia. Este temporizador es un mecanismo de sondeo. Esta no es la forma más eficiente para la comunicación entre el cliente y el servidor, pero es la más simple y confiable. La forma más eficiente es usar la versión 1.1 del protocolo de HTTP para mantener la conexión abierta y para que el servidor envíe actualizaciones al cliente cuando se hagan los movimientos. Este enfoque es mucho más complejo; requiere que el cliente y el servidor soporten el protocolo 1.1 y tiene problemas de escalabilidad con el número de conexiones. Ese enfoque está fuera del ámbito de este artículo. Para juegos simples de demostración como este, un mecanismo de sondeo funciona bien.

Con el código hecho, puede probar la aplicación. Eso significa iniciar el emulador. Debe ver algo como la Figura 3 después de inicializar.

Figura 3. Lanzando el emulador de Android
Lanzando el emulador de Android

Este es el emulador cargando una fantástica interfaz "A N D R O I D". Después de ser cargada, ve la pantalla power-on en la Figura 4.

Figura 4. El emulador lanzado y listo para usarse
El emulador lanzado y listo para usarse

Para entrar al teléfono, deslice el icono de bloqueo hacia la derecha. Esa acción lo lleva a la pantalla de inicio y generalmente lanza la aplicación que está depurando. En este caso, esta acción muestra la pantalla del juego en la Figura 5.

Figura 5. El juego antes de que se hagan movimientos
El juego antes de que se hagan movimientos

Dependiendo del estado de su servidor, puede ver o no ningún movimiento. En este caso, el juego estaba vacío. Los botones Play X y Play O están en la parte superior con el tablero del juego de línea de tres en el centro de la visualización. A continuación, haga clic en Play X, después haga clic en el cuadro del centro para ver algo como la Figura 6.

Figura 6. X toma el cuadro del centro, por supuesto
X toma el cuadro del centro, por supuesto

La Figura 6 muestra la visualización del tablero del juego con una X ahora llenando el cuadro del centro. Para verificar que el servidor estaba conectado, puede ejecutar el comando curl en el script moves.php en el servidor para obtener la lista más reciente de movimientos del juego.

Para probar que funcionen las Os, haga clic en Play O y seleccione el cuadro de una esquina como en la Figura 7.

Figura 7. O toma el cuadro de una esquina
O toma el cuadro de una esquina

Puede jugar con Xs y Os. La aplicación se conecta al servidor para alojar el estado del juego en una ubicación compartida. Y debido al temporizador de actualización, cada usuario puede ver los movimientos realizados por el otro.

Conclusión

¿Es este un juego completo? No realmente. No hay verificación de condición de victoria, los jugadores pueden sobrescribir posiciones y no hay verificación de turno. Pero las piezas de tecnología básicas están presentes: un servidor de juego con el estado almacenado compartido entre los jugadores y una aplicación gráfica nativa en un dispositivo móvil que se conecta al servidor del juego para proporcionar un interfaz para el juego. Puede usar este juego como un punto de partida para su propio juego y construirlo como desee. Sólo recuerde mantenerlo casual y divertido, y tal vez sea el creador del siguiente Words With Friends o multijugador de Angry Birds.

Recursos

Aprender

  • Eclipse: Aprenda más sobre el IDE usado en este artículo para desarrollar la aplicación de Android. También encuentre descargas y plug-ins de Eclipse.
  • Herramientas de Desarrollo de PHP para Eclipse: ¿Necesita un IDE para PHP? El proyecto de Eclipse tiene una extensión para eso más otros plug-ins de Eclipse para prácticamente todo.
  • Android Market: Después de que escribe su juego casual de multijugador en red de Android, súbalo al mercado de Android. Y háganos saber que lo ha hecho en la sección de comentarios de este artículo.
  • El sitio de PHP : Explore la mejor referencia para PHP que está disponible.
  • El W3C: Visite un gran sitio para estándares, en particular el estándar XML es relevante para este artículo.
  • Más artículos de este autor (Jack Herrington, developerWorks, marzo de 2005-a la actualidad): Lea artículos sobre Ajax, JSON, PHP, XML y otras tecnologías.
  • ¿Principiante en XML? Obtenga los recursos que necesita para aprender XML.
  • Área XML en developerWorks: Encuentre los recursos que necesita para avanzar sus habilidades en la arena de XML, incluyendo DTDs, esquemas y XSLT. Vea la Biblioteca técnica XML para obtener una amplia gama de artículos técnicos y consejos, tutoriales, estándares e IBM Redbooks.
  • Certificación IBM XML: Conozca cómo puede usted convertirse en un Desarrollador Certificado IBM en XML y en tecnologías relacionadas.
  • Eventos técnicos y webcasts de developerWorks: Manténgase actualizado(a) con la tecnología en estas sesiones.
  • developerWorks en Twitter: Únase hoya para seguir los tweets developerWorks.
  • developerWorks podcasts: Escuche interesantes entrevistas y discusiones para desarrolladores de software.
  • demostraciones developerWorks on-demand : Observe demostraciones que van desde la instalación de productos y demostraciones de configuración para principiantes, hasta funcionalidades avanzadas para desarrolladores experimentados.

Obtener los productos y tecnologías

Comentar

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Linux
ArticleID=808720
ArticleTitle=Crear un juego de tres en línea en red para Android
publish-date=04092012