Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 游戏架构-配对还是不配对?_C#_Architecture - Fatal编程技术网

C# 游戏架构-配对还是不配对?

C# 游戏架构-配对还是不配对?,c#,architecture,C#,Architecture,我正在构建一个由手机组成的简单游戏——游戏中的角色(暴徒)。每个mob都可以执行某些功能。为了给暴民提供这种功能,我创建了一个行为 例如,假设一个暴徒需要在游戏场中移动,我会给它MoveBehavior——它被添加到暴徒类的内部行为列表中: // Defined in the Mob class List<Behavior> behaviors; // Later on, add behavior... movingMob.addBehavior(new MovingBehavio

我正在构建一个由手机组成的简单游戏——游戏中的角色(暴徒)。每个mob都可以执行某些功能。为了给暴民提供这种功能,我创建了一个行为

例如,假设一个暴徒需要在游戏场中移动,我会给它MoveBehavior——它被添加到暴徒类的内部行为列表中:

// Defined in the Mob class
List<Behavior> behaviors;

// Later on, add behavior...
movingMob.addBehavior(new MovingBehavior());
//在Mob类中定义
列举行为;
//稍后,添加行为。。。
movingMob.addBehavior(新的MovingBehavior());
我的问题是。大多数行为都会操纵暴徒的某些行为。在MoveBehavior示例中,它将更改暴徒在世界上的X、Y位置。然而,每个行为都需要特定的信息,比如“movementRate”——movementRate应该存储在哪里

它应该存储在Mob类中吗?其他暴徒可能会试图通过减慢/加快暴徒的速度与之互动,并且在暴徒级别更容易访问。。。但并不是所有的暴徒都有移动速度,所以会造成混乱


还是应该存储在MoveBehavior类中?这会将其隐藏起来,使其他暴徒更难与之互动-但它不会将非移动暴徒与额外和未使用的属性(例如,不移动的塔楼将永远不需要使用移动速率)混淆起来。

IMHO,移动速率与移动行为相关,而不是与暴徒本身相关,正如你所说,它不一定会移动。所以变量应该与行为相关联,移动速率的改变是对他的行为的改变,而不是对暴徒本身的改变

您还可以创建一个基本Mob类,并派生一个MovingMob类。但我想,这并不适用,一旦你可以任意组合不同的行为

--编辑--

首先,显然在同一个Mob中不会有两次相同类型的行为(比如,没有一个Mob在同一类型中有两个movementBehaviors),因此在这种情况下,集合是更好的选择,因为它可以避免重复

你可以在每个暴徒身上都有一个方法,比如

    public Behavior GetBehavior(Type type)
    {
        foreach (var behavior in behaviorHashSet)
        {
            if ( behavior.GetType() == type)
                return behavior;

        }
        return null;
    }

一旦你有了暴徒,你可以对这种行为做任何你想做的事。另外,您可以更改GetHashCode()和Equals()方法以确保没有重复,或者使GetBehavior方法更快(常数时间)

如果它与行为相关,并且仅在该行为的上下文中有意义,则应将其作为其一部分存储

移动速度只对能够移动的东西有意义。这意味着它应该存储为表示其移动能力的对象的一部分,这似乎是您的移动行为

如果这使得访问变得太困难,那么听起来更像是您的设计存在问题。那么问题就不是“我应该作弊,把一些变量放在Mob类中,而不是放在它所属的行为中”,而是“我应该如何使它更容易与这些个体行为交互”

我可以想出几种方法来实现这一点。最明显的是Mob类上的一个简单成员函数,它允许您选择个人行为,如下所示:

class Mob {
  private List<Behavior> behaviors;
  public T Get<T>(); // try to find the requested behavior type, and return it if it exists
}
Mob m;
MovementBehavior b = m.Get<MovementBehavior();
if (b != null) {
  b.SetMovementRate(1.20f);
}
MovementHelper.SetMovementRate(m, 1.20f);
Monster m = new Monster();
if (m is IMovable)
{
    m.MoveTo(someX, someY);
}

