Podstawy XNA - Transformacje elementów  Udostępnij na: Facebook

Autor: Kuba Ostrowski

Opublikowano: 2012-03-27

Transformacja elementów w grze polega na zmianie ich właściwości, takich jak skala, rotacja czy pozycja. W tym artykule zostanie przedstawione, w jaki sposób dokonać transformacji tekstur widocznych na ekranie.

Przed wykonaniem zadań powinieneś:

Po wykonaniu zadań nauczysz się:

  • jak dokonać transformacji elementów podczas wyświetlania ich na ekranie.

Implementacja

Celem naszej aplikacji będzie wczytanie tekstury, przedstawiającej piłeczkę, oraz wyświetlenie jej na ekranie z zastosowaniem różnych transformacji (Rys. 1.).

Rys. 1. Końcowy efekt naszej aplikacji.

W celu wyświetlenia na ekranie elementów, wraz z zastosowaniem różnych transformacji, musimy wykonać następujące kroki:

  • dodać do projektu Content plik graficzny,
  • dodać obiekt przedstawiający teksturę do klasy Game1,
  • wczytać teksturę do wcześniej utworzonego obiektu w metodzie LoadContent(),
  • wyświetlić obrazek na różne sposoby, używając odmiennych właściwości.

Dodawanie tekstury piłeczki do aplikacji

W pierwszym kroku musimy dodać do projektu pliki graficzne - piłeczki.

  1. Utwórz nowy projekt i nazwij go "TransformacjeElementow". W tym celu:
  • uruchom Visual Studio,
  • wybierz File ‑> New ‑> Project,
  • z kategorii XNA Game Studio 4.0 wybierz projekt Windows Game (4.0),
  • w polu Name wpisz TransformacjeElementow (Rys. 2.).

Rys. 2. Tworzenie nowego projektu.

  1. Pobierz pliki niezbędne do wykonania ćwiczenia:
  • pobierz plik KursXNA.zip,
  • rozpakuj archiwum w dowolnym katalogu,
  • w strukturze katalogów odnajdź plik pilka.png.
  1. Dodaj pliki do projektu:
  • w oknie Solution Explorer naciśnij prawym przyciskiem myszy na "TransformacjeElementowContent (Content)" (Rys. 3.),
  • wybierz Add -> Existing Item,
  • wybierz plik pilka.png i naciśnij przycisk Add.

Rys. 3. Dodawanie grafiki do TransformacjeElementowContent.

Wczytanie grafiki do obiektu klasy Texture2D

W kroku drugim, obraz piłeczki musi zostać wczytany do obiektu klasy Texture2D, który będzie reprezentował naszą teksturę.

  1. Dodaj nowy obiekt typu Texture2D do klasy Game1
  • otwórz plik Game1.cs,
  • w klasie Game1, poniżej linii SpriteBatch spriteBatch; dodaj:
Texture2D pilka;
  1. Wczytaj teksturę do nowo utworzonego obiektu „pilka”:
  • przejdź do metody LoadContent(), po linijce // TODO wpisz następujący kod:
pilka = Content.Load<Texture2D>("pilka");

Stosowanie transformacji

Transformacja elementów polega na zmianie ich właściwości, użytych do wyświetlania. Wystarczy więc użyć odpowiedniego przeciążenia metody SpriteBatch.Draw, podając odmienne wartości od domyślnych. W naszej aplikacji wykorzystamy 2 sposoby przeciążenia:

SpriteBatch.Draw(Texture2D tekstura, Rectangle(int pozycja X,int pozycja Y,int szerokość,int wysokość) prostokąt docelowy, Nullable<Rectangle> prostokąt źródłowy, Color kolor odcienia, float rotacja, Vector2 środek, SpriteEffects efekt, float głębokość);

oraz:

spriteBatch.Draw(Texture2D tekstura, Vector2 pozycja, Nullable<Rectangle> prostokąt źródłowy,Color kolor odcienia, float rotacja, Vector2 środek, float skala, SpriteEffects efekt, 1);
  • Rectangle prostokąt docelowy – określa parametry, z jakimi zostanie wyświetlony nasz obraz, pozycja, szerokość, wysokość,
  • Nullable<Rectangle> prostokąt źródłowy – określa parametry tekstury, jakie zostaną użyte do wyświetlenia naszego obrazu,
  • rotacja – obrót naszej tekstury wokół jej środka w radianach,
  • środek – środek elementu (domyślnie punkt 0,0 obiektu, lewy górny róg),
  • skala – wielkość naszego elementu zostanie przez nią przemnożona,
  • efekt – obrócenie elementu względem osi X lub Y o 180°.
Informacja
Typ Nullable<T> pozwala na podanie wartości null zamiast generycznej T. Jeśli w przeciążeniu SpriteBatch.Draw użyte zostanie null, wykorzystane będą wartości wejściowe naszej tekstury.
  1. Wyświetl elementy, korzystając z odmiennych właściwości:
  • przejdź do metody Draw, po //TODO wpisz:
