Jak powiązać uwolnienie pocisku z animacją strzelania

15

Powiedzmy, że masz animację, która ma się zdarzyć podczas wystrzeliwania kuli. Jak sprawić, by kula pojawiła się na końcu animacji. Jedyne, co mogę wymyślić, to poznać czas trwania animacji i opóźnić ustawienie pozycji pocisków i ustawienie jej do momentu, aż upłynie ten czas. Zastanawiałem się tylko, czy to najlepsze podejście, jak sobie z tym poradzą wszyscy inni?

EDYCJA: Myślę, że mam problem z poprawnym sformułowaniem pytania.

Moje pytanie

tp0w3rn
źródło
Dzięki! Wiele dobrych pomysłów od was wszystkich, podoba mi się pomysł oddzwonienia. Myślę, że spróbuję to zaimplementować, naprawdę nie chciałem polegać na śledzeniu czasu.
tp0w3rn

Odpowiedzi:

8

Zasadniczo jesteś na dobrej drodze - musisz wiedzieć, jak długo trwa animacja, aby zrobić coś takiego. Animacje to coś więcej niż kolekcja ramek, wokół nich znajdziesz wiele innych informacji, których potrzebujesz. Np. Ile jest klatek, czy animacja zapętla się, jak szybko się odtwarza (np. 10 klatek animacji na sekundę, 25 lub 60?). Każda animacja może być zdefiniowana w oparciu o kilka elementów danych, które niektóre uogólnione kody animacji mogą przeglądać i odtwarzać. Powinieneś obudować część animacji własnym bitem kodu, który nie zna nic oprócz tych definicji animacji i sposobu wyświetlania poszczególnych ramek obrazu. Oznacza to, że masz obiekt animacyjny, który możesz załadować, rozpocząć odtwarzanie, zatrzymać odtwarzanie i kazać renderować w określonym miejscu na ekranie.

Elastyczne podejście polega na zastosowaniu pewnego rodzaju definicji animacji do enkapsulacji tego rodzaju informacji. Zamiast mówić „animacja X to wszystkie klatki, po prostu je przeglądaj”, otrzymujesz coś bardziej złożonego.

Np. Z jakimś wymyślonym formatem danych

animacje =
{
  {name = "walk", files = "walk * .png", frameCount = "12", loop = "true"},
  {name = "fire" files = "fire * .png" frameCount = "6",
       wydarzenia = {
           {name = "bulletLeavesGun", frame = "4", param1 = "43", param2 = "30"}
       }
  }
}

Więc twój kod mówi coś takiego:

currentAnimation = animations.Get("fire");
currentAnimation.Play();

To, jak wykrywasz zdarzenia, może polegać na tym, że kod animacji oddzwania do Ciebie (tj. Gdy wykryje nowe zdarzenie, ponieważ animacja została odtworzona do określonej klatki, wywołuje kod gry, aby poinformować go o nowym zdarzeniu), lub poprzez odpytanie animacja taka:

List<Event> events = currentAnimation.EventsSinceLastCheck();
foreach (AnimationEvent event in events)
{
    if (event.name == "bulletLeavesGun")
    {
        Vector2 bulletPosition = new Vector2(event.param1, event.param2);
        Vector2 actualBulletPosition = new Vector2(
                 character.x + bulletPosition.x, 
                 character.y + bulletPosition.y);
        CreateBulletAt(actualBulletPosition);
    }
}

Punkty do odnotowania:

  • Kod animacji powinien istnieć oddzielnie od kodu gry. Naprawdę nie chcesz, aby Twój kod rozgrywki był zbyt ściśle powiązany z problemami z odtwarzaniem animacji.
  • Kod animacji wie, czy zapętlić w oparciu o definicję animacji
  • Kod animacji wie, kiedy animacja jest zakończona, i może oddzwonić do innego kodu, aby powiedzieć „hej, animacja zwana„ ogniem ”właśnie się zakończyła, co chcesz teraz zrobić?”.
  • Kod animacji nie wie nic o zdarzeniach poza tym, że mają one nazwę i niektóre powiązane z nimi dowolne dane (param1 i param2)
  • Kod animacji wie, na której klatce jest aktualnie, a kiedy zmienia się na nową, może sprawdzić i powiedzieć „och, jestem teraz na klatce 4, co oznacza, że ​​wydarzenie o nazwie„ ogień ”właśnie się wydarzyło, dodaj to do moją listę ostatnich wydarzeń, abym mógł powiedzieć każdemu, kto o to zapyta ”.

