Polimorfizm

0

Witam. Od jakiegoś czasu zbieram się aby napisać dla samego siebie grę tekstową RPG, czegoś w stylu naszej polskiej Otchłani. Jako iż będzie to mój pierwszy większy projekt chciałbym się zapytać was w jaki sposób go pisać.
Dużo czytałem na ten temat i dowiedziałem się, że polimorfizm daje mi możliwość łatwego rozszerzania takowej gry (warto zaznaczyć, że struktura nie jest mi jeszcze znana). Wiem jak to działa jednak wcześniej nie miałem okazji aby użyć tego w żadnym programie (lub ja takowej nie widziałem). Chciałbym się dowiedzieć jakie są plusy i minusy polimorfizmu.
Tak jak mówię wcześniej nie miałem z tym styczności większej więc nie mam pojęcia o ewentualnych pozytywach i negatywach. Czytałem o tym w internecie jednak chciałbym zobaczyć osobistą opinię innych użytkowników.

4

Na marginesie: na 99% nie potrzebujesz polimorfizmu w grze komputerowej (przynajmniej nie do rzeczy w stylu klasa gracza) - rzuć okiem na: https://gameprogrammingpatterns.com/.

3

Wadami polimorfizmu mogą być zwiększanie złożoności kodu i to że musisz czasem podczas ogarniania kodu (np. debugowania) ogarnąć co jest rzeczywistą implementacją, co jednak nie jest trudne jak się korzysta z IDE. Generalnie jakieś wady ma, ale w porównaniu to zalet bardzo małe, jak już to bym powiedział że przede wszystkim uważać żeby nie przesadzić,
Zalet ma bardzo sporo, wyobraź sobie że aplikacja www ma obsługę notyfikowania o jakimś zdarzeniu. Dzięki polimorfizmowi możesz łatwo modyfikować możliwości z tym związane, tj ustalić kontrakt np.


public interface NotificationService {
    void notifyUser(UserId userId, Message message);
}

Dzięki czemu możesz łatwo dodawać,zamieniać czy usuwać sposoby notyfikowania użytkowników, wystarczy że w konstruktorze np. zmienisz EmailNotification na SmsNotification, a klienta de facto to w ogóle nie obchodzi. Możesz również na przykład przekazać całą listę implementacji takiego interfejsu np. SmsNotification, EmailNotification,PushNotification do jakiejś klasy i po kolei wywoływać różne implementacje. Ułatwia również tworzenie bibliotek.
Np. możesz mieć w bibliotece do GUI metodę onMouseClicked(MouseEventListener listener);
listener to jest interfejs/klasa abstrakcyjna. Twórcy nie wiedzą jak chcesz jak chcesz to zaimplementować- ale to nie jest istotne. Oni dali kontrakt, i powiedzieli - możesz podpiąc taki listener, a Ty go sobie implementujesz jak chcesz

0

Miałem w głowie zaprojektować projekt
Klasa Character
oraz klasy które z niej dziedziczą
Klasa Player, Klasa NPC oraz Klasa Enemy
Następnie (nie wiem czy jest to możliwe) myślałem nad stworzeniem Klas: Wojownik / Łowca / Mag, które dziedziczyłyby z klasy Player (lub połączyłbym je w inny sposób) aby ustalić z jakiej umiejętności gracz miałby atakować aby nie robić od groma switch'ów / if'ów oraz aby dostawał określone punkty umiejętności.

Teraz zastanawia mnie czy taka konstrukcja wymaga użycia polimorfizmu (zakładając, że w późniejszej fazie gry mógłbym dostać nowe klasy postaci )

