Console.Write("Hello World!");
[warning]Antes de mais nada, este tutorial já tem uns meses que foi escrito, eu pensei tê-lo postado aqui, mas acabei por descobrir que não, então estarei fazendo agora.[/warning]
Introdução:
Nesta postagem trago um texto que tem como principal foco ser curto e prático separado em sete tópicos, de como criar um jogo da memória em C# utilizando a aplicação de formulário.
Além de ser um jogo popular e todos saberem as regras, ainda é algo interessante em se desenvolver em linguagem de programação pois aborda assuntos já estudados em aulas teóricas como: Instância de classes, métodos, vetores, tipagem de dados, listas, estrutura condicional, conversões explícitas, aleatoriedade, propriedades de objetos, eventos, laço de repetição e muita lógica.
Veja como é um prato cheio colocar na prática tudo o que foi ensinado em teoria em uma única aplicação.
1) Design de formulário:
1- Crie um novo projeto chamado JogoDaMemoria e abra seu formulário;
2- Altere a propriedade text do formulário para Jogo da memória;
3- Ajuste seu tamanho na opção size com as dimensões que desejar;
4- Deixe a propriedade StartPosition em Centerscreen e Maximize box em false;
5- Clique com o lado direito do mouse sobre o nome do projeto através do Solution Explore e vá em >> Properties >> Resourcers >> Add Resource >> Add Existing file >> E selecione as imagens que você vai utilizar no jogo. *Guarde bem o nome da imagem que você vai utilizar como verso. No meu caso, o nome é verse;
6- Feche essa janela, clique em Yes e altere o ícone e a imagem de fundo do formulário, caso desejar, com as imagens que importou;
7- Arraste através do toolbox as PictureBoxes que você vai utilizar. Traga ao formulário a quantidade de pictureboxes equivalente à quantidade de cartas do seu jogo (incluindo os pares);
8- Coloque as imagens dentro das pictureboxes e ajuste a opção Size mode para AutoSize;
9- Altere a tag dessas pictureboxes em ordem crescente, iniciando do zero (0).
*Os pares de imagens devem ter o mesmo índice de tag do seu respectivo par;
10- Arraste do toolbox uma label para registrar os movimentos dos jogadores: Essa label deve ter o texto Movimentos:, tamanho, fonte e cor de sua preferência, cor de fundo transparente e deve ter o nome de lblMove. Ao fazer tudo isso, seu formulário deve estar mais ou menos assim:
2) Programando - Início do jogo:
Dê F7 no teclado e abra o código .cs do seu programa.
Dentro da classe do formulário crie quatro variáveis do tipo inteiro: Uma vai registrar os movimentos do jogo, a outra vai gerenciar a quantidade de cliques feitos nas cartas, a terceira vai armazenar quantos pares foram encontrados para checar se o jogo acabou e a última vai guardar a tag das pictureboxes para que possamos checar os pares das cartas e para gerenciar o resgate de imagens dessas pictureboxes.
Crie também duas listas do tipo inteiro: A primeira vai guardar as coordenadas das cartas no formulário através do eixo X, e a outra as coordenadas no eixo Y, para que mais tarde possamos sortear valores aleatórios nessas posições para embaralhar as cartas.
Também crie um vetor do tipo Image para que possamos guardar as imagens das pictureboxes antes delas serem viradas para baixo, assim poderemos resgatar essa imagem quando clicarmos na picturebox de referência. O tamanho deste vetor vai depender da quantidade de cartas que você quer colocar no jogo (desconsiderando os pares).
Agora defina um método privado sem retorno com o nome de InitializeVerse para que possamos virar as cartas para baixo assim que o jogo iniciar e para guardar suas posições dentro das listas.
Dentro do método, inicialize uma varredura nos controles do jogo, especificando o tipo PictureBox (pois queremos apenas acessar os campos dessa classe no formulário) através do laço foreach.
Faça uma conversão de String Format para inteiro na tag da picturebox que está sendo referenciada na variável do laço, atribuindo essa conversão para a variável de indexação de tags.
Coloque dentro do vetor de imagens na posição de indexação da tag, a propriedade da imagem da picturebox referenciada.
Agora faremos com que o verso da imagem fique à mostra: Atribua à propriedade de imagem da picturebox o caminho da imagem importada: Properties.Resourcers.verse
Deixe verdadeira a propriedade Enable da picturebox, e faça duas condições para checar se as coordenadas X e Y daquela pciturebox referenciada ainda não existem nas listas de coordenadas. Caso não existir, adicione essas posições em suas respectivas listas.
Fora do laço, chame um método que iremos criar com o nome de RandomPositions e chame este método que criamos agora dentro da inicialização do formulário:
3) Randomizado posições:
Crie o método com o nome que chamamos acima, sem retorno e privado.
Novamente adicione um laço foreach a realizar uma varredura pelos controles do tipo PictureBox e dentro do laço instancie a classe Random.
Crie duas variáveis que irão armazenar o sorteio de um elemento das listas de posições, obviamente que este sorteio deve ocorrer utilizando o método Next da instância de Random, e dentro do índice das listas.
Crie uma nova lista dentro da classe do formulário, essa lista será do tipo string.
Faça uma condição que verifique se os números sorteados em X e em Y (concatenados e convertidos para string) existem nessa lista que criamos.
Caso essas coordenadas já tenham sido sorteadas, utilize o comando goto e o leve para uma etiqueta com o nome de Repeat. Coloque a etiqueta nos comandos de sorteio, para que assim sejam sorteadas novas coordenadas para a picturebox referenciada.
Criando um else nessa condição, significando que essas coordenadas não existem na lista, então coloque a picturebox nessa posição através da propriedade Location e se utilizando da estrutura Point, passando como argumentos as duas coordenadas que receberam os valores sorteados. Após fazer isso, adicione à lista esses valores para que eles não se repitam.
4) Clicando e revelando imagens:
Vamos criar agora outro método privado, sem retorno com o nome de ImagesReference_Click contendo como parâmetros o object sender e EventsArgs e, para que possamos criar o evento de clique nas pictureboxes.
Para funcionar corretamente, colocamos a referência da picturebox clicada dentro de uma variável do tipo PictureBox (não esquecendo de realizar o casting para este objeto) e logo em seguida fazemos um incremento na variável clicks.
Vamos resgatar a imagem dentro da picturebox através do vetor de imagens: Atribua à propriedade Image do objeto referenciado a imagem no vetor images de acordo com a tag daquela picturebox. Antes disso porém, converta novamente em String.Formart para inteiros a tag da picturebox e armazene na variável TagIndex.
Não se esqueça de dar um refresh nesta picturebox, para que atualize corretamente o conteúdo de sua imagem e de deixar como false sua propriedade Enable.
Volte ao design do formulário, selecione todas suas pictureboxes, vá para a aba Events e na opção Click referencie o método ImagesReference_Click que acabamos de criar.
Teste o debug do formulário e vá clicando sobre as pictureboxes: Perceba que todas elas resgatam as imagens que tinham armazenadas anteriormente e que também estão distribuídas de forma aleatória, cada vez que você roda novamente a depuração.
5) Verificando pares:
Agora precisamos checar se o jogador acertou ou não as cartas reveladas.
Vamos gerenciar primeiramente quais cartas de fato que o jogador revelou e para isso vamos utilizar as tags dessas pictureboxes, ou seja, se as tags das mesmas forem iguais, o jogador acertou o par, caso contrário, as cartas serão viradas pro verso novamente.
Criaremos na classe do formulário um vetor com tamanho 2(dois) do tipo inteiro para guardar em uma de suas posições a tag das duas cartas reveladas no momento.
Crie uma condição que verifique se a variável clicks é igual a 1 (um), caso sim, armazene na posição zero deste vetor a tag da picturebox clicada, caso não, armazene na posição 1(um) deste vetor esta tag.
Também incremente a variável movimentos, atualize a sua label de registro e através de uma variável local do tipo bool checaremos se os pares são iguais, ou seja, se o conteúdo do vetor "tags'' na posição 0 é igual ao conteúdo deste vetor na posição 1.
Após isso, chame um método TurnCards e passe como argumento a variável boolena.
6) Desvirando cartas:
Nesse momento precisamos verificar através do retorno booleano se o jogador acertou as cartas viradas ou não.
Para isso, crie um novo método privado, sem retorno , com o nome de TurnCards e que aceita como parâmetro um booleano.
Para saber de fato quais eram as pictureboxes reveladas, precisamos novamente fazer uma varredura com o laço foreach nos controles das pictureboxes.
Faremos também uma verificação se a tag daquelas pictureboxes referenciadas for igual ao conteúdo do vetor de tags, tanto na posição 0(zero) quanto na posição 1(um). Caso sim, então saberemos quais cartas estão reveladas.
Tendo essa confirmação, podemos enfim checar se a variável booleana retornou true (acertou os pares) ou false (não acertou).
Se acertou os pares, a carta continua com a imagem revelada, sem alterar para seu verso e a variável foundCards recebe um incremento.
Caso não tenha acertado os pares, a variável mostra seu verso novamente e volta como true sua propriedade Enable, e após tudo isso fora do laço, a variável clicks volta a ser zero, chamando também um método que verifica se o jogo acabou.
Note que ao depurar (comentando a chamada do método), quando os pares são errados as cartas se revelam muito rapidamente e não há tempo suficiente para visualizar as mesmas.
Acabe com este problema referenciando a biblioteca System.Threading no topo do formulário. Volte ao método TurnCards e antes do laço chame o procedimento estático Sleep da classe Thread e passe como argumento o tempo de espera para as cartas virarem. Eu deixei 500, mas isso vai de sua opinião, quanto maior o número, mais tempo de espera.
7) Jogo terminado:
Vamos criar o último método do programa que irá checar se o jogo já se encerrou através da variável foundCards.
Crie este método como privado e sem retorno com o nome que chamamos: EndGame
Faça uma verificação que valide se foundCards é igual ao tamanho do vetor de imagens (que guarda as cartas) vezes dois (para considerar os pares encontrados).
Dentro dessa validação exiba uma mensagem ao usuário através do MessageBox o congratulando por ter terminado o jogo e o pergunte se deseja jogar novamente, utilizando a classe MessageBoxButtons com a propriedade YesNo.
Caso o jogador escolha sim, zere as variáveis clicks, movements e foundCards, limpe a lista de registros de coordenadas e chame novamente o método InitializeVerse, para que o jogo recomece.
Caso o jogador não queira mais jogar, chame o método Exit através da classe Application para fechar o formulário automaticamente.
E o jogo está pronto para ser jogado!
Introdução:
Nesta postagem trago um texto que tem como principal foco ser curto e prático separado em sete tópicos, de como criar um jogo da memória em C# utilizando a aplicação de formulário.
Além de ser um jogo popular e todos saberem as regras, ainda é algo interessante em se desenvolver em linguagem de programação pois aborda assuntos já estudados em aulas teóricas como: Instância de classes, métodos, vetores, tipagem de dados, listas, estrutura condicional, conversões explícitas, aleatoriedade, propriedades de objetos, eventos, laço de repetição e muita lógica.
Veja como é um prato cheio colocar na prática tudo o que foi ensinado em teoria em uma única aplicação.
1) Design de formulário:
1- Crie um novo projeto chamado JogoDaMemoria e abra seu formulário;
2- Altere a propriedade text do formulário para Jogo da memória;
3- Ajuste seu tamanho na opção size com as dimensões que desejar;
4- Deixe a propriedade StartPosition em Centerscreen e Maximize box em false;
5- Clique com o lado direito do mouse sobre o nome do projeto através do Solution Explore e vá em >> Properties >> Resourcers >> Add Resource >> Add Existing file >> E selecione as imagens que você vai utilizar no jogo. *Guarde bem o nome da imagem que você vai utilizar como verso. No meu caso, o nome é verse;
6- Feche essa janela, clique em Yes e altere o ícone e a imagem de fundo do formulário, caso desejar, com as imagens que importou;
7- Arraste através do toolbox as PictureBoxes que você vai utilizar. Traga ao formulário a quantidade de pictureboxes equivalente à quantidade de cartas do seu jogo (incluindo os pares);
8- Coloque as imagens dentro das pictureboxes e ajuste a opção Size mode para AutoSize;
9- Altere a tag dessas pictureboxes em ordem crescente, iniciando do zero (0).
*Os pares de imagens devem ter o mesmo índice de tag do seu respectivo par;
10- Arraste do toolbox uma label para registrar os movimentos dos jogadores: Essa label deve ter o texto Movimentos:, tamanho, fonte e cor de sua preferência, cor de fundo transparente e deve ter o nome de lblMove. Ao fazer tudo isso, seu formulário deve estar mais ou menos assim:
2) Programando - Início do jogo:
Dê F7 no teclado e abra o código .cs do seu programa.
Dentro da classe do formulário crie quatro variáveis do tipo inteiro: Uma vai registrar os movimentos do jogo, a outra vai gerenciar a quantidade de cliques feitos nas cartas, a terceira vai armazenar quantos pares foram encontrados para checar se o jogo acabou e a última vai guardar a tag das pictureboxes para que possamos checar os pares das cartas e para gerenciar o resgate de imagens dessas pictureboxes.
Crie também duas listas do tipo inteiro: A primeira vai guardar as coordenadas das cartas no formulário através do eixo X, e a outra as coordenadas no eixo Y, para que mais tarde possamos sortear valores aleatórios nessas posições para embaralhar as cartas.
Também crie um vetor do tipo Image para que possamos guardar as imagens das pictureboxes antes delas serem viradas para baixo, assim poderemos resgatar essa imagem quando clicarmos na picturebox de referência. O tamanho deste vetor vai depender da quantidade de cartas que você quer colocar no jogo (desconsiderando os pares).
Agora defina um método privado sem retorno com o nome de InitializeVerse para que possamos virar as cartas para baixo assim que o jogo iniciar e para guardar suas posições dentro das listas.
Dentro do método, inicialize uma varredura nos controles do jogo, especificando o tipo PictureBox (pois queremos apenas acessar os campos dessa classe no formulário) através do laço foreach.
Faça uma conversão de String Format para inteiro na tag da picturebox que está sendo referenciada na variável do laço, atribuindo essa conversão para a variável de indexação de tags.
Coloque dentro do vetor de imagens na posição de indexação da tag, a propriedade da imagem da picturebox referenciada.
Agora faremos com que o verso da imagem fique à mostra: Atribua à propriedade de imagem da picturebox o caminho da imagem importada: Properties.Resourcers.verse
Deixe verdadeira a propriedade Enable da picturebox, e faça duas condições para checar se as coordenadas X e Y daquela pciturebox referenciada ainda não existem nas listas de coordenadas. Caso não existir, adicione essas posições em suas respectivas listas.
Fora do laço, chame um método que iremos criar com o nome de RandomPositions e chame este método que criamos agora dentro da inicialização do formulário:
Código:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace JogoDaMemoria
{
public partial class Form1 : Form
{
int movements, clicks, foundCards, tagIndex;
List xLocations = new List();
List yLocations = new List();
Image[] images = new Image[9];
public Form1()
{
InitializeComponent();
InitializeVerse();
}
private void InitializeVerse() {
foreach (var pb in Controls.OfType<PictureBox>()) {
tagIndex = int.Parse(String.Format("{0}", pb.Tag));
images[tagIndex] = pb.Image;
pb.Image = Properties.Resources.verse;
pb.Enabled = true;
if (!xLocations.Contains(pb.Location.X)) {xLocations.Add(pb.Location.X);}
if (!yLocations.Contains(pb.Location.Y)) {yLocations.Add(pb.Location.Y);}
}
RandomPositions();
}
}
}
3) Randomizado posições:
Crie o método com o nome que chamamos acima, sem retorno e privado.
Novamente adicione um laço foreach a realizar uma varredura pelos controles do tipo PictureBox e dentro do laço instancie a classe Random.
Crie duas variáveis que irão armazenar o sorteio de um elemento das listas de posições, obviamente que este sorteio deve ocorrer utilizando o método Next da instância de Random, e dentro do índice das listas.
Crie uma nova lista dentro da classe do formulário, essa lista será do tipo string.
Faça uma condição que verifique se os números sorteados em X e em Y (concatenados e convertidos para string) existem nessa lista que criamos.
Caso essas coordenadas já tenham sido sorteadas, utilize o comando goto e o leve para uma etiqueta com o nome de Repeat. Coloque a etiqueta nos comandos de sorteio, para que assim sejam sorteadas novas coordenadas para a picturebox referenciada.
Criando um else nessa condição, significando que essas coordenadas não existem na lista, então coloque a picturebox nessa posição através da propriedade Location e se utilizando da estrutura Point, passando como argumentos as duas coordenadas que receberam os valores sorteados. Após fazer isso, adicione à lista esses valores para que eles não se repitam.
Código:
private void RandomPositions() {
foreach (var pb in Controls.OfType<PictureBox>()) {
Random rnd = new Random();
Repeat:
var X = xLocations[rnd.Next(0, xLocations.Count)];
var Y = yLocations[rnd.Next(0, yLocations.Count)];
if (locationsRegister.Contains(X.ToString() + Y.ToString())) { goto Repeat; }
else
{
pb.Location = new Point(X, Y);
locationsRegister.Add(X.ToString() + Y.ToString());
}
}
}
4) Clicando e revelando imagens:
Vamos criar agora outro método privado, sem retorno com o nome de ImagesReference_Click contendo como parâmetros o object sender e EventsArgs e, para que possamos criar o evento de clique nas pictureboxes.
Para funcionar corretamente, colocamos a referência da picturebox clicada dentro de uma variável do tipo PictureBox (não esquecendo de realizar o casting para este objeto) e logo em seguida fazemos um incremento na variável clicks.
Vamos resgatar a imagem dentro da picturebox através do vetor de imagens: Atribua à propriedade Image do objeto referenciado a imagem no vetor images de acordo com a tag daquela picturebox. Antes disso porém, converta novamente em String.Formart para inteiros a tag da picturebox e armazene na variável TagIndex.
Não se esqueça de dar um refresh nesta picturebox, para que atualize corretamente o conteúdo de sua imagem e de deixar como false sua propriedade Enable.
Volte ao design do formulário, selecione todas suas pictureboxes, vá para a aba Events e na opção Click referencie o método ImagesReference_Click que acabamos de criar.
Teste o debug do formulário e vá clicando sobre as pictureboxes: Perceba que todas elas resgatam as imagens que tinham armazenadas anteriormente e que também estão distribuídas de forma aleatória, cada vez que você roda novamente a depuração.
Código:
private void ImagesRerefence_Click(object sender, EventArgs e) {
PictureBox pic = (PictureBox)sender;
clicks++;
tagIndex = int.Parse(String.Format("{0}", pic.Tag));
pic.Image = images[tagIndex];
pic.Refresh();
pic.Enabled = false;
}
5) Verificando pares:
Agora precisamos checar se o jogador acertou ou não as cartas reveladas.
Vamos gerenciar primeiramente quais cartas de fato que o jogador revelou e para isso vamos utilizar as tags dessas pictureboxes, ou seja, se as tags das mesmas forem iguais, o jogador acertou o par, caso contrário, as cartas serão viradas pro verso novamente.
Criaremos na classe do formulário um vetor com tamanho 2(dois) do tipo inteiro para guardar em uma de suas posições a tag das duas cartas reveladas no momento.
Crie uma condição que verifique se a variável clicks é igual a 1 (um), caso sim, armazene na posição zero deste vetor a tag da picturebox clicada, caso não, armazene na posição 1(um) deste vetor esta tag.
Também incremente a variável movimentos, atualize a sua label de registro e através de uma variável local do tipo bool checaremos se os pares são iguais, ou seja, se o conteúdo do vetor "tags'' na posição 0 é igual ao conteúdo deste vetor na posição 1.
Após isso, chame um método TurnCards e passe como argumento a variável boolena.
Código:
private void ImagesRerefence_Click(object sender, EventArgs e) {
PictureBox pic = (PictureBox)sender;
clicks++;
tagIndex = int.Parse(String.Format("{0}", pic.Tag));
pic.Image = images[tagIndex];
pic.Refresh();
pic.Enabled = true;
if (clicks == 1) { tags[0] = tagIndex; } else {
tags[1] = tagIndex;
movements++;
lblMove.Text = "Movimentos " + movements.ToString();
bool check = tags[0] == tags[1];
TurnCards(check);
}
}
6) Desvirando cartas:
Nesse momento precisamos verificar através do retorno booleano se o jogador acertou as cartas viradas ou não.
Para isso, crie um novo método privado, sem retorno , com o nome de TurnCards e que aceita como parâmetro um booleano.
Para saber de fato quais eram as pictureboxes reveladas, precisamos novamente fazer uma varredura com o laço foreach nos controles das pictureboxes.
Faremos também uma verificação se a tag daquelas pictureboxes referenciadas for igual ao conteúdo do vetor de tags, tanto na posição 0(zero) quanto na posição 1(um). Caso sim, então saberemos quais cartas estão reveladas.
Tendo essa confirmação, podemos enfim checar se a variável booleana retornou true (acertou os pares) ou false (não acertou).
Se acertou os pares, a carta continua com a imagem revelada, sem alterar para seu verso e a variável foundCards recebe um incremento.
Caso não tenha acertado os pares, a variável mostra seu verso novamente e volta como true sua propriedade Enable, e após tudo isso fora do laço, a variável clicks volta a ser zero, chamando também um método que verifica se o jogo acabou.
Note que ao depurar (comentando a chamada do método), quando os pares são errados as cartas se revelam muito rapidamente e não há tempo suficiente para visualizar as mesmas.
Acabe com este problema referenciando a biblioteca System.Threading no topo do formulário. Volte ao método TurnCards e antes do laço chame o procedimento estático Sleep da classe Thread e passe como argumento o tempo de espera para as cartas virarem. Eu deixei 500, mas isso vai de sua opinião, quanto maior o número, mais tempo de espera.
Código:
private void TurnCards(bool check) {
Thread.Sleep(500);
foreach (var pb in Controls.OfType<PictureBox>()) {
if (int.Parse(String.Format("{0}", pb.Tag)) == tags[0] ||
int.Parse(String.Format("{0}", pb.Tag)) == tags[1]){
if (check) { foundCards++; } else {
pb.Image = Properties.Resources.verse;
pb.Enabled = true;
}
}
}
clicks = 0;
EndGame();
}
7) Jogo terminado:
Vamos criar o último método do programa que irá checar se o jogo já se encerrou através da variável foundCards.
Crie este método como privado e sem retorno com o nome que chamamos: EndGame
Faça uma verificação que valide se foundCards é igual ao tamanho do vetor de imagens (que guarda as cartas) vezes dois (para considerar os pares encontrados).
Dentro dessa validação exiba uma mensagem ao usuário através do MessageBox o congratulando por ter terminado o jogo e o pergunte se deseja jogar novamente, utilizando a classe MessageBoxButtons com a propriedade YesNo.
Caso o jogador escolha sim, zere as variáveis clicks, movements e foundCards, limpe a lista de registros de coordenadas e chame novamente o método InitializeVerse, para que o jogo recomece.
Caso o jogador não queira mais jogar, chame o método Exit através da classe Application para fechar o formulário automaticamente.
Código:
private void EndGame(){
if (foundCards == (images.Length*2)) {
MessageBox.Show("Parabéns, você terminou o jogo!", "Parabéns");
DialogResult msg = MessageBox.Show("Deseja jogar novamente?!", "Parabéns", MessageBoxButtons.YesNo);
if (msg == DialogResult.Yes)
{
clicks = 0; movements = 0; foundCards = 0; locationsRegister.Clear(); lblMove.Text = "Movimentos:"; InitializeVerse();
}
else
{
Application.Exit();
}
}
}
E o jogo está pronto para ser jogado!