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

10.10.2010 - Mateusz Osowski
TrudnośćTrudność

Etykiety 3D

Kolejnym usprawnieniem silnika, którego dokonamy będzie wprowadzanie etykiet 3D. Nowa klasa będzie dziedziczyć z istniejącej już klasy TextLabel. Wzbogacimy ją o atrybut pozycji w przestrzeni 3D.
1
2
3
4
5
6
7
8
9
10
11
public class TextLabel3D : TextLabel
{
  public Vector3 Position3D;     
  float HalfWidth;
 
  public TextLabel3D(
    String name, String fontName, float fontSize, 
    ColourValue colourtop, ColourValue colourbottom)
  : base(name, fontName, fontSize, colourtop, colourbottom)
  {
  }
TextLabel3D.cs


Pole Position3D będzie publicznym polem przechowującym właściwą pozycję etykiety. W polu HalfWidth przechowywać będziemy połowę szerokości napisu. Będzie to nam potrzebne do wyśrodkowania napisu względem zadanej pozycji. Konstruktor nowej klasy niczym się nie różni od konstruktora bazowej klasy TextLabel, toteż jego ciało pozostawiamy puste.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  public override void Update()
  {
    Vector3 projected = Engine.Singleton.Camera.ProjectionMatrix 
      * (Engine.Singleton.Camera.ViewMatrix * Position3D);
    float x2d = ((projected.x * 0.5f) + 0.5f);
    float y2d = 1.0f - ((projected.y * 0.5f) + 0.5f);
    if (x2d < 0 || x2d > 1 || y2d < 0 || y2d > 1)
      TextArea.Hide();
    else
      if (IsVisible) TextArea.Show();          
      
    SetPosition(x2d - HalfWidth, y2d);
 
    if (IsCaptionChanged)
    {
      HalfWidth = GetTextWidth() * 0.5f ;
 
      base.Update();
      IsCaptionChanged = false;
    }           
  }
TextLabel3D.cs


Ponieważ etykieta jest w istocie obiektem znajdującym się w dwuwymiarowej przestrzeni ekranu, w każdej klatce musimy odwzorować jej pozycję w przestrzeni 3D na punkt na ekranie. Wykorzystujemy do tego celu macierze widoku i projekcji kamery, których silnik używa do renderowania wszelkich obiektów trójwymiarowych. Macierz widoku (ViewMatrix) przekształci punkt 3D zgodnie z transformacją kamery, a macierz projekcji zamknie jego współrzędne w przedziale ostrosłupa widzenia. Zagadnienie transformacji 3D jest dobrze wyjaśnione w wielu artykułach, które można znaleźć w sieci, a także na tym portalu.

http://www.informatyka.wroc.pl/node/619?page=0,1

Po obliczeniu pozycji punktu w ostrosłupie widzenia musimy je przeskalować (5-6), by uzyskać współrzędne ekranowe. Ponadto pamiętając, że w przestrzeni ekranu oś Y jest skierowana w dół, a nie w górę, jak to ma miejsce w przestrzeni 3D silnika Ogre, odwracamy współrzędną Y (6). Następnie, w przypadku gdy tekst nie znajduje się na ekranie ukrywamy go zwalniając kartę graficzną z jego renderowania. Ustawiając pozycję napisu przesuwamy go o połowę jego szerokość w lewo, dzięki czemu uzyskujemy efekt wyśrodkowania.
W przypadku zmiany etykiety przeliczamy połowę szerokości napisu za pomocą metody GetTextWidth(), którą zaimplementujemy w klasie TextLabel.

Zaimplementujemy teraz metodę GetTextWidth() w klasie TextLabel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  public float GetTextWidth()
  {
    FontPtr font = FontManager.Singleton.GetByName(TextArea.FontName);
    float textWidth = 0;
 
    for (int i = 0; i < TextArea.Caption.Length; i++)
    {   
      if (TextArea.Caption[i] == 32)
        textWidth += 0.5f;
      else
        textWidth += font.GetGlyphAspectRatio(TextArea.Caption[i]);                
    }
 
    return textWidth * TextArea.CharHeight / Engine.Singleton.Camera.AspectRatio;
  }
TextLabel.cs


Metoda nie jest skomplikowana - po pobraniu czcionki za pośrednictwem menadżera czcionek silnika Ogre iterujemy po literach etykiety sumując współczynniki szerokość/wysokość kolejnych znaków. Sumę tą mnożymy przez wielkość znaku uzyskując tym samym interesującą nas wartość. Szerokość musi zostać przeskalowana przez odwrotność współczynnika proporcji ekranu, ponieważ w układzie współrzędnych ekranowych obszar ekranu jest kwadratem, podczas gdy w rzeczywistości tak być nie musi.

Dodajmy możliwość utworzenia obiektu klasy TextLabel3D poprzez fabrykę:
1
2
3
4
5
6
7
8
9
10
11
  public TextLabel3D NewTextLabel3D(
    String fontName, float fontSize, ColourValue colourtop, ColourValue colourbottom)
  {
    TextLabel3D textLabel = new TextLabel3D(
      GetUniqueLabelName(), fontName, fontSize, colourtop, colourbottom);
 
    Panel.AddChild(textLabel.TextArea);
    Labels.Add(textLabel);
 
    return textLabel;
  }
TextLabeler.cs


Metoda wygląda niemalże identycznie jak w przypadku zwykłej etykiety.

Możemy teraz dodać przykładową etykietę do programu:
1
2
3
4
5
6
7
8
9
10
  TextLabel3D tl3d = Engine.Singleton.Labeler.NewTextLabel3D(
    "Primitive", 0.04f, new ColourValue(0.7f, 0.4f, 0), new ColourValue(1,1.0f, 0.6f));
  tl3d.Caption = "Player Character";
  tl3d.IsVisible = true;
 
  while (true)
  {
    ...
    tl3d.Position3D = player.Position + new Vector3(0, 1.0f, 0);
    ...
Program.cs, Main()


Jak widać, użycie nowej klasy jest bardzo proste. W efekcie, nad głową postaci gracza powinien znajdować się odpowiedni napis.




Aktualny kod źródłowy

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

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com