|
Este artigo, não ensina como programar para Android, para isso siga esse roadmap. O intuito desse artigo é mostrar as diferenças entre o Java2D e Android, e também mostrar um jogo como exemplo. Este artigo também mostra alguns conceitos de como programar jogos, e como fazer isso no Android e de quebra um pouquinho de matematica e fisica.
Se você já sabe como programar jogos com Java2D, mudar para o Android vai ser fácil.
O sistema de desenho do Android é parecido com o do Java SE, mas no lugar de denharmos no Graphics, desenhamos no Canvas.
As principais diferenças são:
- Canvas = Graphics
- Matrix = AffineTransform
- Bitmap e Drawable = BufferedImage
Para termos acesso a uma surface para desenho, estendemos a classe View e sobrescrevemos o método onDraw(Canvas) e desenhamos nesse parâmetro.
Assim como em Graphics, precisamos salvar o estado do nosso objeto de desenho antes de alterar suas propriedades, para isso usamos o método save() e depois da rotina de desenho usamos o método restore() para retornar o estado original no Canvas, permitindo qualquer outro método de desenho trabalhe corretamente com ele( desenhos da UI por exemplo).
Então vamos lá, crie um projeto com os seguintes parâmetros.
Target: 2.1
Application Name: JPong
Package Name: br.com.pontov
Create Activity: AndPongActivity
Min SDK Version: 7
Crie uma nova classe no pacote br.com.pontov.jpong chamada GameView que extenda android.view.View.
Precisamos sobrescrever os três construtores de View pois o Android os usa quando está inflando o Layout do XML, e um ultimo para criar Layouts programaticamente, como queremos que a classe se comporte igual não importa como esteja sendo construída então vamos chamar um método init() que faça nossa rotina de construção dentro de cada construtor, com isso nosso código ficará assim.
package br.com.pontov.jpong;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
public class GameView extends View implements Runnable {
public GameView(Context context) {
super(context);
init();
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
//Metodos para inicializar os componentes da nossa tela
}
public void onDraw(Canvas canvas){
//Desenhar na tela
}
}
Os métodos de desenho do canvas fora as coordenadas do objeto que vamos desenhar também precisam de um objeto Paint que indica como o objeto deve ser pintado, e como recomendações do Android, não se deve ficar criando esses objetos dentro do método onDraw que será chamado várias vezes na nossa aplicação, então vamos criá-la no nosso método init() e guardar a referencia dela em uma variável na tela.
Crie os atributos na classe.
private Paint backgroundPaint; private Paint ballPaint;
No método init vamos criar esses objetos e no método onDraw desenhar objetos na tela.
private void init() {
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.BLACK);
ballPaint = new Paint();
ballPaint.setColor(Color.RED);
}
public void onDraw(Canvas canvas){
//Guardar o estado do Canvas
canvas.save();
//Pintando o fundo
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
//Pintando a bola (x, y, radius, Paint)
canvas.drawCircle(80,80,10,ballPaint);
//Retornando o estado do Canvas
canvas.restore();
}
Agora abra o arquivo res/layout/main.xml, ele por enquanto contém o básico de uma aplicação HelloWorld. Para usar nosso componente customizado nas tags do XML só precisamos passar seu nome junto com a declaração do pacote, o XML então vai ficar assim:
Para transformar nossa atividade em FullScreen é simples, basta editar o arquivo AndroidManifest.XML e adicionar o seguinte atributo na tag activity da nossa aplicação:
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
Rodando nosso exemplo já temos algo como isso.
![]()
Vamos transformar nosso exemplo em um jogo de Pong, com isso será possível já apresentar vários conceitos sobre desenvolvimento de jogos e ainda vai ser um resultado legal.
Agora entra a parte interessante, vamos dar algum movimento para nossa bolinha, isso é fácil se você entende de vetores, se ainda não sabe como usá-los voce pode ver aqui.
A principio, a bolinha precisa de uma posição, direção e velocidade e vamos atualizar essa posição através das iterações e redesenhar nossa bolinha.
Vamos criar uma classe chamada Ball no pacote br.com.pontov.pieces para representar nossa bolinha.
Posição e Direção são vetores, e velocidade é um float que é o tanto que nossa bolinha ira andar por iteração.
Nossa classe também é responsável por atualizar a posição da bolinha e oferecer um método para que ela se desenhe em um Canvas.
package br.com.pontov.pieces;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import br.com.pontov.Vector2D;
public class Ball {
private Paint paint;
private Vector2D pos;
private Vector2D dir;
private float speed;
public Ball(Vector2D pos, Vector2D dir, float speed) {
this.pos = pos;
this.dir = dir;
this.speed = speed;
paint = new Paint();
paint.setColor(Color.RED);
}
public void draw(Canvas canvas) {
canvas.save();
canvas.drawCircle(pos.getX(), pos.getY(), 10, paint);
canvas.restore();
}
private void move() {
pos.plusMe(dir.multiply(speed));
}
public void processAI() {
move();
}
}
No Android, assim como no Java2D é necessário chamar os métodos de redesenho na Thread principal da aplicação, mas no lugar de repaint() usamos apenas invalidate(), podemos informar nossa Thread através de um Handler para fazer o refresh da tela.
Voltamos ao AndPongActivity e vamos criar um Handler que vai atualizar o GameView.
Primeiro temos que guardar a referencia do GameView na nossa classe, então criamos uma variável pra ela.
private GameView gameView;
No método onCreate podemos obter essa referencia através do id que demos no XML. E depois criar um Handler para ouvir mensagens da nossa thread para atualizar a tela.
gameView = (GameView) findViewById(R.id.gameView);
guiRefresher = new Handler(){
public void handleMessage(Message msg){
if(msg.what == 10001)//Numero arbitrario
gameView.invalidate();
super.handleMessage(msg);
}
};
Vamos deixar nossa GameView responsavel por iterar nosso jogo e avisar o Handler para fazer o refresh, para isso temos que guardar a referencia do Handler nela e também implementar o método run() para que ele processe o jogo.
Também vamos deixar que ele crie um objeto Ball e deixe ele percorrer a tela.
package br.com.pontov.jpong;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import br.com.pontov.math.Vector2D;
import br.com.pontov.pieces.Ball;
public class GameView extends View implements Runnable {
private int width = 600, height = 1024;
private Paint background;
private Handler handler;
private Ball ball;
public GameView(Context context) {
super(context);
init();
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
background = new Paint();
background.setColor(Color.BLACK);
ball = new Ball(new Vector2D(20, 20), new Vector(1,1), 1.2);
}
public void onDraw(Canvas canvas) {
canvas.save();
//Pintando background
canvas.drawRect(0, 0, getWidth(), getHeight(), background);
//Deixando a bolinha se pintar na posição correta
ball.draw(canvas);
canvas.restore();
}
@Override
public void run() {
while (true) {
try {
ball.processAI();
Message msg = new Message();
msg.what = 10001;//Mesmo id que o Handler espera para atualizar
handler.sendMessage(msg);
Thread.sleep(10);
} catch (Exception e) {
}
}
}
public void setCallbackHandler(Handler guiRefresher) {
this.handler = guiRefresher;
}
}
Voltamos ao AndPongActivity e colocamos nosso Handler na GameView e iniciamos uma Thread para atualizar.
O método onCreate agora ficara o seguinte:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gameView = (GameView) findViewById(R.id.gameView);
guiRefresher = new Handler(){
public void handleMessage(Message msg){
if(msg.what == 10001){
gameView.invalidate();
}
super.handleMessage(msg);
}
};
gameView.setCallbackHandler(guiRefresher);
Thread t = new Thread(gameView);
t.setDaemon(true);
t.start();
}
Rodando nossa aplicação já podemos ver a animação da bolinha indo continuamente na direção south-east (1,1).
O problema é que nossa bolinha vai alcançar o final da tela e continuar, precisamos criar paredes e fazer com que a bolinha colida nela.
A parede também é uma entidade do jogo, e precisa passar pelos mesmos passos que a bolinha passa, "draw" e "processAI" tudo bem que ela não precisa realmente pensar, mas esse é um comportamente comum dos objetos de jogo então vamos criar uma classe que represente uma entidade.
package br.com.pontov.pieces;
import android.graphics.Canvas;
import android.graphics.Rect;
public abstract class Entity {
private Rect bounds;
public Entity(Rect bounds) {
this.bounds = bounds;
}
public Rect getBounds() {
return bounds;
}
public void setBounds(Rect rect) {
this.bounds = rect;
}
public abstract void draw(Canvas canvas);
public abstract void processAI();
}
Mudamos a classe Ball simplesmente para estende-la e como já implementamos esses dois métodos anteriormente ela vai continuar funcionando.
Seguindo a mesma ideia das entidades de jogo, logo imaginamos que os Pads e as Walls serão entidades que bloqueiam a passagem de algo. Então podemos criar uma classe que as represente.
É aqui que entra uma pequena mágica da matemática, para fazer que a nossa bolinha colida para o lado certo, precisamos aplicar uma formula na sua direção com um outro vetor chamado normal, que é um vetor que faz ângulo de 90º com o objeto, vamos simplesmente criar uma entidade que tenha um Vector2D normal que será usado depois nas colisões.
package br.com.pontov.pieces;
import android.graphics.Rect;
import br.com.pontov.math.Vector2D;
public abstract class BlockableEntity extends Entity {
private Vector2D normal;
public BlockableEntity(Rect bounds, Vector2D vec) {
super(bounds);
this.normal = vec;
}
public BlockableEntity(Vector2D vec) {
this.normal = vec;
}
public void setNormal(Vector2D normal) {
this.normal = normal;
}
public Vector2D getNormal() {
return normal;
}
}
Agora podemos criar uma classe que representa uma parede, e ela seria simplesmente o seguinte:
package br.com.pontov.pieces;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import br.com.pontov.math.Vector2D;
public class Wall extends BlockableEntity {
private Paint wallPaint;
public Wall(Rect bounds, Vector2D normal) {
super(bounds, normal);
wallPaint = new Paint();
wallPaint.setColor(Color.GRAY);
}
public void draw(Canvas canvas) {
canvas.save();
canvas.drawRect(getBounds(), wallPaint);
canvas.restore();
}
public void processAI() {
// Do nothing, walls don't think
}
}
Um problema agora é que para cada vez que precisarmos adicionar uma nova entidade na nossa tela teriamos que guardar uma referencia para ela e verificar suas iterações, também seria necessário chamar o método processAI e draw para todos as entidades, para facilitar isso, vamos criar uma classe que vai cuidar disso para nós, chamada PiecesManager, ela simplesmente guarda uma lista das nossas entidades e invocar os métodos corretos nela:
package br.com.pontov;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import android.graphics.Canvas;
import br.com.pontov.Entity;
public class PiecesManager {
private List pieces;
public PiecesManager() {
pieces = new ArrayList();
}
public void addPiece(Entity ent) {
pieces.add(ent);
}
public void draw(Canvas canvas) {
for (Entity ent : pieces)
ent.draw(canvas);
}
public List getPieces() {
return pieces;
}
public void processAI() {
for (Entity ent : pieces)
ent.processAI();
}
}
Com tudo isso pronto, podemos voltar ao GameView e adicionar algumas paredes ao nosso jogo.
Primeiro criamos a variável que guarda nossa instancia de objetos do jogo.
Aproveitamos aqui também e vamos criar uma variável width e height para guardar o tamanho da nossa tela.
private PiecesManager pieces; private int width = 600, height = 1024;//Tamanho do Galaxy Tab
Agora podemos modificar nosso init e criar os objetos e adicionar ao nosso manager.
O construtor da Wall espera um Vector2D que é um vetor que faz 90º na parede, no sentido oposto ao qual a bola vem, mas não se desespere, esses vetores já tem valores pré-definidos, e seguem como a relação abaixo:
Se for a parede esquerda, esse vetor é (1, 0);
Se for a direita (-1, 0);
Se for a de cima (0, -1);
E se for a de baixo (0, 1);
Então nosso init fica assim:
private void init() {
background = new Paint();
background.setColor(Color.BLACK);
manager = new PiecesManager();
manager.addPiece(new Ball(new Vector2D(300, 600), new Vector2D(1, 1), 8));
manager.addPiece(new Wall(new Rect(0, 0, 10, height), new Vector2D(1,0)));//Left
manager.addPiece(new Wall(new Rect(width - 10, 0, width, height), new Vector2D(-1,0)));//Right
manager.addPiece(new Wall(new Rect(0, 0, width, 10), new Vector2D(0,-1)));// Top
manager.addPiece(new Wall(new Rect(0, height - 10, width, height),new Vector2D(0,1)));//Bottom
}
Note que defini variáveis para valores do width e height. Essas variáveis com o tamanho são para facilitar essas contas e deixar um pouco mais escalável, para criar para outro tamanho de tela basta mudar os valores em width e height.
Como nosso manager agora é responsável por invocar todos processAI e draw podemos delegar a ele esses métodos pelo GameView, nosso método draw e run ficam assim.
public void onDraw(Canvas canvas) {
canvas.save();
canvas.drawRect(0, 0, getWidth(), getHeight(), background);
pieces.draw(canvas);
canvas.restore();
}
@Override
public void run() {
while (true) {
try {
pieces.processAI();
Message msg = new Message();
msg.what = 10001;
handler.sendMessage(msg);
Thread.sleep(10);
} catch (Exception e) {
}
}
}
Rodando nosso exemplo já podemos ver as paredes, mas isso não fará que a bolinha automaticamente colida e mude sua direção, precisamos implementar o processAI na classe Ball para verificar se existe alguma BlockableEntity no seu caminho e mudar sua direção se isso acontecer.
Primeiro temos que ter as referencias das peças para que a Ball verifique, podemos simplesmente guardar a referencia de PiecesManager nela e iterar sobre sua lista e verificar se alguma BlockableEntity está colidindo com ela.
A função que representa a mudança do angulo da direção é dada por:
r = v-2 * v.dot( n ) * n;
Onde:
- V é o vetor de momento (direção)
- N é a normal da parede
- R é o vetor de resultado
Se tivessemos sobrecargas de operadores em Java, aplicar essa formula para nossa bolinha seria simplesmente
dir = dir - 2 * dir.dot(wall.getNormal()) * wall.getNormal()
Como não existe, a fórmula é a seguinte:
dir = dir.minus(n.multiply(2).multiply(dir.dot(n)))
Nosso código ficará o seguinte:
package br.com.pontov.pieces;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import br.com.pontov.PiecesManager;
import br.com.pontov.math.Vector2D;
public class Ball extends Entity {
private Paint paint;
private Vector2D pos;
private Vector2D dir;
private float speed;
private PiecesManager pieces;
public Ball(Vector2D pos, Vector2D dir, float speed, PiecesManager manager) {
super();
this.pos = pos;
this.dir = dir;
this.speed = speed;
paint = new Paint();
paint.setColor(Color.RED);
pieces = manager;
}
@Override
public void draw(Canvas canvas) {
canvas.save();
canvas.drawCircle(pos.getX(), pos.getY(), 10, paint);
canvas.restore();
}
private void move() {
pos.plusMe(dir.multiply(speed));
}
@Override
public void processAI() {
Rect bounds = new Rect((int) pos.getX() - 5, (int) pos.getY() - 5,
(int) pos.getX() + 5, (int) pos.getY() + 5);
for (Entity ent : pieces.getPieces()) {
if (ent == this)
continue;
if (ent instanceof BlockableEntity)
if (bounds.intersect(ent.getBounds())) {
Vector2D n = ((BlockableEntity) ent).getNormal();
// r = v-2 * v.dot(n) * n
dir = dir.minus(n.multiply(2).multiply(dir.dot(n)));
}
}
move();
}
}
Na classe GameView só precisamos passar o manager como parametro para Ball e finalmente a bolinha pode ficar rebatendo nas paredes.
O resultado até agora será algo como isso:
Agora precisamos oferecer alguma iteração com o usuário, vamos criar os pads que nada mais são que BlockableEntities que mudam sua posição de acordo com o toque.
package br.com.pontov.pieces;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import br.com.pontov.math.Vector2D;
public class Pad extends BlockableEntity {
private Paint wallPaint;
private int defaultDistanceFromAxis;
public Pad(Vector2D normal, int defaultDistance) {
super(new Rect(), normal);
wallPaint = new Paint();
wallPaint.setColor(Color.GRAY);
this.defaultDistanceFromAxis = defaultDistance;
}
public void notifyMotionEvent(float x, float y) {
setBounds(new Rect((int) x - 150,
defaultDistanceFromAxis, (int) x + 150,
defaultDistanceFromAxis + 10))
}
@Override
public void draw(Canvas canvas) {
canvas.save();
canvas.drawRect(getBounds(), wallPaint);
canvas.restore();
}
@Override
public void processAI() {
// Do nothing, position is handled by events
}
}
Essa classe muda sua posição através do setBounds, precisamos notificá-la dos eventos que o usuário tocou na tela, nós podemos adicionar ela no PiecesManager, mas ainda temos que ter a referencia dela para delegar os eventos, como implementar uma AI está fora do escopo desse artigo, podemos usar o suporte multi-touch dos devices para moverem os Pads separadamente.
Primeiro, criamos as variáveis para guardar as referencias dos nossos pads.
private Pad northPad; private Pad southPad;
Agora inicializamos ele no método init e adicionamos ao PiecesManager para aproveitar o desenho automatizado dos objetos do nosso jogo.
northPad = new Pad(new Vector2D(0, -1), 80); //80 é a distancia default do eixo, no caso desenhar no 80 de y manager.addPiece(northPad); southPad = new Pad(new Vector2D(0, 1), 944);//944 do y manager.addPiece(southPad);
A maneira mais simples de ouvir os eventos de toque é sobrescrevendo o método onTouchEvent(MotionEvent evt) na nossa GameView, podemos sobrescreve-lo e delegar o x e o y para os nossos pads.
public boolean onTouchEvent(MotionEvent evt) {
southPad.notifyMotionEvent(evt.getX(), evt.getY());
northPad.notifyMotionEvent(evt.getX(), evt.getY());
return true;
}
Desse modo o evento vai mover os dois pads na mesma direção.
Como Pad já é uma BlockableEntity a nossa bolinha irá bater nele do mesmo modo que nas paredes sem mais nenhuma alteração necessaria.
A maioria dos devices com Android agora tem suporte a multi-touch, e utiliza-lo também é simples. Mas precisamos de uma permissão especial para isso, basta adicionar a seguinte linha no AndroidManifest.XML
Agora voltamos ao nosso onTouchEvent e vamos verificar os toques de múltiplos usuários, isso é feito através dos métodos getX(int pointer) getY(int pointer). Como nossa aplicação não precisa exatamente se importar com qual dedo está se movendo para tal lugar, vamos apenas adicionar suporte para que qualquer toque em uma área da metade do campo mova o pad daquele lado.
public boolean onTouchEvent(MotionEvent evt) {
for (int i = 0; i < evt.getPointerCount(); i++) {
float y = evt.getY(i);
if (y > 612)
southPad.notifyMotionEvent(evt.getX(i), evt.getY(i));
else
northPad.notifyMotionEvent(evt.getX(i), evt.getY(i));
}
return true;
}
Desse modo dois usuarios podem jogar tocando em uma area da tela.
O resultado até aqui:
Agora que cansamos de ver essa bolinha vermelha kikando pela tela, vamos colocar uma imagem como essa:
Para utiliza-la é facil, primeiro salve esse arquivo dentro da pasta res/drawable-hdpi e voltamos a nossa classe Ball.
A maneira mais fácil de carregar uma imagem é através dos Resources.
Como nossa classe Ball não tem acesso a ela vamos modificar o construtor para aceitar apenas um Bitmap e carregar a imagem na nossa GameView, também modificar o método draw para desenhar a imagem com drawBitmap no lugar de drawCircle com uma cor sólida.
Precisamos guardar a referencia da imagem em um objeto Bitmap.
//Variável para guardar a referência private Bitmap image;
No construtor apenas um novo argumento para receber esse novo parametro e colocar na variavel, e modificar o método draw para desenhar a imagem.
public void draw(Canvas canvas) {
canvas.save();
canvas.drawBitmap(image, pos.getX(),pos.getY(), paint);
canvas.restore();
}
Agora na GameView podemos carregar a imagem e passar como referencia.
A única coisa necessária é utilizar a classe BitmapFactory para fazer o decode da imagem dos resources que nos referimos pelo id.
Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
Agora passamos como referencia para Ball e terminamos o basico de um jogo de Pong.
E o resultado final da nossa aplicação é o seguinte:
Isso conclui nosso game de Pong, claramente é possível melhora-lo em vários aspectos e funcionalidades, mas até aqui já foi o suficiente para mostrar como fazer um jogo de Pong, a matematica envolvida e ainda como fazer isso no Android, espero que tenham aprendido mais sobre todas essas áreas.
Ainda é possivel utilizar aceleração nativa com OpenGL ES 2.0 no Android, mas isso é assunto para outro artigo.
Todo código da aplicação está disponível no repositório do AndroidTechsGames no github, com mais algumas alterações que fui implementando quando me sobrou tempo.
-
29/03/2011 11:11:53 | Bruno Crivelari Sanches

