游戏项目类层次结构的建议

发布于 2024-07-07 21:27:02 字数 880 浏览 5 评论 0原文

我正在编写一个 MUD 引擎,并且刚刚开始研究游戏对象模型,它需要可扩展。

我需要帮助主要是因为我所做的事情感觉很混乱,但我想不出另一个更有效的解决方案。

我有一个名为 MudObject 的类,另一个名为 Container 的类,一个容器可以包含多个 MudObjects,但它是一个 MudObject code> 本身,但是 MudObject 需要知道它们包含在什么中。

所以它们看起来像这样:

public abstract class MudObject
{
    Container containedBy;
}

public abstract class Container : MudObject
{
    List<MudObject> Contains;
}

(请注意这些只是示例以及一些限定符和访问修饰符、属性等错过了)

现在,这本身看起来很混乱,但让我们添加一些其他内容:

Item 是一个 MudObject,所有视觉项目(例如武器)将被继承,但是其中一些也需要是容器(例如箱子)。 但是c#中没有多重继承,所以它归结为接口,最好的选择是让容器成为一个接口(据我所知)但是有一个我不希望它成为接口的原因,也就是说,将 MudObject 添加到容器将导致容器更新 MudObject.containedBy 值。

有什么想法可以让这项工作成功,还是我陷入了让事情变得太复杂的陷阱?
如果是这样,您还有什么建议?

I'm writing a MUD engine and I've just started on the game object model, which needs to be extensible.

I need help mainly because what I've done feels messy, but I can't think of a another solution that works better.

I have a class called MudObject, and another class called Container, A container can contain multiple MudObjects, but is a MudObject itself, however MudObjects need to know what they are contained in.

So they look something like this:

public abstract class MudObject
{
    Container containedBy;
}

public abstract class Container : MudObject
{
    List<MudObject> Contains;
}

(please note these are just example and some qualifiers and access modifiers, properties and such are missed off)

Now just this in itself seems messy, but lets add something else to the mix:

Item is a MudObject that all visual items (such as weapons) will be inherited from, however some of these need to be containers too (like chests). But theres no such as multiple inheritance in c#, So it comes down to interfaces, the best choice would be to make the container an interface (as far as I can see) However there was a reason I didn't want it to be, that being that adding an MudObject to a container will cause the container to update the MudObjects .containedBy value.

Any ideas that would make this work, or am I falling into the trap of making things too complicated?
If so what else could you suggest?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

无远思近则忧 2024-07-14 21:27:02

我认为你过于复杂化了。 如果 MudObjects 可以包含其他 MudObjects,则您需要的单个基类应该遵循以下原则:

public abstract class MudObject
{    
    MudObject containedBy; //technically Parent
    List<MudObject> Contains; //children
}

这类似于 WinForms 和 ASP.NET 的工作方式。 许多容器控件都是控件,并且可以包含子控件的集合。

I think you are overcomplicating. If MudObjects can contain other MudObjects, the single base class you need should be along these lines:

public abstract class MudObject
{    
    MudObject containedBy; //technically Parent
    List<MudObject> Contains; //children
}

This is similar to the way WinForms and ASP.NET works. Many container controls are both controls, and can contain a collection of subcontrols.

蓝眼泪 2024-07-14 21:27:02

您的要求是合理的,并且是 复合设计模式

What you're asking for is reasonable, and is the Composite Design Pattern

黯然 2024-07-14 21:27:02

您想要的非常合理:它与 Windows 窗体控件没有什么不同,Windows 窗体控件本身可以是其他控件的容器。

您需要做的是创建自己的 List

public class MudObjectList : List<MudObject>

实现:除其他外,它实现了添加功能:

public void new Add(MudObject obj)
{
    obj.ContainedBy = this;
    base.Add(obj);
}

注意:此方法会隐藏而不是覆盖旧的方法添加功能

通过这种方式,您可以在添加时立即填充 ContainedBy 属性。 当然,这意味着您的 ContainedBy 可以为 null,这意味着它是顶级对象。

最后,我认为没有必要创建单独的 MudObjectContainer 类,因为容器看起来是 MudObject 所固有的( ff 使用 C# 3.0 自动属性):

public abstract class MudObject
{
    MudObject ContainedBy { get; set; }
    MudObjectList Contains { get; set; }
}

What you want is quite reasonable: it's no different from Windows form controls, which can itself be a container of other controls.

What you do need to do is create your own implementation of List<MudObject>:

public class MudObjectList : List<MudObject>

which implements, among other things, the add functionality:

public void new Add(MudObject obj)
{
    obj.ContainedBy = this;
    base.Add(obj);
}

Note: this method shadows, instead of overrides, the old Add functionality

In this way you immediately populate the ContainedBy attribute upon adding. Of course it's implied that your ContainedBy can be null, which means that it is the top level object.

Finally, I don't think there's a need to make separate MudObject and Container classes, since being a container looks intrinsic to a MudObject (the ff uses C# 3.0 automatic properties):

public abstract class MudObject
{
    MudObject ContainedBy { get; set; }
    MudObjectList Contains { get; set; }
}
鸠书 2024-07-14 21:27:02

为什么不制作所有 MudObjects 容器? ...或者至少,就您的类代码而言,能够包含其他对象。 例如,

public abstract class MudObject
{
    MudObject containedBy;
    List<MudObject> contains;
}

您可以在对象本身上设置某种标志,以识别玩家是否能够实际将东西放入或取出对象本身,而不是使用对象的类型来弄清楚。

Why not make all MudObjects containers? ...or at least, have the ability to contain other objects, in terms of your class code. E.g.

public abstract class MudObject
{
    MudObject containedBy;
    List<MudObject> contains;
}

You can then set some sort of flag on the object itself to identify whether the players are able to actually put things into or out of the object themselves, rather than using the type of the object to figure it out.

岁月打碎记忆 2024-07-14 21:27:02

实际上,让某个东西既是项目又是容器是一个坏主意。 这打破了许多对 IList 做出假设的绑定场景; 因此,对于一个箱子,我可能会想在作为集合的箱子上拥有一个 Items 属性,但让箱子只是一个箱子。

然而,对于所提出的问题...

我很想将 MudObject 作为接口...这样,您可以使用类似以下的内容,它为您提供了任何具体对象的通用容器,以及自动父子关系:

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;
    }
}

Actually, it is a bad idea for something to be both an item and a container. This breaks a number of binding scenarios that make assumptions about IList; so for a chest, I might be tempted to have an Items property on the chest that is the collection, but let the chest just be a chest.

However, for the question as asked...

I would be tempted to make the MudObject the interface... that way, you can use something like the following, which gives you a generic container of any concrete objects, along with automatic parenting:

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;
    }
}
烛影斜 2024-07-14 21:27:02

采用组合而不是继承的想法似乎已经消失了。

也许我可以做更多这样的事情

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; }
}

Going with the idea of composition over inheritance answer which seems to have vanished.

Perhaps I could do something more like this

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; }
}
○愚か者の日 2024-07-14 21:27:02

按照 Marc 的回答,编写几个在幕后维护双向父子关系的类。

Along the lines of Marc's answer, write a couple of classes that maintain a bidirectional parent-child relationship under the hood.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文