这将提供修改行为的简单访问,但不会扰乱Mob类。(当然,将其转换为扩展方法很有诱惑力,但这可能会导致Mob类的公共接口中出现太多杂乱的内容。最好明确说明这是驻留在Mob类本身之外的帮助器功能)

编辑:只有当你没有为每个暴徒实例化一个新的移动行为,而只是有一个单一的移动行为时,下面的答案才真正有意义

我要说的是,当调用
addBehavior()
时,暴民(呃,我讨厌游戏NPC的这个词,但这不是你的错)应该得到一个
BehaviorState
对象,该对象从addBehavior()返回,并保留,并被键入添加的行为。然后为
MovingBehavior
提供一个接口,以便从
movingMob
中轻松检索其
BehaviorState
对象,并将需要存储的内容存储在那里。

那么您想做什么呢? 存储移动速率数据的最简单方法是什么

如果只在MoveBehavior类中需要它,那么它应该在其中:

public class MoveBehavior {
    public int MovementRate { get; set; }
}
如果Mob类固有地需要它,那么它将更容易通过Mob类公开:

public class Mob {
    public int MovementRate { get; set; }
}

public class MoveBehavior {
    public MoveBehavior(Mob mob) { MobInEffect = mob; }

    public Mob MobInEffect {get; set;}

    // can access MovementRate through MovInEffect.MovementRate
}
所以这一切都取决于你试图通过这种行为逻辑实现什么。我建议你推动设计决策,直到你真的需要这样或那样做。首先集中精力做简单的工作,然后重构。通常情况下,进行早期设计猜测会导致架构过于复杂


一个更务实的解决方案… 我的意思是,您首先在
Mob
类中实现您想要的运动:

public class Mob {

    // Constructors and stuff here

    public void Move(long ticks) 
    {
        // do some voodoo magic with movement and MovementRate here
    }

    protected int MovementRate { get; set; }
}
如果你真的需要的话,那么当这一切正常时,将该实现剥离到一个
MoveBehavior
类中:

public class Mob {

    // Constructors and stuff here

    public MoveBehavior Moving { set; get; }

    public void Move(long ticks) 
    {
        Moving.Move(ticks, this);
    }
}

public class MoveBehavior {
    protected int MovementRate { get; set; }

    public void Move(long ticks, Mob mob)
    {
        // logic moved over here now
    }
}
之后,如果您确实需要执行多种类型的行为,但它们共享一个公共接口,那么在此之前创建该接口,并让行为实现该接口。

这是典型的“行为组合”问题。权衡的结果是,行为越独立,它们之间的互动就越困难

从游戏编程的角度来看,最简单的解决方案是确定一组“核心”或“引擎”数据,并将其放在主对象中,然后让行为能够访问和修改该数据-可能通过功能接口

如果您想要特定于行为的数据,这很好,但是为了避免变量名称中的冲突,您可能希望使访问它的接口包括行为名称。比如:

obj.getBehaviorValue("movement", "speed")

obj.setBehaviorValue("movement", "speed", 4)
这样,两种行为都可以定义自己的变量,称为速度,而不会发生碰撞。这种

public class MoveBehavior: Behavior
{
  private static Dictionary<Mob, int> MovementRateProperty;

  public static void SetMovementRate(Mob theMob, int theRate)
  {
     MovementRateProperty[theMob] = theRate;
  }

  public static int GetMovementRate(Mob theMob)
  {
      // note, you will need handling for cases where it doesn't exist, etc
      return MovementRateProperty[theMob];
  }
}

// retrieve the rate for a given mob
int rate = MoveBehavior.GetMovementRate(theMob);
// set the rate for a given mob
MoveBehavior.SetMovementRate(mob, 5);
public interface IMovable
{
    int MovementRate { get; set; }
    void MoveTo(int x, int y);
}

public class Monster : Mob, IMovable
{
    public int MovementRate { get; set; }

    public void MoveTo(int x, int y)
    {
         // ...
    }
}
Monster m = new Monster();
if (m is IMovable)
{
    m.MoveTo(someX, someY);
}