5
  1. Dziedziczenie strukturalne to ogólnie zło:
  • problem diamentowy
  • "nielogiczne" zależności (np. mimo iż w świecie rzeczywistym kwadrat to podtyp prostokąta tak dziedziczenie class Square : public Rectangle nie ma sensu i może być niebezpieczne
  • w większości przypadków zdecydowanie lepiej użyć kompozycji, bo naturalniej oddaje "naturę" rzeczywistości
  1. Dziedziczenie behawioralne (interfejsów) to ogólnie złoto:
  • nie ma problemu z danymi, dziedziczysz tylko dostępne funkcje
  • nie ma drzewa dziedziczenia, więc odpada problem diamentowy i cała reszta
  • "naturalniejszy" i "logiczniejszy" podział, przykładowo używając kwadratów i prostokątów powyżej tworzysz tylko interfejs trait Shape { fn area(&self) -> f64; }

Co do gier, to może zainteresuj się ECS, które lepiej się sprawdza w grach.


Co do samego polimorfizmu to polega to na tym, że masz parę implementacji i wybierasz (w czasie kompilacji lub w czasie wykonania) tę implementację, która pasuje do obecnego typu. Przykładowo:

#include <iostream>
#include <memory>
#include <random>

class Shape {
public:
  virtual double area() const = 0;
};

class Circle : public Shape {
private:
  double r;
public:
  Circle(double r) : r(r) {}
  override double area() const { return M_PI * r *r; }
};

class Rect : public Shape {
private:
  double a, b;
public:
  Rect(double a, double b) : a(a), b(b) {}
  override double area() const { return a * b; }
};

std::unique_ptr<Shape> random_shape() {
	std::random_device rd;
    std::mt19937 gen(rd());
    // give "true" 1/4 of the time
    // give "false" 3/4 of the time
    std::bernoulli_distribution d(0.5);
    
    if (d(gen)) {
    	return std::make_unique<Rect>(2.0,3.0);
    } else {
    	return std::make_unique<Circle>(5.0);
    }
}

int main() {
	std::cout << random_shape()->area() << '\n';
	return 0;
}

To wypisze losowo pole prostokąta lub koła używając polimorficznego wywołania funkcji Shape::area().

0

Dużo dzięki temu zrozumiałem jak działa polimorfizm jednak co w sytuacji kiedy:
Mam

class Character
{
public:
    virtual void create();
};

class Player :public Character
{
    string name;
    string profesja;
    int lvl;
    int hp;
public :
    virtual void create();
};

class NPC :public Character
{
    string name;
    int lvl;
    int hp;
public :
    virtual void create();
};


Teraz chciałbym również aby klasa Player (jak i NPC ale na chwilę obecną go pomińmy) miała do wyboru swoją profesję (którą to my już przypiszemy w voidzie create). Załóżmy, że są 3 profesje Wojownik, Mag, Łowca. Każda z nich będzie miała funkcję attack, która dla każdego z nich zadziała inaczej. Z tego co rozumiem wypadałoby tak zrobić. void attack() dla Wojownika zaatakuje mieczem, dla łowcy łukiem a dla maga kulą ognia. Również inne funkcje, które bym w nich utworzył działałyby dla każdego profesji inaczej. Czy w tej sytuacji również mam stworzyć class Wojownik, class Mag, class Lowca które będą dziedziczyć z klasy Player (lub z klasy Character. Nie mam pojęcia jak będzie poprawnie)? Następnie będę poprzez wskaźnik wywoływać funkcję attack zgodnie z tą, którą wybrał gracz.

Jestem totalnie zielony z polimorfizmem dlatego moje pytania mogą być chaotyczne. Mam jednak nadzieję, że jest coś w tym zrozumiałego. Nie mam pojęcia również na ile mogę sobie pozwolić dzięki temu stąd takie pytania.

1

W takim wypadku IMHO najlepiej byłoby mieć interfejs Profession, który by zawierał funkcje jak effective_attack, effective_intelligence, available_skills, a następnie byłoby to pole wewnątrz NPC oraz Player. Bo jakby nie patrzeć, wojownik to cecha postaci, a nie specjalny typ postaci. Często w grach teraz jest nawet możliwość "resetu" postaci, by na nowo wydać umiejętności.

2

Najpierw zrób grę, potem weź się za polimorfizm, fasady i inne korpo-pierdoły.

Jak zrobić story:
https://medium.com/coinmonks/how-to-create-your-own-text-adventure-12df36411b7f
https://www.makeuseof.com/tag/3-tools-to-create-your-own-text-adventure-games/

czym:
http://www.adrift.co/
https://itsfoss.com/create-interactive-fiction/

Jak wygenerować imiona:
https://www.name-generator.org.uk/fantasy/

Jak zrobić rysunki w ASCII:
http://asciiflow.com/

Jak zrobić grę:
http://howtomakeanrpg.com/a/index.html

Jak zrobić kod do gry:

Level hard:

0

Ze stworzeniem gry nie będzie żadnego problemu, ponieważ mógłbym użyć do całego systemu walki, rozmowy etc. funkcje zaprzyjaźnione oraz masę if'ów switch'ów (jednak przy ewentualnej chęci zmiany / dodania nowego zaklęcia / ruchu prawdopodobnie bym się zgubił). Post ten powstał na celu abym w połowie gry nie zdecydował się na rzucenie wszystkiego w cholerę i stworzenia gry od nowa z użyciem właśnie polimorfizmu. Chciałem się więc dowiedzieć czy działa on dobrze w grach oraz ile mi on wniesie do programu. W każdym razie dziękuję za linki, chętnie skorzystam :).

3

To tak jakbys sie pytal o pierwiastki. Oczywiscie w odpowiednio duzym kodzie znajdzie sie miejsce i na polimorfizm, ale wazniejsze od tego jest miec pomysl na gre.

Takie rzeczy wychodza w trakcie pisania jako potrzeba do zrealizowania a nie podpunkt na zaliczenie.

1

Chciałem się więc dowiedzieć czy działa on dobrze w grach oraz ile mi on wniesie do programu.

Jakaś forma polimorfizmu zawsze jest, bo np. jak uderzasz mieczem, to zadaje on obrażenia niezależnie temu w co trafi, więc musisz dynamicznie wybrać jak i ile obrażeń zadasz. Tutaj jest właśnie polimorfizm. Trudniejszym pytaniem jest czy potrzebujesz dziedziczenia (strukturalnego) w grach, i IMHO nie za bardzo.

1 użytkowników online, w tym zalogowanych: 0, gości: 1