Comparar uma linguagem de programação com um ferramenta de autoria não tem sentido algum.
É o mesmo que comparar uma caixa de ferramentas com um robo e dizer que o robo é muito melhor. De fato, o robo faz mais coisas, mas você sempre vai estar restrito ao que o fabricante dele acha bom ou impõe a você, isso resolve o problema de boa parte das pessoas, mas existe um mundo além do unity e pessoas que não podem ficar preso as suas limitações.
Eu mesmo tenho um projeto de uma empresa que já estamos analisando a melhor estratégia, pois chegamos no limite do unity e ele simplesmente não satisfaz nossos requisitos e é provavel que troquemos por outra tecnologia, talvez ogre, que a principio não faz tudo que o Unity faz, mas assim como o Java, não nos limita.
T+
-
29/03/2011 14:55:45 |201.58.116.xxx| Júlio Brito

Que artigo legal Marcos. Esclarece muitas coisas que são úteis em diversos tipos de aplicações.
Sobre o java 2D e Unity3D lá em cima. É bom saber que ambas usam opengl e apenas tem conceitos de uso diferentes.
-
29/03/2011 16:12:18 |189.59.222.xxx| Marcos Vasconcelos - Ty!

Obrigado, essa foi minha intenção, fora explicar sobre jogos em geral mostrar como fazer isso no Android.
-
29/03/2011 16:59:08 |200.186.180.xxx| Marcos Koga - Awesome!

