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

28.03.2011 - Mateusz Osowski
TrudnośćTrudność
Tematem tego artykułu jest ekwipunek i uzbrojenie. Zaimplementujemy prosty interfejs przeglądania ekwipunku i pozwolimy graczowi wybrać broń bohatera.

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

Zanim zabierzemy się do tworzenia graficznego interfejsu, musimy się zastanowić jak chcemy reprezentować przedmioty. Klasa odpowiedzialna za to powinna nieść ze sobą informacje o siatce i właściwościach fizycznych przedmiotu w świecie gry, a także o rysunku w oknie ekwipunku. Dobrym nawykiem jest unikanie tworzenia niepotrzebnych bytów, więc najlepiej byłoby rozbudować którąś z istniejących klas. Doskonale nada się do tego celu DescribedProfile. Interesują nas dwa nowe pola. Pierwsze - InventoryPictureMaterial będzie przechowywać nazwę materiału obrazka. Ponieważ z czasem przedmiotów przybędzie, warto zrobić sobie szablon materiału przezroczystego obrazka (patrz: QuadMaterial.material). Drugie pole, IsEquipment informować będzie nas o tym, czy przedmiot wchodzi w skład ekwipunku postaci, tj. czy jest noszony przez postać. Informację wykorzystamy do oznaczania ubranych rzeczy na liście, przyda nam się ona również w przyszłości.

1
2
3
4
  ...
  public String InventoryPictureMaterial;
  public bool IsEquipment;
 ...
DescribedProfile.cs


Ekwipunek będziemy przechowywać w formie listy – tak więc dopisujemy ową kolekcję do klasy postaci:
1
  public List<DescribedProfile> Inventory;
Character.cs


Pamiętajmy także, że listę trzeba gdzieś zainicjować, najlepiej w konstruktorze.

Dotychczasowy kod podnoszenia przedmiotów dokonuje jedynie usunięcia przedmiotu. Musimy więc dodać linijkę „zbierającą” przedmiot, dodając go do listy:
1
2
3
4
5
6
7
8
9
...
if (pickItemDownAnimation.TimePosition > pickItemDownAnimation.Length * 0.5f)
{
  Inventory.Add(PickingTarget.Profile);
  Engine.Singleton.ObjectManager.Destroy(PickingTarget);
  PickingTarget = null;
  PickingState = PickingStates.AFTER;
}
Character.cs, Update()


Posiadając działający ekwipunek możemy zająć się tworzeniem interfejsu graficznego. Pomysł jest prosty:



Po lewej stronie ekranu wyświetlimy listę posiadanych przedmiotów. Zaznaczony przedmiot będzie podświetlony na biało, ubrany – na niebiesko. Dodatkowo na środku będziemy pokazywać duży obrazek i opis zaznaczonego przedmiotu.

