BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











Programowanie gier w HTML5 dla Windows 8: Implementacja silnika gry - Pad

13-12-2013 22:03 | AkademiaTresci
W tym artykule dowiesz się jak napisać prosty kontroler ruchu graczem. Dowiesz się również jak wykrywać proste kolizje pomiędzy obiektami.

Autor: Michal Markiewicz

W tym artykule dowiesz się jak napisać prosty kontroler ruchu graczem. Dowiesz się również jak wykrywać proste kolizje pomiędzy obiektami.
Kontroler będzie się aktywował w momencie dotknięcia palcem lewej połowy ekranu. Trzymając palec na ekranie i poruszając nim będziemy przesuwać awatar gracza. Gdy zabierzemy palec z ekranu kontroler dezaktywuje się i zniknie z ekranu. Działanie kontrolera zostało przedstawione na Rys. 1. Pod tym linkiem znajdziesz ukończony projekt z tego rozdziału.

Pad w czasie rozgrywki
Rys. 1. Pad w czasie rozgrywki.

Implementacja pada

1. Dodanie grafiki kontrolera:

  • Otwórz projekt SkyShooter przygotowany w poprzednim odcinku - Implementacja silnika gry – Gracz.
  • W Solution Explorer dodaj nowy folder do katalogu images.
  • Nazwij go pad.
  • Naciśnij prawym klawiszem myszy na nowo dodany folder pad.
  • Wybierz opcję Add -> Exisiting Item
  • Przejdź do folderu z pobranymi grafikami dla projektu.
  • Wybierz obrazki padInRing.png i padOutRing.png.
  • Naciśnij przycisk Add.

2. Implementacja klasy pomocniczej utilities.js:

  • W Solution Explorer do folderu js dodaj nowy plik JavaScript o nazwie utilities.js.
  • Otwórz plik i wklej kod
// Funkcja zwracająca losową liczbę z podanego przedziału a i b.
function randomRange(a, b) {
    return (Math.floor(Math.random() * (1 + b - a))) + a;
}

// Funkcja blokująca liczbę w przedziale min i max
function clamp(x, min, max) {
    return x < min ? min : (x > max ? max : x);
}

// Funkcja zwracająca środek z sumy dwóch liczb
function distance(val1, val2) {
    return Math.abs(val1 - val2);
}

// struktura przetrzymująca współrzędne x i y
function Point(x, y) {
    this.x = x;
    this.y = y;
}

Informacja

Utilities.js przetrzymuje metody pomocnicze, które będziemy używać w naszym projekcie do różnych celów.

  • Otwórz plik default.html.
  • Przeciągnij plik utilities.js do referencji w środku default.html. Pamiętaj, aby referencje dodać przed referencją do default.js tak, jak na Rys. 2.

Referencje w pliku default
Rys. 2. Referencje w pliku default.html.

3. Implementacja kontrolera:

  • W Solution Explorer do folderu js dodaj nowy plik JavaScript o nazwie pad.js.
  • W pliku pad.js wstaw kod
var rings = new Array();
var touchId = null;
            

Informacja

Pierwsza zmienna to tablica przetrzymująca dane dwóch pierścieni, które tworzą pad. Druga zmienna przetrzymuje id punktu dotyku. Urządzenia dotykowe mogą rozpoznawać do dziesięciu palców na raz. Dlatego ważne jest dla nas śledzenie, który palec dotyka ekranu.

  • Pod zmiennymi wstaw kod:
function Ring(x, y, size, imgSrc) {
    this.x = x;
    this.y = y;
    this.width = size;
    this.height = size;
    this.img = new Image();
    this.img.src = imgSrc;
    this.active = false;
}

Informacja

Funkcja Ring to konstruktor do tworzenia poszczególnych pierścieni pada.

  • Pod konstruktorem dodaj kod:
function createPad(size) {
    rings[0] = new Ring(0, 0, size + 50, "/images/pad/padOutRing.png");
    rings[1] = new Ring(0, 0, size, "/images/pad/padInRing.png");
}

function drawPad(ctx) {
    // Rysuj pada, jeżeli jest aktywny
    if (rings[0].active == true) {
        for (var i = 0; i < 2; i++) {
            var r = rings[i];
            ctx.drawImage(r.img, r.x, r.y, r.width, r.height);
        }
    }
}

function showPad(x, y) {
    for (var i = 0; i < 2; i++) {
        var r = rings[i];
        r.active = true;
        r.x = x - r.width / 2;
        r.y = y - r.height / 2;
    }
}
         

Informacja

