Avançar para a área de conteúdo

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Todas as informações enviadas são seguras.

  • Fechar [x]

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Todas as informações enviadas são seguras.

  • Fechar [x]

Criar um Jogo da Velha em Rede para Android

Desenvolva um jogo da velha em rede, para vários jogadores, usando PHP, XML e o kit de desenvolvimento do Android

Jack D. Herrington, Senior Software Engineer, Leverage Software Inc.
Photo of Jack Herrington
Jack Herrington é engenheiro, autor e apresentador que mora e trabalha em Bay Area. É possível se manter atualizado em relação ao seu trabalho e aos seus escritos em http://jackherrington.com.

Resumo:  Desenvolva, neste artigo, o backend de um jogo da velha em rede, para vários jogadores, com um aplicativo frontend Android.

Data:  06/Set/2011
Nível:  Introdutório Também disponível em :   Inglês
Atividade:  4504 visualizações
Comentários:  


Jogo da velha em rede para vários jogadores

Acrônimos usados frequentemente

  • API: Interface de Programação de Aplicativos
  • HPTP: Protocolo de Transporte de Hipertexto
  • IP: Protocolo da Internet
  • SDK: Kit de Desenvolvimento de Software
  • SQL: Linguagem de Consulta Estruturada
  • UI: interface com o usuário
  • XML: Linguagem de Marcação Extensível

Jogos casuais são extremamente populares e muito lucrativos, e é fácil de ver o porquê. Nem todas as pessoas estão interessadas em jogar on-line, sair atirando no FPS contra hordas de pré-adolescentes com reflexos na velocidade da luz. Algumas vezes, é mais interessante participar de um jogo em que você tenha tempo para pensar e organizar uma estratégia, ou cujo objetivo seja cooperar com outros para chegar à vitória.

O grande detalhe sobre jogos casuais, do ponto de vista dos desenvolvedores, é que eles são muito mais fáceis de desenvolver do que os jogos esportivos ou FPS com grande número de elementos gráficos. Assim, é mais fácil para um único desenvolvedor, ou para um grupo de desenvolvedores, produzir a primeira versão de um novo novel game.

Neste artigo, analisaremos os fundamentos básicos da criação de um jogo da velha casual para vários jogadores. O servidor de jogos é um aplicativo da Web baseado em MySQL e em PHP, com uma interface XML. O frontend é um aplicativo nativo Android que funciona em telefones Android.


Desenvolvendo o Backend

O backend inicia com um banco de dados MySQL simples que tem duas tabelas. A Listagem 1 mostra o esquema do banco de dados.


Listagem 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 ) );

A primeira das duas tabelas é a tabela de jogos, que tem apenas o ID exclusivo do jogo. Em um aplicativo de produção, você provavelmente terá uma tabela de usuários, e a tabela de jogos incluirá os IDs de usuário de ambos os jogadores. Para manter a simplicidade, porém, esqueceremos essa abordagem para nos concentrar nos fundamentos básicos de como armazenar os dados do jogo, como fazer a comunicação entre cliente e servidor e como desenvolver o frontend.

A segunda tabela é a tabela de movimentações, que inclui as movimentações individuais de determinado jogo, assim ela tem cinco colunas. A primeira coluna é o ID exclusivo da movimentação. A segunda coluna é o ID do jogo no qual a movimentação se aplica. Em seguida, estão as posições x e y da movimentação. Esses valores devem ser entre 0 e 2 para x e y, partindo do princípio que você tem uma grade três por três. O último campo é a "cor" da movimentação, que é um inteiro que indica X ou O.

Para criar o banco de dados, primeiro use mysqladmin para criá-lo e, em seguida, use o comando mysql para executar o script db.sql como mostrado aqui:

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

Essa etapa cria um novo banco de dados denominado "ttt", que tem o esquema do jogo da velha.

Agora que já se tem o esquema, é necessário criar um modo para iniciar um jogo. Para isso, é necessário ter um script denominado start.php, como na listagem 2.


Listagem 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();
?>