Cara, muito bom o seu artigo. foi bem didático(até para quem não tem muito conhecimento em GameDev) e a escolha de fazer um pong é muito boa, pois além de ver as funcionalidades triviais em um jogo, tem um pouco de matemática ;3
Veja se não tem como anexar uma parte a respeito do áudio =)
-
29/03/2011 17:51:47 |189.59.222.xxx| Marcos Vasconcelos - Ty!

Valeu. Escolhi Pong pois usa um pouco de cada area essencial para um jogo.
E é uma boa idéia sobre audio, mas isso vai ser assunto pra outro artigo.
-
29/03/2011 19:54:58 |200.206.15.xxx| Eduardo Bregaida - Muito Bom

Parabéns Marky, ficou muito bom, vou ler os tutoriais de Android que colocou no seu blog, só preciso de tempo rsss
-
29/03/2011 20:12:37 |189.59.222.xxx| Marcos Vasconcelos - Valeu!

Obrigado. Eu gastei um bom tempo escrevendo estes artigos. Espero que as pessoas também gastem para ler e aprender com eles.
-
01/04/2011 18:45:05 |189.71.224.xxx| Lucas Daltro - Muito Bom!

Pow cara,muito bom o artigo

Mas o android emulator tá travando que só aqui
-
01/04/2011 21:04:09 |200.150.190.xxx| Marcos Vasconcelos - Valeu

