Tworzenie gry w C# z użyciem silnika Ogre - cz.3

10.10.2010 - Mateusz Osowski
TrudnośćTrudność

[Część 1] [Część 2] [Część 3] [Część 4] [Część 5] [Część 6]
[Część 7]

Drugą część cyklu zakończyliśmy implementacją sensora lokalnych obiektów dla naszej postaci. W tej części stworzymy nowy typ obiektów niezbędny w każdej grze tego typu - przedmioty, które da się podnieść. Oprócz tego uzupełnimy funkcjonalność kodu o wyświetlanie etykiet tekstowych w przestrzeni 2D, a także 3D.











Proste etykiety tekstowe



W naszym silniku zaimplementujemy dwa typy etykiet tekstowych:
  • proste - umieszczone w przestrzeni 2D
  • nieco bardziej rozbudowane - związane z punktem w przestrzeni trójwymiarowej.
Na początek zaimplementujemy pierwszy typ etykiety i prostą fabrykę zarządzającą istniejącymi na ekranie napisami.

Utwórz klasę TextLabel w nowym pliku:
1
2
3
4
5
public class TextLabel
{
  public TextAreaOverlayElement TextArea;
  protected bool IsCaptionChanged;
  bool _IsVisible;
TextLabel.cs


Ogre oferuje możliwość wyświetlania dwuwymiarowych, oteksturowanych prostokątów, a także napisów na ekranie za pośrednictwem klasy Overlay i klas z nią związanych. Jedną z nich jest TextAreaOverlayElement - przestrzeń na tekst. W klasie TextLabel definiujemy również pole IsCaptionChanged, które będzie określać, czy treść etykiety uległa zmianie. Atrybut protected sprawia, że pole prywatne nim oznaczone będzie widoczne również w klasach dziedziczących z klasy, w której znajduje się to pole. Prywatne pole _IsVisible określać będzie, czy etykieta ma być widoczna na ekranie.

Definiujemy konstruktor:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  public TextLabel(
    String name, String fontName, float fontSize, ColourValue colourtop, ColourValue colourbottom)
  {
    TextArea = OverlayManager.Singleton.CreateOverlayElement("TextArea", name) 
      as TextAreaOverlayElement;
    TextArea.MetricsMode = GuiMetricsMode.GMM_RELATIVE;
    TextArea.SetDimensions(1.0f, 1.0f);
    TextArea.CharHeight = fontSize;
    TextArea.FontName = fontName;
    TextArea.ColourTop = colourtop;
    TextArea.ColourBottom = colourbottom;            
    
    TextArea.SpaceWidth = TextArea.CharHeight * 0.5f; 
  }
TextLabel.cs


Przyjmuje on kolejno nazwę obiektu tekstu, nazwę czcionki, rozmiar czcionki oraz górny i dolny kolor napisu - tekst bowiem będzie można pokolorować gradientem. W trzecim wierszu tworzymy obiekt przestrzeni tekstu, następnie ustalamy system miary na względny (GMM_RELATIVE). Oznacza to, że pozycja i wymiary będą podawane w odniesieniu do układu współrzędnych rozpoczynającego się w lewym górnym wierzchołku ekranu, w którym szerokość i wysokość ekranu wynoszą 1.0 odpowiednio na osiach X i Y. Rozmiar obszaru tekstu ustalamy jako cały ekran. W wierszu 13 ustalamy szerokość spacji. Będzie ona równa połowie szerokości normalnego znaku.



Zdefiniujmy również pośrednie metody zmieniające właściwości obszaru tekstowego:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  public bool IsVisible
  {
    get { return _IsVisible; }
    set
    {
      _IsVisible = value;
      if (value)
        TextArea.Show();
      else
        TextArea.Hide();
    }
  }
 
  public void SetPosition(float x, float y)
  {
    TextArea.SetPosition(x, y);
  }
 
  public String Caption
  {
    get { return TextArea.Caption; }
    set 
    { 
      TextArea.Caption = value;
      IsCaptionChanged = true;
    }
  }
TextLabel.cs


Potrzebujemy także metody aktualizującej tekst:
1
2
3
4
5
6
7
8
9
  public virtual void Update()
  {
    if (IsCaptionChanged)
    {
      TextArea.Caption = TextArea.Caption;
      IsCaptionChanged = false;
    }
  }
}
TextLabel.cs


Operacja, którą wykonujemy piątym wierszu może się wydawać dziwna. Wątpliwości może również budzić potrzeba odnotowywania zmiany w tekście. Ma to związek z pewnym błędem w kodzie Ogre w wersji, której używamy, bowiem zmiany w tekście dokonane po utworzeniu jego obiektu i przed wyrenderowaniem klatki nie powodują aktualizacji współrzędnych mapowania tekstury czcionki na geometrii napisu, która składa się z prostokątów. Musimy więc zadbać, aby zmiana tekstu została wprowadzona po raz drugi po wyrenderowaniu klatki. W tym celu stworzymy klasę-fabrykę etykiet, która będzie zarządzać także ich aktualizacją w odpowiednim momencie. Metodę Update() czynimy wirtualną, ponieważ będzie wyglądać ona nieco inaczej w przypadku etykiet 3D.