spriteBatch.Begin();
spriteBatch.Draw(pilka, new Rectangle(400, 420, pilka.Width, pilka.Height * 2), new Rectangle(10,10,40,30), Color.White, MathHelper.PiOver2, Vector2.Zero, SpriteEffects.FlipHorizontally, 1);
spriteBatch.Draw(pilka, new Vector2(200,120),null, Color.White, MathHelper.PiOver4,Vector2.Zero,2.5f,SpriteEffects.None, 1);
spriteBatch.Draw(pilka, new Vector2(500, 120), null, Color.White, MathHelper.PiOver4, Vector2.Zero, 3, SpriteEffects.FlipVertically, 1);
spriteBatch.Draw(pilka, new Rectangle(0, 0, pilka.Width * 2, pilka.Height * 2), null, Color.White, 0, new Vector2(pilka.Width,pilka.Height)/2, SpriteEffects.None, 1);
//rotacja w czasie
spriteBatch.End();
  • zapisz zmiany,
  • wciśnij F5 lub wybierz Debug->Start Debugging,
  • sprawdź, czy Twoja aplikacja wyświetliła obrazki tak, jak na Rys. 4.
Informacja
Należy zauważyć, że ostatni rysowany element został wyświetlony na pozycji 0,0, jednak na ekranie widać tylko ¼ naszej piłeczki, stało się tak, ponieważ obiekt rysowany jest zawsze od środka (domyślnie lewy górny róg).

Rys. 4. Tekstury o różnych wartościach wyświetlania.

  1. Rotacja w czasie:
  • do klasy Game1 dodaj nową zmienną typu float, reprezentującą obrót naszej tekstury w czasie, zaraz pod linijką Texture2D pilka:
float rotacja;
  • przejdź do metody Update, po //TODO wpisz:
rotacja = (rotacja + (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.08f) % 360f;
Informacja
Wartość pola rotacja ulega zmianie (za każdym razem, kiedy wywoływana jest funkcja Update) o różnicę w czasie od poprzedniej klatki w milisekundach razy pewna mała stała. Reszta z dzielenia, na końcu, zapobiega operowaniu na wartościach powyżej 360 (ponieważ całkowity obrót może wynieść 360°, a przepełnienie zmiennej może spowodować pewien przeskok).
  • w metodzie Draw, po „//rotacja w czasie”, dodaj linijkę, która dorzuci do kolejki rysowania element o rotacji uzależnionej od wartości nowej zmiennej:
spriteBatch.Draw(pilka, new Rectangle(370, 290, pilka.Width * 2, pilka.Height * 2), null, Color.White,MathHelper.ToRadians(rotacja),Vector2.Zero, SpriteEffects.None, 1);
  • zapisz zmiany,
  • wciśnij F5 lub wybierz Debug->Start Debugging,
  • zobacz, czy nowy element obraca się, a rozmieszczenie podobne jest do tego, zaprezentowanego na Rys. 1.

Podsumowanie

W tym artykule nauczyliśmy się, co to jest transformacja i jak zastosować ją na elemencie wyświetlonym na ekranie.

W kolejnym artykule nauczymy się, jak odtwarzać dźwięki.

Zadanie

W celu utrwalenia nabytej wiedzy, wykonaj proste zadanie. Jedną, dowolną właściwość każdego wyświetlanego elementu (skala, rotacja, pozycja), uzależnij od zmiennej pomocniczej, która ulega zmianie w czasie wykonywania aplikacji.

 

Aby zobaczyć podpowiedzi i rozwiązania zadań przełącz widok tej strony na >> klasyczny <<.

  • właściwości elementów, w celu poprawnego wyświetlenia, mogą wymagać mniejszego lub większego skoku w czasie,
  • podczas obliczania zmian w metodzie Update dla każdej zmiennej, spróbuj zastosować wzór:
zmienna = (zmienna + (float)gameTime.ElapsedGameTime.TotalMilliseconds * niska_stała) % maksymalna_wartość;

float rotacja, skala, przesuniecieX, przesuniecieY;
protected override void Update(GameTime gameTime)
{
            // TODO: Add your update logic here
            rotacja = (rotacja + (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.08f) % 360f;
            skala = (skala + (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.01f) % 10f;
            przesuniecieX =  (przesuniecieX + (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.1f) % 360f;
            przesuniecieY = (przesuniecieY + (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.1f) % 360f;
            base.Update(gameTime);
 }
protected override void Draw(GameTime gameTime)
{
            GraphicsDevice.Clear(Color.CornflowerBlue);
            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(pilka, new Rectangle(400, 420 - (int)przesuniecieY, pilka.Width, pilka.Height * 2), new Rectangle(10, 10, 40, 30), Color.White, MathHelper.PiOver2, Vector2.Zero, SpriteEffects.FlipHorizontally, 1);
            spriteBatch.Draw(pilka, new Vector2(200, 120 + (int)przesuniecieY), null, Color.White, MathHelper.PiOver4, Vector2.Zero,skala, SpriteEffects.None, 1);
            spriteBatch.Draw(pilka, new Vector2(600, 120), null, Color.White,MathHelper.ToRadians(rotacja), Vector2.Zero, 3, SpriteEffects.FlipVertically, 1);
            spriteBatch.Draw(pilka, new Rectangle((int)przesuniecieX,0, pilka.Width * 2, pilka.Height * 2), null, Color.White, 0, new Vector2(pilka.Width, pilka.Height) / 2, SpriteEffects.None, 1);
            //rotacja w czasie
            spriteBatch.Draw(pilka, new Rectangle(370, 290, pilka.Width * 2, pilka.Height * 2), null, Color.White,MathHelper.ToRadians(rotacja),Vector2.Zero, SpriteEffects.None, 1);
            spriteBatch.End();
            base.Draw(gameTime);
}