Moim celem jest stworzenie modułowego / jak najbardziej ogólnego systemu przedmiotów, który mógłby obsługiwać takie rzeczy jak:
- Przedmioty do ulepszenia (+6 Katana)
- Modyfikatory statystyk (+15 do zręczności)
- Modyfikatory przedmiotów (% X szansy na obrażenia Y, szansa na zamrożenie)
- Akumulatory (Magiczny personel z 30 użyciem)
- Zestaw przedmiotów (wyposaż 4 elementy zestawu X, aby aktywować funkcję Y)
- Rzadkość (powszechna, niepowtarzalna, legendarna)
- Disenchantable (włamuje się do niektórych materiałów rzemieślniczych)
- Do wytworzenia (może być wykonany z określonych materiałów)
- Materiał eksploatacyjny (5 min% siły ataku X, leczenie +15 KM)
* Byłem w stanie rozwiązać funkcje, które są pogrubione w następującej konfiguracji.
Teraz próbowałem dodać wiele opcji, aby odzwierciedlić to, co mam na myśli. Nie planuję dodawać wszystkich tych niezbędnych funkcji, ale chciałbym móc je wdrażać według własnego uznania. Są one również kompatybilne z systemem ewidencji i serializacji danych.
Mam zamiar nie używać dziedziczenia w ogóle, ale raczej jednostka jednoskładnikowy dane / napędzany podejście. Początkowo myślałem o systemie, który ma:
- BaseStat: ogólna klasa, która przechowuje statystyki na bieżąco (może być również używana do statystyk przedmiotów i postaci)
- Element: klasa przechowująca dane, takie jak lista, nazwa, typ elementu i rzeczy związane z interfejsem użytkownika, nazwa akcji, opis itp.
- IWeapon: interfejs do broni. Każda broń będzie miała swoją klasę z zaimplementowanym IWeapon. Będzie to miało Atak i odniesienie do statystyk postaci. Gdy broń jest wyposażona, jej dane (statystyka klasy przedmiotu) zostaną wstrzyknięte do statystyki postaci (bez względu na to, co posiada BaseStat, zostanie dodana do klasy postaci jako bonus Stat). Na przykład, chcemy wyprodukować miecz (myśląc produkuj klasy przedmiotów za pomocą jsona), więc miecz doda 5 statystyk do statystyk postaci. Mamy więc BaseStat jako („Attack”, 5) (możemy również użyć enum). Ta statystyka zostanie dodana do statystyki „Ataku” postaci jako BonusStat (która byłaby inną klasą) po jej wyposażeniu. Tak więc klasa o nazwie Sword implementująca IWeapon zostanie utworzona, gdy „ klasy przedmiotów . Statystyki postaci do tego miecza, a podczas ataku może odzyskać całkowitą statystykę Ataku ze statystyk postaci i zadawać obrażenia metodą Ataku.
- BonusStat: sposób dodawania statystyk jako bonusów bez dotykania BaseStat.
- IConsumable: Taka sama logika jak w przypadku IWeapon. Dodawanie statystyk bezpośrednich jest dość łatwe (+15 KM), ale nie jestem pewien, czy przy tej konfiguracji dodam tymczasowe bronie (% x do ataku przez 5 minut).
- IUpgradeable: Można to zaimplementować za pomocą tej konfiguracji. Myślę, że UpgradeLevel to podstawowa statystyka, która zwiększa się po ulepszeniu broni. Po ulepszeniu możemy ponownie obliczyć wartość BaseStat broni, aby dopasować ją do poziomu ulepszenia.
Do tego momentu widzę, że system jest dość dobry. Ale w przypadku innych funkcji, myślę, że potrzebujemy czegoś innego, ponieważ na przykład nie mogę zaimplementować w tym funkcji Craftable, ponieważ mój BaseStat nie byłby w stanie obsłużyć tej funkcji i tutaj utknąłem. Mogę dodać wszystkie składniki jako statystyki, ale to nie miałoby sensu.
Aby ułatwić Ci udział w tym, oto kilka pytań, w których możesz pomóc:
- Czy powinienem kontynuować tę konfigurację, aby wdrożyć inne funkcje? Czy byłoby to możliwe bez dziedziczenia?
- Czy istnieje jakikolwiek sposób na wdrożenie wszystkich tych funkcji bez dziedziczenia?
- O modyfikatorach przedmiotów, jak można to osiągnąć? Ponieważ ma bardzo ogólny charakter.
- Co można zrobić, aby ułatwić proces budowania tego rodzaju architektury, jakieś zalecenia?
- Czy są jakieś źródła, które mogę wykopać, związane z tym problemem?
- Naprawdę staram się unikać dziedziczenia, ale czy sądzisz, że można je z łatwością rozwiązać / osiągnąć dzięki dziedziczeniu, zachowując przy tym możliwość jego utrzymania?
Odpowiedz na tylko jedno pytanie, ponieważ pytania były bardzo szerokie, aby uzyskać wiedzę z różnych aspektów / osób.
EDYTOWAĆ
Po odpowiedzi @ jjimenezg93, stworzyłem bardzo prosty system w języku C # do testowania, działa! Sprawdź, czy możesz coś do niego dodać:
public interface IItem
{
List<IAttribute> Components { get; set; }
void ReceiveMessage<T>(T message);
}
public interface IAttribute
{
IItem source { get; set; }
void ReceiveMessage<T>(T message);
}
Do tej pory IItem i IAttribute są interfejsami podstawowymi. Nie było potrzeby (o której myślę) posiadania podstawowego interfejsu / atrybutu komunikatu, więc utworzymy bezpośrednio testową klasę komunikatu. Teraz dla klas testowych:
public class TestItem : IItem
{
private List<IAttribute> _components = new List<IAttribute>();
public List<IAttribute> Components
{
get
{
return _components;
}
set
{
_components = value;
}
}
public void ReceiveMessage<T>(T message)
{
foreach (IAttribute attribute in Components)
{
attribute.ReceiveMessage(message);
}
}
}
public class TestAttribute : IAttribute
{
string _infoRequiredFromMessage;
public TestAttribute(IItem source)
{
_source = source;
}
private IItem _source;
public IItem source
{
get
{
return _source;
}
set
{
_source = value;
}
}
public void ReceiveMessage<T>(T message)
{
TestMessage convertedMessage = message as TestMessage;
if (convertedMessage != null)
{
convertedMessage.Execute();
_infoRequiredFromMessage = convertedMessage._particularInformationThatNeedsToBePassed;
Debug.Log("Message passed : " + _infoRequiredFromMessage);
}
}
}
public class TestMessage
{
private string _messageString;
private int _messageInt;
public string _particularInformationThatNeedsToBePassed;
public TestMessage(string messageString, int messageInt, string particularInformationThatNeedsToBePassed)
{
_messageString = messageString;
_messageInt = messageInt;
_particularInformationThatNeedsToBePassed = particularInformationThatNeedsToBePassed;
}
//messages should not have methods, so this is here for fun and testing.
public void Execute()
{
Debug.Log("Desired Execution Method: \nThis is test message : " + _messageString + "\nThis is test int : " + _messageInt);
}
}
Są to potrzebne ustawienia. Teraz możemy korzystać z systemu (następuje dla Unity).
public class TestManager : MonoBehaviour
{
// Use this for initialization
void Start()
{
TestItem testItem = new TestItem();
TestAttribute testAttribute = new TestAttribute(testItem);
testItem.Components.Add(testAttribute);
TestMessage testMessage = new TestMessage("my test message", 1, "VERYIMPORTANTINFO");
testItem.ReceiveMessage(testMessage);
}
}
Dołącz ten skrypt TestManager do komponentu w scenie, a podczas debugowania zobaczysz, że komunikat został pomyślnie przekazany.
W celu wyjaśnienia rzeczy: Każdy przedmiot w grze będzie implementował interfejs IItem, a każdy Atrybut (nazwa nie powinna cię mylić, oznacza to cechę / system przedmiotów. Podobnie jak Upgrade lub disnchantable) zaimplementuje IAttribute. Następnie mamy metodę przetwarzania wiadomości (dlaczego potrzebujemy wiadomości zostanie wyjaśniona w kolejnym przykładzie). Tak więc w kontekście możesz dołączyć atrybuty do elementu, a reszta zrobi za Ciebie. Co jest bardzo elastyczne, ponieważ możesz łatwo dodawać / usuwać atrybuty. Tak więc pseudo-przykład byłby Odczuwalny. Będziemy mieć klasę o nazwie Disenchantable (IAttribute), która w metodzie Disenchant poprosi o:
- Lista składników (gdy przedmiot jest odczarowany, jaki przedmiot powinien zostać przekazany graczowi) uwaga: IItem należy rozszerzyć, aby zawierał ItemType, ItemID itp.
- int resultModifier (jeśli wprowadzisz rodzaj wzmocnienia funkcji odczarowywania, możesz przekazać int, aby zwiększyć ilość składników otrzymywanych po odczarowaniu)
- int failureChance (jeśli proces odczarowania ma szansę niepowodzenia)
itp.
Informacje te zostaną dostarczone przez klasę o nazwie DisenchantManager, otrzyma przedmiot i utworzy tę wiadomość zgodnie z przedmiotem (składniki przedmiotu po odczarowaniu) i postępem gracza (resultModifier i failureChance). Aby przekazać tę wiadomość, utworzymy klasę DisenchantMessage, która będzie działać jako ciało dla tej wiadomości. Tak więc DisenchantManager zapełni DisenchantMessage i wyśle go do elementu. Produkt otrzyma wiadomość i przekaże ją do wszystkich dołączonych atrybutów. Ponieważ metoda ReceiveMessage klasy Disenchantable będzie szukać DisenchantMessage, tylko atrybut Disenchantable otrzyma ten komunikat i będzie na nim działał. Mam nadzieję, że to wszystko wyczyściło tak samo jak dla mnie :).