Fabryka etykiet



Utworzymy teraz nieskomplikowaną klasę pozwalającą przechowywać i tworzyć nowe obiekty etykiet tekstowych:
1
2
3
4
5
6
7
public class TextLabeler
{
  Overlay Overlay;
  OverlayContainer Panel;        
  
  public int LabelID;
  public List<TextLabel> Labels;  
TextLabeler.cs


Aby obiekty klasy TextAreaOverlayElement zostały wyrenderowane przez silnik, muszą zostać umieszczone w kontenerze, który z kolei musi znajdować się na "nakładce", obiekcie klasy Overlay. W tym celu definiujemy odpowiednie pola w klasie TextLabeler. Oprócz tego przydatne będzie pole pozwalające generować unikalne nazwy obiektów etykiet - LabelID i oczywiście lista wszystkich etykiet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  public TextLabeler()
  {                        
    Overlay = OverlayManager.Singleton.Create("TextLabeler");                        
 
    Panel = OverlayManager.Singleton.CreateOverlayElement("Panel", "TextLabelerPanel") 
      as OverlayContainer;
    Panel.MetricsMode = GuiMetricsMode.GMM_RELATIVE;
    Panel.SetDimensions(1.0f, 1.0f);
    Panel.SetPosition(0, 0);                   
 
    Overlay.Add2D(Panel);            
    Overlay.Show();
 
    Labels = new List<TextLabel>();
  }
TextLabeler.cs


W konstruktorze tworzymy kolejno obiekt nakładki (4), panel, który rozpinamy na powierzchni całego ekranu i następnie dodajemy do nakładki (6-9), a także obiekt kolekcji etykiet (14). Pierwszy argument metody CreateOverlayElement określa jakiego typu element nakładki chcemy utworzyć. Interesujące nas typy to "Panel" oraz "TextArea". Drugim argumentem jest unikalna nazwa elementu.

Definiujemy metodę generującą unikalne nazwy etykiet:
1
2
3
4
  public string GetUniqueLabelName()
  {
    return (LabelID++).ToString();
  }
TextLabeler.cs


Generowanie tego typu nazw jest nam potrzebne, ponieważ metoda CreateOverlayElement() wymaga podania uniklanej nazwy elementu nakładki.

Tworzymy metodę generującą obiekt etykiety:
1
2
3
4
5
6
7
8
9
10
11
  public TextLabel NewTextLabel(
    String fontName, float fontSize, ColourValue colourtop, ColourValue colourbottom)
  {
    TextLabel textLabel = new TextLabel(GetUniqueLabelName(), 
      fontName, fontSize, colourtop, colourbottom);            
 
    Panel.AddChild(textLabel.TextArea);
    Labels.Add(textLabel);
 
    return textLabel;
  }
TextLabeler.cs


Metoda ta tworzy obiekt etykiety, a także dodaje go do panelu znajdującego się na nakładce. Oprócz tego, korzystając z tego typu metody fabrykującej nakładki jesteśmy zwolnieni z ręcznego dodawania nowego obiektu do listy. Warto rozważyć modernizacje klasy ObjectManager, by tworzyła obiekty gry różnych typów w podobny sposób.

Metoda aktualizująca:
1
2
3
4
5
6
    public void Update()
    {
      foreach (TextLabel label in Labels)
        label.Update();
    }
  }
TextLabeler.cs


Korzystając z pętli aktualizujemy wszystkie elementy kolekcji.

Dodajmy egzemplarz fabryki do klasy Engine:
1
2
3
4
5
6
7
  public TextLabeler Labeler;
  
  public void Initialise()
  {
    ...
    Labeler = new TextLabeler();
  }       
Engine.cs


Musimy także zadbać o aktualizację etykiet w odpowiednim momencie:
1
2
3
4
5
6
7
8
9
  public void Update()
  {
    ...
    Keyboard.Capture();
    Mouse.Capture();            
    Root.RenderOneFrame();            
    Labeler.Update();
    ...
  }
Engine.cs




Zanim wyświetlimy jakikolwiek napis, potrzebujemy dodać do zasobów silnika pliki czcionki. Ściągnij poniższe archiwum i rozpakuj jego zawartość do folderu Media:

Czcionki

Konstrukcja pliku definicji czcionki jest bardzo prosta, nie będzie więc omawiana. Możemy teraz utworzyć przykładową etykietę w klasie Program, po inicjacji silnika:
1
2
3
4
5
6
  ...
  TextLabel tl = Engine.Singleton.Labeler.NewTextLabel(
    "Primitive", 0.04f, new ColourValue(0.7f, 0.4f, 0), new ColourValue(1,0.8f, 0.3f));
  tl.Caption = "Hello World";
  tl.SetPosition(0.1f, 0.1f); 
  ...
Program.cs, Main()


Na ekranie powinien ukazać się napis umieszczony w lewym górnym rogu. Możesz pobrać aktualny kod źródłowy, aby zweryfikować poprawność swojego kodu:

Aktualny kod źródłowy



5
Twoja ocena: Brak Ocena: 5 (3 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com