Valeu. E realmente, o emulador é lento. Eu normalmente apenas vejo se funciona corretamente no emulador depois testo em um device.
-
02/04/2011 02:12:05 |189.71.224.xxx| Lucas Daltro

Queria pode instalar o android no meu BlocK Berry...A cada dia que passa eu me arrependo mais de ter comprado um celular chinês
-
02/04/2011 16:16:25 |189.120.193.xxx| Marcos Vasconcelos - True

Para quem quer desenvolver mobile não compensa comprar um desses.
-
11/04/2011 16:57:47 |200.225.197.xxx| Marcelo - Dúvida

Marcos, estou com uma dúvida, no arquivo ComplexGameView.java, método init(), lá dentro tem um laço for que itera só uma vez. Qual era sua intenção nesse laço?
-
11/04/2011 19:17:41 |189.59.222.xxx| Marcos Vasconcelos

Na verdade, é que eu havia adicionado muitas bolinhas na tela para rebater todas. Mas para tirar as pics pro artigo eu só coloquei 1 para tirar uma foto com apenas uma bolinha.
-
12/04/2011 12:20:37 |201.26.209.xxx| João Pagotto - Rotação de Imagens PNG

Olá parabéns pelo artigo.
Gostaria de fazer uma bussula customizada para o Android, aí vem o problema hehehe. Como fazer uma rotação de uma imagem PNG no Draw do Android ?
[']s
-
12/04/2011 13:05:51 |189.59.222.xxx| Marcos Vasconcelos

voce faz isso girando o Canvas com o método rotate(float graus)
-
23/04/2011 00:16:46 |87.216.116.xxx| Marcelo Barros - Rotação

Seu artigo esta muito bom mesmo, mts parabens.
Sera que me pode ajudar, imagina que pintamos uma roda, e giramos o dedo a volta dela, que detecte se estamos girando na direcção dos ponteiros do relogio ou ao contrario.Abraço.
-
23/04/2011 01:51:36 | Vinícius Godoy de Mendonça

Isso você resolve facilmente através da classe de vetores. Tendo dois pontos (o do início e o do fim do movimento), use o método angleSign para obter o angulo entre eles, com sinal.
O sinal indica se o sentido é horário ou anti-horário.
-
23/04/2011 02:03:23 |87.216.116.xxx| Marcelo Barros - Rotação

Não entendi muito bem, imagina que eu começo num ponto, e dou uma volta de 180 graus na direcao do relogio, e não dei uma volta 180 graus ao contrario do ponteiro do relogio.
Desculpa a ignorancia.
Cumprimentos.
-
23/04/2011 03:15:45 | Vinícius Godoy de Mendonça

Você leu chegou a ler o link sobre vetores? http://bit.ly/usovetores
-
24/04/2011 23:46:52 |87.216.116.xxx| Marcelo Barros

Deu jeito, vou usar Produtos Escalares.
Voces tem alguma comunidade, onde me possa juntar?
Abraço.
-
25/04/2011 00:57:37 | Vinícius Godoy de Mendonça

Nós somos participantes ativos do fórum programadores de jogos:
http://www.programadoresdejogos.com.br
-
24/04/2011 23:03:24 |87.216.116.xxx| Marcelo Barros - OnFling

Ando a dias com isto.
Tenho a minha classe principal "main" (Activity), criei uma class que estende de uma imageview "ImageViewI" e logo adiciona a minha classe principal (activity).
Eu quero detectar um onfling mas quando este é sobre a minha classe "ImageViewI", sera que posso nessa class implementar OnGestureListener, ou se for nesta mesma class como nao é activity nao vai fazer nada.
Posso de alguma maneira fazer isto na minha class main, para que so de bola quando o onfling é sobre a minha "ImageViewI".Desculpem ser chato.
Abraço.
-
24/04/2011 23:25:02 |87.216.116.xxx| Marcelo Barros - Resolvido

Penso que o tenho resolvido, na verdade não precisso usar um onfling.
Com isto ja me resolve, e uma boa opcão?"meucomponente".setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//meus metodos
return false
}
});
-
25/04/2011 16:49:51 |189.59.222.xxx| Marcos Vasconcelos

