Jogo da velha em rede para vários jogadores
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.
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.
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.
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
- O componente site Android Developer: faça o download do SDK e do plug-in do Eclipse.
- Versões de avaliação de produtos IBM: Faça o download ou explore as versões de teste on-line no IBM SOA Sandbox e entre em contato com as ferramentas de desenvolvimento de aplicativos e produtos de middleware do DB2®, Lotus®, Rational®, Tivoli®e WebSphere®.
Discutir
- Fóruns de discussão da zona de XML: Participe de qualquer uma das várias discussões relacionadas a XML.
- A comunidade do developerWorks: Entre em contato com outros usuários do developerWorks e explore os blogs, fóruns, grupos e wikis voltados para desenvolvedores.
