por Fernando Lorenzon em 11/06/2010 as 13:17

Continuando a explicar o conceito de Scenes, vimos como o Game1 trabalha com as scenes. Vamos agora ver como é a implementação dentro da Scene.

A classe base Scene é bastante simples. Ela é uma classe abstrata, o que significa que você não pode utilizá-la diretamente. A única maneira de utilizar é extendendo a classe com o uso de herança. Por quê? Porque nela temos somente a estrutura básica, mas a implementação de cada scene depende do contexto do jogo. Aí sim criamos uma nova classe herdada dela, como por exemplo SceneTitle ou SceneAction.

Veja o código de Scene:

C:
  1. public abstract class Scene
  2. {
  3.     public List SpriteList;
  4.     public List FontList;
  5.     public Color BackgroundColor;
  6.  
  7.     public Scene()
  8.     {
  9.         Classes.Globals.MustLoadContent = true;
  10.         this.BackgroundColor = Color.Black;
  11.         this.SpriteList = new List();
  12.         this.FontList = new List();
  13.     }
  14.  
  15.     public virtual void LoadScene()
  16.     {
  17.         this.LoadSpriteList();
  18.         this.LoadFontList();
  19.     }
  20.  
  21.     public virtual void UnloadScene()
  22.     {
  23.         this.SpriteList = null;
  24.         this.FontList = null;
  25.     }
  26.  
  27.     public virtual void LoadSpriteList()
  28.     {
  29.  
  30.     }
  31.  
  32.     public virtual void LoadFontList()
  33.     {
  34.  
  35.     }
  36.  
  37.     public virtual void Update()
  38.     {
  39.     }
  40. }

Como já falei, temos uma lista de fontes e uma lista de sprites. A classe Font assim como Sprite é também uma criação minha, que encapsula coisas mais chatas do framework.

Outra coisa que temos é uma cor de fundo em BackgroundColor, que a scene vai armazenar e o Game1.Draw() vai utilizar para pintar todo o buffer. Para quem não sabe, antes de pintar sprites na tela, o buffer é todo zerado com uma cor única, para limpar a sujeira toda dos Draws anteriores e começar do zero. Geralmente a cor padrão é preto, mas dependendo da tela você pode usar outra cor. Na tela de partida do jogo eu uso cinza, porque cada pedaço do HUD do jogo fica um pouco afastado do outro e o cinza acaba se destacando entre esses pedaços, simulando uma pequena borda:

borda_de_background

A bordinha cinza na verdade faz parte do fundo cinza, e sobrou porque todo o resto foi pintado por cima, sobrando somente esses filetes entre os painéis do HUD.

Por padrão eu carrego a cor preta. Mas em qualquer lugar, a qualquer momento é possível trocar a cor.

Uma coisa necessária paar que a scene recém-criada possa ter seus sptires e fontes carregados pelo Game1, eu uso a linha de código no construtor:

Globals.MustLoadContent = true;

O Game1.Update() verifica o valor e se estiver true, volta para o Game1.LoadContent(). Senão, continua fazendo o update:

C:
  1. protected override void Update(GameTime gameTime)
  2. {
  3.     Globals.CurrentScene.Update();
  4.  
  5.     base.Update(gameTime);
  6.  
  7.     if (Globals.MustLoadContent)
  8.     {
  9.         this.LoadContent();
  10.     }
  11.     else
  12.     {
  13.         this.Draw(gameTime);
  14.     }
  15. }

O que isso significa é que todas as vezes que crio uma nova scene (e imaginando que a utilizarei imediatamente) o jogo vai parar de rodar a scene anterior e carregar novamente os sprites desta nova scene. Assim que tudo carrega, faço isso:

Globals.MustLoadContent = false;

Algo tosco e básico, mas altamente inteligível ("entendível"). Não gosto de implementações que usam conceitos avançados de orientação a objeto  porque fica muito difícil de entender o que está acontecendo. Na verdade a orientação a objetos serve justamente para esconder código e facilitar/segmentar o desenvolvimento de sistemas. Aqui a idéia é ser didático, então tudo tem que ser simples.

Veja a implementação completa de SceneTitle, referente à primeira tela do jogo, com o menu principal e o título:

