Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas


Introdução ao Desenvolvimento de Jogos com XNA: Análise do Código, Notas de estudo de Engenharia Elétrica

Uma análise do código de introdução ao desenvolvimento de jogos usando a plataforma xna da microsoft. O texto abrange as transformações 3d da nave, a execução de sons, e a criação de classes para representar a lógica da nave no jogo. Além disso, são discutidos conceitos básicos da plataforma xna, como a matriz de transformação padrão, a posição e rotação dos modelos, e a aceleração da nave.

Tipologia: Notas de estudo

Antes de 2010

Compartilhado em 06/05/2008

jorge-albuquerque-11
jorge-albuquerque-11 🇧🇷

5

(1)

9 documentos

1 / 28

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
Faculdade Integrada do Ceará
Fábrica de Projetos de Software
Grupo de Estudo em Jogos Eletrônicos
Introdução ao Desenvolvimento de Jogos com XNA
Prof. Jorge Albuquerque, D.Sc., MBA
Fortaleza
2007
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c

Pré-visualização parcial do texto

Baixe Introdução ao Desenvolvimento de Jogos com XNA: Análise do Código e outras Notas de estudo em PDF para Engenharia Elétrica, somente na Docsity!

Faculdade Integrada do Ceará

Fábrica de Projetos de Software

Grupo de Estudo em Jogos Eletrônicos

Introdução ao Desenvolvimento de Jogos com XNA

Prof. Jorge Albuquerque, D.Sc., MBA

Fortaleza

Sobre este material

Este material é baseado nos tutoriais disponíveis no site XNA Creators Club (MICROSOFT, 2007a, 2007b) e no XNA Developer Center (MICROSOFT, 2007c). Os vídeos tutoriais disponibilizados no site XNA Creators Club estão disponíveis na integra no CD do curso na pasta \VIDEO. Os códigos fontes dos respectivos tutoriais estão disponíveis na pasta \PROJETOS. A pasta \STARTERKITS apresenta os instaladores dos projetos de dois jogos completos: “Marbles” (estilo “tetris”) e “Racing Game” (estilo “corrida”).

Todos os pacotes necessários ao desenvolvimento na plataforma XNA estão disponíveis na pasta \PACOTES do mesmo CD, destacando o Visual C# Express 2005, Visual C# Express 2005 SP1 e o framework XNA (instalação mínima).

Como forma de entendimento, recomenda-se fortemente a leitura dos comentários dos códigos fontes apresentados pois a explicação de seu funcionamento via de regra se encontra nestes.

2.5. Na classe Game1, substitua o método LoadGraphicsContent pelo código abaixo:

// Modelo 3D para visualização. private Model _ModeloNave;

// O proporção da tela determina como manter a propoção da tela para os objetos 2D e 3D. private float _AspectRatio;

