Tutorial – Jogo de Damas em JavaFX (parte 2)

Vamos a segunda parte do tutorial de construir um jogo de damas com JavaFX.

Em relação à primeira parte do tutorial fiz algumas mudanças no código necessárias à continuação do joguinho.

O resultado visual dessa parte do tutorial é esse:

tutorial_damas_2_002

Notem o mouse controlando uma das peças azuis (que estão mais brilhosas exatamente porque é a vez do azul jogar).  Algumas peças estão faltando porque já foram comidas.

Vamos ao tutorial… =D

O código base:

import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.effect.*;
import javafx.scene.effect.light.*;
import javafx.scene.shape.*;
import javafx.scene.input.*;
import javafx.ext.swing.*;
import javafx.animation.*;
import java.lang.*;
import java.util.*;

def SIZE = 30;  //tamanho de uma casa
def SIZE2 = SIZE/2; //metade de uma casa
def SIZEP = 12; //raio de uma peca

class Peca extends CustomNode{
    var x: Number;//posicao na tela
    var y: Number;//posicao na tela
    var i: Integer;//posicao no tabuleiro
    var j: Integer;//posicao no tabuleiro
    var jogador: Integer;//numero do jogador (1 ou 2)
    init{//chamado quando criar o objeto
        updateXY();//atualiza XY em relaçao a IJ
    }
    public function toIJ(valor: Number):Integer{//converter de XY para IJ
        return Math.round((valor - SIZE2) / SIZE).intValue();
    }
    public function toXY(valor: Integer):Number{//converter de IJ para XY
        return valor * SIZE + SIZE2;
    }
    public function updateXY():Void{//atualiza XY em relaçao a IJ
        x = toXY(i);
        y = toXY(j);
    }
    public function updateIJ():Void{//atualiza IJ em relaçao a XY
        i = toIJ(x);
        j = toIJ(y);
    }
    override function create():Node{
        return Circle{
            centerX: bind x centerY: bind y radius: SIZEP

            fill: bind RadialGradient{
                centerX: 0.5 centerY: 0.5 focusX: 0.3 focusY: 0.3
                radius: 0.5 proportional: true
                stops:[
                    Stop{
                        offset: 0.0
                        color: if(jogador == 1)Color.color(1,0.8,0.8)else Color.LIGHTBLUE
                    }
                    Stop{
                        offset: 0.4
                        color: if(jogador == 1)Color.RED else Color.BLUE
                    }
                    Stop{
                        offset: 0.8
                        color: if(jogador == 1)Color.DARKRED else Color.DARKBLUE
                    }
                ]
            }
            effect: DropShadow{
                offsetX: 1 offsetY: 1
                radius:3
                spread: 0.1
            }
        }
    }
}

Stage{
    title: "Damas"
    scene: Scene{
        width: SIZE*8
        height: SIZE*8
        fill: Color.WHITE
        content: [
            for(i in [0..4])
                for(j in [0..4])[
                    Rectangle{
                        x: i*2*SIZE y: j*2*SIZE width: SIZE height: SIZE
                        fill: Color.GRAY
                    }
                    Rectangle{
                        x: i*2*SIZE+SIZE y: j*2*SIZE+SIZE width: SIZE height: SIZE
                        fill: Color.GRAY
                    }
                ]
            for(i in [0..4])
                for(j in [0..2])[
                    Peca{
                        i: if((j mod 2) == 0) i*2 else i*2+1
                        j: j
                        jogador: 1
                    }
                    Peca{
                        i: 7 - (if((j mod 2) == 0) i*2 else i*2+1)
                        j: j + 5
                        jogador: 2
                    }
                ]
        ]
    }
}

E o resultado desse código são peças bonitas porém bem mais leves que antes. Os efeitos de luz que tinha na primeira parte do tutorial eram muito pesados e quando ia movimentar as peças ficava muito lento. Então criei outro efeito parecido com luz (RadialGradient) soh que bem mais leve =D

tutorial_damas_2_001

Também removi os ‘bind’s porque não pode existir ‘bind’ entre duas variáveis ao mesmo tempo. (a bind b e b bind a). No lugar deles criei duas funções updateXY() e updateIJ() que fazem a mesma função dos binds (atualizar as variaveis XY em relação a IJ e vice-versa). Usar essas funções em vez dos bind’s é util porque posso controlar quando quero atualizar as variáveis. Com o bind não tenho esse controle, elas são atualizadas automaticamente (as vezes isso é util, mas nesse caso não).

