Utwórz system oparty na zachowaniu / składnikach dla gier

11

tło

Zajmuję się tworzeniem gier jako hobby i szukam lepszego sposobu na ich zaprojektowanie. Obecnie używam standardowego podejścia OOP (zajmuję się rozwojem przedsiębiorstwa od 8 lat, więc przychodzi to w ujęciu kwartalnym). Weźmy na przykład „złego”

public class Baddie:AnimatedSprite //(or StaticSprite if needed, which inherit Sprite)
{
    //sprite base will have things like what texture to use, 
    //what the current position is, the base Update/Draw/GetInput methods, etc..
    //an AnimatedSprite contains helpers to animated the player while 
    //a StaticSprite is just one that will draw whatever texture is there
}

Problem

Powiedzmy, że tworzę platformówkę 2D i potrzebuję baddie, aby móc skakać. Zwykle robię to, dodając odpowiedni kod w metodach Update / GetInput. Więc jeśli będę musiał zmusić gracza do czołgania się, kaczkowania, wspinania się itp., Kod się tam pojawi.

Jeśli nie jestem ostrożny, metody te są zaśmiecone, więc w końcu tworzę takie pary metod

CheckForJumpAction(Input input) i DoJump()

CheckforDuckAction(Input input) i DoDuck()

tak wygląda GetInput

public void DoInput(Input input)
{
    CheckForJumpAction(input);
    CheckForDuckAction(input);
}

i wygląda aktualizacja

public void Update()
{
    DoJump();
    DoDuck();
}

Jeśli pójdę i stworzę inną grę, w której gracz musi skakać i uchylać się, zwykle wchodzę do gry, która ma tę funkcjonalność i kopiuję ją. Bałagan, wiem. Właśnie dlatego szukam czegoś lepszego.

Rozwiązanie?

Bardzo podoba mi się, jak Blend ma zachowania, które mogę dołączyć do elementu. Myślałem o zastosowaniu tej samej koncepcji w moich grach. Spójrzmy więc na te same przykłady.

Stworzyłbym podstawowy obiekt Behavior

public class Behavior
{
    public void Update()
    Public void GetInput()
}

Za pomocą tego mogę tworzyć zachowania. JumpBehavior:BehavioriDuckBehavior:Behavior

Następnie mogę dodać kolekcję zachowań do bazy Sprite i dodać to, czego potrzebuję do każdej jednostki.

public class Baddie:AnimatedSprite
{
    public Baddie()
    {
        this.behaviors = new Behavior[2];
        this.behaviors[0] = new JumpBehavior();
        //etc...
    }

    public void Update()
    {
        //behaviors.update
    }

    public GetInput()
    {
        //behaviors.getinput
    }
}

Więc teraz Jeśli chciałbym używać Jump and Duck w wielu grach, mogę po prostu zapanować nad zachowaniami. Mógłbym nawet stworzyć bibliotekę dla zwykłych.

Czy to działa?

