Search Unity

How to return variable type from a method? (amongst inherited classes)

Discussion in 'Scripting' started by FeastSC2, Jun 18, 2018.

  1. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    How can I get a variable type from a method? I looked on the net and I saw in NET 4.0 there's the dynamic keyword but we can't use that in Unity.

    The method I have currently is this:

    Code (CSharp):
    1.   private AiOptions GetCorrectAiOption(UnitToLoad unitToLoad)
    2.     {
    3.         AiOptions solution = null;
    4.         switch (unitToLoad)
    5.         {
    6.             case Unit.Normal:
    7.                 solution = NormalOptions.Options[0];
    8.                 break;
    9.             case Unit.Explosive:
    10.                 solution = ExplosiveOptions.Options[0];
    11.                 break;
    12.             case Unit.Flying:
    13.                 solution = FlyingOptions.Options[0];
    14.                 break;
    15.         }
    16.  
    17.         if (solution == null)
    18.             Debug.LogError("No option for: " + unitToLoad);
    19.         return solution;
    20.     }
    NormalOptions, ExplosiveOptions and FlyingOptions all inherit from AiOptions.
    How can the method give me the type of the Options (Normal, explosive, flying, ...)?

    Code (CSharp):
    1.  
    2. // Fake code
    3.  private T GetCorrectAiOption(UnitToLoad unitToLoad) where T is AiOptions
    4.     {
    5.        
    6.     }
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    You can use dynamic if you change your scripting runtime version to 4.x (depending on what version of Unity you use, this may not be available, I believe it started with 2017)
     
  3. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    540
    Don't use dynamic for this kind of things, it's bad for performance.
    And i don't even see how they would help you here, because even if you use dynamic you still have to know which methods/properties the object has otherwise you would get a runtime exception, so the only safe way to use the dynamic option would be to only use properties/methods that all have in common and these should be in the AiOptions base class, so i don't see any benefit in using dynamic over the base class as a return type.

    Im not sure what you actually want, it might be possible with generics.
    You have to post more details or examples what you are trying to do.

    If you have a generic like this T GetCorrectAiOption<T> then you would have to call it like this:
    myOption = GetCorrectAiOption<MyOptionType>(some parameters) which means you would already have to know which OptionType you want to get from the method, that way you could check if the unit has a specific option type, is that what you want?

    If you don't want to specific a type with the method call, then you can't do it with generics and the only way to do it is to return a common base class or interface like in your first example, but then the next question would be why does your current solution not work / why do you need the specific type?
     
    Last edited: Jun 18, 2018
  4. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    That's not what I want, the method must give me the type of what it returns.

    It seems to me that it could be useful in a lot of situations. I understand that dynamic is not typesafe but why would it impact performance?
    Anyway, I can't go the dynamic road because it's still only experimental in Unity.
     
  5. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    540
    Search (google) "C# dynamic performance" there are many posts which explain why they impact performance.

    They are not usefull in "a lot of situations", they should only be used in very, very specific situations.

    Maybe you try to do something that could be solved easier in a different way, but i can't provide any alternatives because i don't know what you want to do with the returned object and why you need it dynamic or why returning the base class isn't enough.
     
    Last edited: Jun 18, 2018
  6. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    4.x is not experimental in 2018 (and honestly, worked just fine in 2017), but I do agree with @R1PFake that there may be other options that might give you better results.
     
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    When and how would it give the type? It's already returning something... it can't return multiples.

    Dynamic is useful if you may return/reference various objects of disparate type. As in, they don't share any specific base type that is convenient. Like if you wanted to be able to return both int, string, and GameObject from the same function.

    All your types inherit from the same base type. So really, the base type is better since it can statically type the reference rather than implicitly determine the type at every call to it (which dynamic requires since anything might be referenced in it).

    ...

    If you want to know what child type the object is, you can use the 'is' operator.

    Code (csharp):
    1.  
    2. var option = GetCorrectAiOption(UnityToLoad.Normal);
    3. if(option is NormalOptions)
    4. {
    5.     var norm = option as NormalOptions;
    6.     //peform operations based on the fact it's a NormalOptions
    7. }
    8.  
    Although really if you asked for a Normal, and anticipate a Normal, you can just avoid this all together and say:
    Code (csharp):
    1.  
    2. var option GetCorrectAiOption(UnityToLoad.Normal) as NormalOptions;
    3. if(option != null)
    4. {
    5.     //do stuff as if it were a normal
    6. }
    7.  
    Thing is, I'd suggest this all has a massive code smell. Why is it you're passing in what appears to be enums to get back some specific type. Why isn't there a 'GetNormalAiOption' and a 'GetExplosiveAiAoption' function?

    Why do you need this generalized access point using an enum? Is it because you allow the option to be configurable in the inspector? Why not use a switch then and call the appropriate function in regard to it?

    Furthermore... why do you need to know what specific type it is. If you have an abstracted interface like this where the type can be resolved from an enum. Doesn't that imply that any of the objects should be usable interchangeably to one another? So then should AiOption's interface have the members necessary to use them interchangeably?

    If you have a weird edge case to know what the type is... well, that's what the 'is' operator is for. But if it's not a weird edge case... well this all has a lot of code smell to me. But I don't know your specific use requirements, nor am I you, so I don't know why you've chosen this path. But if I were the one reviewing your code I'd be calling you over to point out the fact this has a lot of code smell to it.
     
  8. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,518
    I think it's more of a general design question than it is a 'how to I return the right type' question. The calling method has to know what type it is in order to do anything useful with it so I'm not really sure you have the right layout here for what you're trying to accomplish.

    Seems like you might as well just make one options class and subset the options under booleans like
    IsExplosive
    .

    It really depends on how you are going to use this.
     
  9. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    I'll just show you what I have in my code, I wouldn't mind having something poorly optimized here since it's mostly about showing in the inspector what the Options are for my AI units.

    Here's how it works : a UnitSpawner spawns a creature and gives it its AiOptions, each creature has different options (#NameOfTheCreature#Options.cs that inherits from AiOptions).

    I think it's best I show you the code. You'll see that specifically in the UnitSpawner I must create lots of switch and if statements to account for the fact that my methods can't return a variable class.

    Here's what I wanted to achieve, I can pick any enemies and choose its options with enum dropdowns as well as seeing what the options actually do: thanks to the function in my code called DisplayInInspector.

    The code I gave here is for the Options of my unit called ShadowFlying.

    upload_2018-6-22_17-7-58.png

    Code (CSharp):
    1. using Sirenix.OdinInspector;
    2. #if UNITY_EDITOR
    3. using UnityEditor;
    4. #endif
    5. using UnityEngine;
    6. using UnityEngine.Events;
    7.  
    8. public class UnitSpawner : Spawnable
    9. {
    10.     [Tooltip("Spawn the monster without any delay.")]
    11.     public bool DirectSpawn = false;
    12.  
    13.     private static PooledFx SpawnEffect;
    14.     private float AccuTime;
    15.  
    16.     [Tooltip("The time it takes for the monster to spawn")] [DisableInEditorMode, DisableInPlayMode, HideIf("IfDirect")]
    17.     public float TimeBeforeSpawn = 1f;
    18.  
    19.     private bool GetSpawnEffect = true;
    20.  
    21.     [ShowIf("IfShadowNormal"), PropertyOrder(-1)]
    22.     public ShadowNormalOptions.Option ShadowNormalOption;
    23.  
    24.     [ShowIf("IfShadowExplosive"), PropertyOrder(-1)]
    25.     public ShadowExplosiveOptions.Option ShadowExplosiveOption;
    26.  
    27.     [ShowIf("IfShadowFlying"), PropertyOrder(-1)]
    28.     public ShadowFlyingOptions.Option ShadowFlyingOption;
    29.  
    30.     [ShowIf("IfSpeareus"), PropertyOrder(-1)]
    31.     public SpeareusOptions.Option SpeareusOption;
    32.  
    33.     [ShowIf("IfNecroWaker"), PropertyOrder(-1)]
    34.     public NecroWakerOptions.Option NecroWakerOption;
    35.  
    36.     [ShowIf("IfBaphomet"), PropertyOrder(-1)]
    37.     public BaphometOptions.Option BaphometOption;
    38.  
    39.     [ShowIf("IfBowman"), PropertyOrder(-1)]
    40.     public BowmanOptions.Option BowmanOption;
    41.  
    42.     [ShowIf("IfVoidSplitter"), PropertyOrder(-1)]
    43.     public VoidSplitterOptions.Option VoidSplitterOption;
    44.  
    45.     public UnitAIGroup SpawnedUnitGroup { get; private set; }
    46.  
    47.     public UnityEvent OnUnitDead;
    48.     public bool UnitDied { get; private set; }
    49.  
    50.     public bool UnitIsSpawned { get; private set; }
    51.  
    52. #if UNITY_EDITOR
    53.     [OnInspectorGUI, PropertyOrder(-1)]
    54.     private void DrawInfoBox()
    55.     {
    56.         EditorGUILayout.HelpBox(ShowOptions(), MessageType.Info);
    57.     }
    58. #endif
    59.  
    60.     private AiOptions CorrectOption
    61.     {
    62.         get { return GetCorrectOption(); }
    63.     }
    64.  
    65.     private string ShowOptions()
    66.     {
    67.         string result = string.Empty;
    68.         if (IfShadowNormal()) result = CorrectOption.DisplayInInspector(typeof(ShadowNormalOptions), "Options", CorrectOption.GetOption(OptionNr()));
    69.         if (IfShadowExplosive()) result = CorrectOption.DisplayInInspector(typeof(ShadowExplosiveOptions), "Options", CorrectOption.GetOption(OptionNr()));
    70.         if (IfShadowFlying()) result = CorrectOption.DisplayInInspector(typeof(ShadowFlyingOptions), "Options", CorrectOption.GetOption(OptionNr()));
    71.         if (IfNecroWaker()) result = CorrectOption.DisplayInInspector(typeof(NecroWakerOptions), "Options", CorrectOption.GetOption(OptionNr()));
    72.         if (IfSpeareus()) result = CorrectOption.DisplayInInspector(typeof(SpeareusOptions), "Options", CorrectOption.GetOption(OptionNr()));
    73.         if (IfBaphomet()) result = CorrectOption.DisplayInInspector(typeof(BaphometOptions), "Options", CorrectOption.GetOption(OptionNr()));
    74.         if (IfBowman()) result = CorrectOption.DisplayInInspector(typeof(BowmanOptions), "Options", CorrectOption.GetOption(OptionNr()));
    75.         if (IfVoidSplitter()) result = CorrectOption.DisplayInInspector(typeof(VoidSplitterOptions), "Options", CorrectOption.GetOption(OptionNr()));
    76.  
    77.         return result;
    78.     }
    79.  
    80.     bool IfDirect()
    81.     {
    82.         return DirectSpawn;
    83.     }
    84.  
    85.     bool IfShadowNormal()
    86.     {
    87.         return UnitToLoad == Unit.ShadowNormal;
    88.     }
    89.  
    90.     bool IfShadowExplosive()
    91.     {
    92.         return UnitToLoad == Unit.ShadowExplosive;
    93.     }
    94.  
    95.     bool IfShadowFlying()
    96.     {
    97.         return UnitToLoad == Unit.ShadowFlying;
    98.     }
    99.  
    100.     bool IfSpeareus()
    101.     {
    102.         return UnitToLoad == Unit.Speareus;
    103.     }
    104.  
    105.     bool IfNecroWaker()
    106.     {
    107.         return UnitToLoad == Unit.NecroWaker;
    108.     }
    109.  
    110.     bool IfVoidSplitter()
    111.     {
    112.         return UnitToLoad == Unit.VoidSplitter;
    113.     }
    114.  
    115.     bool IfBaphomet()
    116.     {
    117.         return UnitToLoad == Unit.Baphomet;
    118.     }
    119.  
    120.     bool IfBowman()
    121.     {
    122.         return UnitToLoad == Unit.Bowman;
    123.     }
    124.  
    125.     public override void Rename()
    126.     {
    127.         gameObject.name = UnitToLoad.ToString() + " (" + OptionName() + ")";
    128.     }
    129.  
    130.     public string OptionName()
    131.     {
    132.         if (IfShadowNormal()) return ShadowNormalOption.ToString();
    133.         if (IfShadowExplosive()) return ShadowExplosiveOption.ToString();
    134.         if (IfShadowFlying()) return ShadowFlyingOption.ToString();
    135.         if (IfNecroWaker()) return NecroWakerOption.ToString();
    136.         if (IfSpeareus()) return SpeareusOption.ToString();
    137.         if (IfBaphomet()) return BaphometOption.ToString();
    138.         if (IfBowman()) return BowmanOption.ToString();
    139.         if (IfVoidSplitter()) return VoidSplitterOption.ToString();
    140.  
    141.         Debug.LogError("error");
    142.         return null;
    143.     }
    144.  
    145.     public int OptionNr()
    146.     {
    147.         if (IfShadowNormal()) return (int)ShadowNormalOption;
    148.         if (IfShadowExplosive()) return (int)ShadowExplosiveOption;
    149.         if (IfShadowFlying()) return (int)ShadowFlyingOption;
    150.         if (IfNecroWaker()) return (int)NecroWakerOption;
    151.         if (IfSpeareus()) return (int)SpeareusOption;
    152.         if (IfBaphomet()) return (int)BaphometOption;
    153.         if (IfBowman()) return (int)BowmanOption;
    154.         if (IfVoidSplitter()) return (int)VoidSplitterOption;
    155.  
    156.         Debug.LogError("error");
    157.         return -1;
    158.     }
    159.  
    160.     public UnitAI Spawn()
    161.     {
    162. //        Debug.Log("Preparing unit!");
    163.         SpawnedUnitGroup = Instantiate(GetUnitPrefab(UnitToLoad));
    164.         UnitIsSpawned = true;
    165.  
    166.         SpawnedUnitGroup.OnDestroy += OnUnitDied;
    167.         SpawnedUnitGroup.transform.SetParent(CreatureContainer.Instance.transform);
    168.         SpawnedUnitGroup.gameObject.SetActive(false);
    169.  
    170.         SpawnedUnitGroup.BaseOptions = GetCorrectOption();
    171.  
    172.         return SpawnedUnitGroup.UnitAi;
    173.     }
    174.  
    175.     private void PoolSpawn()
    176.     {
    177.         SpawnedUnitGroup.gameObject.SetActive(true);
    178.         SpawnedUnitGroup.transform.position = transform.position.With(z: 0f);
    179. //        Debug.Break();
    180.     }
    181.  
    182.     public override void Awake()
    183.     {
    184.         base.Awake();
    185.         if (SpawnEffect == null)
    186.             SpawnEffect = Resources.Load<PooledFx>("Misc/SpawnEffect");
    187.     }
    188.  
    189.     void OnEnable()
    190.     {
    191.         SpawnedUnitGroup = null;
    192.     }
    193.  
    194.     void OnDestroy()
    195.     {
    196.         if (SpawnedUnitGroup != null)
    197.         {
    198.             SpawnedUnitGroup.OnDestroy -= OnUnitDied;
    199.         }
    200.     }
    201.  
    202.     protected override void OnValidate()
    203.     {
    204.         base.OnValidate();
    205.         if (SpawnedUnitGroup)
    206.         {
    207.             var options = GetCorrectOption();
    208.             SpawnedUnitGroup.BaseOptions = options;
    209.         }
    210.     }
    211.  
    212.     void Update()
    213.     {
    214.         SpawnCreature();
    215.     }
    216.  
    217.     private void OnUnitDied()
    218.     {
    219.         UnitDied = true;
    220.  
    221.         if (OnUnitDead != null)
    222.             OnUnitDead.Invoke();
    223.     }
    224.  
    225.     private void SpawnCreature()
    226.     {
    227.         if (DirectSpawn)
    228.         {
    229.             if (SpawnedUnitGroup == null) Spawn();
    230.             PoolSpawn();
    231.             gameObject.SetActive(false);
    232.             return;
    233.         }
    234.  
    235.         if (GetSpawnEffect)
    236.         {
    237.             GetSpawnEffect = false;
    238.             Instantiate(SpawnEffect, transform.position.With(z: 0f), Quaternion.identity);
    239.         }
    240.  
    241.         AccuTime += Time.deltaTime;
    242.         if (AccuTime > TimeBeforeSpawn)
    243.         {
    244.             if (SpawnedUnitGroup == null) Spawn();
    245.             PoolSpawn();
    246.             gameObject.SetActive(false);
    247.         }
    248.     }
    249.  
    250.     void OnDisable()
    251.     {
    252.         // For next time we get enabled.
    253.         GetSpawnEffect = true;
    254. //        SpawnedUnitGroup = null;
    255.     }
    256.  
    257.     private AiOptions GetCorrectOption()
    258.     {
    259.         AiOptions solution = null;
    260.         switch (UnitToLoad)
    261.         {
    262.             case Unit.ShadowNormal:
    263.                 solution = ShadowNormalOptions.Options[ShadowNormalOption];
    264.                 break;
    265.             case Unit.ShadowExplosive:
    266.                 solution = ShadowExplosiveOptions.Options[ShadowExplosiveOption];
    267.                 break;
    268.             case Unit.ShadowFlying:
    269.                 solution = ShadowFlyingOptions.Options[ShadowFlyingOption];
    270.                 break;
    271.             case Unit.NecroWaker:
    272.                 solution = NecroWakerOptions.Options[NecroWakerOption];
    273.                 break;
    274.             case Unit.Speareus:
    275.                 solution = SpeareusOptions.Options[SpeareusOption];
    276.                 break;
    277.             case Unit.Baphomet:
    278.                 solution = BaphometOptions.Options[BaphometOption];
    279.                 break;
    280.             case Unit.Bowman:
    281.                 solution = BowmanOptions.Options[BowmanOption];
    282.                 break;
    283.             case Unit.VoidSplitter:
    284.                 solution = VoidSplitterOptions.Options[VoidSplitterOption];
    285.                 break;
    286.         }
    287.  
    288.         if (solution == null)
    289.             Debug.LogError("No option for: " + UnitToLoad);
    290.         return solution;
    291.     }
    292. }

    Code (CSharp):
    1. using System;
    2. using System.Linq;
    3. using UnityEngine;
    4.  
    5. public abstract class AiOptions
    6. {
    7.     public bool StartGoingLeft = true;
    8.  
    9.     [Range(0, 1)] public float RevengeChances = .5f;
    10.     public bool IsAllowedRevenge = true;
    11.     public float RevengeTimeTreshold = 1.5f;
    12.     public float LastAttackTimeTreshold = 0.8f;
    13.     public float DetectEnemyDistance = 30f;
    14.  
    15.     public abstract AiOptions GetOption(int _option);
    16.  
    17.     public string DisplayInInspector(Type _type, string _ignore, AiOptions _option)
    18.     {
    19.         string result = string.Empty;
    20.  
    21.         //        Debug.Log("(Option)_option: " + (Option)_option);
    22.  
    23.         var fieldValues = _option.GetType()
    24.             .GetFields()
    25.             .Select(field => field.GetValue(_option))
    26.             .ToList();
    27.  
    28.         // Field names : OK
    29.         var fieldNames = _type.GetFields()
    30.             .Select(field => field.Name)
    31.             .ToList();
    32.  
    33.         for (int i = 0; i < fieldValues.Count; i++)
    34.         {
    35.             var fieldName = fieldNames[i];
    36.  
    37.             if (fieldName == _ignore) continue;
    38.  
    39.             result += fieldName + ": ";
    40.             var fieldValue = fieldValues[i];
    41.             result += fieldValue.ToString() + "\n";
    42.         }
    43.  
    44.         return result;
    45.     }
    46. }
    47.  
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class ShadowFlyingOptions : AiOptions
    5. {
    6.     public bool PlaysArrival;
    7.     public bool PlaysArrivalStart;
    8.  
    9.     public float ArrivalTime;
    10.     public float TargetHeight;
    11.  
    12.     public Vector2 ArrivalStartDelayTime;
    13.  
    14.     public enum Option
    15.     {
    16.         StandardL,
    17.  
    18.         ArrivalLongL,
    19.         ArrivalLongRandomDelayL,
    20.         ArrivalLongR,
    21.         ArrivalLongRandomDelayR,
    22.         ArrivalShortL,
    23.         ArrivalShortRandomDelayL,
    24.         ArrivalShortR,
    25.         ArrivalShortRandomDelayR,
    26.     }
    27.  
    28.     public static Dictionary<Option, ShadowFlyingOptions> Options = new Dictionary<Option, ShadowFlyingOptions>
    29.     {
    30.         {
    31.             Option.StandardL,
    32.             new ShadowFlyingOptions
    33.             {
    34.                 StartGoingLeft = false,
    35.                 PlaysArrival = false,
    36.                 PlaysArrivalStart = false,
    37.                 TargetHeight = 0,
    38.                 ArrivalTime = 0,
    39.                 ArrivalStartDelayTime = Vector2.zero
    40.             }
    41.         },
    42.  
    43.         {
    44.             Option.ArrivalLongL,
    45.             new ShadowFlyingOptions
    46.             {
    47.                 StartGoingLeft = true,
    48.                 PlaysArrival = true,
    49.                 PlaysArrivalStart = true,
    50.                 TargetHeight = 25,
    51.                 ArrivalTime = .7f,
    52.                 ArrivalStartDelayTime = Vector2.zero
    53.             }
    54.         },
    55.         {
    56.             Option.ArrivalLongRandomDelayL,
    57.             new ShadowFlyingOptions
    58.             {
    59.                 StartGoingLeft = true,
    60.                 PlaysArrival = true,
    61.                 PlaysArrivalStart = true,
    62.                 TargetHeight = 25,
    63.                 ArrivalTime = .7f,
    64.                 ArrivalStartDelayTime = new Vector2(.2f, 1)
    65.             }
    66.         },
    67.         {
    68.             Option.ArrivalLongR,
    69.             new ShadowFlyingOptions
    70.             {
    71.                 StartGoingLeft = false,
    72.                 PlaysArrival = true,
    73.                 PlaysArrivalStart = true,
    74.                 TargetHeight = 25,
    75.                 ArrivalTime = .7f,
    76.                 ArrivalStartDelayTime = Vector2.zero
    77.             }
    78.         },
    79.         {
    80.             Option.ArrivalLongRandomDelayR,
    81.             new ShadowFlyingOptions
    82.             {
    83.                 StartGoingLeft = false,
    84.                 PlaysArrival = true,
    85.                 PlaysArrivalStart = true,
    86.                 TargetHeight = 25,
    87.                 ArrivalTime = .7f,
    88.                 ArrivalStartDelayTime = new Vector2(.2f, 1)
    89.             }
    90.         },
    91.  
    92.         {
    93.             Option.ArrivalShortL,
    94.             new ShadowFlyingOptions
    95.             {
    96.                 StartGoingLeft = true,
    97.                 PlaysArrival = true,
    98.                 PlaysArrivalStart = true,
    99.                 TargetHeight = 12,
    100.                 ArrivalTime = .7f,
    101.                 ArrivalStartDelayTime = Vector2.zero
    102.             }
    103.         },
    104.         {
    105.             Option.ArrivalShortRandomDelayL,
    106.             new ShadowFlyingOptions
    107.             {
    108.                 StartGoingLeft = true,
    109.                 PlaysArrival = true,
    110.                 PlaysArrivalStart = true,
    111.                 TargetHeight = 12,
    112.                 ArrivalTime = .7f,
    113.                 ArrivalStartDelayTime = new Vector2(.2f, 1)
    114.             }
    115.         },
    116.         {
    117.             Option.ArrivalShortR,
    118.             new ShadowFlyingOptions
    119.             {
    120.                 StartGoingLeft = false,
    121.                 PlaysArrival = true,
    122.                 PlaysArrivalStart = true,
    123.                 TargetHeight = 12,
    124.                 ArrivalTime = .7f,
    125.                 ArrivalStartDelayTime = Vector2.zero
    126.             }
    127.         },
    128.         {
    129.             Option.ArrivalShortRandomDelayR,
    130.             new ShadowFlyingOptions
    131.             {
    132.                 StartGoingLeft = false,
    133.                 PlaysArrival = true,
    134.                 PlaysArrivalStart = true,
    135.                 TargetHeight = 12,
    136.                 ArrivalTime = .7f,
    137.                 ArrivalStartDelayTime = new Vector2(.2f, 1)
    138.             }
    139.         },
    140.     };
    141.  
    142.  
    143.     public override AiOptions GetOption(int _option)
    144.     {
    145.         return Options[(Option)_option];
    146.     }
    147. }
     
  10. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    For AiOption.DisplayInInspector, you could use _option.GetType() instead of passing the type manually, which would eliminate the switch statement in ShowOptions.

    For the others, it's tricky because you're uniting info from multiple named fields; I don't think you can eliminate the switch entirely. You could however combine the switches to a single point that returned the name and AiOption together, and/or make the name a property of AiOption, so there's only be a single update point needed.

    Also, it looks like you may be passing more data than you need to. GetCorrectOption already returns the exact option you selected, but then you're manually retrieving that again via OptionNr/GetOption, and passing it to DisplayInInspector. Unless I'm misunderstanding the code, you don't need the _option parameter at all - it should be the same as this.
     
    FeastSC2 likes this.
  11. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    That's true, thanks :) !

    Since each AiOptions implementation has its own Dictionary<#Creature#Option (enum), #Creature#Option (class)> that can't be done in this particular situation I think.
     
  12. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    DisplayInInspector doesn't use that dictionary (and if it did, it could access it via GetOption); it just uses the fields - which are already on the object itself, because the object that you're calling DisplayInInspector on is the same one you're passing as an argument to that method.