Microsoft XNA, część 2

10.05.2010 - Marcin Oczeretko
TrudnośćTrudność

    W związku z tym, że najważniejszą cechą piłki będzie jej zdolność do odbijania się, zaczniemy od stworzenia metody Bounce(). Będzie ona dość uniwersalna, abyśmy nie musieli pisać osobnej metody do każdego rodzaju odbić (tzn. od ściany, od siatki, od głowy). Niechże przyjmuje ona następujące parametry: wektor normalny, wartość przez jaką mnożymy wektor prędkości piłki po odbiciu, dodatkową prędkość uzyskaną przez odbicie.

    Nie jest celem tego tekstu dokładne omówienie sposobów na implementowanie kolizji, odbić i innych rzeczy związanych z fizyką, ograniczę się zatem do niezbędnego minimum. Najbardziej nieoczywistą rzeczą może być tutaj konstrukcja wektora normalnego dla naszego odbicia, nie jest to jednak nic trudnego. Otóż jeśli piłka odbija się od ściany, to wektor normalny jest po prostu wektorem prostopadłym do niej. Gdy piłka odbija się od głowy postaci gracza - czyli zaszła kolizja dwóch kół, to wektorem normalnym jest wektor łączący ich środki. Na poniższych obrazkach wektor zielony to prędkość przed odbiciem, zółty - po odbiciu, czerwony - wektor normalny.

wektor normalny (koło+linia) wektor normalny (dwa koła)

    Więcej wiedzy z fizyki na razie nam się nie przyda, gdyż sprytnie wykorzystamy statyczną metodę Reflect() klasy Vector2, aby wykonano za nas resztę obliczeń. Poniżej znajduje się cały kod metody Bounce().

1
2
3
4
5
public void Bounce(Vector2 normal, float speedChange, Vector2 addSpeed)
{
    speed = speedChange * Vector2.Reflect(speed, normal);
    speed += addSpeed;
}

    Dopiszmy teraz potrzebne stałe i zadeklarujmy nowe pola (w tym brakujące speed). Postępujemy prawie tak samo, jak w trakcie tworzenia klasy Player.

Pokaż/ukryj kod
1
2
3
4
5
6
7
8
9
10
11
12
13
private static readonly Vector2 gravity = new Vector2(0, 0.3f),
                                maxSpeed = new Vector2(16, 18),
                                minSpeed = new Vector2(0.5f, 0.2f),
                                speedFall = new Vector2(0.999f, 1),
                                positionOffset = new Vector2(35,35);
 
private const float radius = 35;
 
private Rectangle bounds, net;
 
private Vector2 speed = Vector2.Zero;
 
private bool netCollision;

    Zmieńmy też konstruktor, aby otrzymywać w nim pożądane parametry:

1
2
3
4
5
6
7
8
public Ball(Rectangle bounds, Vector2 position,
                              Rectangle net, Game game)
    : base(game)
{
    this.bounds = bounds;
    this.position = position;
    this.net = net;
}

    Nowością są jedynie net i netCollision. Pierwszy obiekt będzie przechowywał informację o położeniu siatki na planszy, drugi przyda się przy wykrywaniu z nią kolizji, gdyż uniemożliwi wielokrotne odbijanie się piłki od siatki (o czym piszę dalej). Zanim jednak je wykorzystamy, zajmijmy się odbijaniem piłki od ścian - z użyciem metody Bounce() będzie to bardzo proste. Zmodyfikuj metodę Update w następujący sposób:

Pokaż/ukryj kod metody Update()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public override void Update(GameTime gameTime)
{
    HandleNetCollision();
 
    if (position.Y + texture.Height > bounds.Bottom)
    {
        Bounce(Vector2.UnitY, 0.5f, Vector2.Zero);
        position.Y = bounds.Bottom - texture.Height;
    }
    else if (position.Y < bounds.Top)
    {
        Bounce(Vector2.UnitY, 0.8f, Vector2.Zero);
        position.Y = bounds.Top;
    }
 
    if (position.X + texture.Width > bounds.Right)
    {
        Bounce(Vector2.UnitX, 0.8f, Vector2.Zero);
        position.X = bounds.Right - texture.Width;
    }
    else if (position.X < bounds.Left)
    {
        Bounce(Vector2.UnitX, 0.8f, Vector2.Zero);
        position.X = bounds.Left;
    }
 
 
    if (position.Y + texture.Height < bounds.Bottom - 5)
        speed += gravity;
 
    speed *= speedFall;
            
    if (Math.Abs(speed.X) > maxSpeed.X)
        speed.X = ((speed.X > 0) ? 1 : -1) * maxSpeed.X;
    else if (Math.Abs(speed.X) < minSpeed.X)
        speed.X = 0;
 
    if (Math.Abs(speed.Y) > maxSpeed.Y)
        speed.Y = ((speed.Y > 0) ? 1 : -1) * maxSpeed.Y;
    else if (Math.Abs(speed.Y) < minSpeed.Y)
        speed.Y = 0;
 
 
    position += speed;
 
    base.Update(gameTime);
}

    W linijkach $ 5-25 $ zajmujemy się odbijaniem piłki od ścian, gdy ta dotknie krawędzi ekranu. Jako wektory normalne podajemy w parametrach Vector2.UnitX (czyli wektor $ [1,0] $) lub Vector2.UnitY($ [0,1] $) zależnie od tego, z którą ścianą piłka miała kontakt. Wiersze $ 28 $ i $ 29 $ sprawiają, że grawitacja będzie ściągać w dół piłkę, jeśli ta będzie odpowiednio wysoko. Od pozycji podłoża odejmujemy tam $ 5 $, aby piłka zatrzymywała się po kilku odbiciach od dolnej krawędzi - to takie drobne oszustwo. Dzieki temu piłka szybciej utraci prędkość i nie będzie odbijać się na bardzo małe wysokości. W linijce $ 31 $ mamy opisany spadek prędkości piłki, a następnie ograniczamy jej szybkość tak, by nie przekraczała określonych uprzednio wartości ($ 33-41 $).

5
Twoja ocena: Brak Ocena: 5 (1 ocena)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com