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! |