O script é iniciado pela conexão ao banco de dados. Em seguida, ele executa uma instrução INSERT na tabela de jogos e retorna o ID que foi gerado. A partir desse ponto, ele cria um documento XML, inclui o ID em uma tag do jogo e exporta o XML.

É necessário executar esse script para obter um jogo no banco de dados porque o aplicativo Android simples não tem uma interface para criar os jogos. Este é o código:

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

Agora, já se tem o primeiro jogo. Para ver a lista de jogos, use o script games.php que está na Listagem 3.


Listagem 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();
?>

Esse script, como o script start.php, é iniciado pela conexão ao banco de dados. Depois disso, ele consulta a tabela de jogos para ver o que está disponível. E, a partir desse ponto, cria um novo documento XML, inclui uma tag de jogos e inclui tags de jogos para cada um dos jogos disponíveis.

Ao executar esse script na linha de comandos, é possível ver algo como:

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

Também é possível executar esse script no navegador da Web para ver a mesma saída.

Excelente! Com a API dos jogos finalizada, é hora de escrever o código do servidor para manipular as movimentações. Esse código inicia com o desenvolvimento de um script auxiliar, denominado show_moves, que obtém as movimentações atuais de determinado jogo e as exporta como XML. A listagem 4 mostra o código PHP para essa função auxiliar.


Listagem 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();
}
?>

O script seleciona um identificador do banco de dados e o ID do jogo. Desse ponto, ele executa o SQL para obter a lista de movimentações. Em seguida, ele cria um documento XML com as movimentações de determinado jogo.

Essa função auxiliar foi criada porque há dois scripts que a usam. O primeiro é um script (moves.php) que retorna as movimentações atuais do jogo especificado. A Listagem 5 mostra esse script.


Listagem 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'] );
?>

Esse é um script simples que inclui o código de função auxiliar, conecta-se ao banco de dados e chama a função show_moves com o ID do jogo especificado. Para testar esse código, use o comando curl para chamar o script no servidor a partir da linha de comandos:

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

Infelizmente, não foi feita nenhuma movimentação ainda, então ela não é uma saída especialmente interessante. Para resolver isso, é necessário incluir o script final na API do servidor. A Listagem 6 mostra o script move.php.


Listagem 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'] );
?>

Esse script é iniciado pela inclusão da função auxiliar e conexão ao banco de dados. Ele executa, em seguida, duas instruções SQL. A primeira remove qualquer movimentação que possa entrar em conflito com a movimentação que está sendo enviada. A segunda insere uma nova linha na tabela de movimentações para a movimentação especificada. O script então retorna a lista de movimentações para o cliente. Essa etapa evita que o cliente tenha de fazer duas solicitações cada vez que fizer uma movimentação. A largura de banda não é barata, assim, sempre que for possível, é recomendável conglomerar solicitações.

Para testar isso, tudo o que se pode fazer é uma movimentação:

$ 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>

Com o código do servidor de jogo completo, é possível desenvolver o frontend do Android para esse jogo em rede para vários jogadores.


Desenvolvendo o Frontend do Android

Primeiro, instale o Android SDK e algumas versões da plataforma do Android e, finalmente, o Eclipse e o plug-in do Android Eclipse. Felizmente, tudo isso está bem documentado no site do Android (consulte Recursos para obter os links). Uma descrição detalhada de como configurar o seu ambiente de desenvolvimento ocuparia este artigo inteiro e mais.

Depois de configurar o ambiente de desenvolvimento, ative o Eclipse e inicie um novo projeto Android. Deve-se visualizar algo similar à figura 1.


Figura 1. Criando o Aplicativo Android no Eclipse

A figura 1 mostra o assistente de projeto para aplicativos Android. Insira um nome de projeto, selecione o botão de opções Create new project in workspace e especifique o local para o código com os elementos da UI. Na lista de verificação Build Target, selecione uma plataforma Android. Para este código, usei o Android 2.3.1. O código é bem simples, assim é possível usar qualquer versão que preferir. Se nenhuma plataforma constar na lista, então será necessário fazer download e instalar as plataformas como descrito nas instruções de configuração do Android SDK. Esteja ciente de que o download dessas plataformas pode demorar.

