Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Inheritance and serialized fields

Discussion in 'Editor & General Support' started by VascoCorreia, Mar 17, 2023.

  1. VascoCorreia

    VascoCorreia

    Joined:
    Dec 5, 2020
    Posts:
    23
    My project has 2 players, a human, and a spirit. They both have a custom PlayerController that has some similar behavior and then some extra different methods for each.

    I thought about creating a parent class for PlayerController (Monobehaviour), and then 2 child classes one for each character.

    Now when inheriting, the child components also shows the serialized fields in the inspector (check image).
    I was wondering if there was a way to only show the fields in the inspector in the parent class when using inheritance.

    Maybe I am thinking about this incorrectly and inheritance is a bit obsolete here since I could just create a separate script for each, but I would just be repeating code.

    Any suggestions?

    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6.  
    7. public class ParentController: MonoBehaviour
    8. {
    9.  
    10.     public CharacterController Controller { get; private set; }
    11.     protected PlayerInputAction InputAction { get; private set; }
    12.  
    13.     [SerializeField] public Camera PlayerCamera { get; set; }
    14.  
    15.     private float _ySpeed;
    16.     private Vector2 _playerInput;
    17.  
    18.     [SerializeField] private Vector3 _velocityTest;
    19.     [SerializeField] private bool _onGround;
    20.     [SerializeField, Range(0f, 10f)] private float _maxJumpHeight = 2f;
    21.     [SerializeField, Range(0f, 50f)] private float _maxSpeed = 10f;
    22.     [SerializeField, Range(0f, 25f)] private float _pushPower = 3f;
    23.  
    24.     void Awake()
    25.     {
    26.         Controller = GetComponent<CharacterController>();
    27.         InputAction = new PlayerInputAction();
    28.         InputAction.Player.Enable();
    29.  
    30.         //X
    31.         InputAction.Player.Jump.performed += Jump;
    32.     }
    33.  
    34.     //Default values
    35.     void Start()
    36.     {
    37.         _maxJumpHeight = 2f;
    38.         _maxSpeed = 10f;
    39.     }
    40.     void Update()
    41.     {
    42.         //_onGround = Controller.isGrounded; // inspector debuggging    
    43.         calculateVelocity();
    44.         applyGravity();
    45.  
    46.     }
    47.     void OnControllerColliderHit(ControllerColliderHit hit)
    48.     {
    49.         MovePushableObject(hit.collider.attachedRigidbody, hit);
    50.     }
    51.  
    52.     public void Jump(InputAction.CallbackContext context)
    53.     {
    54.         if (Controller.isGrounded)
    55.             _ySpeed += Mathf.Sqrt(_maxJumpHeight * -2.0f * Physics.gravity.y);
    56.     }
    57.  
    58.     private void applyGravity()
    59.     {
    60.         _ySpeed += Physics.gravity.y * Time.deltaTime;
    61.  
    62.         //if we set it to zero the Controller.isGrounded property does not work as intended so we must set it to a small negative value
    63.         if (Controller.isGrounded)
    64.         {
    65.             _ySpeed = -0.01f;
    66.         }
    67.     }
    68.     public void calculateVelocity()
    69.     {
    70.         //get player input
    71.         //get PlayerCameras forward and right vectors
    72.         //multiply input X vector by PlayerCamera right vector
    73.         //multiply input Z vector by PlayerCamera forward vector
    74.         //add these two vectors
    75.  
    76.         _playerInput = InputAction.Player.Movement.ReadValue<Vector2>();
    77.  
    78.         Vector3 forward = PlayerCamera.transform.forward;
    79.         Vector3 right = PlayerCamera.transform.right;
    80.         forward.y = 0;
    81.         right.y = 0;
    82.         forward = forward.normalized;
    83.         right = right.normalized;
    84.  
    85.         Vector3 forwardRelativeVerticalInput = forward * _playerInput.y;
    86.         Vector3 rightRelativeVerticalInput = right * _playerInput.x;
    87.  
    88.         Vector3 PlayerCameraRelativeMovement = forwardRelativeVerticalInput + rightRelativeVerticalInput;
    89.  
    90.         float magnitudeTest = Mathf.Clamp01(PlayerCameraRelativeMovement.magnitude) * _maxSpeed;
    91.  
    92.         PlayerCameraRelativeMovement.Normalize();
    93.         _velocityTest = PlayerCameraRelativeMovement * magnitudeTest;
    94.         _velocityTest.y = _ySpeed;
    95.         _velocityTest = AdjustvelocityToSlope(_velocityTest);
    96.  
    97.         Controller.Move(_velocityTest * Time.deltaTime);
    98.     }
    99.  
    100.     private Vector3 AdjustvelocityToSlope(Vector3 velocity)
    101.     {
    102.         Debug.DrawRay(transform.position, Vector3.down);
    103.  
    104.         var ray = new Ray(transform.position, Vector3.down);
    105.  
    106.         if (Physics.Raycast(ray, out RaycastHit hitInfo, 2f))
    107.         {
    108.             var rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
    109.             var adjustedvelocity = rotation * velocity;
    110.  
    111.             //only adjust the _velocity if were moving down a slope which means out Y component of velocity must be negative, otherwise dont change velocity
    112.             if (adjustedvelocity.y < 0)
    113.             {
    114.                 return adjustedvelocity;
    115.             }
    116.         }
    117.         return velocity;
    118.     }
    119.  
    120.     //Since we use characterControllers we need to code manually rigidbodies being pushed by Rory
    121.     private void MovePushableObject(Rigidbody body, ControllerColliderHit hit)
    122.     {
    123.         // no rigidbody nothing happens
    124.         if (body == null || body.isKinematic)
    125.         {
    126.             return;
    127.         }
    128.  
    129.         Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);
    130.  
    131.         //apply the push
    132.  
    133.         body.AddForce(pushDir * _pushPower, ForceMode.Force);
    134.     }
    135.  
    136.  
    137.  
    138.     protected IEnumerator Cooldown(float cooldownTime, Action<bool> callback)
    139.     {
    140.         callback(false);
    141.         yield return new WaitForSeconds(cooldownTime);
    142.         callback(true);
    143.     }
    144.  
    145.  
    146. }
    147.  
    Child Class:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6.  
    7. public class SpiritController : PlayerController
    8. {
    9.     [SerializeField] private LayerMask _possessionLayerMask;
    10.     [SerializeField] private bool _canPossess;
    11.     [SerializeField] private float _possessionCooldown = 2f;
    12.     [SerializeField, Range(10f, 30f)] private float _possessionDistanceFromCamera = 15f;
    13.     [SerializeField] private float _truePossessionDistanceFromPlayer; //value not starting on camera but starting from player
    14.  
    15.     public event Action<possessionEventArgs> possessionSucessfull;
    16.     public Action possessionFailed;
    17.  
    18.     // Start is called before the first frame update
    19.     void Awake()
    20.     {
    21.         //R1
    22.         InputAction.Player.Possess.performed += tryPossession;
    23.     }
    24.  
    25.     void Start()
    26.     {
    27.         _canPossess = true;
    28.         _possessionDistanceFromCamera = 15f;
    29.     }
    30.  
    31.     // Update is called once per frame
    32.     void Update()
    33.     {
    34.         _truePossessionDistanceFromPlayer = _possessionDistanceFromCamera - Vector3.Distance(PlayerCamera.transform.position, transform.position) - Controller.radius;
    35.     }
    36.     //When clicking R1
    37.     //casts an array from the camera so that the direction is correctly aligned with the crosshair
    38.     //if collides with a possessable enemy invokes event, that sends as an argument the gameObject that was hit
    39.     //if does not collide send an event to UI to spawn text on screen
    40.     private void tryPossession(InputAction.CallbackContext context)
    41.     {
    42.         if (_canPossess)
    43.         {
    44.             StartCoroutine(Cooldown(_possessionCooldown, (i) =>
    45.             {
    46.                 _canPossess = i;
    47.             }));
    48.  
    49.             Ray ray = new(PlayerCamera.transform.position, PlayerCamera.transform.forward * _possessionDistanceFromCamera);
    50.  
    51.             if (Physics.Raycast(ray, out RaycastHit info, _possessionDistanceFromCamera, _possessionLayerMask))
    52.             {
    53.                 possessionSucessfull?.Invoke(new possessionEventArgs(info.collider.gameObject));
    54.             }
    55.             else
    56.             {
    57.                 possessionFailed?.Invoke();
    58.             }
    59.         }
    60.     }
    61.     //Allows us too see the ray for possession
    62.     private void OnDrawGizmos()
    63.     {
    64.         Gizmos.color = Color.red;
    65.         Gizmos.DrawRay(PlayerCamera.transform.position, PlayerCamera.transform.forward * _possessionDistanceFromCamera);
    66.     }
    67. }
    68.  
     

    Attached Files:

  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,611
    You could make a custom inspector for your type. But then, what's the point of using inheritance?

    Naturally when you use inheritance you don't want both the base type and the derived type on the same game object, because the derived type IS the base type too.

    Remember Unity is component based, so it's often better to break down functionality into smaller components.
     
    VascoCorreia likes this.