A ideia dessa parte do tutorial é fazer as peças se movimentarem! Então vamos adicionar ao Circle das peças a funçao onMouseDragged que controla quando vc arrasta algum componente com o mouse!

Na função create da Peca adicione a funçao para a variavel onMouseDragged:

    override function create():Node{
        return Circle{
            ...
            onMouseDragged: function(e: MouseEvent):Void{
                x = e.sceneX;
                y = e.sceneY;
            }
        }
    }

Essa função vai pega a posição do mouse na cena, e atribui à posição da peça na tela! Assim quando vc arrastar algum componente, ele vai seguir a posição do mouse na tela! Com isso vc já pode movimentar as peças no tabuleiro! Mas as peças estão sem controle! Vc pode colocar a peça em qualquer posiçao! Vamos corrigir isso adicionando a funçao onMouseReleased que controla quando o botão do mouse for solto! Nessa função vc vai calcular a posição da peça quando foi solta, e ajusta-la pra ficar centralizada em alguma casa.

onMouseReleased: function(e: MouseEvent):Void{
    x = e.sceneX;
    y = e.sceneY;
    updateIJ();
    updateXY();
}

A lógica é a seguinte, vc coloca a peça na posição onde o botão do mouse foi solto, quando atualizar a posição da peça no tabuleiro (updateIJ()) vc vai ter (i,j) como numeros inteiros, e quando atualizar a posição da peça na tela (updateXY()) vc centraliza a peça numa casa, porque estará atualizando (x,y) em relaçao à (i,j) que jah estão atualizados. Dessa forma, quando vc soltar uma peça, ela vai ficar centralizada na casa mais próxima.

Vamos agora criar uma classe Jogo que vai armazenar algumas variáveis importantes.

class Jogo{
    var tabuleiro: Hashtable = new Hashtable();
    var jogador = 2;//começa com o jogador azul
    var pecas: Peca[];
    init{
        pecas = for(i in [0..3])
            for(j in [0..2])[
                Peca{
                    i: if((j mod 2) == 0) i*2 else i*2+1
                    j: j
                    jogador: 1
                    jogo: this
                }
                Peca{
                    i: 7 - (if((j mod 2) == 0) i*2 else i*2+1)
                    j: j + 5
                    jogador: 2
                    jogo: this
                }
            ];
    }
    public function mudarJogador():Void{
        if(jogador == 1){
            jogador = 2;
        }else{
            jogador = 1;
        }
    }
}

Essa classe possui um tabuleiro (tabuleiro:Hashtable) que armazena quais peças ainda estão no jogo (quando uma peça for comida, ela será removida desse Hashtable), uma variavel jogador que controla de quem é a vez de jogar (azul ou vermelho) e um array de peças. A função mudarJogador() o nome já diz pra que serve =D. Na inicialização todas as peças são criadas e recebem uma referencia à variavel jogo. Para isso é necessário adicionar a variável ‘jogo’ na classe Peca.

class Peca extends CustomNode{
    ...
    var jogo: Jogo;//referencia ao jogo
    ...
}

Criamos uma variável para guardar o jogo e como as peças foram criadas dentro do jogo, não precisamos cria-las dentro do Scene:

var jogo: Jogo = Jogo{};
Stage{
	title: "Damas"
	scene: Scene{
		...
		content: [
			//criação dos retangulos do tabuleiro
			...
			jogo.pecas
		]
	}
}

O código tah começando a ficar organizado =D

Agora que temos a variavel tabuleiro que controla quais variaveis estão no jogo, no init da classe Peca vamos colocar o comando pra peça ser adicionada ao tabuleiro, e criamos também a funçao IJtoIndex(i,j) que converte as coordenadas (i,j) num índice de um array unidimensional.

	init{
		updateXY();
		jogo.tabuleiro.put( IJtoIndex(i,j) , this);
	}
	public function IJtoIndex(i: Integer, j: Integer):Integer{
		return j*8 + i;
	}