Na seção Properties, preencha o nome do aplicativo e o nome do pacote. Usei "Tic Tac Toe" e "com.jherrington.tictactoe" nos respectivos campos. Em seguida, selecione a caixa de seleção Create Activity e insira um nome para a atividade. Usei "TicTacToeActivity" como nome da atividade.

Clique em Finish para ver um novo projeto parecido com a figura 2.


Figura 2. Os arquivos do projeto TicTacToe

A figura 2 mostra os diretório de nível superior e arquivos de um aplicativo Android (os diretórios são src, gen, Android 2.3.1 e res, e os arquivos são assets, AndroidManifest.xml, default.properties e proguard.cfg). Os itens importantes são:

  • O diretório res, que contém os recursos
  • O diretório src, que tem a origem Java™
  • O arquivo de manifesto, que contém as informações biográficas sobre o aplicativo

Sua primeira edição é para o arquivo de manifesto. A maior parte do arquivo já está correta, mas é necessário incluir a permissão para a Internet de modo que o aplicativo possa fazer solicitações sobre a Internet. A Listagem 7 mostra o arquivo de manifesto completo.


Listagem 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>

A única mudança foi incluir a tag de permissão de uso na parte superior do arquivo.

Sua próxima tarefa é projetar a UI. Para isso, ajuste o arquivo layout.xml, que está contido no diretório res/layout. A Listagem 8 mostra os novos conteúdos para esse arquivo.


Listagem 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>

Esse é um layout simples. Na parte superior está um conjunto de dois botões agrupados em um layout de linha com uma orientação horizontal. Esses dois botões são os botões X e O que os usuários usam para especificar com que cor estão jogando.

O resto do código é preenchido por uma classe BoardView, que mostra o quadro do jogo da velha com o jogo atual. O código para a classe BoardView está na Listagem 11.

Com o layout em mãos, agora é o momento de escrever algum código Java para o aplicativo. Esse código é iniciado com a classe TicTacToeActivity na Listagem 9. As atividades são os blocos de construção básicos dos aplicativos Android. Cada aplicativo tem uma ou mais atividades que representam os diversos estados do aplicativo. À medida que se navega pelo aplicativo, é possível criar uma pilha de atividades que pode ser abandonada usando o botão voltar do telefone. O aplicativo TicTacToe tem apenas uma única atividade.


Listagem 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 );
      }
    }
}

A atividade tem dois métodos. O primeiro é o método onCreate, que desenvolve a interface com o usuário, conecta o manipulador onClick aos botões X e O e inicia o timer de atualização. O timer de atualização é usado para atualizar o estado do jogo a cada 200 milissegundos. Esse recurso permite que ambos os jogadores vejam quando o outro jogador faz uma movimentação.

O manipulador onClick define a cor atual do quadro dependendo de se o usuário clica no botão X ou O.

A classe GameService class, na Listagem 10, é uma classe singleton que representa o servidor de jogos e o estado atual de determinado jogo.


Listagem 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());
    }
  }
}

Esse código é a parte do código mais interessante no aplicativo. Primeiro, há o método updatePositions, que toma o XML retornado do servidor, procura pelos elementos da movimentação e, em seguida, atualiza o array com o conjunto atual das movimentações. O array de posições tem um valor para cada posição no quadro. Zero indica um espaço vazio, 1 representa "O" e 2 representa "X".

As outras duas funções, startGame e setPosition, são o modo como você se comunica com o servidor. O método startGame solicita o conjunto atual de movimentações do servidor e atualiza a lista de posições. O método setPosition publica a movimentação para o servidor criando uma solicitação de postagem HTTP e configurando os dados para a postagem ao usar um array de pares nome-valor, que são codificados para o transporte. Em seguida, analisa o XML da resposta para atualizar a lista de posições.

Se observado de perto, o IP usado para conexão ao servidor é realmente interessante. Não é "localhost" nem "127.0.0.1", é "10.0.2.2," que é um alias para a máquina na qual o emulador está executando. Como o telefone Android é, em si mesmo, um sistema UNIX®, ele tem seus próprios serviços no host local. Fascinante, não? Nem sempre é tão claro que o telefone não é um telefone em si, mas um computador completo que cabe em suas mãos e que, por ventura, contém um telefone integrado a ele.