protected override void LoadGraphicsContent( bool loadAllContent ) { if (loadAllContent) { // Carrega o modelo da nave a partir do arquivo \Content\Models\p1_wedge.fdx _ModeloNave = content.Load( "Content\Models\p1_wedge" ); }

// Determina a proporção da tela pela razao entre a lagura e o comprimento da tela _AspectRatio = (float) GraphicsDeviceManager.DefaultBackBufferWidth / GraphicsDeviceManager.DefaultBackBufferHeight;; }

2.6. Na classe Game1, substitua o método Draw pelo código abaixo:

// Inicia a posição inicial do modelo 3D na origem e sua rotação em 0 radianos. private Vector3 _PosicaoNave = Vector3.Zero; private float _RotacaoNave = 0.0f;

// Inicia a posição da camera no espaço para a matriz de visualização. private Vector3 _PosicaoCamera = new Vector3( 0.0f, 50.0f, 5000.0f );

protected override void Draw( GameTime gameTime ) { // Limpa a tela com um fundo azul graphics.GraphicsDevice.Clear( Color.CornflowerBlue );

// Copia a referencia das transformações para o modelo 3D da nave. Matrix[] transforms = new Matrix[_ModeloNave.Bones.Count]; _ModeloNave.CopyAbsoluteBoneTransformsTo( transforms );

// Desenha o modelo. O modelo pode possuir múltiplos Meshes. foreach (ModelMesh mesh in _ModeloNave.Meshes) { // Inicia a orientação do Mesh no espaço assim como a posição da câmera, // e da projeção do mesmo. foreach (BasicEffect effect in mesh.Effects) { // Define iluminação padrão effect.EnableDefaultLighting();

// Define a posição do modelo 3D da nave pela matriz de rotação no eixo Y // (a nave vai girar na horizontal) e a matriz de translação da posição //da nave. effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY( _RotacaoNave ) * Matrix.CreateTranslation( _PosicaoNave );

// Define a posição da câmera no espaço, assim como sua direção e //seu o eixo de rotação effect.View = Matrix.CreateLookAt( _PosicaoCamera, Vector3.Zero, Vector3.Up );

// Define os planos de projeção (discutido em detalhes na Parte 5.1) effect.Projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians( 45.0f ), _AspectRatio, 1.0f, 10000.0f ); } // Desenha o mesh. mesh.Draw(); } }}

2.7. Para fazer a nave girar em torno do próprio eixo, substitua o método Update pelo código abaixo:

protected override void Update( GameTime gameTime ) { // Para cada milisegundo, a rotação da nave é incrementando em 0,1 graus _RotacaoNave += (float)gameTime.ElapsedGameTime.TotalMilliseconds

  • MathHelper.ToRadians( 0.1f );

base.Update( gameTime ); }

2.8. Execute o debug com o botão F5. O resultado deverá ser uma nave rodando no próprio eixo conforme ilustrado abaixo:

Figura 3: Apresentação do modelo 3D (.fbx). Exercícios:

  1. Faça a nave girar simultaneamente nos eixos X, Y e Z. Dica: No método Draw, verifique as transformações do effect.World.
  2. Faça a nave se deslocar na tela. Dica: Verifique o efeito da variável de estado dado pelo vetor 3D _PosicaoNave.
  3. Modifique a iluminação da nave. Dica: Implemente parâmetros de iluminação no método Draw. Consulte a referência da classe BasicEffect na documentação do XNA na seção “How To: Use BasicEffect” (MICROSOFT, 2007c). Esta documentação também é acessível no Help do XNA Game Studio Express na opção “How Do I?”.
  4. Adicione um fundo a tela. Dica: Consulte a documentação do XNA “How to: Make a Scrolling Background” (MICROSOFT, 2007c). Certifique-se de chamar o método SpriteBatch.Draw que permite especificar o parâmetros layerDepth, definindo o mesmo para o valor 1.0f.

Exercícios:

  1. Faça a nave fazer as curvas mais rapidamente.
  2. Faça a nave se movimentar mais rapidamente (modifique a “aceleração”).
  3. Faça a nave se movimentar para mais rápido ou mais devagar (modifique o “atrito”).
  4. Faça a nave andar de para trás (“andar de ré”) se pressionada a seta “para baixo”.

NOTA: Para controle via joysticks e gamepads veja o Adentro 1.

Parte 4: Fundamentos: Adicionando Som

4.1 Pressione o botão direito do mouse na janela “Solution Explorer” (Add Æ New Folder) para adicionar o diretório “Content\Audio” e o subdiretórios “Content\Audio\Waves”. 4.2 Selecione com o botão direito do mouse a pasta “Content\Audio\Waves” e selecione Add Æ Existing Item... Selecione os arquivos engine_2.wav e hyperspace_activate.wav (disponível no CD do curso na pasta

\AULA1\Content\Audio\Waves). Selecione novamente o botão direito na pasta “Content\Audio” e adicione o arquivo MyGameAudio.xap (disponível no CD do curso na pasta \AULA1\Content\Audio).

NOTA: Para informações sobre a criação do arquivo xap, veja o Adentro 2

4.3. Substitua o método Initialize() pelo código abaixo:

private AudioEngine _AudioEngine; private WaveBank _WaveBank; private SoundBank _SoundBank; protected override void Initialize() { // Carrega o arquivo de definição de sons (.xap) gerado com a ferramenta XACT _AudioEngine = new AudioEngine( "Content\Audio\MyGameAudio.xgs" ); _WaveBank = new WaveBank( _AudioEngine, "Content\Audio\Wave Bank.xwb" ); _SoundBank = new SoundBank( _AudioEngine, "Content\Audio\Sound Bank.xsb" ); base.Initialize(); }

3.1. Substitua o método UpdateKeyboardInput() pelo código abaixo:

private Cue _SomMotorNave; protected void UpdateKeyboardInput() { // Para cada tecla pressiona (podem ser pressionadas mais de uma tecla) foreach (Keys key in Keyboard.GetState().GetPressedKeys()) { // Rotação para esquerda (0,1 rad) quando pressiona a tecla esquerda if (key == Keys.Left) _RotacaoNave += 0.1f; // Rotação para direita (0,1 rad) quando pressiona a tecla direita if (key == Keys.Right) _RotacaoNave -= 0.1f;

// Se pressiona a tecla “para cima”, acelera a nave. // O vetor de aceleração tem a mesma direção da nave. Vector3 aceleracao = Vector3.Zero; if (key == Keys.Up) { aceleracao.X = -(float)Math.Sin(_RotacaoNave); eceleracao.Z = -(float)Math.Cos(_RotacaoNave);

// NAVE ACELERANDO!!!

// Se o som do motor da nave não foi criado, cria o mesmo e toca. if (_SomMotorNave == null) { _SomMotorNave = soundBank.GetCue( "engine_2" ); _SomMotorNave.Play(); } // Se o som do motor da nave já foi criado e está pausado, libera o “pause” else if (_SomMotorNave.IsPaused) { _SomMotorNave.Resume(); } } else { // NAVE NÃO ESTÁ ACELERANDO!!! // Se o som do motor da nave existe e está tocando, pausa o mesmo. if (_SomMotorNave != null && _SomMotorNave.IsPlaying) { _SomMotorNave.Pause(); } }

// Incrementa a velocidade da nave através da aceleração _VelocidadeNave += aceleracao;

// Encerra jogo se pressiona “Escape” if (key == Keys.Escape) this.Exit();

// Move a nave para a origem se pressiona “Enter” if (key == Keys.Enter) { _PosicaoNave = Vector3.Zero; _VelocidadeNave = Vector3.Zero; _RotacaoNave = 0.0f;

// Executa som do “hiper-espaço”. _SoundBank.PlayCue( "hyperspace_activate" ); } } }

Exercícios:

  1. Adicione uma música de fundo ao jogo. Dica: Utilize a ferramenta XACT, descrita na Adentro 2, para adicionar novos sons.
  2. Adicione um controle de volume para a música de fundo. Dica: Consulte a referência do XNA na seção “How to: Change Sound Volume Levels” (MICROSOFT, 2007c).

No entanto, podem-se abstrair outras generalizações deste modelo. Observa-se que a lógica de execução dos sons não pertence necessariamente à lógica da nave. A lógica de execução do som pode ser entendida como uma funcionalidade a parte consumida pela nave. Desta forma, a execução dos sons é de responsabilidade de um terceira classe “SoundPlayer”, associada por composição com a classe nave. Desta forma, a lógica de execução de som empregada pela nave pode ser reaproveitada em outros cenários. Novamente, a divisão de responsabilidades e o encapsulamento do código favorece o entendimento, manutenção e extensão do código do jogo.

Ademais, uma nova generalização pode ser proposta, na medida em que a nave é , em última estância, um elemento gráfico do jogo. Assim como a nave, outros elementos gráficos teriam comportamentos em comum. Todos os elementos gráficos que possamos vir a criar no futuro teriam um modelo “mesh” (propriedade “Model”), uma posição no espaço (propriedades “Posicao”) e matrizes de transformação (propriedade “Transforms”). Desta forma, classe “Ship” é definida por herança a partir da classe “Element”.

A classe “Element” encapsula as responsabilidades de desenhar o elemento gráfico na tela, empregando as propriedades de modelo e posição.

Por sua vez a classe “Ship” especializa a classe “Element”, extendendo a representação do movimento da próprios da nave, tais como: modelo físico de velocidade e rotação, assim como de manipulação deste modelo pelo teclado e joystick. Portanto a classe “Ship” disporia de uma interface mínima contanto apenas de sua rotação (propriedade “Rotacao”) e um método de atualização (método “Update”), recebendo por parâmetros a entrada do teclado/joystick e representando o comportamento do seu movimento em resposta a esta entrada.

Como forma, de facilitar futuras extensões ao código proposto são ainda extraídas as interfaces das classes “SoundPlayer” e “Element” (figura abaixo), dando o primeiro passo para estruturação de fábricas de produtos concretos ao jogo, parametrizando diversos aspectos de seu comportamento.

Figura 5 – Interfaces propostas para a refatoração do código da Parte 4.

Parte 5.1.1: Modelando as interfaces

  1. De acordo com a discussão anterior, crie diretórios Elements e Sound de forma a organizar o novo código em pastas

lógicas (“namespaces”), conforme ilustrado na figura abaixo. Dica: Sobre o “Solution Explorer”, pressione o botão direito do mouse e selecione AddÆNew Folder...

Figura 6 – Criando estrutura de diretórios da solução proposta.

  1. Crie as interfaces ISoundPlayer e IElement, respectivamente, nas pastas “\Sound” e “\Elements”. Dica: sobre o “Solution Explorer”, pressione o botão direito do mouse e selecione AddÆClass... Copie os códigos abaixo para as novas classes:

Interface ISoundPlayer (ISoundPlayer.cs)

using Microsoft.Xna.Framework.Audio;

namespace MyFirstGame.Sound {

///

/// Interface para execução de sons /// interface ISoundPlayer {

///

/// Obtem uma referência para um sim a partir de seu nome no SoundBank /// (arquivo .xsb) /// /// Nome do som no SoundBank (arquivo .xsb) /// rastro do som Cue GetCue(string cueName);

///

/// Executa um som a partir de seu nome no SoundBank (arquivo .xsb) /// /// Nome do som no SoundBank (arquivo .xsb) void Play(string cueName);

///

/// Executa o som (continua se pausado) /// /// Rastro do som (Cue) void Play(Cue cue);

  1. Crie a classe SoundPlayer e Element, respectivamente, nas pastas “\Sound” e “\Elements”. Dica: sobre o “Solution Explorer”, pressione o botão direito do mouse e selecione AddÆClass... Copie os códigos abaixo para as novas classes:

Classe SoundPlayer (SoundPlayer.cs)

using Microsoft.Xna.Framework.Audio;

namespace MyFirstGame.Sound { ///

/// Classe responável pela execução de sons /// class SoundPlayer: ISoundPlayer { private AudioEngine _AudioEngine; private WaveBank _WaveBank; private SoundBank _SoundBank;

///

/// Criação do objeto de execução de som /// /// Caminho para o arquivo .xap /// Nome do WaveBank no XACT (arquivo .xwb) /// Nome do SoundBank no XACT (arquivo .xsb) public SoundPlayer(string audioEngine, string waveBank, string soundBank) { _AudioEngine = new AudioEngine(audioEngine); _WaveBank = new WaveBank(_AudioEngine, waveBank); _SoundBank = new SoundBank(_AudioEngine, soundBank); }

//

/// Executa um som a partir de seu nome no SoundBank (arquivo .xsb) /// /// Nome do som no SoundBank (arquivo .xsb) public void Play(string cueName) { // Se o soundBank foi iniciado e o nome do som não é nulo if (_SoundBank != null && !string.IsNullOrEmpty(cueName)) _SoundBank.PlayCue(cueName); }

///

/// Obtem uma referência para um sim a partir de /// seu nome no SoundBank (arquivo .xsb) /// /// Nome do som no SoundBank (arquivo .xsb) /// Rastro do som (Cue) public Cue GetCue(string cueName) { // Se o soundBank foi iniciado e o nome do som não é nulo if (_SoundBank != null && !string.IsNullOrEmpty(cueName)) return _SoundBank.GetCue(cueName); return null; }

///

/// Executa o som (continua se pausado) /// /// Rastro do som (Cue) public void Play(Cue cue) { // Não é possível executar sons de referência nula. if (cue == null) return; // Se o som estava pausado, continua sua execução if (cue.IsPaused) { cue.Resume(); return; } // Caso contrário, se o som estiver preparado, inicia-se sua execução if (cue.IsPrepared) cue.Play(); }

///

/// Pausa a execução do som /// /// Rastro do som (Cue) public void Pause(Cue cue) { // Se o rastro não é nulo e o som está sendo executado, pausa o mesmo if (cue != null && cue.IsPlaying) { cue.Pause(); } }

///

/// Para a execução do som (imediatamente) /// /// rastro do som public void Stop(Cue cue) { if (cue != null && cue.IsPlaying) { cue.Stop(AudioStopOptions.Immediate); } } } }

// Copia as transformações padrões do modelo para // um vetor de matrizes de transformação _Transforms = new Matrix[_Model.Bones.Count]; _Model.CopyAbsoluteBoneTransformsTo(_Transforms);

// Um elemento é constituído de vários meshes foreach (ModelMesh mesh in _Model.Meshes) { // Cada mesh pode ter vários efeitos aplicados foreach (BasicEffect effect in mesh.Effects) { // Para cada mesh aplica os efeitos de iluminação padrão, e as // matrizes de projeção e visualização effect.EnableDefaultLighting(); effect.Projection = projectionMatrix; effect.View = viewMatrix; } } }

///

/// Desenha o elemento na tela /// public abstract void Draw();

///

/// Desenha o elemento na tela, usando a matriz de transformação /// /// matriz de transformação protected void Draw(Matrix modelTransform) { // Modela o deslocamento para a posição do elemento através // de uma matriz de translação a sua posição corrente if (_Posicao != Vector3.Zero) modelTransform *= Matrix.CreateTranslation(_Posicao);

// Um modelo é constituído de vários mesh, // desenha-se o modelo desenhando seus meshs foreach (ModelMesh mesh in _Model.Meshes) { // Antes de desenhar, é preciso definir a orientação do mesh foreach (BasicEffect effect in mesh.Effects) { // Ajusta a orientação de cada mesh segundo a matriz de transformação effect.World = _Transforms[mesh.ParentBone.Index] * modelTransform; } // Desenha o mesh, empregando os efeitos (matriz de transformação) acima. mesh.Draw(); } } } }

  1. Crie a classe Ship nas pasta “\Elements”. Copie os códigos abaixo para a nova classe:

Classe Ship (Ship.cs)

using System; using System.Text; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Audio; using MyFirstGame.Sound;

namespace MyFirstGame.Elements { ///

/// Classe que representa a lógica da nave no jogo /// class Ship : Element { private ISoundPlayer _SoundPlayer; private Cue _EngineSoundCue; /// /// Uma nave é criada definindo o objeto de execução de seus sons. /// /// Objeto de execução de sons public Ship(ISoundPlayer soundPlayer) { _SoundPlayer = soundPlayer; _EngineSoundCue = _SoundPlayer.GetCue("engine_2"); }

///

/// Uma nave é criada definindo seu modelo e o objeto de execução de seus sons. /// /// Modelo da nave /// Objeto de execução de sons public Ship(Model model, ISoundPlayer soundPlayer) { Model = model; _SoundPlayer = soundPlayer; _EngineSoundCue = _SoundPlayer.GetCue("engine_2"); } /// /// Executa o som do motor da nave /// public void PlayEngine() { _SoundPlayer.Play(_EngineSoundCue); } /// /// Pausa a execução do motor da nave /// public void PauseEngine() { _SoundPlayer.Pause(_EngineSoundCue); }

///

/// Atualização do modelo físico da nave para o quadro (frame) corrente /// protected void Update() { // Define a posição da nave a partir de sua velocidade para o // quadro (frame) corrente. A variação da posição é dada pela velocidade: // x = xo + v.t // Ou seja, em um quadro (t = 1 unidade): x = xo + v ou x += v _Posicao += _Velocidade;

// Reduz a velocidade em 5% a cada quadro (reproduz o efeito de "atrito") _Velocidade *= 0.95f; }

// Constante: aceleração private const float _AceleracaoStep = 3.0f; // Calcula a aceleração (variação da velocidade da nave) protected Vector3 GetAceleracao() { Vector3 aceleracao = Vector3.Zero; // A aceleração tem a mesma direção da rotação da nave aceleracao.X = -(float)Math.Sin(_Rotacao); aceleracao.Z = -(float)Math.Cos(_Rotacao);

// A intensidade da aceleração é dada pela constante _AceleracaoStep return _AceleracaoStep * aceleracao; }

// Constante: velocidade da rotação da nave em radianos private const float _RotacaoStep = 0.1f; ///

/// Atualiza os estados da nave (movimento) a partir da entrada do teclado /// /// estado do teclado public void Update(KeyboardState controllerState) { // Varias teclas podem ser pressionadas ao mesmo tempo. // Repetir o processamento para cada tecla. foreach (Keys key in controllerState.GetPressedKeys()) { // Dobra a esquerda (incrementa a rotação da nave) if (key == Keys.Left) Rotacao += _RotacaoStep; // Dobra a direita (decrementa a rotação da nave) if (key == Keys.Right) Rotacao -= _RotacaoStep; // Botao de aceleracao if (key == Keys.Up) { // Adiciona a aceleracao na velocidade: v = vo + a.t _Velocidade += GetAceleracao(); // Executa o som do motor quando a nave esta acelerando PlayEngine(); } else // Pauda o som do motor quando a nave não está acelerando PauseEngine();

// Atualiza o modelo físico da nave Update();

// Aciona o hiper-espaço se pressionada a tecla "Enter" if (key == Keys.Enter) HyperSpace(); } }

///

/// Atualiza os estados da nave (movimento) a partir da /// entrada do joystick do X-Box 360 /// /// Estado do joystick public void Update(GamePadState controllerState) { // Se o controle não estiver conectado não é possível processamento if (controllerState == null || !controllerState.IsConnected) return;

// A rotação da nave é dada pela direção X do // direcional do polegar (thumbstick direito) Rotacao -= controllerState.ThumbSticks.Left.X * 0.10f;

// Finalmente, a matriz de rotação é adicionada ao // vetor de velocidade se controle estiver para frente. _Velocidade += _MatrizRotacao.Forward * _AceleracaoStep * controllerState.Triggers.Right;

// Se o controle está para frente, executa o som do motor, // caso contrario, pausa o mesmo if (controllerState.Triggers.Right > 0) PlayEngine(); else PauseEngine();

// Atualiza o modelo físico da nave Update();

// Aciona o hiper-espaço se pressionado o botão "A" if (controllerState.Buttons.A == ButtonState.Pressed) HyperSpace(); }

///

/// Desenha o modelo da nave. /// public override void Draw() { // Desenha o modelo da nave através de sua matriz de rotação Draw(_MatrizRotacao); } } }