Jeśli nie potrzebujesz, aby wystrzelenie pocisku odbyło się w ramach animacji, ale tylko po jej zakończeniu, możesz uzyskać znacznie mniej złożony system bez pojęcia zdarzeń. Ale nadal będziesz chciał systemu, w którym animacje odtwarzają się same, wiedzą, jak długo one trwają, i może oddzwonić do kodu gry po zakończeniu animacji.

MrCranky
źródło
Nie zgadzam się z utrzymywaniem logicznych animacji (teraz w ramce 4 oznacza to, że wydarzenie o nazwie „ogień” właśnie się wydarzyło). Animacje powinny być ślepe i głupie. Musiałem wykonać logikę po stronie serwera i animacje łzawiące, a interfejs użytkownika z gry jest czymś, czego nie chcę robić ponownie. Naprawdę polecam używanie bardzo krótkich i podzielonych na segmenty animacji, odtwarzanie ich równolegle z logiką, aby logika wyzwalała sekwencje animacji z prędkością określoną przez logikę. Nigdy nie testuj logiki pod kątem statusu animacji.
Coyote
Podział animacji na części wydaje się dość niepotrzebny. Zgadzam się z tym, że nie będę sprawdzać statusu animacji, ale to pozostawia jego pierwszą rekomendację. Nie wiem, czy miał na myśli osobny system zdarzeń, aby oddzielić kod animacji od reszty gry (wzorzec obserwatora?), Ale tak bym to zrobił. Ani „logika”, jak ją ułożysz, nie powinna wiedzieć o kodzie animacji, i odwrotnie.
jhocking
@ Coyote Powiedziałbym, że łączysz dwie osobne rzeczy. Tak, logika po stronie serwera powinna zawsze być niezależna od wizualizacji (ponieważ nie chcesz uruchamiać systemu animacji, aby dowiedzieć się, kiedy wystrzelona jest kula), ale to nie pomoże ci zbudować systemu animacji na kliencie . Na kliencie absolutnie nie chcesz, aby wizualizacje były bezmyślnie podporządkowane serwerowi, ponieważ wyglądałoby to okropnie - pociski pojawiały się w dziwnych momentach i nie były zsynchronizowane z postacią, ponieważ między grą a serwerem występowało opóźnienie . Nie ma powodu, dla którego nie możesz mieć obu (cd ...)
MrCranky,
@Coyote (cd.), Rozgrywka może być sterowana przez serwer oddzielony od grafiki. Kula jest wystrzeliwana w czasie X na serwerze, a klient odzwierciedla tę akcję, natychmiast rozpoczynając odtwarzanie animacji ognia, przy czym obraz wystrzeliwujący pocisk opóźnia się o kilka klatek w stosunku do symulacji gry pociskiem. Pomiędzy wiernością wizualną a symulacją rozgrywki należy dokonywać wszelkiego rodzaju kompromisów, więc stwierdzenie, że „animacje powinny być ślepe i głupie” jest po prostu naiwne. Czasami zdarzenia absolutnie muszą być powiązane z ramkami animacji, ponieważ żadna inna metoda nie sprawi, że będą wyglądać lub brzmieć poprawnie.
MrCranky
@ Coyote Właściwie teraz o tym myślę, strzelanie pociskami jest okropnym tego przykładem, głównie z powodu odpowiedzi od thedaian poniżej. Wypalanie powinno nastąpić od razu. Lepszym przykładem może być odpalenie efektów wizualnych, gdy postać wyląduje - serwer i klient zsynchronizują się, kiedy postać zacznie skakać, ale wizualny sposób wyświetlania pozostawia się klientowi. A kiedy animacja trafi w prawą klatkę, w której stopa uderza o ziemię, zdarzenie VFX powinno się uruchomić. Podobnie zdarzenia są potrzebne, jeśli trzeba podjąć decyzję dotyczącą określonej klatki animacji, czy rozgałęzić się na inną animację.
MrCranky
3

W pewnym sensie będziesz musiał poczekać, aż animacja się zakończy, i utworzyć pocisk w tym momencie.

Ustawienie timera będzie działać, jeśli masz pewność, że szybkość animacji jest stała. Niewielką odmianą może być kod wewnętrzny pocisku, który sprawia, że ​​czeka on niewidocznie przez chwilę, zanim pojawi się i ruszy.