Então, em que ponto estamos? Temos a atividade, que é o componente principal para o aplicativo. Temos também a configuração do layout da UI e o código Java para conexão ao servidor. Agora, é necessário desenhar o quadro do jogo, o que é feito pela classe BoardView na Listagem 11.


Listagem 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);
        }
      }
    }
  }

}

A maioria do trabalho aqui é realizada no método onTouch, que responde ao toque do usuário em uma célula específica no quadro do jogo, e o método onDraw, que pinta o quadro do jogo usando o mecanismo de pintura do Android.

O método onTouch usa as funções de dimensionamento para descobrir um retângulo para cada posição na célula. Em seguida, usa o método contains no retângulo para ver se o usuário clicou dentro da célula. Em caso afirmativo, ele dispara uma solicitação para o serviço do jogo para fazer a movimentação.

A função onDraw usa as funções de dimensionamento para desenhar as linhas do quadro e desenhar quaisquer Xs e Os jogados. O singleton GameServer é usado para seu array de posições, que tem o estado atual de cada quadrado no quadro do jogo.

A última classe que você precisa é o UpdateTimer, que usa o serviço do jogo para atualizar as posições do quadro com seus valores mais recentes. A Listagem 12 mostra o código do timer.


Listagem 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(); } });
  }
}

O timer é inicializado pela classe TicTacToeActivity quando o aplicativo inicializa. Esse timer é um mecanismo de pesquisa. Esse não é o modo mais eficiente de comunicação entre o cliente e o servidor, mas é o modo mais simples e confiável. O modo mais eficiente é usar a versão 1.1 do protocolo HTTP para manter a conexão aberta e fazer o servidor enviar atualizações para o cliente quando movimentações são feitas. Essa abordagem é um pouco mais complexa e requer que o cliente e o servidor suportem o protocolo 1.1, bem como também tem problemas de escalabilidade com o número de conexões. Essa abordagem está fora do escopo deste artigo. Para jogos de simples demonstração como este, um mecanismo de pesquisa funciona suficientemente bem.

Com o código feito, é possível testar o aplicativo. Isso significa inicializar o emulador. Deve-se ver algo como a figura 3 depois da inicialização.


Figura 3. Ativando o emulador Android

Este é o emulador carregando uma excelente interface "A N D R O I D". Depois de ela ser carregada, é possível ver a tela de ativação na figura 4.


Figura 4. O emulador ativado e pronto para uso

Para inserir no telefone, deslize o ícone de bloqueio para a direita. Essa ação o levará à tela de início e, geralmente, ativará o aplicativo que você está depurando. Neste caso, essa ação exibirá a tela do jogo na figura 5.


Figura 5. O jogo antes de uma movimentação ter sido realizada

Dependendo do estado do servidor, será possível ou não visualizar alguma movimentação. Neste caso, o jogo estava vazio. Os botões Play X e Play O estão no alto com o quadro do jogo da velha no meio da exibição. Em seguida, clique em Play X, e clique no quadro do centro para ver algo como a Figura 6.


Figura 6. X inserido no quadrado central, é claro

A Figura 6 mostra o quadro do jogo com um X agora ocupando o quadrado central. Para verificar se o servidor estava conectado, é possível executar o comando curl no script moves.php no servidor para obter a lista mais recente de movimentações do jogo.

Para testar se os Os funcionam, clique em Play O e selecione um quadrado no canto, como na Figura 7.


Figura 7. O é inserido em um quadro no canto

É possível jogar com Xs e Os. O aplicativo se conecta ao servidor para manter o estado do jogo em um local compartilhado. E devido ao timer de atualização, cada usuário pode ver as movimentações feitas pelo outro.

Conclusão

O jogo está completo? Na verdade, não. Não há nenhuma verificação de condição de vitória, os jogadores podem sobrescrever posições e não há nenhuma verificação de quem é a vez. Mas as peças da tecnologia básica estão presentes: um servidor de jogos com estado armazenado compartilhado entre os jogadores, e um aplicativo gráfico nativo em um dispositivo móvel que se conecta ao servidor de jogos para fornecer uma interface para o jogo. É possível usar esse jogo como ponto de partida para seu próprio jogo e desenvolvê-lo como quiser. Lembre-se apenas de mantê-lo casual e divertido, e você pode ter, por conta própria, o próximo Words With Friends ou Angry Birds de vários jogadores.


