×
Namespaces

Variants
Actions

Jogo Snake no Windows Phone 7

From Nokia Developer Wiki
Jump to: navigation, search

Este artigo explica como criar um jogo simples (Snake) no Windows Phone 7.

WP Metro Icon Joystick.png
WP Metro Icon XNA.png
SignpostIcon WP7 70px.png
Article Metadata

Exemplo de código
Código fonte: Media:PTMSnakeIt.zip

Testado com
SDK: Windows Phone 8.0 SDK
Aparelho(s): Nokia Lumia 800

Compatibilidade
Plataforma(s):
Windows Phone 7.5

Artigo
Tradução:
Por ronaldo.duarte
Última alteração feita por hamishwillee em 17 Feb 2014

Contents

Introdução

Neste artigo, mostraremos como criar um jogo simples (Snake) no Windows Phone. Você pode mover a cobra em diferentes direções com gestos.

Jogo Snake Jogo Snake Jogo Snake

Criação de projeto de jogo no Visual Studio

Para criar um novo jogo, abra o Visual Studio, e, após selecionar a opção File->New Project, escolha o template Windows Phone Game (4.0). O nome do projeto neste exemplo é SnakeIt.

Projeto jogo Windows Phone

A linguagem escolhida para este jogo é C#.

Gráficos

Inicialmente é necessário criar os bitmaps para a comida e a cobra. Serão usados bitmaps de 25x25 pixels.

Comida

No Solution Explorer, clique com o botão direito sobre o projeto SnakeItContent e selecione Add->New Item.

Escolha "Bitmap File" e nomeie o arquivo como FoodTile.bmp.

Criação de bitmap

Desenhe um retângulo verde de 25x25 pixels como bitmap da comida. Lembre-se de gravar a figura.

Retângulo verde como comida

Cobra

Faça o mesmo (retângulo de 25x25 pixels) na cor vermelha. Nomeie o arquivo como SnakeTile.bmp.

Fonte

É necessário criar uma fonte para as mensagens exibidas ao jogador. No Solution Explorer, clique com o botão direito sobre o projeto SnakeContent e selecione Add->New Item.

Selecione Sprite Font e nomeio o arquivo como Segoe20.spritefont. . Fonte para o jogo

Abra o arquivo da fonte e altere o tamanho da fonte para 20:

<Size>20</Size>

Lembre-se de gravar o arquivo.

Criando a classe Food (comida)

Clique com o botão direito sobre o projeto SnakeIt e selecione Add->New Item. Crie uma classe chamada Food.

Criação da classe Food

A classe Food armazena a posição e a textura da comida. A largura e a altura são usados para detecção de colisão.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
 
namespace SnakeIt
{
class Food
{
// Textura representando a comida
public Texture2D Texture;
 
// Posição da comida relativa ao canto superior esquerdo da tela
public Vector2 Position;
 
// Largura da textura da comida
public int Width
{
get { return Texture.Width; }
}
 
// Altura da textura da comida
public int Height
{
get { return Texture.Height; }
}
 
// Inicializa textura e posição da comida.
public void Initialize(Texture2D texture, Vector2 position)
{
Texture = texture;
Position = position;
}
 
// Desenha a comida na tela
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
}
}
}

Criando a classe Snake (cobra)

Crie a classe Snake com o mesmo procedimento usado para criar a classe Food.

Usings

A classe utiliza o framework XNA e a classe Lista do namespace para armazenar os bitmaps da cobra.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

Propriedades

Algumas propriedades são úteis para gerenciar o movimento da cobra. O bitmap representando a cobra será carregado em um Texture e a posição inicial é definida. Todos os bitmaps da cobra serão armazenados na lista . A direção da cobra será gerenciada pela class com gestos.

// Textura
public Texture2D Texture;
 
// Posição
public Vector2 Position;
 
// Bitmaps
public List<Tile> snakeTiles;
 
// Velocidade e incremento da velocidade
public float speed;
const float speedIncrement = 0.2f;
 
// Direção // 1==esquerda, 2==cima, 3==direita, 4==baixo
public int direction;
 
// Indicação de criação de novo bitmap
public bool newTile;

Inicialização

Game1 inicializará o objeto Snake. A textura da cobra será carregada e a posição inicial determinada. As propriedades do objeto são ajustadas em seus valores padrão quand o jogo inicia e posteriormente quando o jogador inicia um novo jogo. A cobra move-se por padrão 5 pixels à esquerda e a cabeça da cobra será criada.