Assim como no Swing, voce pode implementar um Listener para cada componente, então voce pode implementar no seu ImageView e ainda ter listeners em outros lugares.
-
30/04/2011 14:25:52 |187.113.68.xxx| Junior - Não consegui

Grande Marcos, não consegui rodar este exemplo.
No console do eclipse não aparece nenhum erro, as mensagens finais são:
[2011-04-30 11:21:29 - GameView] Success!
[2011-04-30 11:21:29 - GameView] Starting activity br.com.pontov.AndPongActivity on device emulator-5554
[2011-04-30 11:21:31 - GameView] ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=br.com.pontov/.AndPongActivity }E na Tela do Emulador aparece "Sorry - The Application jPong (process br.com.pontov) has stopped unexpectedly. Please try again."
Fiquei na dúvida se fiz algo correto. QUando criei o novo projeto uma nova classe chamada AndPongActivity é criada e criei uma nova chamada GameView. Percebi que AndPongActivity fica em br.com.pontov e GameView em br.com.pontov.jpong. Sera que isso ?
-
30/04/2011 16:30:01 |189.120.193.xxx| Marcos Vasconcelos

Entao, o erros do Android, são mostrados na View LogCat do Eclipse.
Para abri-lo vá em:
Window > Show View> Other... > +Android > LogCatEsse é como o Console, mas mostra as informações do que acontece no emulador.
Seu StackTrace estará lá.
Da uma olhada e poste novamente.
-
30/04/2011 17:08:18 |187.113.68.xxx| Junior - Stack Trace