Também adicionamos à classe Peca a variavel dama:Boolean que controla se a peça é uma dama (ou seja, chegou ao outro lado do tabuleiro). Criamos a função comer() que faz com que a peça seja removida do tabuleiro:Hashtable e fique invisível. Também adicionamos a função podeComer() que verifica se uma peça pode comer alguma outra em volta, isso serve para que se possa comer 2 peças ou mais numa única jogada. Cada vez que termina uma jogada, é chamada a função mudarJogador() que passa a vez para o outro jogador. Veja como fica o código com esse monte de modificação:

import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.effect.*;
import javafx.scene.effect.light.*;
import javafx.scene.shape.*;
import javafx.scene.input.*;
import javafx.ext.swing.*;
import javafx.animation.*;
import java.lang.*;
import java.util.*;

def SIZE = 30;  //tamanho de uma casa
def SIZE2 = SIZE/2; //metade de uma casa
def SIZEP = 12; //raio de uma peca

class Peca extends CustomNode{
	var x: Number;
	var y: Number;
	var i: Integer;
	var j: Integer;
	var jogador: Integer;
	var jogo: Jogo;
	var dama: Boolean = false;
	init{
		updateXY();
		jogo.tabuleiro.put( IJtoIndex(i,j) , this);
	}
	public function podeComer():Boolean{

		if(i+2 <= 7 and j+2 = 0 and j+2 = 0 and j-2 >= 0){
			if(jogo.tabuleiro.containsKey(IJtoIndex(i-1,j-1))){
				var peca: Peca = jogo.tabuleiro.get(IJtoIndex(i-1,j-1)) as Peca;
				if(peca.jogador != this.jogador){
					if(not jogo.tabuleiro.containsKey(IJtoIndex(i-2,j-2))){
						return true;
					}
				}
			}
		}

		if(i+2 = 0){
			if(jogo.tabuleiro.containsKey(IJtoIndex(i+1,j-1))){
				var peca: Peca = jogo.tabuleiro.get(IJtoIndex(i+1,j-1)) as Peca;
				if(peca.jogador != this.jogador){
					if(not jogo.tabuleiro.containsKey(IJtoIndex(i+2,j-2))){
						return true;
					}
				}
			}
		}

		return false;
	}
	public function IJtoIndex(i: Integer, j: Integer):Integer{
		return j*8 + i;
	}
	public function toIJ(valor: Number):Integer{
		return Math.round((valor - SIZE2) / SIZE).intValue();
	}
	public function toXY(valor: Integer):Number{
		return valor * SIZE + SIZE2;
	}
	public function updateXY():Void{
		x = toXY(i);
		y = toXY(j);
	}
	public function updateIJ():Void{
		jogo.tabuleiro.remove(IJtoIndex(i,j));
		i = toIJ(x);
		j = toIJ(y);
		jogo.tabuleiro.put(IJtoIndex(i,j), this);

		if((jogador == 1 and j == 7) or (jogador == 2 and j == 0)){
			dama = true;
		}
	}
	public function comer():Void{
		jogo.tabuleiro.remove(IJtoIndex(i,j));
		this.visible = false;
	}
	override function create():Node{
		return Circle{
			centerX: bind x centerY: bind y radius: SIZEP

			fill: bind RadialGradient{
				centerX: 0.5 centerY: 0.5
				focusX: 0.3 focusY: 0.3
				radius: 0.5
				proportional: true
				stops:[
					Stop{
						offset: 0.0
						color: if(jogador == 1)Color.color(1,0.8,0.8)else Color.LIGHTBLUE
					}
					Stop{
						offset: 0.4
						color: if(jogador == 1)Color.RED else Color.BLUE
					}
					Stop{
						offset: 0.8
						color: if(jogador == 1)Color.DARKRED else Color.DARKBLUE
					}
					Stop{
						offset: 1.0
						color: if(dama)Color.YELLOW else Color.BLACK
					}
				]
			}

			effect: DropShadow{
				offsetX: 1 offsetY: 1
				radius:3
				spread: 0.1
			}

			//1 movendo
			onMouseDragged: function(e: MouseEvent):Void{
				if(this.jogador != jogo.jogador){
					return;
				}
				x = e.sceneX;
				y = e.sceneY;
			}
			//2 movendo e soltando no lugar correto
			onMouseReleased: function(e: MouseEvent):Void{
				if(this.jogador != jogo.jogador){
					return;
				}

				x = e.sceneX;
				y = e.sceneY;

				var ii = toIJ(x);
				var jj = toIJ(y);

				var dx = Math.abs(ii-i);
				var dy = Math.abs(jj-j);

				var index = IJtoIndex(ii,jj);

				if( (dx > 0 or dy > 0) and dx == dy ){//se moveu (e na diagonal)

					if( ii >= 0 and ii = 0 and jj  j) or
												(jogador == 2 and jj < j)){// andou pra frente
										updateIJ();
										updateXY();
										jogo.mudarJogador();
									}else{//andou pra traz
										updateXY();
									}
								}
							}else{//mais que uma casa
								if(dx == 2){//2 casas
									var i_med = (ii+i)/2;
									var j_med = (jj+j)/2;
									var index_med = IJtoIndex(i_med,j_med);
									if(jogo.tabuleiro.containsKey(index_med)){//pulou peça
										var peca: Peca = jogo.tabuleiro.get(index_med) as Peca;
										if(peca.jogador != this.jogador){//outro jogador
											peca.comer();
											updateIJ();
											updateXY();
											if(not podeComer()){
												jogo.mudarJogador();
											}
										}else{
											updateXY();
										}
									}else{//nao pulou peça
										updateXY();
									}
								}else{//mais que duas casas
									updateXY();
								}
							}
						}
					}else{//fora do tabuleiro
						updateXY();
					}
				}else{//se nao moveu ou nao foi na diagonal
					updateXY();
				}
			}
		}
	}
}