Nie potrafię rozgryźć, jak podzielić stan między nimi. Patrząc na Skok i Kaczkę, oba wpływają nie tylko na bieżącą część rysowanej tekstury, ale także na stan gracza. (Skok zastosuje z czasem malejącą siłę skierowaną w górę, podczas gdy kaczka po prostu zatrzyma ruch, zmieni teksturę i rozmiar zderzenia baddiego.

Jak mogę to powiązać, aby działało? Czy powinienem utworzyć właściwości zależności między zachowaniami? Czy każde zachowanie powinno wiedzieć o rodzicu i bezpośrednio je modyfikować? Jedną rzeczą, o której myślałem, jest możliwość przekazania delegata każdemu zachowaniu, aby zostało wykonane po jego uruchomieniu.

Jestem pewien, że analizuję więcej problemów, ale moim głównym celem jest umożliwienie łatwego ponownego wykorzystania tych zachowań między grami i bytami w tej samej grze.

Więc oddaję to tobie. Czy chcesz wyjaśnić, jak / jeśli można to zrobić? Czy masz lepszy pomysł? Zamieniam się w słuch.


źródło
To może być lepiej zapytać na gamedev.stackexchange.com ?
rcapote
Lubię, dokąd zmierzasz, ale nie jestem pewien, jak rozwiązać problemy, które poruszasz. Bardzo interesujące i ma implikacje wykraczające poza tworzenie gier.
Edward Strange
@rcapote nie jest to strona do tablicy / dyskusji?
2
@Joe - nie, nie. Nie wspominaj tu nawet o „dyskusji”, bo inaczej przestaniesz istnieć. Ta strona służy wyłącznie do pytania-> odpowiedzi-> zakończonego typu interakcji.
Edward Strange
Cóż, oparte na komponentach systemy encji to coś, co ostatnio zaczęło zdobywać popularność w społeczności twórców gier, więc pomyślałem, że możesz zwrócić na to większą uwagę, ale prawdopodobnie i tak otrzymasz dobre odpowiedzi. Oto interesująca dyskusja na gamedev.net na temat tego problemu, który może Cię zainteresować: gamedev.net/topic/…
rcapote

Odpowiedzi:

1

Spójrz na prezentację. Brzmi dość blisko rodzaju wzoru, którego szukasz. Ten wzorzec obsługuje zachowania i właściwości dołączalne. Nie wydaje mi się, żeby prezentacja o tym wspominała, ale można również tworzyć dołączalne wydarzenia. Ten pomysł jest podobny do właściwości zależności używanych w WPF.

Nieldy
źródło
9

Dla mnie brzmi to jak podręcznikowy przypadek użycia Wzorca Strategii . W tym schemacie twoje ogólne zachowania można zdefiniować w interfejsach, które pozwolą ci wymieniać różne implementacje zachowań w czasie wykonywania (pomyśl o tym, jak wzmocnienie może wpłynąć na zdolność skakania lub biegania twojej postaci).

Dla (wymyślonego) przykładu:

// The basic definition of the jump behavior
public interface IJumpBehavior {
    void Jump();
}

Teraz możesz wdrożyć różne rodzaje skoków, z których może korzystać Twoja postać, niekoniecznie znając szczegóły dotyczące każdego konkretnego skoku:

// This is the jump the character may have when first starting
public class NormalJump : IJumpBehavior {

     public void Jump() {
         Console.WriteLine("I am jumping, and being pretty boring about it!");
     }

}

// This is the jump of a character who has consumed a power-up
public class SuperJump : IJumpBehavior {
    public void Jump() { 
         Console.WriteLine("I am all hopped up on star power, now my jumps can do damage!");
     }
}

Wszystkie postacie, które chcesz mieć możliwość skakania, mogą teraz zawierać odniesienie do IJumpable i mogą zacząć korzystać z różnych implementacji

public class HeroCharacter {

    // By default this hero can perform normal jump.
    private IJumpBehavior _jumpBehavior = new NormalJump();

    public void Jump() {
        _jumpBehvaior.Jump();
    }

    // If you want to change the hero's IJumpable at runtime
    public void SetJump(IJumpBehavior jumpBehavior) {
      _jumpBehavior = jumpBehavior;
    }

}

Wówczas Twój kod może wyglądać mniej więcej tak:

HeroCharacter myHero = new HeroCharacer();

// Outputs: "I am jumping, and being pretty boring about it!"
myHero.Jump()

// After consuming a power-up
myHero.SetJump(new SuperJump());

// Outputs: "I am all hopped up on star power, now my jumps can do damage!"
myHero.Jump();

Teraz możesz z łatwością ponownie wykorzystać te zachowania, a nawet rozszerzyć je później, aby dodać więcej rodzajów skoków lub stworzyć więcej gier zbudowanych na jednej platformie Side-scrolling.

mclark1129
źródło
choć nie do końca to, czego szukam, jest bardzo blisko i podoba mi się. Będę się tym bawić, żeby się upewnić. dzięki!
1

Rzuć okiem na architekturę DCI, która może pomóc w programowaniu OO.

Implementacja architektury w stylu DCI w języku C # wymaga użycia prostych klas domen oraz użycia obiektów (zachowań) kontekstowych z metodami rozszerzeń i interfejsami, aby umożliwić klasom domeny współpracę w ramach różnych ról. Interfejsy służą do oznaczania ról wymaganych w scenariuszu. Klasy domen implementują interfejsy (role), które dotyczą ich zamierzonego zachowania. Współpraca między rolami odbywa się w obiektach kontekstu (zachowania).

Można utworzyć bibliotekę typowych zachowań i ról, które mogą być współużytkowane przez obiekty domeny z oddzielnych projektów.

Zobacz DCI w C # dla przykładów kodu w C #.

Ed James
źródło
0

Co powiesz na przekazanie obiektu kontekstu do zachowania, które zapewnia metody zmiany stanu duszków / obiektów, na które zachowanie ma wpływ? Teraz, jeśli obiekt ma kilka zachowań, każde z nich jest wywoływane w pętli, co daje im szansę na zmianę kontekstu. Jeśli pewna modyfikacja właściwości wyklucza / ogranicza modyfikację innych właściwości, obiekt kontekstowy może zapewnić metody ustawiania pewnego rodzaju flag wskazujących na ten fakt lub może po prostu zaprzeczyć niepożądanym modyfikacjom.

Jonny Dee
źródło