Valeu pela resposta Marcos, fui na perspectiva DDMS e la encontrei o LogCat. Segue o filtro considerando somente os erros do PID de meu aplicativo:
Uncaught handler: thread main exiting due to uncaught exception
java.lang.RuntimeException: Unable to start activity ComponentInfo{br.com.pontov/br.com.pontov.AndPongActivity}: android.view.InflateException: Binary XML file line #1: Error inflating class linearlayout
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2 496)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:25 12)
at android.app.ActivityThread.access$2200(ActivityThread.java:119)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863) at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4363)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit. java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:61
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.view.InflateException: Binary XML file line #1: Error inflating class linearlayout
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:576)
at android.view.LayoutInflater.inflate(LayoutInflater.java:385)
at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindo w.java:19
at android.app.Activity.setContentView(Activity.java:1622)
at br.com.pontov.AndPongActivity.onCreate(AndPongActivity.java:11)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java: 1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2 459)
... 11 more
Caused by: java.lang.ClassNotFoundException: android.view.linearlayout in loader dalvik.system.PathClassLoader@44e8c790
at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:243)
at java.lang.ClassLoader.loadClass(ClassLoader.java:573)
at java.lang.ClassLoader.loadClass(ClassLoader.java:532)
at android.view.LayoutInflater.createView(LayoutInflater.java:466)
at android.view.LayoutInflater.onCreateView(LayoutInflater.java:544)
at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(Phon eLayoutInflater.java:66)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:563)
... 19 more
Unable to open stack trace file '/data/anr/traces.txt': Permission denied
-
30/04/2011 20:57:27 |189.120.193.xxx| Marcos Vasconcelos

Hmm...
"android.view.linearlayout"
O correto é LinearLayout (letras maiusculas)
-
11/05/2011 23:45:38 |87.216.123.xxx| Marcelo Barros - Selection

Ola,
Desculpa postar isto aqui, mas não sei como entrar em contacto com voce e a verdade é que tenho certeza que voce pode resolver isto.
Tenho uma listview e quero fazer um setSelection(pos) e que a linha que foi selecionada mude de cor, isto sem ter de recorrer necessariamente a codigo.
Visto que eu ja tenho um estilos, um bocado do que esta defenido
Quando ponho o dedo em cima da linha ela muda para a cor azul, mas quando tiro volta ao normal, não fica com a cor de seleccionado.
Mas a parte de isto eu queria com o método setSelection(pos) escolher uma linha e que ela ficasse igualmente selecccionada e mudasse de cor.Cumprimentos.
-
12/05/2011 13:39:58 |200.170.114.xxx| Marcos Vasconcelos

O melhor lugar para voce postar isso seria no forum de Android no www.guj.com.br
Mas respondendo a sua duvida, voce vai precisar guardar o indice de onde voce está selecionando e modificar o seu Adapter para retornar a linha desse indice com a cor de fundo diferente.
-
12/05/2011 14:01:33 |193.146.142.xxx| Marcelo Barros

Desculpa estar a chatear, mas "modificar o seu Adapter para retornar a linha desse indice com a cor de fundo diferente", dizes fazer via codigo, o genero de um metod que altera o background?
Nativamente e com os estilos xml nao e possivel?
-
12/05/2011 17:24:30 |200.170.114.xxx| Marcos Vasconcelos

Talvez até seja, mas eu digo programaticamente mesmo.
-
20/05/2011 17:43:07 |187.5.159.xxx| derUbar - MapView

Olà,
Vc saberia se e possivel criar um poligono delimitando uma area no mapview no emulador do android?
Eu gostaria de trabalhar com poligonos igual a funçao do google maps.
-
20/05/2011 20:48:32 |200.170.114.xxx| Marcos Vasconcelos

Eu não entendi sua pergunta.
Se o que voce quer é criar aqueles desenhos que aparecem como popup no Google Maps por poligonos, é possivel sim.
Voce tem o seu objeto Path e tem que dar um translate nele antes de desenha-lo
-
20/05/2011 20:57:18 |187.5.159.xxx| derUbar - Mapa

Eu tenho o mapa em meu emulador, no google maps existem a opçao criar mapa, nessa opçao eu posso criar linhas, poligonos, marcar locais com objetos e marcar trajetos via estradas.
Eu precisaria ter essas funçoes em meu mapa no android. Ou pelo menos a funçao de criar poligonos.
-
25/05/2011 21:42:24 |200.162.69.xxx| Java Player

