Olá pessoal, tudo certo?
Aprendi a programar muito cedo. Comecei a brincar com BASIC antes de completar 10 anos de idade. C, Pascal e Assembly vieram logo em seguida.
Colecionava revistas, como a saudosa Micro-Sistemas, onde encontrava mensalmente códigos que transcrevia dezenas de vezes.
Aprendi cedo a gostar de algoritmos gráficos e a detestar as diversificadas interfaces de programação das placas de vídeo da época.
Também dessa época é a minha aversão e paixão por ponteiros, minha predileção por linguagens de nível mais baixo, por registradores, contadores e flags.
Minha motivação? Queria escrever meus próprios jogos.
Infelizmente, nunca havia encontrado tempo ou disposição para fazer algo interessante com as tecnologias que conhecia. Tudo parecia difícil demais, complexo demais ou trabalhoso demais.
Felizmente, para mudar essa história, surgiu o XNA. Essa nova série trata dessa tecnologia.
O que é XNA?
XNA é um framework para desenvolvimento de jogos desenvolvido pela Microsoft que, supostamente, torna a criação de jogos(para PC, Xbox 360 e Windows Phone 7) muito mais fácil (ou menos difícil).
Na versão 4.0, suporta a criação de jogos para PC, Xbox 360 e Windows Phone 7.
Requisitos de sistema
Nessa série usaremos o XNA Game Studio 4.0, que é uma extensão do Visual Studio para desenvolvimento de games com XNA.
XNA Game Studio 4.0 utiliza o XNA Framework 4.0. É suportado por todas as versões do Visual Studio 2010 com suporte a C# (Standard, inclusive) ou ainda pelo Visual C# 2010 Express Edition.
XNA 4.0 permite que desenvolvamos games para Windows Vista, Windows 7, Xbox 360 e Windows Phone 7.
Para executar jogos feitos em XNA no Windows, a máquina deverá estar equipada com uma placa de vídeo que suporte WDDM 1.1 e DirectX 10 (ou posterior).
XNA Game Studio 4.0 está disponível (sem custos) aqui. A instalação é fácil.
Criando o primeiro aplicativo XNA
Tendo instalado o XNA Game Studio 4.0, podemos iniciar o desenvolvimento da primeira aplicação XNA. Isso é muito simples:
- No Visual Studio, selecione File –> New –> Project;
- Selecione “Installed Templates”;
- Selecione Visual C# –> XNA Game Studio 4.0;
- Na lista de templates (painel da direita), selecione “Windows Game (4.0)”;
- Informe um nome para o projeto (sugiro FirstXnaApp);
- Informe a pasta onde deseja salvar seu projeto (a janela deverá estar semelhante a figura abaixo);
- Clique em OK.
Executando o aplicativo, teremos o seguinte resultado:
Nada muito animador, mas, acredite, é o “esqueto” para um game.
Perceba que se seu computador não possuir recursos de vídeo suficientes, a aplicação indicará falha durante a carga.
Vendo como as coisas funcionam
O template padrão do Visual Studio carrega uma boa quantidade de código. Comecemos pelo arquivo Program.cs:
using System; namespace FirstXnaApp { #if WINDOWS || XBOX static class Program { static void Main(string[] args) { using (Game1 game = new Game1()) { game.Run(); } } } #endif }
O código de inicialização é muito simples. Na prática, uma instância da classe Game1 é instanciada e tem seu método Run executado.
Eis a classe Game1 (arquivo Game1.cs). Removi os comentários para reduzir tamanho do post.
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; namespace FirstXnaApp { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); base.Draw(gameTime); } } }
Neste código, podemos perceber a presença de dois atributos, um construtor e mais cinco métodos.
O objeto GraphicsDeviceManager
Como pode ser percebido em nossa listagem anterior, um objeto do tipo GameDeviceManager é instanciado logo na inicialização do Game. Este objeto é muito importante pois ele provê ao desenvolvedor uma forma padrão de acessar os recursos gráficos de um PC, de um Xbox 360 ou do Windows Phone 7.
GraphicsDeviceManager possui uma propriedade chamada GraphicsDevice que representa o dispositivo gráfico disponível no computador.
Toda comunicação entre um programa XNA e a placa de vídeo (ou melhor, a Unidade de Processamento Gráfico [GPU]) presente no dispositivo , ocorre através desse objeto.
O objeto SpriteBatch
O segundo atributo mantido pela classe Game1 é do tipo SpriteBatch. Trata-se do objeto que usaremos para desenhar Sprites.
Um sprite é uma imagem 2D ou 3D que usamos para compor uma cena. As cenas de jogos 2D são compostas por múltiplos sprites.
A imagem acima explica bem o conceito: cada jogador é um sprite, a imagem do background é um sprite, os placares são sprites, a mensagem “Fight!” é um sprite.
Numa generalização grosseira, podemos assumir que um sprite em nossa cena como uma espécie de controle em um formulário.
Carregando objetos do Game: Método Initialize e LoadContent
O método Initialize é o “lugar certo” para iniciar variáveis e outros objetos. A lógica de execução do tipo Microsoft.Xna.Framework.Game (de onde derivamos Game1) garante que o objeto GameDeviceManager já terá sido inicializado e poderá ser utilizado na inicialização de objetos que dependam de suas configurações.
O método LoadContent é chamado após a execução do método Initialize ou em qualquer momento onde seja aconselhável recarregar objetos gráficos do game (quando ocorrer mudanças nas configurações do dispositivo gráfico, por exemplo). Nesse método deverá ocorrer a carga de imagens, modelos 3D, sons, entre outros recursos.
The Game Loop
Logo que tenha sido concluída a carga do jogo, nos métodos Initialize e LoadContent, é iniciada uma rotina identificada como Game Loop.
O conceito de Game Loop representa uma mudança substancial na forma como estamos habituados a pensar quando codificamos aplicações “normais”. Na prática, nosso código XNA nunca fica “esperando” (por baixo do capô, para toda aplicação desktop existe um App Loop também, mas esse é um tema para outro post)
Essencialmente, um Game Loop consiste de uma série de métodos que são chamados repetidamente até que o jogo encerre. No XNA, o game loop chama repetidamente apenas dois métodos: Update e Draw. Toda a lógica do game deverá ser acionada através desses métodos. Perceba:
- O método Draw é usado para (surpresa!) “desenhar coisas” . Obviamente, você pode colocar código que faz outras coisas sendo executado a partir desse método, mas… não é legal.
- O método Update é lugar para “processar o estado do game”. Na prática, podemos dizer que todo processamento que não envolva desenho deve ser chamado a partir desse método. Onde acionar cálculo de atualização da posição de objetos? No método Update! Onde checar colisões? No método Update! Onde atualizar o placar? No método Update (desenhar o placar, é no Draw)! Verificar se o jogo foi concluído? No método Update.
Certo!?
Sem registro de eventos! Não insista!
Outro aspecto provavelmente novo para quem começa a desenvolver games é a inexistência de registro de eventos.
Geralmente, aplicações desktop são programadas apenas para responder eventos do usuário. Por exemplo, se estiver escrevendo um formulário de cadastro, provavelmente fará o projeto da interface adicionando campos que precisam ser preenchidos, um botão para Salvar, outro para Descartar. Seu programa fará alguma coisa apenas quando um desses botões for pressionado, ou enquanto usuário faz alguma digitação, ou na mudança de foco. De qualquer forma, seu programa apenas reage a eventos disparados por ações dos usuários.
Por outro lado, desenvolvimento de games é apenas “influenciado” pelos eventos do usuário. O programa “continua rodando” no lugar de ficar esperando que alguma coisa aconteça. Por exemplo, no lugar de esperar que o sistema “anuncie” que o usuário moveu o mouse, o jogo precisa “perguntar” para o sistema se o mouse foi movido. Importante: um jogo sempre está executando alguma ação, independente de qualquer entrada do usuário.
Uma lógica mal implementada faz com que um jogo fique pouco responsivo. A “culpa” não é do dispotivo e sim do código mal-feito.
Examinando nosso código, percebemos esse conceito claramente no método Update.
// ... protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); base.Update(gameTime); } // ...
Esse código faz com que o jogo seja encerrado quando o usuário pressionar o botão “back” no gamepad (controle).
The Game State
Como mencionado antes, o método Update é onde devemos realizar qualquer alteração de estado do jogo. Aliás, Game State é um conceito extremamente importante; é a forma como o jogo “sabe” o que está acontecendo.
Jogos tipicamente possuem “estados” significativamente diferentes, como:
- mostrando um splash screen;
- mostrando telas de placar;
- mostrando seleção de jogadores;
- durante a “ação”
-
- jogador em estado normal;
- jogador com mais força;
- jogador “finalizando” uma jogada (fatality do Mortal Kombat, lembra?)
- mostrando indicadores de fim-de-partida.
Tipicamente, todas as modificações no “estado” do jogo ocorrem dentro do método Update e são “desenhados” no método Draw.
Finalizando o jogo: método UnloadContent
Com XNA, sempre que o game loop é interrompido, o método UnloadContent é chamado.
Esse método é usado para “destruir” quaisquer conteúdos que tenham sido carregados, durante o método LoadContent, e precisem de algum tratamento especial. Tipicamente, XNA (assim como .NET) irá fazer a garbage collection. Entretanto, caso algum objeto que precise de tratamento especial tenha sido carregado, o método UnloadContent é o lugar onde essa descarga deverá ocorrer.
Fluxo básico de um jogo em XNA
Para encerrar por hoje, apresento um resumo gráfico do fluxo de execução de um jogo em XNA:
Mais uma vez:
- A execução do game começa no método Initialize;
- O método LoadContent é executado em seguida e faz a carga do “conteúdo” que será utilizado durante o jogo (texturas, modelos 3D, sons);
- Inicia-se o GameLoop
-
- Update “pega” os eventos e atualiza o estado;
- Draw atualiza o display;
- UnloadContent() é executado quando o jogo termina
Por hoje, era isso!
Alberto Monteiro
22/02/2011
Cara vai ser maneira esse serie, sempre quis aprender XNA, e agora com alguem dando uma força a + e ainda mais do seu nível, vai ser Show…
So uma duvida vai fazer um jogo de xadrez usando a Strong Chess Engine(produzida aqui)?
elemarjr
22/02/2011
É uma idéia … Deverei fazer algo sim … Só falta o @juanplopes dar sinal de vida e entregar o refactoring
Fábio Lucas
22/02/2011
Mt legal elemarjr! Parabéns!!
Ari C. Raimundo
23/02/2011
Elemar,
Ótimo post. Gostaria de fazer duas perguntas:
Você já trabalhou com o Direct 3D 10/11 em modo gerenciado? Se sim, qual seria a diferença de performance para o código não-gerenciado?
O que você recomenda para trabalhar com o D3D em modo gerenciado? XNA, SlimDX, Sharp DX?
Obs: Minha intenção não é desenvolver games, mas aplicações que necessitam de um processamento gráfico avançado (trabalhar com imagens grandes e outros).
Obrigado e parabéns pelo blog, sempre acompanho seus posts.
elemarjr
23/02/2011
Honestamente, nunca percebi diferença tão significativa entre as versões gerenciadas e não gerenciadas.
Acredito, honestamente, que a performance seja mais uma resultante de sua estratégia de implementação.
Utilize XNA apenas se deseja desenvolver Games. Caso contrário, use DirectX diretamente.
Se trabalha com 3D, talvez queira considerar OpenGL.
Mande-me um email detalhando melhor suas necessidades para que eu possa lhe dar uma resposta mais dirigida.
[]s
Ari C. Raimundo
23/02/2011
Elemar,
Já respondeu.
Muito obrigado !
Abraços !
roberta
23/02/2011
Nossa, Elemar, esse post me deu saudade da época da faculdade.
Em 2007, acho que o XNA tinha sido recém-lançado, o utilizamos pra dois projetos em duas disciplinas – Jogos e Computação Musical. Lembro da dificuldade que foi chegar àquela tela azul inicial. Os jogos ficaram bem legais, um era uma espécie de guitar hero com teclado e midi, e o outro um joguinho ‘RPG’ isométrico que usava o mapa do Recife Antigo. Fizemos até uma game engine para mapas isométricos + XNA, já que não havia suporte nativo. Bons tempos, vou ver se recupero o source dos projetos pra botar no github 
elemarjr
23/02/2011
Muito bacana, Roberta!
Vou adorar ver seus projetos. Quando colocar o projeto no Github, me dê um toque.
Joseane Palhares
25/03/2011
Ate quem fim alguém fez um post com xna bem esplicadinho do inicio mesmo pra leigos, obg elomarjr vou começar já!! flw…
Joseane Palhares
25/03/2011
Ate quem fim alguém fez um post com xna bem explicativo do inicio mesmo pra leigos, obg elemarjr vou começar já!! flw…
Thiago Romam
08/08/2011
Cara, post fodástico, já salvei em meus favoritos.
Estou lendo a sua materia pela revista .Net da minha empresa e vi aqui no site tambem.
Sempre quiz aprender XNA, mas com essa ajuda se torna real hehe.
Parabens.
02/09/2011
Parou tudo! muito bom a serie, to lendo da revista também, e cheguei no seu blog, muito obrigado por compartilhar com a gente esse conhecimento.
Já fui la na ultima parte pra saber onde vou chegar com essa serie, la pela parte 10 pra mim já era suficiente nesse começo, mas tu fez muito mais, parabéns e obrigado novamente.
elemarjr
02/09/2011
muito obrigado pelo feedback. Fico feliz que tenha gostado.