W tym tutorialu rozbudujemy stworzoną wcześniej przez nas grę. A więc do dzieła.
1. Rozbudowanie klasy WeightKlasę Weight musimy wzbogacić o booleana:
public Boolean IsFlying;
Chcemy mieć do niego dostęp z pliku Game1.cs, więc musi być on publiczny. Upubliczniamy również teksturę, ponieważ będzie ona zmieniana w trakcie gry.
Dodajemy parametr do konstruktora i przypisujemy do niego pole:
public Weight(Texture2D texture, Rectangle rectangle, Vector2 position, float gravity, Boolean isFlying)
{
Texture = texture;
Rectangle = rectangle;
Position = position;
Gravity = gravity;
IsFlying = isFlying;
}
2. Nowe pola w Game1.csStwórzmy teraz kolejne cztery pola:
int score;
Boolean isPlayerAlive;
Random random;
Texture2D weightTexture_2;
Int i boolean nie wymagają tłumaczenia, natomiast random jest generatorem liczb losowych. Do nowych pól przypiszmy teraz wartości w metodzie LoadContent()
score = 0;
isPlayerAlive = true;
random = new Random();
weightTexture_2 = Content.Load<Texture2D>("weight2");
Nowa tekstura:
3. Zmiany w metodzie Update()Pierwszym, co rzuca się w oczy jest podkreślenie pod deklaracją nowego obiektu klasy Weight. Jest to spowodowane dodaniem nowego parametru do rzeczonej klasy. Żeby nie było błędów, do deklaracji trzeba dodać nowy parametr:
Weight weight = new Weight(weightTexture, new Rectangle(mouseRectangle.X, mouseRectangle.Y, 64, 64), new Vector2(mouseRectangle.X, mouseRectangle.Y), 0f, true);
Graczem chcemy ruszać tylko wtedy, kiedy jest żywy. By to osiągnąć, sterowanie postacią trzeba wsadzić w warunek if:
if (isPlayerAlive == true)
{
if (Keyboard.GetState().IsKeyDown(Keys.Right))
{
playerRectangle.X += 3;
}
if (Keyboard.GetState().IsKeyDown(Keys.Left))
{
playerRectangle.X -= 3;
}
}
Dodajmy też warunek if do zrzucania odważników, by nie było to możliwe po śmierci postaci.
if (Mouse.GetState().LeftButton == ButtonState.Pressed && canClick == true && isPlayerAlive == true)
{
Weight weight = new Weight(weightTexture, new Rectangle(mouseRectangle.X, mouseRectangle.Y, 64, 64), new Vector2(mouseRectangle.X, mouseRectangle.Y), 0f, true);
weightList.Add(weight);
canClick = false;
}
Coś co zapomniałem wytłumaczyć w poprzednim tutorialu - && oznacza logiczne 'and', czyli by blok if był wykonany, wszystkie warunki muszą być spełnione.
W poprzednim tutorialu było małe niedopatrzenie - zrobiłem dwie pętle foreach w miejscu, gdzie można załatwić wszystko w jednej. Kopiujemy zawartość jednej pętli do drugiej, a niepotrzebną usuwamy.
foreach (Weight weight in weightList)
{
weight.Position.Y += weight.Gravity;
weight.Rectangle.Y = (int)weight.Position.Y;
weight.Gravity += 0.1f;
if (weight.Rectangle.Intersects(playerRectangle))
{
playerRectangle.Y = -100;
}
}
Odważniki powinny spadać i uderzać w gracza tylko wtedy, kiedy są w powietrzu, a gracz żyje. W tym celu wstawiamy warunek do pętli foreach odpowiadającej za spadanie:
foreach (Weight weight in weightList)
{
if (weight.IsFlying == true)
{
weight.Position.Y += weight.Gravity;
weight.Rectangle.Y = (int)weight.Position.Y;
weight.Gravity += 0.1f;
if (weight.Rectangle.Intersects(playerRectangle) && isPlayerAlive == true)
{
playerRectangle.Y = -100;
}
}
}
Kiedy odważnik uderzy w ziemię, chcemy by się zatrzymał. W tym celu dodajemy kolejny warunek do pętli foreach:
if (weight.Rectangle.Intersects(groundRectangle))
{
weight.Rectangle.Y = 384;
weight.IsFlying = false;
}
Jeśli wykryta jest kolizja między odważnikiem a podłożem, odważnik jest ustawiany na wysokości 384 i zmieniana jest wartość pola odpowiadającego za jego latanie oraz zabijanie postaci.
Czas zaktualizować kolizje odważnika z graczem:
if (weight.Rectangle.Intersects(playerRectangle))
{
isPlayerAlive = false;
weight.Texture = weightTexture_2;
}
Teraz zamiast przemieszczać gracza w niewidoczne miejsce, zmieniamy wartość booleana. Zmieniamy również teksturę odważnika, który uderzył w gracza.
4. Zmiany w metodzie Draw()Jeśli uruchomiliście grę po przeprowadzeniu zmian w Update(), zobaczyliście, że po uderzeniu odważnika w gracza, gracz nadal jest wyświetlany. Pora uzależnić jego wyświetlanie od booleana isPlayerAlive.
if (isPlayerAlive == true)
{
spriteBatch.Draw(playerTexture, playerRectangle, Color.White);
}
Mogliście zauważyć również, że gracz jest wyświetlany za odważnikami. Spowodowane jest to kolejnością rysowania - każda kolejna rzecz jest rysowana na poprzedniej. Żeby zmienić kolejność rysowania, wystarczy zmienić kolejność kodu - blok kodu stworzony przed chwilą należy przenieść pod pętlę foreach, która odpowiada za rysowanie odważników:
foreach (Weight weight in weightList)
{
weight.Draw(spriteBatch);
}
if (isPlayerAlive == true)
{
spriteBatch.Draw(playerTexture, playerRectangle, Color.White);
}
5. Pisanie tekstu i wynikiŻeby mieć możliwość napisania czegokolwiek, trzeba stworzyć nowy SpriteFont. W tym celu zadeklarujmy:
SpriteFont font;
Teraz trzeba utworzyć plik czcionki. Żeby to zrobić,
prawoklik na [Nazwa projektu]Content (Content) →
Add... →
New Item... →
Sprite Font. Wybieramy nazwę fonta (w moim przypadku score) i klikamy
OK.
Teraz chwila o pliku XML stworzonego fonta. Omówię tu trzy najważniejsze rzeczy:
<FontName>Segoe UI Mono</FontName>
To oczywiście nazwa czcionki.
<Size>14</Size>
Kolejna oczywistość - rozmiar czcionki.
<CharacterRegions>
<CharacterRegion>
<Start>& #32;</Start>
<End>& #126;</End>
</CharacterRegion>
</CharacterRegions>
Ten kawałek kodu odpowiada za to, jakie znaki wpakować do czcionki. Domyślnie nie ma w niej polskich znaków. Żeby były one dostępne, trzeba rozszerzyć CharacterRegion - między znacznikami <End> i </End> wpisać
& #399;
Dzięki temu czcionka będzie zawierała cały pakiet znaków Łaciński Rozszerzony - B. Ważne - Kody symboli nie mają spacji między & i #.
Wracamy do Game1.cs. W LoadContent() przypisujemy polu font naszego spritefonta:
font = Content.Load<SpriteFont>("score");
Teraz możemy zająć się aktualizacją wyniku. Za każdy odważnik którego uniknęliśmy zostanie dodany punkt. Należy zaktualizować kod kolizji odważnika z podłożem.
if (weight.Rectangle.Intersects(groundRectangle))
{
weight.Rectangle.Y = 384;
weight.IsFlying = false;
if (isPlayerAlive == true)
{
score += 1;
}
}
Punkt jest dodawany, jeśli odważnik uderza w ziemię, a gracz żyje. Wynik trzeba jeszcze narysować.
W metodzie Draw() dopisujemy:
spriteBatch.DrawString(font, "Wynik: " + score.ToString(), new Vector2(10, 10), Color.Black);
DrawString przyjmuje cztery parametry: czcionkę, tekst, wektor i kolor. Przed napisaniem wyniku (pole score) trzeba go skonwertować do stringa, czym zajmuje się metoda ToString.
6. Liczby losoweChcemy, by odważniki były zrzucane w losowych miejscach. Najpierw zmieńmy trochę kod aktualizacji pozycji odważników:
weight.Position.Y += weight.Gravity;
weight.Rectangle.Y = (int)weight.Position.Y;
weight.Rectangle.X = (int)weight.Position.X;
weight.Gravity += 0.1f;
Dzięki temu pozycja pozioma prostokątów jest równa pozycji poziomej wektora.
Teraz zmieńmy kod odpowiadający za zrzut odważników:
Weight weight = new Weight(weightTexture, new Rectangle(0, 0, 64, 64), new Vector2(random.Next(0,737), -64), 0f, true);
Prostokąt ustawiamy w pozycji 0,0 ponieważ aktualizuje się on względem wektora. Do wektora z kolei przypisujemy wartość X losowaną spomiędzy 0 a 736 (736, nie 737) i wartość Y równą -64.
7. PosłowieKolejny tutorial ukończony. Oczywiście czekam na komentarze, krytykę, pytania etc.
Źródło projektu:
https://dl.dropbox.com/u/67704253/Xna%20Tutorial/2/source2.zip