내일배움캠프/TIL

내일배움캠프 9일차 TIL - 인터페이스

서보훈 2024. 9. 25. 20:31

인터페이스 관련 내용의 활용을 복기하고자 이전에 사용했었던 내용을 가져왔습니다.

 

인터페이스를 활용할 때, IDamageable 인터페이스를 만들어서 피해를 받을수 있는 오브젝트를 구분해서 데미지를 줄 수 있습니다.

IDamageable 인터페이스와 이를 가지고있는 Enemy 클래스입니다.

public interface IDamageable
{
    public void TakeDamage(float damage);
}

 

 public class Enemy : MonoBehaviour, IDamageable
 {
 
        //데미지 입기
        public void TakeDamage(float damage)
        {
            //데미지 계산
            currentHealth -= damage;

            //데미지 효과

            //죽음 체크
            if (currentHealth <= 0f && isDeath == false)
            {
                Die();
            }
        }
}

 

그리고 여기 플레이어가 총을 쏠때 호출되는 코루틴함수 입니다.

IEnumerator PistolFire()
{            
    isFire = true;

    //앞 방향으로 레이를 쏘고 레이에 맞는 Enemy에게 데미지 주기
    RaycastHit hit;
    if(Physics.Raycast(firePoint.position, firePoint.forward, out hit, attackRange))
    {
        //hit info
        GameObject impactEff = Instantiate(hitImpactPrefab, hit.point, Quaternion.LookRotation(hit.normal));
        Destroy(impactEff, 2f);

        //
        IDamageable damageable = hit.transform.GetComponent<IDamageable>();
        if (damageable != null)
        {
            damageable.TakeDamage(attackDamage);
        }
    }

    //발사 효과 이펙트
    animator.SetTrigger(fireTrigger);
    pistolShot.Play();

    //fireTime초 지연 후에 
    yield return new WaitForSeconds(fireTime);

    isFire = false;
}

 

레이캐스팅 부분을 넘기고 피격당한 대상에게 IDamageable를 컴포넌트로써 사용중인지 탐색하여 이를 저장합니다.

IDamageable damageable = hit.transform.GetComponent<IDamageable>();
if (damageable != null)
{
    damageable.TakeDamage(attackDamage);
}

if(damageable != null) 을 사용하여 대상에게 IDamageable이 붙은 컴포넌트가 있다면 해당 컴포넌트를 작동합니다.

 

여기에 또다른 IDamageable을 사용하는 클래스, BreakObject 가 있습니다.

이 클래스는 자신의 변수인 isUnbreakable이 false일경우 TakeDamage를 통해 한번 공격받으면 부숴지도록 구현되어있습니다.

public class BreakObject : MonoBehaviour, IDamageable
{

    public void TakeDamage(float damage)
    {
        if(isUnbreakable)
        {
            return;
        }

        health -= damage;

        if(health < 0f && isDeath == false)
        {
            Die();
        }
    }
}

 

이 클래스 또한 IDamageable 을 통해 함수를 호출하기 때문에 위의 내용을 그대로 사용할 수 있습니다.

만약 이 두 클래스에 Damageable 클래스를 사용하지 않는다면 이렇게 구현하게 됩니다.

IEnumerator PistolFire()
{            
    isFire = true;

    //앞 방향으로 레이를 쏘고 레이에 맞는 Enemy에게 데미지 주기
    RaycastHit hit;
    if(Physics.Raycast(firePoint.position, firePoint.forward, out hit, attackRange))
    {
        //hit info
        GameObject impactEff = Instantiate(hitImpactPrefab, hit.point, Quaternion.LookRotation(hit.normal));
        Destroy(impactEff, 2f);

        if (hit.transform.tag == enemyTag) //적을 맞추었다
        {
            Robot robot = hit.transform.GetComponent<Robot>();
            if (robot)
            {
                robot.TakeDamage(attackDamage);
            }

            Zombie zombie = hit.transform.GetComponent<Zombie>();
            if (zombie)
            {
                zombie.TakeDamage(attackRange);
            }

        }
        else if(hit.transform.tag == "BreakObejct")
        {
            //깨지는 오브젝트 깨트리기
        }
        
    }

    //발사 효과 이펙트
    animator.SetTrigger(fireTrigger);
    pistolShot.Play();

    //fireTime초 지연 후에 
    yield return new WaitForSeconds(fireTime);

    isFire = false;
}

 

EnemyTag의 내용은 Enemy 라는 클래스를 부모클래스로 가지고 있고, Enemy 클래스에 TakeDamage가 구현되어있으면 이 클래스를 통해 호출이 가능합니다.

하지만 파괴 가능한 오브젝트는 다릅니다. 이 클래스는 자신의 변수에 따라 행동이 달라지기때문에 Enemy 클래스의 자식으로 넣을수 없으며, 오버라이드 기능을 사용한다고 해도 파괴가능 오브젝트가 Enemy 클래스의 자식클래스인것은 뭔가 이상합니다.

if (hit.transform.tag == enemyTag) //적을 맞추었다
{
    Robot robot = hit.transform.GetComponent<Robot>();
    if (robot)
    {
        robot.TakeDamage(attackDamage);
    }

    Zombie zombie = hit.transform.GetComponent<Zombie>();
    if (zombie)
    {
        zombie.TakeDamage(attackRange);
    }

    //......
}
else if(hit.transform.tag == "BreakObejct")
{
    //깨지는 오브젝트 깨트리기
}

 

이러한 경우를 처리하는데 유용한것이 인터페이스입니다.