W zależności od platformy programistycznej może istnieć funkcja aktualizacji animacji lub wywołania zwrotnego, która pozwoli odpowiedzieć dokładnie w momencie, gdy animacja osiągnie żądany punkt. Tak na przykład zrobiłbym to z Flixelem.

Gregory Avery-Weir
źródło
1
addAnimationCallbackMetodę Flixela można zastosować w jednostce strzelającej. W funkcji wywołania zwrotnego można sprawdzić, czy bieżąca klatka animacji strzelania jest ramką, która powinna utworzyć element punktora. Jeśli tak, możesz dodać punktor na ekranie.
Snow Blind,
2

Prosta odpowiedź: Zakładając, że masz animację, w którą chcesz zagrać, gdy gracz naciśnie przycisk „strzelania”, a następnie po zakończeniu gry pojawi się kula. Najlepiej jest unikać sztywnego kodowania czasu animacji i wystrzelić pocisk po zakończeniu animacji (przy użyciu funkcji wywołania zwrotnego lub czegoś, w zależności od platformy). Nie mogę wymyślić żadnej innej metody, która nie byłaby zbyt skomplikowana.

Alternatywna odpowiedź na projekt gry: Chyba że istnieje naprawdę, naprawdę dobry powód, aby to zrobić, chciałbym uniknąć opóźnienia od naciśnięcia przycisku „fire” i mający pojawić się kula. O ile animacja nie jest naprawdę, bardzo krótka (jedna lub dwie klatki, maksimum, w zasadzie błysk wylotowy), sprawi, że reakcja przycisku strzału będzie wolna i po prostu stanie się denerwująca dla typowego gracza. Nawet jeśli zdecydujesz się na użycie animacji przed wypuszczeniem pocisków (turowe gry RPG i gry taktyczne byłyby dopuszczalnymi powodami do tego), pomyślałbym o włączeniu gdzieś opcji „wyłącz animacje”, aby umożliwić gra porusza się szybciej, jeśli gracz chce.

thedaian
źródło
Tak, nie zmuszaj ognia, by reagował powoli. To jest tak jak typowy problem ze skokami; animatorzy dokonują wielkiego podsumowania, ale gracze oczekują, że będą w powietrzu, gdy tylko klikną przycisk.
jhocking
2

Jak mówi MrCranky; trzymaj osobno animację i logikę.

Ale co najważniejsze, logika musi pozostać częścią nadrzędną .

  • Po naciśnięciu przycisku ognia powinieneś uruchomić „ akcję ” losowania w stanie swojej postaci (logika).
  • Ta czynność powinna uruchomić animację rysowania ze wszystkimi parametrami (czas życia itp.).
  • Po zakończeniu akcji losowania możesz uruchomić akcję strzelania (może wystrzelić raz lub więcej w zależności od broni)
  • Ta akcja może generować pociski i wyzwalać animację ognia .

Należy pamiętać, że kontrolowanie interfejsu użytkownika za pomocą logiki jest jedynym sposobem na zapewnienie, że będziesz w stanie zachować, ponownie wykorzystywać i udostępniać logikę później (nowy renderer, nowa gra, wersja serwera bez renderera ...)

Kojot
źródło
1

Po pierwsze, użyłbym systemu zdarzeń (wzorca obserwatora?) Do rozdzielenia fragmentów kodu. Nie dotyczy to tylko animacji, ale z pewnością ma zastosowanie. Następnie kod animacji może po prostu powiedzieć dispatchEvent (zdarzenie), a inna część kodu nasłuchuje tego zdarzenia, przy czym żadna część kodu nie musi się o sobie znać.

Teraz kod animacji musi faktycznie zawierać odwołanie do programu rozsyłającego zdarzenia (łatwe do wykonania dzięki wstrzyknięciu zależności) i musisz mieć XML lub JSON, który faktycznie definiuje twoje animacje. Coś jak:

{
  animation: {
    name: shoot,
    length: 12,
    spritesheet: shoot.png
    event: {
      frame: 4,
      name: bulletLeavesGun,
    },
  },
}

Wczytaj dane podczas ładowania animacji i poproś kod animacji, aby wywołał zdarzenie, które znajduje się w tej klatce podczas odtwarzania.

jhocking
źródło