C:
  1. public class SceneTitle : Scene
  2. {
  3.     private Sprite titleScreen;
  4.     private Sprite cursor;
  5.  
  6.     private Font title;
  7.     private Font arcade;
  8.     private Font freeGame;
  9.     private Font oreCollecting;
  10.     private Font chooseShip;
  11.     private Font currentShip;
  12.     private Font exit;
  13.  
  14.     public SceneTitle() : base()
  15.     {
  16.     }
  17.  
  18.     public override void LoadScene()
  19.     {
  20.         base.LoadScene();
  21.  
  22.         Globals.SelectedShip.X = this.currentShip.Position.X + 70;
  23.         Globals.SelectedShip.Y = this.currentShip.Position.Y - 5;
  24.         Globals.FontInfo1.Text = "Pressione Enter para selecionar";
  25.  
  26.         this.gameOptions = Enum.Options.Arcade;
  27.     }
  28.  
  29.     public override void LoadSpriteList()
  30.     {
  31.         this.titleScreen = new Sprite(800, 450, "Images\\Menu\\LunarLander");
  32.         this.cursor = new Sprite(16, 16, "Images\\Misc\\VMenuCursor");
  33.         this.cursor.Y = 550;
  34.  
  35.         this.SpriteList = new System.Collections.Generic.List<Sprite>();
  36.  
  37.         this.SpriteList.Add(this.titleScreen);
  38.         this.SpriteList.Add(this.cursor);
  39.         this.SpriteList.Add(Globals.SelectedShip);
  40.     }
  41.  
  42.     public override void LoadFontList()
  43.     {
  44.         this.title = new Font("Fonts\\Title", "XNA Lander", new Vector2(270, 22), Color.Lime);
  45.         this.arcade = new Font("Fonts\\MenuOptions", "Arcade", new Vector2(10, 533), Color.Lime);
  46.         this.freeGame = new Font("Fonts\\MenuOptions", "Jogo Livre", new Vector2(75, 533), Color.Lime);
  47.         this.oreCollecting = new Font("Fonts\\MenuOptions", "Coletar Minério", new Vector2(160, 533), Color.Lime);
  48.         this.chooseShip = new Font("Fonts\\MenuOptions", "Escolher Nave", new Vector2(275, 533), Color.Lime);
  49.         this.exit = new Font("Fonts\\MenuOptions", "Sair", new Vector2(380, 533), Color.Lime);
  50.         this.currentShip = new Font("Fonts\\MenuOptions", "Nave Atual", new Vector2(600, 533), Color.Lime);
  51.  
  52.         this.FontList = new System.Collections.Generic.List<Font>();
  53.  
  54.         this.FontList.Add(this.title);
  55.         this.FontList.Add(this.arcade);
  56.         this.FontList.Add(this.freeGame);
  57.         this.FontList.Add(this.oreCollecting);
  58.         this.FontList.Add(this.chooseShip);
  59.         this.FontList.Add(this.currentShip);
  60.         this.FontList.Add(this.exit);
  61.  
  62.         this.FontList.Add(Globals.FontInfo1);
  63.     }
  64.  
  65.     public override void Update()
  66.     {
  67.         Functions.KeyReading();
  68.  
  69.         if (Globals.EnterKeyPressed)
  70.         {
  71.             //entra na opção selecionada
  72.         }
  73.  
  74.         if (Classes.Globals.RightKeyPressed)
  75.         {
  76.             //move o cursor para a direita
  77.         }
  78.         else if (Classes.Globals.LeftKeyPressed)
  79.         {
  80.             //move o cursor para a esquerda
  81.         }
  82.     }

Como o código de Update() ficaria muito extenso, eu cortei parte dele, pois aqui o objetivo é entender como implementar uma scene inteira.

No LoadSpriteList() eu instancio os sprites que utilizarei e adiciono cada um na lista. Faço o mesmo com as fontes no método LoadFontList().

No caso dos sprites, os 3 únicos utilizados aqui são a figura que ilustra a tela, o cursor de seleção e a figura da nave atualmente selecionada. Como o sprite da nave selecionada eu utilizo durante todo o jogo, eu não preciso declarar este objeto na scene. Eu declaro em Globals. Por isso só existem essas duas declarações de sprite:

private Sprite titleScreen;
private Sprite cursor;

Porém, ao carregar os sprites que serã0 utilizados na scene, eu carrego junto com os dois anteriores o sprite da nave que está lá no Globals:

this.SpriteList.Add(this.titleScreen);
this.SpriteList.Add(this.cursor);
this.SpriteList.Add(Globals.SelectedShip);

Veja a tela de título:

xnalander

Com isso encerramos este tópico sobre scenes.

Parte 4 - Scenes
Parte 6 - Audio

Tags: , ,
Categorias: Desenvolvimento | No Comments »


Deixe um comentário