Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question How can I re-initialize a variable with different type?

Discussion in 'Scripting' started by josh50536, Mar 31, 2024.

  1. josh50536

    josh50536

    Joined:
    Sep 26, 2021
    Posts:
    4
    Hello,

    I would like to know how I can reinitialize a variable with a different type. More specifically, I'm declaring a variable with a scriptable object type in a monobehaviour class, and I want to declare a new variable with a derived scriptable object type under a derived monobehaviour class. To clear any confusion, here is an example:

    Code (CSharp):
    1. public class CarController : MonoBehaviour
    2. {
    3.     public CarData carData {get; set;} //CarData is a scriptable object class.
    4. }
    5.  
    6. public class PlayerCarController : CarController
    7. {
    8.     public new PlayerCarData carData {get; set;} //PlayerCarData is also a scriptable object class, which derives from CarData.
    9. }
    I tried the above code, however, it doesn't really work like I would want. I'm getting errors like:

    "NullReferenceException: Object reference not set to an instance of an object
    CarController.FixedUpdate () (at Assets/Scripts/CarController.cs)"

    "The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(PlayerCarController) <carData>k__BackingField"

    Is there a way to do what I'm trying to accomplish? And what's the most efficient way to go about it?
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,528
    Omit the carData in the player script. Use the base class property and cast it like this:
    var playercardata = carData as PlayerCarData;

    Assignment works without casting.
     
  3. josh50536

    josh50536

    Joined:
    Sep 26, 2021
    Posts:
    4
    Is there a way to retain the same carData variable but changing it's type to PlayerCarData? Specifically, initializing it when declaring the class property?
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,528
    You can do it with a seperate property that returns the carData properly casted like this:
    Code (CSharp):
    1. public class PlayerCarController : CarController
    2. {
    3.     public PlayerCarData PlayerCarData
    4.     {
    5.         get { return carData as PlayerCarData; }
    6.         set { carData = value; }
    7.     }
    8. }
    Note that C# convention for properties has them written in PascalCase (CarData), not CamelCase (carData). This can quickly lead to confusion because fields are commonly also in CamelCase.

    Why are you subclassing anyway? I have a hunch you do so because it's the OOP thing to do, but it's rarely a good approach. The classic conundrum being: Enemy => SwordEnemy, Enemy => ShieldEnemy, ??? => SwordWithShieldEnemy

    Typically aggregation or a more data-driven approach are preferable. For example if your CarController is either AI or Player controlled, it could have an InputController (class or interface) field and both AI and player input implement InputController and CarController gets assigned an instance of either of these, thus becoming either AI controlled or player controlled.
     
    josh50536 likes this.
  5. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,932
    To really do this you'd have to use generics. Something like:

    Code (CSharp):
    1. public class CarController<T> where T : CarData {
    2.   public T CarData { get; set; }
    3. }
    4.  
    5. public class PlayerCarController : CarController<PlayerCarData> {
    6. }
    7.  
    8. public class TestCode {
    9.   static void Example() {
    10.     // Not functional but just to show you the compiler is happy:
    11.     PlayerCarController pcc = new();
    12.     PlayerCarData carData = pcc.CarData;
    13.   }
    14. }
     
    Nad_B and josh50536 like this.
  6. josh50536

    josh50536

    Joined:
    Sep 26, 2021
    Posts:
    4
    I went ahead and tried what you suggested but I'm still getting the same errors, maybe there's something else wrong in the code?

    Only reason I am deriving from a base CarController class is because, as you assumed, I also have an "EnemyAIController" deriving from it. In which both the PlayerCarController and the EnemyAIController have many similar methods, for example, both have a similar moving method which is called in the FixedUpdate. I was also planning on organizing my code more and as you mentioned, I might incorporate a new class for input and so forth.

    And for the name of the property, I originally had fields instead of properties, I was just dabbling around and didn't really pay mind to the name convention.
     
  7. josh50536

    josh50536

    Joined:
    Sep 26, 2021
    Posts:
    4
    I'm a novice in C#, and I did venture in a bit with generics, but they do scare me lol. I will try and use this suggestion, I'll update you when I have.