W tym temacie przedstawię wam trochę więcej informacji na temat rysowania w XNA.
Na początku wyświetlimy tekst a potem przejdziemy do wyświetlenia animacji 2D.
Aby móc wyświetlić tekst w naszej grze będziemy potrzebowali zmiennej przechowywującej naszą czcionkę:
SpriteFont font; // nasza czcionka
oraz samej czcionki. Możemy ją szybko utworzyć w Visualu:
- klikamy prawym na nasz Content a następnie Add i New Item
|
\/
- wybieramy Sprite font, i zmieniamy nazwę na font.spritefont
Teraz wystarczy że wczytamy naszą utworzoną czcionkę do zmiennej font i zainicjujemy nasz bufor grafiki:
I już możemy korzystać ze zmiennej font do wyświetlania tekstu na ekranie:
Wygląd naszej gry po zdebugowaniu:
Dodatkowo zaznaczyłem na żółto konstruktor nowego obiektu typu Vector2, określa on położenie tekstu i oczywiście można parametry w nim podane edytować w zależności od potrzeb a nawet ustawiać w innych funkcjach jak np. Update() by później je tylko przekazać do tej instrukcji.
Vector2(100,
200) Pierwszy parametr to położenie na osi X Drugi parametr to położenie na osi Y |
----------------------------------------------------------------------------------------------------
Przy okazji pisania kodu, może być wam uciążliwe wyłączanie aplikacji poprzez konieczność ciągłego klikania w X. Dlatego nim przejdziemy do animacji postaci zaznajomię was z obsługą klawiatury:)
Żeby móc operować naszym programem poprzez klawiaturę potrzebujemy utworzyć obiekt przechowujący stan naszych klawiszy(czyli które z nich są naciśnięte a które zwolnione), umieszczamy go na początku klasy naszej gry:
KeyboardState keybStat;
Teraz chcemy żeby w każdym obiegu pętli był sprawdzany świeży stan klawiszy dlatego musimy użyć przypisania:
keybState = Keyboard.GetState();
I już możemy sprawdzać czy nie został naciśnięty klawisz Esc, a jeśli tak to zakończymy nasza aplikacje:
if
(keybState.IsKeyDown(Keys.Escape)) Exit() ;
Oczywiście w żadnym razie nie na da się takie proste wczytywanie stanu i sprawdzanie jeśli będziemy chcieli poruszać się np. o jedną pozycje w menu, albo wykonać tylko raz daną akcję dla pojedynczego wciśniętego przycisku. Dlaczego? ponieważ nasza gra wykonuje się około 60 razy na sekundę i dla każdego obiegu nasze ciągłe trzymanie palca na klawiszu to jakby klikanie go co każdy przebieg pętli gry, nawet szybkie kliknięcie to kilkanaście pętli co się równa kilkunastą wywołań akcji w naszych if-ach.
Żeby temu zaradzić stosujemy drugą zmienną przechowującą stan poprzedniego stanu przed naszym nowym, będziemy je porównywać w naszych if-ach:
Dla zobrazowania, nasz utworzone wcześniej tekst będziemy przesuwać o 20 pikseli w prawo dla każdego kliknięcia klawiszem SPACJI. Zaczniemy od dodania instrukcji:
keybStateOld =
keybState;
keybState = Keyboard.GetState();
Nie dajemy jej za, ponieważ nie miałoby to sensu, gdyż oba obiekty
przechowujące stan klawiszy posiadały by ten sam stan klawiszy.
|
dodajemy do zmiennych klasy gry obiekt:
Vector2 pozycjaTXT;
ustawiamy jego wartość początkową w metodzie LoadContent() np. na taką:
pozycjaTXT = new Vector2(0,
100);
if
(keybState.IsKeyDown(Keys.Space) &&
keybStateOld.IsKeyUp(Keys.Space))
{
pozycjaTXT += new Vector2(20,
0);
}
na koniec zmieniamy w instrukcji rysującej nasz tekst:
new Vector2(100, 200)
na
pozycjaTXT
Całość wygląda tak:
i sprawdzamy efekt naszego kodu. Po kliknięciu SPACJI nasz tekst powinien przemieszczać się o 20 pikseli w prawo. To na razie wszystko o przyciskach, ale analizując kod możecie zobaczyć kilka rzeczy:
-zwracanie wartości logicznej(prawda/fałsz) przez stan klawiatury.
- musimy sami zadeklarować czy chcemy sprawdzić czy przycisk jest naciśnięty, czy może wolimy sprawdzić czy przycisk jest zwolniony odwołując się do odpowiednich funkcji obiektu KeyboarState
--------------------------------------------------------------------------------------------------
Wróćmy jednak do animacji, na którą teraz przyszła kolej.
Mamy 2 podstawowe możliwości wyświetlania animacji:
- za pomocą pojedynczych obrazów, przedstawiających pojedyncze klatki
- lub za pomocą tablicy takich klatek na jednym obrazku, zwaną spritem
My zajmiemy się tą drugą możliwością. Do tego będzie oczywiście potrzebny nam taki sprite, ja użyję sprite od lf-a 2(Little Fighters 2):
Jest to w sumie lekko modyfikowany sprit, Wy jeśli chcecie możecie pobrać inny obraz postaci z LF2 lub wsiąść ten. Nie ma to różnicy ponieważ kwadratu są tutaj tej samej wielkości w każdej z postaci.
Oczywiście nasz obraz należy dodać do Content i wczytaj w metodzie LoadContent() do utworzonego dla niego obiektu Texture2D:
Teraz utworzymy nową funkcję tworzącą miernik, dzięki któremu będziemy mogli odmierzać obieg pętli:
-po pierwszw dodajmy zmienną:
int
TIMER;
Mamy już timer, teraz zaczniemy pisać kod do obsługi animowania klatek:
-dodajemy kilka zmiennych do naszej klasy:
public Rectangle sourceRectangle; // położenie
aktualnej klatki
int blokX; // rząd X
naszych animacji w sprite
int
blokY; // rząd Y naszych animacji w sprite
int
wielkośćKlatek; // szerokość i wysokość pojedyńczej
klatki
int
PozycjaX; //pozycja animacji na osi X
int
PozycjaY; // pozycja animacji na osi Y
w metodzie ContentLoad() dodajemy:
wielkośćKlatek = 80;
PozycjaX = 50;
PozycjaY = 100;
Wygląda to tak:
if
(TIMER % 10 == 0) // zmiana animacji 6 razy na sekunde
{
if
(blokX < 7)
{
blokX++;
}
else
{
blokX = 4;
}
blokY = 0;
}
//
Utworzenie nowego kwadratu aktualnej klatki
sourceRectangle = new Rectangle(blokX
* wielkośćKlatek, blokY * wielkośćKlatek, wielkośćKlatek, wielkośćKlatek); oraz pozwalająca przesuwać naszą animacje na 4 strony:
if
(keybState.IsKeyDown(Keys.Up)) PozycjaY--;
if
(keybState.IsKeyDown(Keys.Down)) PozycjaY++;
if
(keybState.IsKeyDown(Keys.Left)) PozycjaX--;
if
(keybState.IsKeyDown(Keys.Right))
PozycjaX++;
teraz w metodzie Draw() dodajemy:
Rectangle
pozycja = new Rectangle(PozycjaX,
PozycjaY, wielkośćKlatek, wielkośćKlatek);
spriteBatch.Draw(firen, pozycja,
sourceRectangle, Color.White);
Skorzystałem tutaj z funkcji Draw ale pobierającej jeden parametr więcej(nasz sourceRectangle), który jest kwadratem zawierającym wycinek fragmentu obrazka. |
Możemy przetestować nasza aplikację i zobaczyć co się dzieje:)
Jak widzicie postać ma bardzo szybkie ruchy... zastanówcie się dlaczego?
Dla ułatwienia dodam że brakuje pewnej rzeczy i umieszczę cały kod mojej aplikacji oczywiście już w pełni działający(jeśli chcecie z niej skorzystać skopiujcie tylko wnętrze klasy gry, bez namespace i bibliotek.)
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Input;
namespace
Tutek
{
public class Game1 :
Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager
graphics;
SpriteBatch
spriteBatch;
SpriteFont
font; // nasza czcionka
KeyboardState
keybState; // stan klawiszy
KeyboardState
keybStateOld; // stan klawiszy
Vector2
pozycjaTXT; // pozycja naszego tekstu na ekranie
Texture2D
firen; // nasz sprit z postacią
int
TIMER; // zmienna odpowiedzialna za animacje
public Rectangle sourceRectangle; // położenie
aktualnej klatki
int
blokX; // rząd X naszych animacji w sprite
int
blokY; // rząd Y naszych animacji w sprite
int
wielkośćKlatek; // szerokość i wysokość pojedyńczej
klatki
int
PozycjaX; //pozycja animacji na osi X
int
PozycjaY; // pozycja animacji na osi Y
public
Game1()
{
graphics = new
GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected
override void
Initialize()
{
base.Initialize();
}
protected
override void
LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("font");
// Wczytujemy czcionkę
pozycjaTXT = new Vector2(0,
100); // początkową pozycji tekstu
firen = Content.Load<Texture2D>("firen0copysg6");
// wczytanie sprita
wielkośćKlatek = 80;
PozycjaX = 50;
PozycjaY = 100;
}
protected
override void
UnloadContent()
{
}
protected
override void
Update(GameTime gameTime)
{
keybStateOld = keybState;
keybState = Keyboard.GetState();
if
(keybState.IsKeyDown(Keys.Escape)) Exit(); // Wyśjcie po nacisnieciu Esc
if
(keybState.IsKeyDown(Keys.Space) &&
keybStateOld.IsKeyUp(Keys.Space))
{
pozycjaTXT += new Vector2(20,
0);
}
UpTime();
//------------------ANIMACJA------------------------------
if
(TIMER % 10 == 0) // 6 razy na sekunde
{
if
(blokX < 7)
{
blokX++;
}
else
{
blokX = 4;
}
blokY = 0;
}
//
Utworzenie nowego kwadratu aktualnej klatki
sourceRectangle = new Rectangle(blokX
* wielkośćKlatek, blokY * wielkośćKlatek, wielkośćKlatek, wielkośćKlatek);
//---------------------STEROWANIE--------------------------
if
(keybState.IsKeyDown(Keys.Up)) PozycjaY--;
if
(keybState.IsKeyDown(Keys.Down)) PozycjaY++;
if
(keybState.IsKeyDown(Keys.Left)) PozycjaX--;
if
(keybState.IsKeyDown(Keys.Right))
PozycjaX++;
base.Update(gameTime);
}
protected
override void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.DrawString(font, "Test pisania po ekranie", pozycjaTXT, Color.White);
//-----------------------------ANIMACJA------------------------------
Rectangle
pozycja = new Rectangle(PozycjaX,
PozycjaY, wielkośćKlatek, wielkośćKlatek);
spriteBatch.Draw(firen, pozycja,
sourceRectangle, Color.White);
//-------------------------------------------------------------------
spriteBatch.End();
base.Draw(gameTime);
}
// powiększa
czas lub go resetuje
public void UpTime()
{
if
(TIMER < 60)
{
TIMER++;
}
else
{
TIMER = 0;
}
}
}
}
|
Dzięki wszystkim za uwagę,
Silver
PS. Rozwiązaniem powyższej zagadki jest po prostu brak wywołania funkcji UpTime()