Pierwsza funkcja tworzy pad i zapisuje go w tablicy rings.
Druga funkcja rysuje pad na ekranie gry, gdy jest aktywny.
Trzecia funkcja aktywuje pada i ustawia go w przesłanych współrzędnych.

  • Pod funkcją showPad() dodaj kod:
function touchStart(event) {
    if (touchId == null) {
        if (event.x < (window.innerWidth / 2)) {
            touchId = event.pointerId;
            showPad(event.x, event.y);
        }
    }
}
            

Informacja

Zdarzenie (event), które wywołuje funkcje showPad() oraz zapisuje id punktu dotyku. Kod wykonuje się tylko, gdy użytkownik dotknie lewej połowy ekranu.

  • Pod funkcją touchStart() dodaj kod
function touchMove(event) {
    if (touchId != null && touchId == event.pointerId) {
        var r = rings[0];
        var delta = 65;

        r.x = event.x - r.width / 2;
        r.x = clampPosition(r.x, rings[1].x, delta);

        r.y = event.y - r.height / 2;
        r.y = clampPosition(r.y, rings[1].y, delta);
    }
}

function clampPosition(value1, value2, delta) {
    if (value2 < value1) {
            return clamp(value1, value2, value2 + delta);
    } else if (value2 > value1) {
        return clamp(value1, value2 - delta * 1.5, value2);
    }
}

function touchEnd(event) {
    if (touchId == event.pointerId) {
        touchId = null;
        for (var i = 0; i < 2; i++) {
            rings[i].active = false;
        }
    }
}

Informacja

Pierwsza funkcja to zdarzenie wywoływane, gdy gracz porusza palcem po ekranie. Zadaniem funkcji jest poruszanie pierścieniem za palcem gracza.
Funkcja clamPosition() ma za zadanie utrzymywanie ruchomego pierścienia w ustalonym promieniu od stałego pierścienia.
Trzecia funkcja - touchEnd jest wywoływana, gdy użytkownik zabiera palec z ekranu. Resetujemy id dotyku oraz włączamy pada, do jego ponownej aktywacji.

  • Zaraz pod funkcją touchEnd() dodaj kod:
function direction(val1, val2) {
    if (val1 > val2) {
        return 1;
    } else if (val1 < val2) {
        return -1;
    }
}

function getDirections() {
    var r1 = rings[0];
    var r2 = rings[1];
    var x = 0;
    var y = 0;

    if (r1.active == true) {
        // środek współrzędnych pada
        var r1X = r1.x + r1.width / 2;
        var r1Y = r1.y + r1.height / 2;
        var r2X = r2.x + r2.width / 2;
        var r2Y = r2.y + r2.height / 2;

        // sprawdź, w którym kierunku porusza się pad.
        if (distance(r1X, r2X) > 10) {
            x = direction(r1X, r2X);
        }
        if (distance(r1Y, r2Y) > 10) {
            y = direction(r1Y, r2Y);
        }
    }
    // sprawdź czy zmienne nie mają wartości NaN, która 
    // spowoduje błędy podczas działania kontrolera
    x = isNaN(x) ? 0 : x;
    y = isNaN(y) ? 0 : y;

    return new Point(x, y);
}

Informacja

Pierwsza funkcja zwraca 1 lub -1 w zależności, która z przesłanych liczb jest większa.
getDirection() umożliwia sterowanie postacią gracza. Kierunek zwracany jest dopiero, gdy odległość pomiędzy pierścieniami jest większa niż 10px.

  • Otwórz default.html.
  • Przeciągnij z Solution Explorer plik pad.js do referencji w default.html, tak jak na Rys. 3.

Referencje w pliku default
Rys. 3. Referencje w pliku default.html.

4. Podłączenie kontrolera do gry:

  • Otwórz plik default.js.
  • W funkcji initialize() zaraz pod SCREEN_HEIGHT… dodaj kod:
// nasłuchiwanie zdarzeń (event handlers)
canvas.addEventListener("MSPointerDown", touchStart);
canvas.addEventListener("MSPointerMove", touchMove);
canvas.addEventListener("MSPointerUp", touchEnd);

Informacja

Kod umożliwiający wywołanie trzech funkcji pada w reakcji na odpowiednie zdarzenia.

  • Dalej w funkcji initialize() zaraz po createPlayer() dodaj kod
createPad(100); // przesyłamy rozmiar pada.
  • W metodzie draw() na końcu dodaj kod odpowiedzialny za rysowanie pada:
drawPad(ctx); 
  • Znajdź metodę updatePlayer() i zastąp ją kodem