Zaczynamy od utworzenia klasy HUDInventory. Chcemy, aby lista przedmiotów była przewijana. Osiągniemy to wypełniając kwadraciki przedmiotami o indeksach przesuniętych o wartość przewinięcia. Chcemy, aby kwadracikom łatwo dało się przypisać rysunki i podświetlenia. W tym celu tworzymy wewnętrzną klasę InventorySlot:
1
2
3
4
5
6
7
8
9
class HUDInventory
{
  class InventorySlot
  {
    public const float Size = 0.11f;
    public static float Width;
    SimpleQuad BgQuad;
    SimpleQuad Picture;
    SimpleQuad BlueQuad;
HUDInventory.cs


Pole statyczne Width wypełnimy dopiero, gdy będzieli znali proporcje ekranu. BgQuad będzie kwadracikiem tła widocznym zawsze. Picture, jak sama nazwa wskazuje, będzie obrazkiem, a BlueQuad niebieskim podświetleniem widocznym gdy przedmiot jest w użyciu.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public InventorySlot(float left, float top)
{                
  BgQuad = Engine.Singleton.Labeler.NewSimpleQuad(
    "QuadMaterial", 
    left, top, Width, Size, 
        new ColourValue(1, 1, 1), 1);
  Picture = Engine.Singleton.Labeler.NewSimpleQuad(
    "QuadMaterial", 
    left, top, Width, Size, 
    new ColourValue(1, 1, 1), 2);
  BlueQuad = Engine.Singleton.Labeler.NewSimpleQuad(
    "HighlightBlueMaterial", 
        left, top, Width, Size, 
        new ColourValue(1, 1, 1), 3);
 
  Picture.IsVisible = false;
  BlueQuad.IsVisible = false;
}
HUDInventory.cs


Inicjujemy kwadraciki i ukrywamy je. Każdy z kwadracików wyląduje na osobnej warstwie ogrowego Overlay.
Przydatna będzie możliwość ustawienia widoczności wszystkich elementów:
1
2
3
4
5
6
7
8
9
  public bool IsVisible
  {
    set
    {
      BgQuad.IsVisible = value;
      Picture.IsVisible = value;
      BlueQuad.IsVisible = value;
    }
  }
HUDInventory.cs, InventorySlot


Kluczową funkcjonalnością jest ustawienie obrazka i podświetlenia na podstawie informacji o przedmiocie:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  public void SetItem(DescribedProfile item)
  {
    if (item != null)
    {
      Picture.Panel.MaterialName = item.InventoryPictureMaterial;
      BlueQuad.IsVisible = item.IsEquipment;
    }
    else
    {
      Picture.Panel.MaterialName = "QuadMaterial";
      BlueQuad.IsVisible = false;
    }
  }  
}
HUDInventory.cs, InventorySlot



W klasie głównej musimy utworzyć pola i ustalić stałe:
1
2
3
4
5
const int SlotsCount = 7;
const float SlotsSpacing = 0.02f;
 
SimpleQuad SelectQuad;        
InventorySlot[] Slots;
HUDInventory.cs


Stała SlotsCount określać będzie ilość prostokącików widocznych na ekranie, a SlotsSpacing odstępy między nimi, a także między krawędziami ekranu. SelectQuad rysować będzie prostokąt wyboru. Wszystkie kwadraciki trzymać będziemy w zwyczajnej liście Slots.

1
2
3
4
5
6
int _SelectIndex;
int _ViewIndex;
 
SimpleQuad DescriptionBg;
SimpleQuad SelectedPicture;
TextLabel DescriptionLabel;
HUDInventory.cs


Prywatne pola _SelectIndex i _ViewIndex będą odpowiedzialne za odpowiednio: indeks wybranego zaznaczonego przedmiotu w ekwipunku (-1 w przypadku braku zaznaczenia lub braku przedmiotów) i indeks elementu, od którego wyświetlane będą przedmioty na liście. Dostęp do nich określimy przez właściwości. Kolejne trzy pola związane będą z kolejno ramką opisu, powiększonym obrazkiem zaznaczonego przedmiotu i jego opisem.
1
2
3
4
5
6
7
8
public HUDInventory()
{
  InventorySlot.Width = InventorySlot.Size / Engine.Singleton.Camera.AspectRatio;
  Slots = new InventorySlot[SlotsCount];
  for (int i = 0; i < SlotsCount; i++)
    Slots[i] = new InventorySlot(
      SlotsSpacing, SlotsSpacing + i * (InventorySlot.Size + SlotsSpacing
      ));
HUDInventory.cs


Zakładamy, że obiekt HUDInventory będzie tworzony po inicjalizacji kamery, więc możemy obliczyć szerokość kwadracika. Następnie tworzymy określoną ilość kwadracików pozycjonując je odpowiednio wzdłuż pionu ekranu.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  DescriptionBg = 
    Engine.Singleton.Labeler.NewSimpleQuad(
      "QuadMaterial", 0.2f, 0.5f, 0.6f, 0.45f, ColourValue.White, 1);
  SelectedPicture = Engine.Singleton.Labeler.NewSimpleQuad("QuadMaterial", 
    0.21f, 
    0.58f,
    0.3f / Engine.Singleton.Camera.AspectRatio, 
    0.3f, ColourValue.White, 2);
  DescriptionLabel = Engine.Singleton.Labeler.NewTextLabel("Primitive", 0.03f, 
    new ColourValue(0.7f, 0.4f, 0), new ColourValue(1, 1.0f, 0.6f), 2);
  DescriptionLabel.SetPosition(0.45f, 0.51f);
  
  IsVisible = false;
}
HUDInventory.cs, konstruktor


Tworzymy ramkę opisu o sztywno ustalonych wymiarach i umiejscowieniu. Rozmiar czcionki użytej w opisie jest mniejszy niż w przypadku dialogów. Na koniec ukrywamy wszystko ustawiając właściwość IsVisible, którą zaraz napiszemy.
1
2
3
4
5
6
7
public Character Character
{
  get
  {
    return Engine.Singleton.HumanController.Character;
  }
}
HUDInventory.cs


Ponieważ przy wypełnianiu i aktualizowaniu listy przedmiotów często będziemy odnosić się do postaci podpiętej do kontrolera postaci, tworzymy sobie odpowiedni akcesor skracający dostęp.
1
2
3
4
5
6
7
8
void UpdateView()
{           
  for (int i = ViewIndex; i < ViewIndex + SlotsCount; i++)
    if (i < Character.Inventory.Count)
      Slots[i - ViewIndex].SetItem(Character.Inventory[i]);
  else
    Slots[i - ViewIndex].SetItem(null);
}
HUDInventory.cs


Metoda UpdateView() zgodnie z planem wypełnia kolejne kwadraciki odpowiadającymi przedmiotami. Pozostałe kwadraciki, których nie będzie się dało już pokryć ekwipunkiem zostaną wyczyszczone.
5
Twoja ocena: Brak Ocena: 5 (3 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com