Recursos

Aprender

  • Eclipse: saiba sobre o IDE usado neste artigo para desenvolver o aplicativo Android. Localize também downloads e plug-ins do Eclipse.

  • Ferramentas de Desenvolvimento de PHP para Eclipse: necessita de um IDE para PHP? O projeto Eclipse tem uma extensão para isso, além de outros plug-ins do Eclipse para praticamente quase tudo.

  • Mercado do Android: depois de escrever o seu jogo casual em rede do Android, faça upload dele para o mercado de trabalho do Android. E permita-nos saber o que você fez usando a seção comentários deste artigo.

  • Site PHP : explore a melhor referência para PHP que está disponível.

  • A W3C: Visite um ótimo site sobre padrões, em particular o padrão XML é relevante para este artigo.

  • Mais artigos desse autor (Jack Herrington, developerWorks, março de 2005-hoje): Leia artigos sobre Ajax, JSON, PHP, XML e outras tecnologias.

  • Iniciante em XML? Obtenha os recursos necessários para aprender XML.

  • Área de XML do developerWorks: localize os recursos necessários para avançar em conhecimentos na arena XML, incluindo DTDs, esquemas e XSLT. Consulte o site Biblioteca técnica de XML para obter um intervalo amplo de artigos técnicos e dicas, tutoriais, padrões e IBM Redbooks.

  • Certificação XML da IBM: Descubra como se tornar um Desenvolvedor Certificado pela IBM em XML e tecnologias relacionadas.

  • eventos técnicos e webcasts do developerWorks: Mantenha-se atualizado em relação à tecnologia nessas sessões.

  • DeveloperWorks no Twitter: Inscreva-se hoje para seguir os tweets do developerWorks.

  • Podcasts do developerWorks: Ouça entrevistas e discussões interessantes para desenvolvedores de software.

  • Demos on demand do developerWorks: Acompanhe demos que abrangem desde a instalação de produto e configuração para iniciantes até funcionalidade avançada para desenvolvedores experientes.

Obter produtos e tecnologias

Discutir

Sobre o autor

Photo of Jack Herrington

Jack Herrington é engenheiro, autor e apresentador que mora e trabalha em Bay Area. É possível se manter atualizado em relação ao seu trabalho e aos seus escritos em http://jackherrington.com.

Ajuda para Relatar Abuso

Relatar abuso

Obrigado. Esta entrada foi sinalizada para atenção do moderador.


Ajuda para Relatar Abuso

Relatar abuso

Falha no envio do Relatório de abuso. Tente novamente mais tarde.


developerWorks: Registre-se


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Selecione seu nome de exibição

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

(Deve possuir de 3 a 31 caracteres.)


Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Classificar este artigo

Comentários

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=755923
ArticleTitle=Criar um Jogo da Velha em Rede para Android
publish-date=09062011
author1-email=jherr@pobox.com
author1-email-cc=

Conheça a IBM da sua cidade

Virtual Branch Office Brasil

A IBM está mais perto do que você imagina!


Tags

Help
Use o campo de pesquisa para encontrar todos os tipos de conteúdo no My developerWorks com essa tag.

Use a barra de rolagem para ver mais ou menos tags.

Tags populares mostra as principais tags para esta zona de conteúdo em particular (por exemplo, Java technology, Linux, WebSphere).

Minhas tags mostra suas tags para esta zona de conteúdo em particular (por exemplo, Java technology, Linux, WebSphere).

Use o campo de pesquisa para localizar todos os tipos de conteúdo no Meu developerWorks com essa tag. Tags populares mostra as tags principais para essa zona de conteúdo particular (por exemplo, tecnologia Java, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere). Minhas tags mostra as suas tags para essa zona de conteúdo em particular (por exemplo, tecnologia Java, Linux, WebSphere).