Onde encontro o código da classe:
br.com.pontov.Vector2D ?
Está numa biblioteca disponível para download?
Obrigado
-
07/09/2011 01:26:38 |187.57.181.xxx| Jeferson

Ola Marky, estou tendo o mesmo problema do junior tambem.. a stack trace esta identica.. mas, mudando de linearlayout para LinearLayout. obtive o mesmo erro, a configuração do projeto, e as classes estao exatamente como voce demostrou no tutorial.. o que pode ser ?
-
07/09/2011 17:49:50 | Marcos Vasconcelos

Cara, da uma olhada melhor nos XMLs, por que a pouco tempo descobri que é o CSS do PontoV que deixa tudo minusculo.
-
11/10/2011 03:16:36 |187.106.129.xxx| Marcos Almeida - Problemas com GoogleMaps

Marcos Vasconcelos boa noite,
Estou tentando plotar um simples mapa no android, mais esta muito complicado. Não sei o que faço de errado.
Recebo a mensagem de erro abaixo:Sorry! The application HelloMap ( process org.android.maps ) has stopped unexpectedly. Please try again
View image
E esse é o erro:
ERROR/Zygote(33): setreuid() failed. errno: 2
10-11 02:54:33.144:
ERROR/Zygote(33): setreuid() failed. errno: 17
10-11 02:54:34.634:
ERROR/BatteryService(59): usbOnlinePath not found
10-11 02:54:34.634:
ERROR/BatteryService(59): batteryVoltagePath not found
10-11 02:54:34.634:
ERROR/BatteryService(59): batteryTemperaturePath not found
10-11 02:54:34.654:
ERROR/SurfaceFlinger(59): Couldn't open /sys/power/wait_for_fb_sleep or /sys/power/wait_for_fb_wake
10-11 02:54:42.234:
ERROR/PackageManager(59): Package com.android.gesture.builder signatures do not match the previously installed version; ignoring!
10-11 02:54:43.636:
ERROR/EventHub(59): could not get driver version for /dev/input/mouse0, Not a typewriter
10-11 02:54:43.636:
ERROR/EventHub(59): could not get driver version for /dev/input/mice, Not a typewriter
10-11 02:54:43.843:
ERROR/System(59): Failure starting core service
10-11 02:54:43.843:
ERROR/System(59): java.lang.SecurityException
10-11 02:54:43.843:
ERROR/System(59): at android.os.BinderProxy.transact(Native Method)
10-11 02:54:43.843:
ERROR/System(59): at android.os.ServiceManagerProxy.addService(ServiceManagerNative.java:14 6)
10-11 02:54:43.843:
ERROR/System(59): at android.os.ServiceManager.addService(ServiceManager.java:72)
10-11 02:54:43.843:
ERROR/System(59): at com.android.server.ServerThread.run(SystemServer.java:184)
10-11 02:54:45.074:
ERROR/SoundPool(59): error loading /system/media/audio/ui/Effect_Tick.ogg
10-11 02:54:45.093:
ERROR/SoundPool(59): error loading /system/media/audio/ui/KeypressStandard.ogg
10-11 02:54:45.093:
ERROR/SoundPool(59): error loading /system/media/audio/ui/KeypressSpacebar.ogg
10-11 02:54:45.104:
ERROR/SoundPool(59): error loading /system/media/audio/ui/KeypressDelete.ogg
10-11 02:54:45.104:
ERROR/SoundPool(59): error loading /system/media/audio/ui/KeypressReturn.ogg
10-11 02:54:48.543:
ERROR/ThrottleService(59): Could not open GPS configuration file /etc/gps.conf
10-11 02:54:50.474:
ERROR/logwrapper(142): executing /system/bin/tc failed: No such file or directory
10-11 02:54:50.554:
ERROR/logwrapper(143): executing /system/bin/tc failed: No such file or directory
10-11 02:54:50.723:
ERROR/logwrapper(144): executing /system/bin/tc failed: No such file or directory
10-11 02:55
4.493:
ERROR/HierarchicalStateMachine(59): TetherMaster - unhandledMessage: msg.what=3
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): FATAL EXCEPTION: main
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{org.android.maps/org.android.maps.HelloMaps}: java.lang.ClassNotFoundException: org.android.maps.HelloMaps in loader dalvik.system.PathClassLoader[/system/framework/com.google.android.map s.jar:/data/app/org.android.maps-1.apk]
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2 585)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:26 79)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at android.app.ActivityThread.access$2300(ActivityThread.java:125)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033) 10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at android.os.Handler.dispatchMessage(Handler.java:99)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at android.os.Looper.loop(Looper.java:123)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at android.app.ActivityThread.main(ActivityThread.java:4627)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at java.lang.reflect.Method.invokeNative(Native Method)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at java.lang.reflect.Method.invoke(Method.java:521)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit. java:86
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at dalvik.system.NativeStart.main(Native Method)
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): Caused by: java.lang.ClassNotFoundException: org.android.maps.HelloMaps in loader dalvik.system.PathClassLoader[/system/framework/com.google.android.map s.jar:/data/app/org.android.maps-1.apk]
10-11 02:55:27.363:
ERROR/AndroidRuntime(300): at dalvik.system.PathClassLoader.findClass(PathClassLoader.ja...
-
11/10/2011 14:14:45 | Marcos Vasconcelos

Olá,
Primeiro, voce nao precisa de toda essa StackTrace, a parte importante é:
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{org.android.maps/org.android.maps.HelloMaps}: java.lang.ClassNotFoundException: org.android.maps.HelloMaps in loader dalvik.system.PathClassLoader[/system/framework/com.google.android.map s.jar:/data/app/org.android.maps-1.apk]
Voce tem certeza que essa classe existe nesse lugar? Voce declarou ela no AndroidManifest.xml?
-
29/11/2011 18:20:31 |189.81.145.xxx| Lucas - Jogo

Olá você poderia me mandar as pastas já com o jogo só pra min testar aqui.. pois esta dando muito erro quando tento criar através dos códigos q você fez...
obrigadomeu email
martins_lucas1@yahoo.com.br
-
29/11/2011 18:27:53 | Marcos Vasconcelos

Baixou do repositorio no github?
https://github.com/MarkyVasconcelos/AndroidTechsGames
-
15/12/2011 18:39:20 |187.57.65.xxx| ViniciusFreitas - Point2D

Baixei o fonte do Vector2D mais não funciona da erro dizendo que esta faltando o pacote java.awt.geom.Point2D, como resolvo isso?
-
15/12/2011 18:39:57 |187.57.65.xxx| ViniciusFreitas

Baixei o fonte do Vector2D mais da erro dizendo que falta o java.awt.geom.Point2d
-
28/01/2012 00:26:18 |201.27.166.xxx| Diogo Barbosa

Marcos, da uma força ai.
"super(new Rect())";
Essa linha referente ao construtor da classe ball está dando erro aqui...
Essa chamada a super está realmente válida?
-
28/01/2012 16:36:52 | Marcos Vasconcelos

Arrumado, isso só é valido depois que Ball extends Entity.
Obrigado por avisar.
-
16/02/2012 11:59:00 |200.192.240.xxx| Anônimo

Adorei, você pretende ensinar tudo o que der sobre fazer jogos no Android? Gostaria muito de aprender a fazer aqueles efeitos de magia.
-
16/03/2012 20:31:09 |200.18.170.xxx| sergio

Marcos gostaria de saber o que vc me indica, para uma aplicação do tipo controle de temperatura no android com animação do tipo, terei um termometro e a medida que a temperatura aumenta ele muda tb a escala nele, tipo como se o nivel de mercurio sobe ou desce. pegarei os dados dessa temperatura via web usando sockets, o que preciso mesmo é a parte da animação
Parabéns pelo artigo
-
18/03/2012 20:32:07 | Marcos Vasconcelos

Eu faria na mão mesmo (interpolar os valores através do tempo), com o termometro sendo uma View sua que tem todas as logicas de animação.
-
29/03/2012 21:30:31 |200.239.65.xxx| Anônimo - Muito BOm o Post

mas gostaria de saber se nao tem um material sobre a programação andoroid voltada a jogos
-
12/04/2012 02:25:47 |187.56.216.xxx| Fernando França - Dúvida sobre handler...

Olá primeiramente parabéns pelo artigo, pois é graças a pessoas como vc que eu venho aprendendo e muito nessa minha longa jornada android!
Com base no seu artigo eu estou desenvolvendo um marcador de tempo ultilisando o handler.
Funciona da seguinte forma: O cara escolhe a quantidade de batidas por minuto e o sistema fica marcando esse tempo com "clicks".
Está acontecendo que está havendo um atraso em milisegundos segundo meu logcat.
Como essa seria uma ferramenta de precisão, não poderia atrasar nem um mísero milisegundo...
Usar o handler é uma boa opção ou existe uma melhor para esse caso?
Obs: trava tanto no emulador quanto no celular pois acompanhei com um cronometro.
Se você puder me ajudar mais ainda eu ficarei agradecido!
vlw!!!
-
12/04/2012 11:06:29 | Marcos Vasconcelos

Fico feliz de poder ajudar na sua jornada.
Sobre sua questão, voce não tem controle de que as mensagens do handler vão ser processadas assim que enviadas, então voce não pode garantir a precisão que precisa.
No seu caso, voce deveria implementar o loop do jogo para guardar o tempo, roda-lo na main-thread e ir atualizando a tela pela diferença do tempo desde o ultimo loop, mas nessa abordagem os eventos de toque devem ser enviados para uma fila de mensagens para serem processados na fase proccessLogics(long uptime).
Acredito que o ViniGodoy seria a pessoa ideal para falar sobre essa tecnica.
-
14/04/2012 20:31:53 |187.75.67.xxx| Fernando França - Valew!

Valeu Marcos, vou estudar seguindo esse direcionamento seu. Assim que estiver pronto eu te mando o link para vc dar uma olhada!
-
17/04/2012 20:27:57 |189.15.206.xxx| Anonimo - Vcs

Valeu cara espero q post mais
comandos mas esse ai ja da pra desenvolver pelo menos um jogo que nem bounce?
-
28/07/2012 15:47:22 |189.68.48.xxx| Gabriel - Dúvidas

Olá, Marcos..parabéns pelo post, gostei muito..
estava seguindo seu tutorial e logo no começo (na primeira página) travei em uma coisa...toda vez q adiciono no arquivo XML e coloco isso
da erro e o emulador desliga a aplicação..
sabe oque pode ser??...
parece q ele nao aceita oabraços e valeu =]
-
28/07/2012 15:49:40 |189.68.48.xxx| Gabriel

acho q nao foi a linha do erro "^^
mas eh na ultima parte da primeira pagina, no arquivo XML
acho q o problema eh no br.com.pontov.gameviewmas nao sei como resolver
=[
-
29/09/2012 04:48:24 |189.35.138.xxx| Marcos Vinicius

Eu estou no 2º ano de ciência da computação e eu e meu grupo (3 karas) estamos usando muito o Java2D para programarmos uma cópia do jogo pokemon, é muito bacana kara, experiencia muito boa, principalmente pra mim que sou o mais iniciante do grupo, estou aprendendo muito e estou apaixonado pelo desenvolvimento de jogos!!! Estudando C++ aqui no site de vcs (que por sinal é ótimo)!!! Comprando livros e tal. Muito bom ter um local pra debater sobre.











Java 2D? Usa Unity3D que é bem melhor.