function updatePlayer() {
// aktualizuj gracza jezeli niejest zniszczony
if (!player.destroyed) {
animateObject(player);
// pobierz kierunek ruchu pada
var dir = getDirections();
      movePlayer(dir.x, dir.y);
}
}

function movePlayer(x, y) {
player.x += x * player.speed;
player.y += y * player.speed;
}

Informacja

W metodzie updatePlayer() dodaliśmy kod, który pobiera kierunek, w którym gracz porusza padem. Następnie przesyłamy je do metody movePlayer(); movePlayer() dodaje do obecnej pozycji postaci kierunek pomnożony przez prędkość, z jaką ma się poruszać.

Na tym etapie mamy w pełni funkcjonalny kontroler ruchu, który porusza postacią gracza we wskazanym kierunku. Uruchom projekt i przetestuj kontroler.

Kolizje z ekranem

Może już zauważyłeś podczas testowania, że postać gracza nie zatrzymuje się przy krawędziach ekranu. Zamiast tego znika. Dlaczego tak się dzieje?. Dzieje się tak, ponieważ nie zaprogramowaliśmy ograniczeń wirtualnego świata gry. W tym momencie nasz świat gry wybiega bezgranicznie poza ekran naszego urządzenia. Musimy napisać logikę, która zatrzyma gracza, gdy będzie przy krawędzi.

1. Dodanie granic wirtualnego świata:

  • Otwórz plik default.js.
  • Znajdź funkcję playerUpdate().
  • W środku metody, zaraz pod movePlayer dodaj kod
playerScreenBounds();
  • Znajdź funkcję drawPlayer() i zaraz pod nią wstaw kod
function playerScreenBounds() {
// lewa krawędź
if (player.x = SCREEN_WIDTH - player.frameWidth) {
player.x = SCREEN_WIDTH - player.frameWidth;
}
// górna krawędź
if (player.y = SCREEN_HEIGHT - player.frameHeight) {
player.y = SCREEN_HEIGHT - player.frameHeight;
}
}

Dla zrozumienia metody playerScreenBounds() przyjrzyjmy się Rys. 4. Nasz ekran w lewym górnym rogu ma współrzędne (0,0). Współrzędne (x,y) każdego rysowanego na ekranie obiektu znajdują się w lewym górnym rogu obiektu. Przy kolizjach z lewą krawędzią lub górną sprawa wygląda prosto, po prostu sprawdzamy czy współrzędne obiektu są mniejsze niż zero. Jeżeli są mniejsze, oznacza to, iż obiekt wyszedł poza krawędź. Inaczej sprawdza się dolną lub prawą krawędź. Gdybyśmy porównali same współrzędne obiektu np. z prawą krawędzią doszłoby do kolizji, niestety niepoprawnej. Statek znalazłby się poza ekranem urządzenia. Aby sprawdzić kolizje poprawnie musimy wziąć pod uwagę długość obiektu i dodać ją do współrzędnych:

   (x,y) obiektu + długość obiektu >= szerokość ekranu

Można też, jak w naszym przypadku, porównać współrzędne obiektu z końcem ekranu pomniejszonym o długość ekranu:

   (x,y) obiektu >= szerokość ekranu -  długość obiektu

Kolizja gracza z ekranem
Rys. 4. Kolizja gracza z ekranem.

Na tym etapie mamy w pełni funkcjonalny kontroler ruchu, który porusza postacią gracza we wskazanym kierunku ograniczając wyjście obiektu poza krawędź ekranu. Uruchom projekt i przetestuj kontroler.

Podsumowanie

W tym artykule dowiedzieliśmy się jak można napisać dotykowy kontroler ruchu. Zobaczyliśmy jak można ograniczyć przestrzeń wirtualnego świata gry do rozmiarów ekranu urządzenia.
W kolejnym artykule nauczymy się jak napisać system strzelania pociskami w naszej grze.

Komentarze 2

KotoGames
KotoGames
0 pkt.
Nowicjusz
22-12-2013
oceń pozytywnie 0
Hej, Super artykuł. Kilka uwag: 1. var rings = new Array(); tablice sa super bo mozna cos w petli robic ale w tym konkretnym przykladzie kod bylby bardziej czytelny jakby były po prostu 2 zmienne : innerRing, outerRing. 2. rings[1] - tu wstawiamy jest obrazek InnerRing , ale potem w touchMove check jest czy rusza sie rings[0] - myslalem ze OuterRing ma byc nieruchomy.
KotoGames
KotoGames
0 pkt.
Nowicjusz
22-12-2013
oceń pozytywnie 0
ech ciężko tu jakis komentarz dać bo ciągle mam: Treść komentarza zawiera nieprawidłowe znaki lub jest za długa
pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.