Podstawy XNA - Transformacje elementów
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ś:
- znać podstawy języka C#,
- wiedzieć, jak utworzyć nowy projekt XNA,
- potrafić wyświetlić obraz na ekranie.
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.
- 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.
- Pobierz pliki niezbędne do wykonania ćwiczenia:
- pobierz plik KursXNA.zip,
- rozpakuj archiwum w dowolnym katalogu,
- w strukturze katalogów odnajdź plik pilka.png.
- 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ę.
- Dodaj nowy obiekt typu Texture2D do klasy Game1:
- otwórz plik Game1.cs,
- w klasie Game1, poniżej linii SpriteBatch spriteBatch; dodaj:
Texture2D pilka;
- 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. |
- 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.
- 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);
}