Conselhos de Classe hierarquia de itens de jogo
Pergunta
Eu estou escrevendo um motor de lama e I acabou de começar no modelo de objeto do jogo, que precisa ser extensível.
Preciso de ajuda, principalmente porque o que eu fiz sente confuso, mas eu não posso pensar de uma outra solução que funciona melhor.
Eu tenho uma classe chamada MudObject
, e outra classe chamada Container
, Um recipiente pode conter múltiplas MudObjects
, mas é um MudObject
si, no entanto MudObject
s precisa saber o que eles estão contidos.
Assim que algo parecido com isto:
public abstract class MudObject
{
Container containedBy;
}
public abstract class Container : MudObject
{
List<MudObject> Contains;
}
(por favor, note que estes são apenas exemplo e alguns qualificadores e modificadores de acesso, propriedades e tais são perdidas off)
Agora é só isso por si só parece confuso, mas vamos acrescentar algo mais à mistura:
Item
é um MudObject
que todos os itens visuais (como armas) será herdada de, entretanto, algumas destas necessidade de ser recipientes também (como caixas). Mas não há nenhuma como a herança múltipla em c #, por isso se resume às interfaces, a melhor opção seria fazer o recipiente de uma interface (tanto quanto eu posso ver) No entanto, houve uma razão que eu não quero que ele seja, que é que a adição de um MudObject
para um recipiente fará com que o recipiente para atualizar o valor MudObject
s .containedBy
.
Todas as idéias que fariam este trabalho, ou estou cair na armadilha de fazer as coisas muito complicadas?
Se assim o que mais você poderia sugerir?
Solução
O que você está pedindo é razoável, e é o padrão Composite Design
Outras dicas
Eu acho que você está complicar. Se MudObjects pode conter outros MudObjects, a classe base única que você precisa deve ser ao longo destas linhas:
public abstract class MudObject
{
MudObject containedBy; //technically Parent
List<MudObject> Contains; //children
}
Esta é semelhante à forma como WinForms e obras ASP.NET. Muitos controles de recipiente são ambos os controles, e pode conter uma coleção de subcontrols.
O que você quer é bastante razoável:. Não é diferente de controles de formulários do Windows, o que em si pode ser um recipiente de outros controles
O que você precisa fazer é criar sua própria implementação de List<MudObject>
:
public class MudObjectList : List<MudObject>
que implementa, entre outras coisas, a funcionalidade add:
public void new Add(MudObject obj)
{
obj.ContainedBy = this;
base.Add(obj);
}
Nota: Este método sombras, em vez de substituições, o antigo adicionar funcionalidade
Desta forma você preencher imediatamente o atributo ContainedBy mediante adição. Claro que está implícito que o seu ContainedBy pode ser null
, o que significa que é o objeto de nível superior.
Finalmente, eu não acho que há uma necessidade de fazer aulas MudObject
e Container
separadas, desde que seja um recipiente parece intrínseca a um MudObject
(o ff usa C # 3.0 propriedades automáticas):
public abstract class MudObject
{
MudObject ContainedBy { get; set; }
MudObjectList Contains { get; set; }
}
Por que não fazer todos os MudObjects recipientes? ... ou, pelo menos, ter a capacidade de conter outros objetos, em termos de seu código de classe. Por exemplo.
public abstract class MudObject
{
MudObject containedBy;
List<MudObject> contains;
}
Você pode então definir algum tipo de bandeira no próprio objeto para identificar se os jogadores são capazes de coisas realmente colocar para dentro ou fora do objeto a si mesmos, ao invés de usar o tipo de objeto para descobrir isso.
Na verdade, é uma má idéia para algo ser tanto um item e um recipiente. Isso quebra uma série de cenários que fazem suposições sobre IList vinculativa; assim para um baú, eu poderia ser tentado a ter uma propriedade Items em do peito que é a coleção, mas deixar o peito ser apenas um peito.
No entanto, para a pergunta como pediu ...
Eu seria tentado a fazer a MudObject a interface ... dessa forma, você pode usar algo como o seguinte, o que lhe dá um contêiner genérico de quaisquer objetos concretos, juntamente com a parentalidade automática:
public interface IMudObject
{
IMudObject Container { get; set; }
/* etc */
}
public class MudContainer<T> : Collection<T>, IMudObject
where T : IMudObject
{
public IMudObject Container { get; set; }
protected override void ClearItems()
{
foreach (T item in this)
{
RemoveAsContainer(item);
}
base.ClearItems();
}
protected override void InsertItem(int index, T item)
{
SetAsContainer(item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
RemoveAsContainer(this[index]);
base.RemoveItem(index);
}
protected override void SetItem(int index, T item)
{
RemoveAsContainer(this[index]);
SetAsContainer(item);
base.SetItem(index, item);
}
void RemoveAsContainer(T item)
{
if (item != null && ReferenceEquals(item.Container, this))
{
item.Container = null;
}
}
void SetAsContainer(T item)
{
if (item.Container != null)
{
throw new InvalidOperationException("Already owned!");
}
item.Container = this;
}
}
Indo com a idéia de composição ao longo resposta herança que parece ter desaparecido.
Talvez eu pudesse fazer algo mais como este
public class Container<T> where T : MudObject
{
List<T> Contains;
MudObject containerOwner;
public Container(MudObject owner)
{
containerOwner = owner;
}
// Other methods to handle parent association
}
public interface IMudContainer<T> where T : MudObject
{
Container<T> Contains { get; }
}
public class MudObjectThatContainsStuff : IMudContainer
{
public MudObjectThatContainsStuff()
{
Contains = new Container<MudObject>(this);
}
public Contains { get; }
}
Ao longo das linhas de resposta de Marc, escrever um par de classes que mantêm uma relação pai-filho bidirecional sob o capô.