// Inicialização
public void Initialize(Texture2D texture, Vector2 position)
{
Position = position;
Texture = texture;
Reset();
}
 
// Propriedades em valores padrão (novo jogo)
public void Reset()
{
speed = 5f;
direction = 1;
Tile tile = new Tile(Texture, Position);
snakeTiles = new List<Tile>();
snakeTiles.Add(tile);
newTile = false;
}

Atualização

O jogo atualizará a cobra a cada frame, pelo método Update. Inicialmente a cabeça da cobra é obtida (é o primeiro bitmap da lista snakeTiles). Caso a cobra tenha comido, um novo bitmap deve ser adicionado para torná-la maior, na posição atual da cabeça headPosition, e inserido na lista snakeTiles. Em seguida apenas a cabeça da cobra é movida, tornando-a maior. Caso um novo bitmap não tenha sido adicionado, movemos o último bitmap para a cabeça da cobra e atualizamos a sua posição de acordo com direção e velocidade.

public void Update() 
{
// cabeça da cobra
Vector2 headPosition = snakeTiles[0].Position;
 
// novo bitmap?
if (newTile)
{
// cria um novo bitmap
Tile tile = new Tile(Texture, headPosition);
// insere novo bitmap na segunda posição da lista, na mesma posição da cabeça na tela
snakeTiles.Insert(1, tile);
}
// move o último bitmap para a posição da cabeça
else if (snakeTiles.Count > 1)
{
Tile last = snakeTiles[snakeTiles.Count - 1];
last.Position = headPosition;
snakeTiles.RemoveAt(snakeTiles.Count-1);
snakeTiles.Insert(0, last);
}
 
// aumenta a velocidade se a cobra comeu
if (newTile)
{
speed += speedIncrement;
newTile = false;
}
 
// move a cabeça da cobra para sua nova posição
switch (direction)
{
case 1: snakeTiles[0].Position.X -= speed; break;
case 2: snakeTiles[0].Position.Y -= speed; break;
case 3: snakeTiles[0].Position.X += speed; break;
case 4: snakeTiles[0].Position.Y += speed; break;
}
}

Desenho

Cada bitmap da cobra é desenhado no método Draw. Este método é chamado pela classe Game1.