class Jogo{
	var tabuleiro: Hashtable = new Hashtable();
	var jogador = 2;//azul
	var pecas: Peca&#91;&#93;;
	init{
		pecas = for(i in &#91;0..3&#93;)
			for(j in &#91;0..2&#93;)&#91;
				Peca{
					i: if((j mod 2) == 0) i*2 else i*2+1
					j: j
					jogador: 1
					jogo: this
				}
				Peca{
					i: 7 - (if((j mod 2) == 0) i*2 else i*2+1)
					j: j + 5
					jogador: 2
					jogo: this
				}
			&#93;;
	}
	public function mudarJogador():Void{
		if(jogador == 1){
			jogador = 2;
		}else{
			jogador = 1;
		}
	}
}

var jogo: Jogo = Jogo{};

Stage{
	title: "Damas"
	scene: Scene{
		width: SIZE*8
		height: SIZE*8
		fill: Color.WHITE
		content: &#91;
			Group{
				content: for(i in &#91;0..4&#93;)
					for(j in &#91;0..4&#93;)&#91;
						Rectangle{
							x: i*2*SIZE y: j*2*SIZE
							width: SIZE height: SIZE
							fill: Color.GRAY
						}
						Rectangle{
							x: i*2*SIZE+SIZE y: j*2*SIZE+SIZE
							width: SIZE height: SIZE
							fill: Color.GRAY
						}
					&#93;
			}
			jogo.pecas

		&#93;
	}

}&#91;/sourcecode&#93;

Algumas explicações:
<ul>
	<li><strong>jogo.tabuleiro.containsKey(IJtoIndex(i,j))</strong>: verifica se existe uma peça na posição (i,j)</li>
	<li><strong>jogo.tabuleiro.get(IJtoIndex(i,j)) as Peca</strong>: petorna a peça na posiçao (i,j)</li>
	<li><strong>if(peca.jogador != jogo.jogador)</strong>: verifica se a peça atual é do jogador da vez (azul ou vermelho)</li>
	<li><strong>updateIJ()</strong> e <strong>updateXY()</strong>: move a peça para a posição escolhida</li>
	<li>apenas <strong>updateXY()</strong>: move a peça de volta à posição inicial</li>
</ul>
Agora as peças se movem apenas nas posições corretas, uma peça pode comer outra, peças não podem andar para traz, e quando chegam ao outro lado do tabuleiro viram damas (contorno amarelo). Vejam a imagem:

<img class="aligncenter size-full wp-image-163" title="tutorial_damas_2_003" src="https://raphaelmarques.wordpress.com/wp-content/uploads/2008/12/tutorial_damas_2_003.jpg" alt="tutorial_damas_2_003" width="248" height="268" />

Agora vamos melhorar alguns detalhes. Quando vc arrasta uma peça, ela pode ficar por baixo de outra, dependendo das peças. Vamos resolver isso adicionando à peça a função onMousePressed que controla quando o botão do mouse é pressionado. A lógica é quando clicar sobre uma peça ela fica sobre todas as outras peças.

override function create():Node{
	return Circle{
		...
		//3 movendo pra frente
		onMousePressed: function(e: MouseEvent):Void{
			this.toFront();
		}
	}
}

E falta agora destacar as peças as vez (azuis ou vermelhas). Podemos fazer isso deixando as outras peças (que não são da vez) menos brilhosa deixado-as meio transparentes. Para isso precisamos apenas adicionar a variavel opacity das peças:

override function create():Node{
	return Circle{
		...
		opacity: bind if(jogo.jogador == jogador) 1.0 else 0.5
		...
	}
}

O resultado é que as peças que não sao da vez ficam menos brilhosas:

tutorial_damas_2_004

Dessa forma, quando for a vez da peça ela tera opacidade total (1.0) e quando não for a vez da peça, ela ficará meio transparente (0.5).

Agora vamos adicionar a opção de resetar o jogo. Para que quando o jogo termine possa voltar ao estado inicial. Para isso adicionamos a função reset() à classe Jogo e adicionamos a função onKeyPressed ao grupo que contém os elementos do tabuleiro para quando o usuário pressionar ESPAÇO  jogo volte ao estado inicial.

class Jogo{
	...
	public function reset():Void{
		tabuleiro.clear();
		var index = 0;
		for(i in [0..3]){
			for(j in [0..2]){
				var peca: Peca = pecas[index++];
				peca.i = if((j mod 2) == 0) i*2 else i*2+1;
				peca.j = j;
				peca.updateXY();
				peca.visible = true;
				peca.dama = false;
				tabuleiro.put(peca.IJtoIndex(peca.i,peca.j), peca);

				peca = pecas[index++];
				peca.i = 7 - (if((j mod 2) == 0) i*2 else i*2+1);
				peca.j = j + 5;
				peca.updateXY();
				peca.visible = true;
				peca.dama = false;
				tabuleiro.put(peca.IJtoIndex(peca.i,peca.j), peca);
			}
		}
	}
}

var jogo: Jogo = Jogo{};

Stage{
	...
	scene: Scene{
		...
		content: [
			Group{
				content: ...
				onKeyPressed: function(e: KeyEvent):Void{
					if(e.code == KeyCode.VK_SPACE){
						jogo.reset();
					}
				}
			}
			jogo.pecas
		]
	}
}

Terminado =D

Agora vejam o código completo com alguns printlns que mostram a lógica do onMouseReleased.

import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.effect.*;
import javafx.scene.effect.light.*;
import javafx.scene.shape.*;
import javafx.scene.input.*;
import javafx.ext.swing.*;
import javafx.animation.*;
import java.lang.*;
import java.util.*;

def SIZE = 30; //tamanho de uma casa
def SIZE2 = SIZE/2; //metade de uma casa
def SIZEP = 12; //raio de uma peca

def DEBUG = true;

class Peca extends CustomNode{
var x: Number;
var y: Number;
var i: Integer;
var j: Integer;
var jogador: Integer;
var jogo: Jogo;
var dama: Boolean = false;
init{
updateXY();
jogo.tabuleiro.put( IJtoIndex(i,j) , this);
}
public function podeComer():Boolean{

if(i+2 <= 7 and j+2 = 0 and j+2 = 0 and j-2 >= 0){
if(jogo.tabuleiro.containsKey(IJtoIndex(i-1,j-1))){
var peca: Peca = jogo.tabuleiro.get(IJtoIndex(i-1,j-1)) as Peca;
if(peca.jogador != this.jogador){
if(not jogo.tabuleiro.containsKey(IJtoIndex(i-2,j-2))){
return true;
}
}
}
}

if(i+2 = 0){
if(jogo.tabuleiro.containsKey(IJtoIndex(i+1,j-1))){
var peca: Peca = jogo.tabuleiro.get(IJtoIndex(i+1,j-1)) as Peca;
if(peca.jogador != this.jogador){
if(not jogo.tabuleiro.containsKey(IJtoIndex(i+2,j-2))){
return true;
}
}
}
}

return false;
}
public function IJtoIndex(i: Integer, j: Integer):Integer{
return j*8 + i;
}
public function toIJ(valor: Number):Integer{
return Math.round((valor – SIZE2) / SIZE).intValue();
}
public function toXY(valor: Integer):Number{
return valor * SIZE + SIZE2;
}
public function updateXY():Void{
x = toXY(i);
y = toXY(j);
}
public function updateIJ():Void{
jogo.tabuleiro.remove(IJtoIndex(i,j));
i = toIJ(x);
j = toIJ(y);
jogo.tabuleiro.put(IJtoIndex(i,j), this);

if((jogador == 1 and j == 7) or (jogador == 2 and j == 0)){
dama = true;
}
}
public function comer():Void{
jogo.tabuleiro.remove(IJtoIndex(i,j));
this.visible = false;
}
override function create():Node{
return Circle{
centerX: bind x centerY: bind y radius: SIZEP
opacity: bind if(jogo.jogador == jogador) 1.0 else 0.5

fill: bind RadialGradient{
centerX: 0.5 centerY: 0.5
focusX: 0.3 focusY: 0.3
radius: 0.5
proportional: true
stops:[
Stop{
offset: 0.0
color: if(jogador == 1)Color.color(1,0.8,0.8)else Color.LIGHTBLUE
}
Stop{
offset: 0.4
color: if(jogador == 1)Color.RED else Color.BLUE
}
Stop{
offset: 0.8
color: if(jogador == 1)Color.DARKRED else Color.DARKBLUE
}
Stop{
offset: 1.0
color: if(dama)Color.YELLOW else Color.BLACK
}
]
}

effect: DropShadow{
offsetX: 1 offsetY: 1
radius:3
spread: 0.1
}

//1 movendo
onMouseDragged: function(e: MouseEvent):Void{
if(this.jogador != jogo.jogador){
return;
}
x = e.sceneX;
y = e.sceneY;
}
//2 movendo e soltando no lugar correto
onMouseReleased: function(e: MouseEvent):Void{
if(this.jogador != jogo.jogador){
return;
}

x = e.sceneX;
y = e.sceneY;

var ii = toIJ(x);
var jj = toIJ(y);

var dx = Math.abs(ii-i);
var dy = Math.abs(jj-j);

var index = IJtoIndex(ii,jj);

if(DEBUG)println(“nmoveto {ii},{jj}”);

if( (dx > 0 or dy > 0) and dx == dy ){//se moveu (e na diagonal)
if(DEBUG)println(“moveu”);

if( ii >= 0 and ii = 0 and jj j) or
(jogador == 2 and jj < j)){// andou pra frente if(DEBUG)println("andou pra frente"); updateIJ(); updateXY(); jogo.mudarJogador(); }else{//andou pra traz if(DEBUG)println("andou pra traz"); updateXY(); } } }else{//mais que uma casa if(DEBUG)println("mais que 1 casa"); if(dx == 2){//2 casas if(DEBUG)println("2 casas"); var i_med = (ii+i)/2; var j_med = (jj+j)/2; var index_med = IJtoIndex(i_med,j_med); if(jogo.tabuleiro.containsKey(index_med)){//pulou peça if(DEBUG)println("pulou peça"); var peca: Peca = jogo.tabuleiro.get(index_med) as Peca; if(peca.jogador != this.jogador){//outro jogador if(DEBUG)println("pulou peça de outro jogador"); peca.comer(); updateIJ(); updateXY(); if(not podeComer()){ jogo.mudarJogador(); } }else{ if(DEBUG)println("pulou peça do mesmo jogador"); updateXY(); } }else{//nao pulou peça if(DEBUG)println("nao pulou peça"); updateXY(); } }else{ if(DEBUG)println("mais que 2 casas"); updateXY(); } } } }else{//fora do tabuleiro if(DEBUG)println("fora do tabuleiro"); updateXY(); } }else{//se nao moveu ou nao foi na diagonal if(DEBUG)println("nao moveu"); updateXY(); } } //3 movendo pra frente onMousePressed: function(e: MouseEvent):Void{ this.toFront(); } } } } class Jogo{ var tabuleiro: Hashtable = new Hashtable(); var jogador = 2;//azul var pecas: Peca[]; init{ pecas = for(i in [0..3]) for(j in [0..2])[ Peca{ i: if((j mod 2) == 0) i*2 else i*2+1 j: j jogador: 1 jogo: this } Peca{ i: 7 - (if((j mod 2) == 0) i*2 else i*2+1) j: j + 5 jogador: 2 jogo: this } ]; } public function mudarJogador():Void{ if(jogador == 1){ jogador = 2; }else{ jogador = 1; } } public function reset():Void{ tabuleiro.clear(); var index = 0; for(i in [0..3]){ for(j in [0..2]){ var peca: Peca = pecas[index++]; peca.i = if((j mod 2) == 0) i*2 else i*2+1; peca.j = j; peca.updateXY(); peca.visible = true; peca.dama = false; tabuleiro.put(peca.IJtoIndex(peca.i,peca.j), peca); peca = pecas[index++]; peca.i = 7 - (if((j mod 2) == 0) i*2 else i*2+1); peca.j = j + 5; peca.updateXY(); peca.visible = true; peca.dama = false; tabuleiro.put(peca.IJtoIndex(peca.i,peca.j), peca); } } } } var jogo: Jogo = Jogo{}; Stage{ title: "Damas" scene: Scene{ width: SIZE*8 height: SIZE*8 fill: Color.WHITE content: [ Group{ content: for(i in [0..4]) for(j in [0..4])[ Rectangle{ x: i*2*SIZE y: j*2*SIZE width: SIZE height: SIZE fill: Color.GRAY } Rectangle{ x: i*2*SIZE+SIZE y: j*2*SIZE+SIZE width: SIZE height: SIZE fill: Color.GRAY } ] onKeyPressed: function(e: KeyEvent):Void{ if(e.code == KeyCode.VK_SPACE){ jogo.reset(); } } } jogo.pecas ] } }[/sourcecode] Terminamos a segunda parte desse tutorial! (segunda parte bem gigante neh...) O segredo é estudar esse código, principalmente a funçao onMouseReleased que possui quase toda a lógica de funcionamento do jogo de damas. Só está faltando obrigar um jogador a comer uma peça do adversário quando puder. Atualmente isso só acontece se o jogador comeu a 1ª peça e é obrigado a comer a 2ª. Até a próxima (e última) parte desse tutorial. o/

10 Respostas to “Tutorial – Jogo de Damas em JavaFX (parte 2)”


  1. 1 Mayckon Domingues 24/09/2009 às 12:51

    Meu amigo, sou iniciante em JavaFx, gostei muito desse tutorial de damas, Parabéns!

    Você tem algum material que possa me passar de como chamar outra tela em JavaFx (um form que chame outro) e como fazer conexão com banco de dados?

    Um abraço!

    Mayckon Domingues.

  2. 2 Raphael Marques 24/09/2009 às 19:39

    nunca vi como abrir outra janela com JavaFX
    mas a comunicaçao dom BD é igual java (JDBC ou Hibernate)

  3. 3 vc tem tutorial mais material pra criaçao de outros jogos 28/12/2009 às 14:15

    parece ser show

  4. 4 Mii... 03/01/2010 às 12:52

    eu não entendi nada…eu queria saber de um jogo de damas que coma para trás,vce pode me arrumar um sem baixar?? se vce botasse isso no seu site com certeza ia entrar várias pessoas…eu posso até indicar para bastantes amigos…bjjjjss,faz isso por mim

  5. 5 Mii... 21/01/2010 às 16:03

    quαl é o nome do jogo de dαmαs que come pαrα trαs??
    bjuuuuss!!!

  6. 6 miguel 01/07/2011 às 06:37

    bem pessoal eu trabalhei o estagio todo a fazer a minha pap que era o jogo de dama e hoje no dia de entrega encontro isto. fdx que merda

  7. 7 João Paulo 26/10/2011 às 16:44

    Onde podemos baixar o código completo? No corpo do método que trata o evento do mouseReleased tem chaves (}) sobrando.

  8. 8 luzimar 17/03/2015 às 23:04

    muito bem so tem um erro é a regra as peças devem esta nas casas brancas digo porq conheço a regra mais muito bem feito o seu trabalho

  9. 9 Romano 12/12/2015 às 22:48

    Poderia colocar o projeto para todos nós podermos baixa lo. Grato

  10. 10 Felipe Osmar de Aviz 03/05/2016 às 21:14

    Oi amigo, pode publicar seu projeto completo?


Deixe um comentário




Categorias

Estatísticas

  • 143.319 hits