Introdução
Este artigo vai tentar cobrir a criação de um ambiente na mesma tela do Monitor, para 2 players.
As telas que serão preparadas, estarão no formato horizontal; as 2 telas estarão no formato retangular, sendo o “pedaço da tela” de cima sendo do player 1, e o “pedaço da tela” de baixo sendo do player 2.
O objeto Viewport
Para um ambiente 2D puro e aquele em que os objetos são renderizados na tela através do objeto SpriteBatch – as principais propriedades de um objeto ViewPort são:
- Viewport.X
- Viewport.Y
- Viewport.Width
- Viewport.Height
Viewport.Width e Viewport.Height
Por padrão(inicialmente), qualquer jogo, criado no XNA, tem objetos sendo renderizados em uma janela(já provida pelo XNA FrameWork) com dimensões 800x600(800 pixels de largura(width) e 600 pixels de altura(height)).
O local em que os objetos são renderizados na tela, não está restrito ao tamanho da janela(Game Window), mas quem determina esse comportamento, é o objeto Viewport atribuído ao GraphicsDevice do jogo.
As propriedades Viewport.Width e Viewport.Height determinam o local aonde os objetos são renderizados na tela, por meio das dimensões de altura e largura da superfície em que são esperados os objetos a serem renderizados.
Viewport.X e Viewport.Y
Os objetos a serem renderizados na tela, são posicionados na tela, de acordo com a origem. Viewport.X e Viewport.Y determinam essa origem e os objetos quando renderizados na tela, são posicionados em relação a origem determinada.
Origem é o local em que os eixos se cruzam.
Por padrão, Viewport.X e Viewport.Y têm o valor 0(zero). Então temos o seguinte ambiente:
Foto 1 – Demonstração das dimensões e a origem da superfície em que objetos são, por padrão, renderizados
Ambiente para 2 players
Ao preparar um ambiente tanto para 2 players ou até mesmo, mais de 2 divisões na tela, tenha em mente que você precisará modificar os valores do Viewport atribuído ao GraphicsDevice do jogo.
O Viewport de um GraphicsDevice é acessado através de propriedade. Viewport é uma estrutura(struct). Tenha em mente que estruturas, quando acessadas através de propriedades, não podem ter os valores de seus campos modificados diretamente. Então, para preparar um ambiente para X players, será necessário o uso de um Viewport auxiliar. A preparação do ambiente será feita nesse Viewport auxiliar e após modificar os campos nesse Viewport auxiliar, daí então, será atribuído esse Viewport auxiliar ao Viewport do GraphicsDevice.
Inicialmente declara-se o viewPort auxiliar:
...
///
/// objeto auxiliador, para definir as dimensões das 2 telas dos players
///
Viewport viewPort;
...
Especificamente para esta demonstração, será necessário guardar a altura inicial do Viewport. Confira os comentários no sumário:
///
/// Guarda a altura inicial da tela. Objeto iniciado antes das
/// divisões das telas dos players; no metodo Initialize.
/// Será util para "resetar" a altura do "ViewPort";
/// preservando a altura inicial(antes das divisões) para a proxima
/// passagem no metodo Draw
///
int heightViewPort;
O uso desses objetos auxiliares, vai depender diretamente da quantidade de “sub-telas” que você quer obter em seu jogo. Lembre-se que o que está sendo demonstrado, é uma tela com “2 sub-telas” no formato horizontal; um ambiente para 2 players.
No método Initialize, temos a inicialização do Viewport auxiliar com o Viewport atual do jogo e também será guardado a altura original do Viewport:
protected override void Initialize()
{
...
this.viewPort = this.GraphicsDevice.Viewport;
this.heightViewPort = this.viewPort.Height;
}
A preparação das telas dos players, acontece de fato, no método Draw:
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
//configurando as dimensões da tela(o ViewPort) para o player 1
//Neste momento, a tela do player 1, fica com um formato retangular
this.viewPort.Height = viewPort.Height / 2;
this.GraphicsDevice.Viewport = viewPort;
Neste momento, a superfície que recebe os objetos a serem renderizados na tela, está com o seu Viewport com origem (0, 0) em relação a janela(Game Window), e com uma área retangular de 800x300. A foto abaixo demonstra isso:
//Desenhando na tela o player 1 e tudo relativo a tela dele
this.spriteBatch.Begin();
this.spriteBatch.Draw(this.player1, position1, Color.Red);
this.spriteBatch.End();
//configurando as dimensões da tela(o ViewPort) para o player 2
this.viewPort.Y = viewPort.Height;
this.GraphicsDevice.Viewport = viewPort;
Agora, neste momento, a superfície que recebe os objetos a serem renderizado na tela, está ocupando uma área retangular, com origem (0, 300) em relação a janela(Game Window), e com uma área retangular de 800x300. A foto abaixo demonstra isso:
//Desenhando na tela o player 2 e tudo relativo a tela dele
this.spriteBatch.Begin();
this.spriteBatch.Draw(this.player2, position2, Color.CornflowerBlue);
this.spriteBatch.End();
Agora, será necessário preparar o ambiente para a próxima passagem no método Draw. O melhor a fazer é reinicializar as dimensões da superfície. O código abaixo faz isso:
//Reinicializando as dimensões da tela, para a configuração inicial(normal)
this.viewPort.Y = 0;
this.viewPort.Height = this.heightViewPort;
this.GraphicsDevice.Viewport = viewPort;
base.Draw(gameTime);
}
O código completo:
///
/// Neste "Game", a região superior da tela, pertence ao player 1. Já a parte
/// inferior(a parte de baixo), pertence ao player 2.
///
/// Para movimentar o player 1 utilize as teclas: W, A, S e D
/// Para movimentar o player 2 utilize as SETAS
/// public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
///
/// textura que vai ficar na parte de cima da tela
///
Texture2D player1;
///
/// textura que vai ficar na parte de baixo da tela
///
Texture2D player2;
///
/// objeto auxiliador, para definir as dimensões das 2 telas dos players
///
Viewport viewPort;
///
/// Guarda a altura inicial da tela. Objeto iniciado antes das
/// divisões das telas dos players; no metodo Initialize.
/// Será util para "resetar" a altura do "ViewPort";
/// preservando a altura inicial(antes das divisões) para a proxima
/// passagem no metodo Draw
///
int heightViewPort;
///
/// posição do player 1
///
Vector2 position1;
///
/// posicao do player 2
///
Vector2 position2;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
this.position1 = new Vector2(0, 0);
this.position2 = new Vector2(0, 0);
base.Initialize();
this.viewPort = this.GraphicsDevice.Viewport;
this.heightViewPort = this.viewPort.Height;
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
this.player1 = this.Content.Load<Texture2D>("player1");
this.player2 = this.Content.Load<Texture2D>("player2");
}
protected override void Update(GameTime gameTime)
{
KeyboardState keyboard = Keyboard.GetState();
#region Controle do Player 1
if (keyboard.IsKeyDown(Keys.W))
{
position1.Y -= 5;
}
if (keyboard.IsKeyDown(Keys.S))
{
position1.Y += 5;
}
if (keyboard.IsKeyDown(Keys.A))
{
position1.X -= 5;
}
if (keyboard.IsKeyDown(Keys.D))
{
position1.X += 5;
}
#endregion
#region Controle do Player 2
if (keyboard.IsKeyDown(Keys.Up))
{
position2.Y -= 5;
}
if (keyboard.IsKeyDown(Keys.Down))
{
position2.Y += 5;
}
if (keyboard.IsKeyDown(Keys.Left))
{
position2.X -= 5;
}
if (keyboard.IsKeyDown(Keys.Right))
{
position2.X += 5;
}
#endregion
//Posições do player 1 e 2, sendo expostas na barra de titulo da janela
this.Window.Title = "Player 1: " +
this.position1 + " - Player 2: " + this.position2;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
//configurando as dimensões da tela(o ViewPort) para o player 1
//Neste momento, a tela do player 1, fica com um formato retangular
this.viewPort.Height = viewPort.Height / 2;
this.GraphicsDevice.Viewport = viewPort;
//Desenhando na tela o player 1 e tudo relativo a tela dele
this.spriteBatch.Begin();
this.spriteBatch.Draw(this.player1, position1, Color.Red);
this.spriteBatch.End();
//configurando as dimensões da tela(o ViewPort) para o player 2
this.viewPort.Y = viewPort.Height;
this.GraphicsDevice.Viewport = viewPort;
//Desenhando na tela o player 2 e tudo relativo a tela dele
this.spriteBatch.Begin();
this.spriteBatch.Draw(this.player2, position2, Color.CornflowerBlue);
this.spriteBatch.End();
//Reinicializando as dimensões da tela, para a configuração inicial(normal)
this.viewPort.Y = 0;
this.viewPort.Height = this.heightViewPort;
this.GraphicsDevice.Viewport = viewPort;
base.Draw(gameTime);
}
}
Atenção!!!
Observe que foram necessários 2 blocos(Begin/End do SpriteBatch) foram utilizados para renderização das texturas.
Caso o ambiente para o player 1 e 2, fosse preparado em torno de um único bloco (Begin/End), o resultado obtido seria algo totalmente diferente que a proposta em foco.
Algumas imagens do código acima:
Na foto abaixo, observe o que acontece quando o player 1 tenta descer, e o player 2 tenta subir. O objetivo era esse!!!
O resultado obtido ao final, me fez lembrar do modo para 2 players no jogo Sonic 2. E para você? Fez recordar algum jogo que proporcionou-lhe momentos bons e únicos?!?!
Todas as imagens acima, foram criadas e/ou editadas com a ferramenta Microsoft Expression Design 2 Beta.
Dúvidas, Críticas e Sugestões são bem vindas.
Obrigado!
|