Meu objetivo é criar um sistema de itens modular / o mais genérico possível que possa lidar com coisas como:
- Itens atualizáveis (+6 Katana)
- Modificadores de estatística (+15 de destreza)
- Modificadores de Item (% X de chance de causar dano em Y, chance de congelar)
- Itens Recarregáveis (equipe Magic com 30 usos)
- Definir itens (Equipe 4 peças de X para ativar o recurso Y)
- Raridade (comum, única, lendária)
- Desencantável (quebra em alguns materiais de artesanato)
- Artesanato (pode ser fabricado com certos materiais)
- Consumível (5min% X de poder de ataque, cura +15 hp)
* Consegui resolver recursos que são ousados na configuração a seguir.
Agora, tentei adicionar muitas opções para refletir o que tenho em mente. Não pretendo adicionar todos esses recursos necessários, mas gostaria de poder implementá-los como achar melhor. Estes também devem ser compatíveis com o sistema de inventário e serialização de dados.
Estou planejando não usar a herança , mas uma abordagem orientada a componentes / dados da entidade. Inicialmente, pensei em um sistema que tivesse:
- BaseStat: uma classe genérica que contém estatísticas on-the-go (pode ser usada para itens e estatísticas de personagens também)
- Item: uma classe que contém dados como lista de, nome, tipo de item e itens relacionados à interface do usuário, actionName, descrição etc.
- IWeapon: interface para arma. Toda arma terá sua própria classe com o IWeapon implementado. Isso terá Attack e uma referência às estatísticas do personagem. Quando a arma é equipada, seus dados (stat da classe Item) serão injetados no status do personagem (qualquer BaseStat que possua, será adicionado à classe do personagem como um bônus Stat). Por exemplo, queremos produzir uma espada (pensando em produza classes de itens com json), então a espada adicionará 5 de ataque às estatísticas do personagem. Portanto, temos um BaseStat como ("Attack", 5) (também podemos usar enum). Esta estatística será adicionada à estatística "Ataque" do personagem como um BonusStat (que seria uma classe diferente) ao equipá-lo. Portanto, uma classe chamada Sword implementa IWeapon será criada quandoA classe do item é criada. Assim, podemos injetar estatísticas de personagem nesta espada e, ao atacar, ela pode recuperar o total de estatísticas de ataque do status de personagens e causar dano no método de ataque.
- BonusStat: é uma maneira de adicionar estatísticas como bônus sem tocar no BaseStat.
- IConsumable: A mesma lógica do IWeapon. Adicionar estatísticas diretas é bastante fácil (+15 hp), mas não tenho certeza sobre adicionar armas temporárias com essa configuração (% x para atacar por 5 min).
- IUpgradeable: Isso pode ser implementado com esta configuração. Estou pensando em UpgradeLevel como uma estatística básica, que é aumentada após a atualização da arma. Quando atualizado, podemos recalcular o BaseStat da arma para corresponder ao seu nível de atualização.
Até este ponto, posso ver que o sistema é razoavelmente bom. Mas, para outros recursos, acho que precisamos de outra coisa, porque, por exemplo, não consigo implementar o recurso Craftable nisso, pois o meu BaseStat não seria capaz de lidar com esse recurso e foi aí que fiquei preso. Posso adicionar todos os ingredientes como Stat, mas isso não faria sentido.
Para facilitar sua contribuição, aqui estão algumas perguntas que você pode ajudar:
- Devo continuar com esta instalação para implementar outros recursos? Seria possível sem herança?
- Existe alguma maneira em que você possa pensar para implementar todos esses recursos sem herança?
- Sobre os modificadores de itens, como alguém pode conseguir isso? Porque é muito genérico em sua natureza.
- O que pode ser feito para facilitar o processo de construção desse tipo de arquitetura, alguma recomendação?
- Existem fontes que eu possa cavar relacionadas a esse problema?
- Eu realmente tento evitar a herança, mas você acha que isso seria resolvido / alcançado com facilidade com facilidade, mantendo-a razoavelmente sustentável?
Sinta-se à vontade para responder a apenas uma pergunta, pois mantive as perguntas muito amplas para que eu possa obter conhecimento de diferentes aspectos / pessoas.
EDITAR
Seguindo a resposta de @jjimenezg93, criei um sistema muito básico em C # para teste, ele funciona! Veja se você pode adicionar algo a ele:
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);
}
Até agora, IItem e IAttribute são interfaces base. Não havia necessidade (que eu possa pensar) de ter uma interface / atributo base para a mensagem, portanto, criaremos diretamente uma classe de mensagem de teste. Agora para as classes de teste:
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);
}
}
Estas são as configurações necessárias. Agora podemos usar o sistema (a seguir é para o 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);
}
}
Anexe esse script do TestManager a um componente na cena e você poderá ver na depuração que a mensagem foi passada com êxito.
Para explicar as coisas: Todos os itens do jogo implementam a interface IItem e todos os Atributos (o nome não deve confundi-lo, significa o recurso / sistema do item. Como Atualizável ou Desencantável) implementará o IAttribute. Então, temos um método para processar a mensagem (por que precisamos de uma mensagem será explicado em mais um exemplo). Portanto, no contexto, você pode anexar atributos a um item e deixar o resto fazer por você. O que é muito flexível, porque você pode adicionar / remover atributos com facilidade. Portanto, um pseudo-exemplo seria desencantável. Teremos uma classe chamada Disenchantable (IAttribute) e, no método Disenchant, solicitará:
- Liste os ingredientes (quando o item estiver desencantado, qual item deve ser entregue ao jogador) nota: O item II deve ser estendido para ter ItemType, ItemID etc.
- int resultModifier (se você implementar um tipo de aumento no recurso desencantador, poderá passar um int aqui para aumentar os ingredientes recebidos quando desencantado)
- int failChance (se o processo de desencanto tiver uma chance de falha)
etc.
Essas informações serão fornecidas por uma classe chamada DisenchantManager, ele receberá o item e formará esta mensagem de acordo com o item (ingredientes do item quando desencantado) e a progressão do jogador (resultModifier e failChance). Para passar esta mensagem, criaremos uma classe DisenchantMessage, que atuará como um corpo para esta mensagem. Portanto, o DisenchantManager preencherá uma DisenchantMessage e a enviará ao item. O item receberá a mensagem e a passará para todos os Atributos anexados. Como o método ReceiveMessage da classe Disenchantable procurará uma DisenchantMessage, apenas o atributo Disenchantable receberá essa mensagem e atuará nela. Espero que isso esclareça tanto as coisas quanto para mim :).