public void Draw(SpriteBatch spriteBatch)
{
for (int i = 0; i < snakeTiles.Count; i++)
{
spriteBatch.Draw(snakeTiles[i].Texture, snakeTiles[i].Position, null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
}
}

Classe Tile (classe interna à classe Snake)

Uma classe interna à classe Snake é aplicada para gerenciar os bitmaps que formam a cobra.

class Tile
{
public Vector2 Position;
public Texture2D Texture;
 
public Tile(Texture2D texture, Vector2 position) {
Position = position;
Texture = texture;
}
 
// Largura do bitmap
public int Width
{
get { return Texture.Width; }
}
 
// Altura do bitmap
public int Height
{
get { return Texture.Height; }
}
}

Classe Game1

A classe Game1 é a principal classe em jogos XNA. Aqui são inicializados e carregado o conteúdo do jogo. Gestos e detecção de colisão são gerenciados no método Update. O método Draw verifica o modo do jogo (Mode) e desenha o menu, o jogo, ou informação de quando o jogo termina.

Usings

XNA é usado e o namespace Microsoft.Xna.Framework.Input.Touch para detectar gestos.

using System;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

Propriedades

graphics é usado para obter informação do display. spriteBatch é usado para desenhar no display. Modos distintos de jogo são usados para controlar o método Draw(), para desenhar o menu, o jogo ou o término. O modo atual do jogo é armazenado na propriedade Mode, e todas os possíveis modos na enumeração GameMode.

// informação sobre o display
private GraphicsDeviceManager graphics;
 
// desenha objetos do jogo
private SpriteBatch spriteBatch;
 
// cobra
private Snake snake;
 
// comida
private Food food;
 
// fonte
private SpriteFont segoe20;
 
// posição do placar no display
private Vector2 scorePosition;
 
// placar
private float score;
private const float FOOD_POINTS = 20;
 
// modo atual do jogo
private GameMode Mode;
 
// modos possíveis do jogo (Menu, Running, Complete)
enum GameMode {Menu, Running, Complete}

Construtor

Inicialmente o diretório raiz dos recursos do jogo (gráficos e fonte) é especificado através da propriedade Content da classe GraphicsDeviceManage. A taxa de frames é configurada como o padrão do Windows Phone de 30 fps. É necessário ativar os gestos "tap" e "flick". Os modos do jogo são alterados com gestos "tap" e o movimento da cobra é controlado pelo gesto "flick".

public Game1()
{
// Graphics Device manager
graphics = new GraphicsDeviceManager(this);
 
// Content manager root directory
Content.RootDirectory = "Content";
 
// Frame rate is 30 fps by default for Windows Phone.
TargetElapsedTime = TimeSpan.FromTicks(333333);
 
// Extend battery life under lock.
InactiveSleepTime = TimeSpan.FromSeconds(1);
 
// Detech Tap and Flick gestures
TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Flick;
}

Inicialização do jogo

O método Initialize permite efetuar inicialização do jogo antes que ele comece a executar. Aqui ajustamos o modo e criamos os objetos Snake e Food.

protected override void Initialize()
{
// Ajusta o modo para "menu", menu será desenhado no método Draw()
Mode = GameMode.Menu;
// Cria Snake e Food
snake = new Snake();
food = new Food();
// Placar inicializado em zero
score = 0f;
 
base.Initialize();
}

Carregando conteúdo

LoadContent é chamada uma vez por jogo e é o local para carregar todo o seu conteúdo. Inicialmente nos conectamos ao dispositivo gráfico com SpriteBatch para que possamos desenhar objetos no display. Depois carregamos e criamos texturas para as classes Snake e Food. Finalmente criamos a fonte e determinamos a posição do texto do placar.

protected override void LoadContent()
{
// usado para desenhar texturas
spriteBatch = new SpriteBatch(GraphicsDevice);
 
// cria a textura para a cobra e a coloca no meio do display
Vector2 snakePosition = new Vector2(
GraphicsDevice.Viewport.TitleSafeArea.X + GraphicsDevice.Viewport.TitleSafeArea.Width / 2,
GraphicsDevice.Viewport.TitleSafeArea.Y + GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
snake.Initialize(Content.Load<Texture2D>("SnakeTile"), snakePosition);
 
// cria a texture da comida e a coloca em uma posição aleatória no display
Vector2 foodPosition = RandPosition();
food.Initialize(Content.Load<Texture2D>("FoodTile"), foodPosition);
 
// cria uma fonte
segoe20 = this.Content.Load<SpriteFont>("Segoe20");
 
// posiciona o placar no canto superior esquerdo do display
scorePosition = new Vector2(20, 20);
}

Atualizando o jogo

O método Update executa a lógica do jogo, como atualização do mundo, verificação de colisões, aquisição de entradas e execução de áudio. Aqui atualizamos a posição e velocidade da cobra e detectamos colisões, caso o jogo esteja rodando. Gestos são sempre avaliados.

protected override void Update(GameTime gameTime)
{
// permite sair do jogo
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
 
if (Mode == GameMode.Running)
{
snake.Update();
CheckCollision();
}
 
CheckGestures();
 
base.Update(gameTime);
}

Verificando colisões

As posições da cabeça da cobra e da comida são utilizadas para construir retângulos com a classe Rectangle. O método Rectangle.Intersects verifica se há sobreposição da cobra e da comida. Caso exista, o placar é atualizado e a comida é movida para uma nova posição aleatória, além de indicar que a cobra deve aumentar de tamanho. O jogo termina se a cabeça da cobra é movida fora dos limites do display.

private void CheckCollision()
{
// verificação de sobreposição da cobra e da comida
int snakeHeadX = (int)snake.snakeTiles[0].Position.X;
int snakeHeadY = (int)snake.snakeTiles[0].Position.Y;
Rectangle rectangle1 = new Rectangle(snakeHeadX, snakeHeadY, snake.snakeTiles[0].Width, snake.snakeTiles[0].Height);
Rectangle rectangle2 = new Rectangle((int)food.Position.X, (int)food.Position.Y, food.Width, food.Height);
 
if (rectangle1.Intersects(rectangle2))
{
// incrementa placar
score += FOOD_POINTS;
// muda a posição da comida
food.Position = RandPosition();
// aumenta o tamanho da cobra
snake.newTile = true;
}
 
// limites do display
if (snakeHeadX < 0 ||
snakeHeadY < 0 ||
snakeHeadX + snake.snakeTiles[0].Width > GraphicsDevice.Viewport.Width ||
snakeHeadY + snake.snakeTiles[0].Height > GraphicsDevice.Viewport.Height)
{
Mode = GameMode.Complete;
}
}

O método RandPosition é usado para mover a comida para uma nova posição aleatória no display:

private Vector2 RandPosition()
{
// posição aleatória
Random random = new Random();
Vector2 position = new Vector2(
GraphicsDevice.Viewport.TitleSafeArea.X + random.Next(GraphicsDevice.Viewport.TitleSafeArea.Width - 45) + 20,
GraphicsDevice.Viewport.TitleSafeArea.Y + random.Next(GraphicsDevice.Viewport.TitleSafeArea.Height - 45) + 20);
return position;
}

Verificando gestos

Gestos são verificados a cada execução do método Update. Primeiro é verificado se existem gestos disponíveis. Caso afirmativo, é verificado qual gesto deve ser processado. Caso o jogador tenha executado o gesto "flick", a direção da cobra é ajustada adequadamente. O modo do jogo é alterado caso um gesto "tap" tenha sido realizado.

private void CheckGestures()
{
// há gestos disponíveis?
while (TouchPanel.IsGestureAvailable)
{
// obtém o gesto
GestureSample gesture = TouchPanel.ReadGesture();
 
// flick ou tap?
switch (gesture.GestureType)
{
case GestureType.Flick:
// get drag delta amout
Single x = gesture.Delta.X, y = gesture.Delta.Y;
 
// flick horizontal ou vertical?
if (Math.Abs(x) > Math.Abs(y))
{
// esquerda ou direita?
if (x < 0)
{
if (snake.direction == 3 && snake.snakeTiles.Count() > 1) Mode = GameMode.Complete;
else snake.direction = 1; // esquerda
}
else
{
if (snake.direction == 1 && snake.snakeTiles.Count() > 1) Mode = GameMode.Complete;
else snake.direction = 3; // direita
}
}
else
{
// acima ou abaixo?
if (y < 0)
{
if (snake.direction == 4 && snake.snakeTiles.Count() > 1) Mode = GameMode.Complete;
else snake.direction = 2; // acima
}
else
{
if (snake.direction == 2 && snake.snakeTiles.Count() > 1) Mode = GameMode.Complete;
else snake.direction = 4; // abaixo
}
}
break;
case GestureType.Tap:
if (Mode == GameMode.Menu)
{
Mode = GameMode.Running;
}
else if (Mode == GameMode.Complete)
{
snake.Reset();
score = 0f;
Mode = GameMode.Running;
}
break;
}
}
}

Desenhando no display

O método Draw é invocado quando o jogo precisa ser desenhado. Neste exemplo a cor de fundo do display é azul. O método verifica em qual modo o jogo se encontra. Quando o jogo é carregado, uma mensagem explicando como iniciar o jogo é exibida. Quando o jogo estiver rodando, a cobra, a comida e o placar serão exibidos. Quando o jogo terminar, o placar final e informações sobre como reiniciar são exibidos.

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
 
// começa a desenhar
spriteBatch.Begin();
 
if (Mode == GameMode.Menu)
{
string GameStart = "FreeSnake\nTap the Screen to Start";
Vector2 stringSize = segoe20.MeasureString(GameStart);
spriteBatch.DrawString(segoe20,
GameStart,
new Vector2((GraphicsDevice.Viewport.Width - stringSize.X) / 2, (GraphicsDevice.Viewport.Height - stringSize.Y) / 2), Color.White);
}
else if (Mode == GameMode.Running)
{
// desenha a cobra
snake.Draw(spriteBatch);
// desenha a comida
food.Draw(spriteBatch);
// desenha o placar
spriteBatch.DrawString(segoe20, "Score:"+score, scorePosition, Color.White);
}
else if (Mode == GameMode.Complete)
{
snake.Draw(spriteBatch);
string GameOver = "Game Over\nScore : " + score +"\nTap the Screen to Restart";
Vector2 stringSize = segoe20.MeasureString(GameOver);
spriteBatch.DrawString(segoe20,
GameOver,
new Vector2((GraphicsDevice.Viewport.Width - stringSize.X) / 2, (GraphicsDevice.Viewport.Height - stringSize.Y) / 2), Color.White);
}
// termina de desenhar
spriteBatch.End();
 
base.Draw(gameTime);
}

Sumário

Este exemplo mostra como criar um jogo simples para Windows Phone usando gestos.

Você pode obter o código fonte aqui: File:PTMSnakeIt.zip.

This page was last modified on 17 February 2014, at 00:23.
86 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×