替换嵌套的switch语句

时间:2018-08-29 22:09:49

标签: c# unity3d

我正在尝试为用于修改静态游戏控制器内部变量的按钮创建多用途脚本。单击时,每个都应修改序列化枚举绑定到的正确变量。我的想法是,我可以将它们拍打在任何按钮上,只需通过更改按钮脚本实例上的几个标志,即可将它们更改为什么数据。

麻烦在于可能的排列数量,我有两个“手臂”,每个都有四个“单元”,每个单元都有五个“属性”。我最终看到的是一堆看上去很密集的代码,我确定可以以某种方式解决,但我找不到任何解决方案。

(为清楚起见,activeArmyunitToChangeunitNumattributeToChange均为enums

if (activeArmy == ArmyNum.army1)
{
    switch (attributeToChange)
    {
        case AttributeType.Accuracy:
            switch (unitToChange)
            {
                case UnitNum.unit1:
                    //if it's not too large
                    GameController.controller.army1.unit1.accuracy++;
                    //recalculate unit cost
                    GameController.controller.army1.unit1.RecalculateCost()
                    //then give the new number to the UI to display it
                    break;

现在,我很想能够做这样的事情:

GameController.controller.[activeArmy].[unitToChange].[attributeToChange]++;

有没有可能这样的路径?有没有办法用枚举代替类似这样的变量的路径?

这是我可以通过更改体系结构来解决的问题吗?

2 个答案:

答案 0 :(得分:6)

您拥有由内而外的逻辑;这就是造成您的问题的原因。以另一种顺序进行操作,然后将所有内容分解为各种小方法:

Army GetArmy(ArmyNum a)
{
  switch(a)
  {
    case ArmyNum.army1: return controller.army1;
    ...
  }
}

Unit GetUnit(Army a, UnitNum u)
{
  switch(u)
  {
    case UnitNum.unit1: return a.unit1;
    ...
  }
}

void ChangeAttribute(GameAttribute a, Army a, Unit u)
{
  switch (attributeToChange) {
    case GameAttribute.Accuracy:
      u.accuracy += 1;
      break;
      ...

现在呼叫站点很简单:

Army a = GetArmy(activeArmy);
Unit u = GetUnit(a, unitToChange);
ChangeAttribute(attributeToChange, a, u); 

这里的课程是制作小方法,每种方法都做得非常好。然后从那些较小的方法中组合出更复杂的方法。

完成后,就可以开始更改实现细节了。例如,我们可能会注意到GetUnitArmy的正确方法,因此我们将其签名更改为

Unit GetUnit(UnitNum u) // Now a method of Army
{
  switch(u)
  {
    case UnitNum.unit1: return this.unit1; // this, not 'a'.
    ...
  }
}

现在呼叫站点是

Army a = GetArmy(activeArmy);
Unit u = a.GetUnit(unitToChange);
ChangeAttribute(attributeToChange, a, u); 

这显然更好。

现在假设您制作了一个名为Dictionary<ArmyNum, Army>的{​​{1}}。然后,您重写:

Armies

对于GetUnits同样如此。到达那里后,您可以找到使用Army GetArmy(ArmyNum a) => Armies[a]; GetArmy的站点,并将其替换为GetUnitArmies[a],依此类推。

您的想法是迈向一个将所有参数都参数化的世界。通过进行小的更改,逐步做到这一点,每个更改都是正确的。

一个高级解决方案将是与代表相关的动作:

Armies[a].Units[u]

然后您可以写:

actions = new Dictionary<GameAttribute, Action<Army, Unit>>() 
{
  { Attribute.Accuracy, (a, u) => { u.accuracy += 1; } }, 
  ...
};

但是跑步前先走!随着程序逻辑变得越来越复杂,您会发现您再次使复杂的代码变得一团糟,然后您将尝试使用良好的OO原理将其组织到类中,并且可能会使情况变得更糟 >。我写了一系列有关如何使用OO解决此类问题的文章。如果您对此主题感兴趣,请参阅https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/

答案 1 :(得分:1)

这里是您可以尝试的灵活体系结构。我建议为您的军队/单位/属性而不是枚举使用字符串标识符,因为使用枚举会将您锁定在一组值列表中,并且您永远无法拥有比枚举更多的军队或单位。

如果继续使用枚举,请将Army / Unit / Attribute对象的Name属性设置为枚举的ToString值,以使此代码起作用。例如,army.Name = ArmyTypes.Navy.ToString()

public class Army
{
    // The army's name
    public string Name { get; set; }

    // List of units in the army
    public List<Unit> Units { get; set; }

    // Get the named unit from this army
    public Unit GetUnit(string name)
    {
        foreach (var unit in this.Units)
        {
            // Case insensitive comparison
            if (string.Compare(unit.Name, name, true) == 0)
            {
                return unit;
            }
        }
        return null;
    }
}

public class Unit
{
    // The unit's name
    public string Name { get; set; }

    // The unit's attributes
    public List<UnitAttribute> Attributes { get; set; }

    // Get the named attribute from this army
    public UnitAttribute GetAttribute(string name)
    {
        foreach (var attribute in this.Attributes)
        {
            if (string.Compare(attribute.Name, name, true) == 0)
            {
                return attribute;
            }
        }
        return null;
    }
}

public class UnitAttribute
{
    // The attribute's name
    public string Name { get; set; }

    // Current attribute value and maximum attribute value
    public int CurrentValue { get; set; }
    public int MaxValue { get; set; }

    // Add a value to this attribute and don't let it go above its maximum
    public void Add(int value)
    {
        this.CurrentValue += value;
        if (this.CurrentValue > this.MaxValue)
        {
            this.CurrentValue = this.MaxValue;
        }
    }
}

public class Controller
{
    public List<Army> Armies { get; set; }

    // I'd recommend using string parameters for your buttons instead 
    // of enum parameters, but you can do it either way
    public void ModifyUnitButtonHandler(ArmyTypes army, UnitTypes unit, UnitAttributeTypes attribute, int value)
    {
        // I'm not sure if the ? operator exists in Unity's default .NET version.
        // If not, you can use a few if/then statements to make sure that nothing
        // returns null
        GetArmy(army.ToString())?
            .GetUnit(unit.ToString())?
            .GetAttribute(attribute.ToString())?
            .Add(value);
    }

    private Army GetArmy(string name)
    {
        foreach (var army in this.Armies)
        {
            if (string.Compare(name, army.Name, true) == 0)
            {
                return army;
            }                
        }
        return null;
    }
}