Dodanie bonusów przedmiotów do statystyk gracza

projektowanie-gier
gamedev

#1

Hej, zmagam się z logicznym podejściem implementacji dodania bonusów graczowi.
Mam klase Player i tam mam propertisy hp, mana, poziom obrony, siła itd…
Mam też klasę EqItem i tam mam propertisy bonusHp, bonusMana, bonusPoziomObrony, bonusSiła itd…

Otóż nie wiem jak dodać te bonusy do postaci.
Mam metodę Equip(EqItem item) w Playerze i niby tam mogę dodać wszystkie bonusy z przedmiotu, ale wydaje mi się to krótkodystansow. Pomożecie?


#2

Robisz sobie klasę Stats z polami:
hp, mana, itd

klasa ma funkcję AddStats(Stats modifier) która zwraca nowe statsy

Każdy item ma pole stats

Zrób w player po 2 pola na staty:

  • Stats baseStats
  • Stats totalStats

I funkcję recalculateStats() która liczy sumę:
totalStats = baseStats.Clone
foreach (e in EquipedItems) totalStats.Add(e.stats)

dzięki temu jak potrzebujesz stata do obliczenia to bierzesz total stats
jak coś się zmieni to możesz zupdateować tylko total stats
jak coś się pochrzani to liczysz updateStats() i się naprawi

przy Equip/Unequip możesz po prostu dodać albo odjąc staty przedmiotu
a jak coś się zrąbie albo masz jakaś skomplikowaną logikę to przy equipie liczysz pełne recalculate stats i też spoko,
a masz wydajność bo equip jest rzadko i raz na milion lat można sobie przeliczyć
w sumie na nowoczesnym sprzęcie to możesz za każdym razem jak potrzebujesz policzyć i też nie będzie tragedii


#3

Poczytaj o wzorcu projektowym dekorator


#4

jak dla mnie dekorator to overkill,
jak potem chcesz dodać np warstwy i żeby dodać mnożniki statów z priorytetami i capem na max/min to taki standardowy dekorator tylko skomplikuje
a tablice itemów za interfejsem GetStats można łatwo rozszerzyć o sortowanie po warstwach i jakiś custom pomiędzy warstwami
dekorator to może ew do tymczasowych stat buff/debuff ale też taki specyficzny


#5

Brzmi dobrze, dziękuję za odpowiedź.
I będzie dobrze, jeżeli item np. posiada tylko 1 bonus do hp prawda? Wtedy tylko ta statystyka będzie miała inna wartość inną niż 0, cała reszta 0. Zależy mi na tym, żeby wiedzieć, że jakaś statystyka się zmieniła, więc będę sprawdzał to ifem czy != 0. Uważasz to za ok rozwiązanie?

Albo nawet po operacji dodania statystyk mogę sprawdzić jakie zmienne różnią się od tych przed operacją.

Sama mechanika ze Statsami mi się bardzo podoba i nawet już zacząłem ją pisać :smiley:


#6

Napisałem troche kodu, klasa Stats posiada jeszcze porównanie i wywołanie eventów jeśli jakieś statystyki się zmieniły.


#7

konkretny kod to już mocno pod konkretny projekt i jego wymagania
ale jak dobrze podzielisz to będzie potem łatwiej debugować i wprowadzać zmiany

z eventami bym uważał bo można potem łatwo się pogubić jak przepływa sterowanie w programie
zwłaszcza jak eventy muszą coś odpalić w jakiejś kolejności albo wykonanie eventu zajmuje więcej czasu i coś na niego czeka
ale to mocno zależy od konkretnej organizacji kodu i jakie są wymagania projektu
czasem sprawdzą się dobrze

i te obiekty co mają/modyfikują staty warto wrzucić za jakiś interfejs
interface SimpleStatSource()
{
Stats GetStats();
}
interface ComplexStatSource()
{
enum StatType{ BaseStats; FinalStats}
Stats GetStats(StatType);
}

to potem można to opakowywać właśnie różnymi dekoratorami, dekomponować, albo nawet dawać jakieś zupełnie customowe implementacje typu item co zmienia staty w zależności od fazy księżyca

ale konkretny kod to już sztuka i co najwyżej jak coś wrzucisz to dostaniesz porady
tak czy siak zrobi się paździerz w kodzie ale jak posłuchasz dobrych rad to mniejszy i łatwiej się odkopać niż jak będzie wszystko na pałę
ale w drugą stronę też nie można przesadzić, jak powsadzasz wszystko za obserwatory, fasady i skomplikowane generici z eventami i w tle chodzą jakieś couroutines to też to potem ciężko ogarnąć
ale to wymaga praktyki więc się nie przeskoczy


#8

No dokładnie, ja szukam rozwiązania, które będzie najoptymalniejsze dla mojej wiedzy. Wg mnie nie ma na siłe szukać rozwiązań najlepszych, tylko takich które ja jako początkujący jestem w stanie zrozumieć.
Co do samych interfejsów i to o czym napisałeś “customowe implementacje…” to rozumiem, że jeżeli zastosuje taki interfejs, w klasie Player to przekazując gdzieś do A ten SimpleStatSource to ten A będzie po swojemu zmieniał te staty a jak przekażemy do B to B będzie po swojemu zmieniał?

A te ify sprawdzające czy zmieniła sie statystyka to można jakoś zmienić czy tak może zostać?


#9

To bardziej pytanie jaką grę robisz
W jednej grze to w ogóle nie musisz sprawdzać bo i po co
W innej masz jakieś konkretne wymagania i wtedy sprawdzasz.

Ja to szczerze mówiąc zupełnie nie rozumiem po co Ci te eventy.
Nie wiem jaką grę robisz ale jak dla mnie to nikt nie będzie słuchał takich eventów.
A różne systemy będą miały jakieś swoje różne zasady.


#10

Co do interfejsów to chodzi o to że zamiast przekazywać Item, Player, Enemy itd
przekazujesz SimpleStatSource albo ComplexStatSource
I potem jak chcesz zrobić np item i item z klejnotami i item którego staty zależą od fazy księżyca
to robisz 3 klasy ale każda implementuje interfejs ISimpleStatSource więc klasy gracza nie obchodzą szczegóły implementacyjne
to pozwala zachować porządek i nie psuć wszystkiego jakimiś głupimi ifami wszędzie, a równocześnie jak chcesz coś dodać co zachowuje się podobnie to dodajesz


#11

Eventy są po to żeby różne mechaniki przy Inicie podpięły one się do eventów i jak np. mam event OnPlayerDamageTaken to te mechaniki na to zareagowały np. pasek hp się zmienił, ekran czerwony sie lekko zrobił itp. Wg mnie jest to bardzo rozszerzalne, bo jeśli zechce w przyszłości zaimplementować nową mechanikę to podpinam sie tylko do eventu i nasłuchuje.


#12

Ja bym uważał bo zaraz wszystko zacznie wywoływać wszystko.
Tego typu rzeczy dałbym oddzielne Eventy jak właśnie HpChanged z TakeDamage i Heal
A nie StatChanged gdzie sobie posklejasz system ekwipunku, damage, heal i nie wiadomo co jeszcze ci ciężko to będzie debugować bo wszystko wisi na Evencie “coś się stało ze statsami”


#13

Eventy to bardzo fajna rzecz, a szczególnie wzorzec obserwator. Proste, rozszerzalne, i wydajne. Możesz je deklarować w interfejsach, abstrakcyjnych klasach, co mogą dziedziczyć wspólne zachowanie. Możesz się zainteresować czymś takim, nie mam na myśli użycie biblioteki, tylko ogólny koncept https://github.com/neuecc/UniRx