Search Unity

Question How may I reformat this to use more composition instead of all inheritance?

Discussion in 'Scripting' started by NatiSFG, Mar 16, 2023.

  1. NatiSFG

    NatiSFG

    Joined:
    Jul 21, 2017
    Posts:
    24
    I'm learning inheritance and composition in C# for the first time and based on the below so far, is there a cleaner/more modular way I could restructure my code by implementing composition for my 2d platformer game? I'd like to bring in composition before the game gets too big.

    To give some context (I know this is a separate issue), I'm also in the middle of trying to figure out a way that only the enemy gets damaged when the player jumps on top of the enemy and so the player does not get damaged. I have two colliders on the enemy (see attached image) and I was also messing around with OverlapArea(). I'm not really sure how to go about this.

    Base class:

    Code (CSharp):
    1. public class Character : MonoBehaviour {
    2.  
    3.     public int maxHealth;
    4.     public int currentHealth;
    5.     public HealthBar healthBar;
    6.  
    7.     [SerializeField] protected float damageInterval = 2f; //in seconds
    8.     protected float currentDamageInterval;
    9.  
    10.     protected SpriteRenderer sr;
    11.     private Rigidbody2D rb;
    12.  
    13.     protected virtual void Start() {
    14.         sr = GetComponent<SpriteRenderer>();
    15.         rb = GetComponent<Rigidbody2D>();
    16.         currentHealth = maxHealth;
    17.         healthBar.SetMaxHealth(maxHealth);
    18.     }
    19.  
    20.     protected virtual void Update() {
    21.         //subtract 1 real life second
    22.         if (currentDamageInterval >= 0)
    23.             currentDamageInterval -= Time.deltaTime;
    24.     }
    25.  
    26.     public bool CanTakeDamage() {
    27.         return (currentDamageInterval < 0);
    28.     }
    29.  
    30.     public virtual void TakeDamage(int damage) {
    31.         currentHealth -= damage;
    32.         healthBar.SetHealth(currentHealth);
    33.  
    34.         int yDamageVelocity = 15;
    35.         rb.velocity = new Vector2(rb.velocity.x, yDamageVelocity);
    36.  
    37.         //set currentDamageInterval to start a new cooldown/delay period (2 seconds)
    38.         currentDamageInterval = damageInterval;
    39.     }
    40. }
    Children classes:

    Code (CSharp):
    1. public class Player : Character {
    2.  
    3.     [SerializeField] private Transform groundCheck;
    4.     [SerializeField] private LayerMask hazardLayer;
    5.  
    6.     private Color[] colors = { Color.red, Color.white };
    7.     private Coroutine damageFlash;
    8.  
    9.     protected override void Start() {
    10.         base.Start();
    11.         maxHealth = 10;
    12.     }
    13.  
    14.     protected override void Update() {
    15.         base.Update();
    16.         //if the current number in the currentDamageInterval is <= 0 or in other words if
    17.         //2 seconds of delay has passed, take damage
    18.         if (CanTakeDamage())
    19.             if (Physics2D.OverlapCircle(groundCheck.position, 0.5f, hazardLayer)) {
    20.                 TakeDamage(1);
    21.             }
    22.  
    23.         //for testing purposes
    24.         if (Input.GetKeyDown(KeyCode.H))
    25.             TakeDamage(1);
    26.     }
    27.  
    28.     public override void TakeDamage(int damage) {
    29.         base.TakeDamage(damage);
    30.         //guarantee at most, one coroutine runs at a time
    31.         if (damageFlash != null)
    32.             StopCoroutine(damageFlash);
    33.         damageFlash = StartCoroutine(DamageFlashing(1f, .1f));
    34.     }
    35.  
    36.     IEnumerator DamageFlashing(float duration, float interval) {
    37.         int index = 0;
    38.         //var is a replacement for WaitForSeconds bc it would be redundant
    39.         var wait = new WaitForSeconds(interval);
    40.  
    41.         for (float elapsedTime = 0; elapsedTime < duration; elapsedTime += interval) {
    42.             //divides the index by 2 and returns the remainder
    43.             sr.color = colors[index % 2];
    44.             index++;
    45.             //waits the interval time and then continues the next color in the flashing duration
    46.             yield return wait;
    47.         }
    48.         damageFlash = null;
    49.     }
    50. }
    Code (CSharp):
    1. public class Enemy : Character {
    2.  
    3.     [SerializeField] private LayerMask player;
    4.  
    5.     protected override void Start() {
    6.         maxHealth = 3;
    7.         base.Start();
    8.     }
    9.  
    10.     protected override void Update() {
    11.         base.Update();
    12.  
    13.         //if (CanTakeDamage()) {
    14.         //    if (Physics2D.OverlapArea(recieveDamageCheckP1.position, recieveDamageCheckP2.position, player)) {
    15.         //        TakeDamage(1);
    16.         //        Debug.Log("Enemy taking damage");
    17.         //    }
    18.         //}
    19.     }
    20.  
    21.     private void OnCollisionEnter2D(Collision2D collision) {
    22.         //if(collision.layer)
    23.     }
    24.  
    25.     void OnTriggerStay(Collider other) {
    26.         if (other.TryGetComponent(out Player player)) {
    27.             if (CanTakeDamage()) {
    28.                 TakeDamage(1);
    29.             }
    30.         }
    31.     }
    32. }
     

    Attached Files:

  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,752
    Make a mover Component to move it.

    Make a damage taker Component to accept damage and kill it

    If you need crazy death effects, make a death effect Component and have everybody call that for death.

    If you want to flash the player when hit, make a sprite flasher Component that searches for a damage taker Component and subscribes to it for when to flash when damage is taken.

    etc.
    etc.
    etc.

    Classic "has a..." versus "is a..."
     
    spiney199 likes this.