본문 바로가기

유니티(오버로딩,상속,가상화/추상화,인터페이스 실습) _ 멋쟁이사자처럼 유니티 부트캠프 후기 22회차

@salmu2025. 6. 17. 17:37
반응형

[22회차 수업내용]

1. 오버로딩

2. 상속 

3. 가상화, 추상화

4. 인터페이스

 

주로 지난 내용의 복습/실습으로 이루어졌다. 

2025.06.13 - [프로그래밍/유니티 부트캠프] - 유니티(형변환,객체지향프로그래밍) _ 멋쟁이사자처럼 유니티 부트캠프 후기 21회차

 

 

1. 오버로딩

  • 메서드 이름은 같고, 매개변수(타입, 개수)가 다른 것
  • 목적: 같은 동작을 상황에 따라 다양하게 처리
public class StudyOverloading : MonoBehaviour
{
    void Start()
    {
        Attack();
        Attack(true);
        Attack(10f);
        Attack(10, new GameObject("몬스터"));
    }

    public void Attack()
    {
        Debug.Log("공격");
    }

    public void Attack(bool isMagic)
    {
        if (isMagic)
            Debug.Log("마법 공격");
    }

    public void Attack(float damage)
    {
        Debug.Log($"{damage} 데미지 만큼 공격");
    }

    public void Attack(float damage, GameObject target)
    {
        Debug.Log($"{target.name}에게 {damage} 데미지 만큼 공격");
    }
}

 

[코드 입력 결과]

공격
마법 공격
10 데미지 만큼 공격
몬스터에게 10 데미지 만큼 공격

 

 

 

2. 상속

부모 클래스(기본 클래스)의 속성과 메서드(기능)를 자식 클래스(파생 클래스)가 물려받아 사용

  • 중복 제거 → 코드 재사용
  • 공통 기능 통합 관리 → 유지보수 편함
  • 확장성 → 자식 클래스가 고유 기능 추가 가능

 

* 상속 적용 예시

//person 안에 Student와 Soldier 공통기능은 함께 + 타입별 기능은 각자 = 코드 효율성 증가

public class Person : MonoBehaviour
{
    public string name;
    public int age;

    public void Walk()
    {
        Debug.Log("Walk");
    }

    public void Run()
    {
        Debug.Log("Run");
    }
}
//사람
public class Student : Person
{
    public string schoolName;
    public string classNumber;

    public void Study()
    {
        Debug.Log("Study");
    }
}
//솔져
public class Soldier : Person
{
    public string soldierNumber;

    public void Shoot()
    {
        Debug.Log("Shoot");
    }
}

 

[persons 상속 예시]

public class StudyInheritance : MonoBehaviour
{
    // Person을 상속한 Student, Soldier 객체들을 저장하는 리스트
    public List<Person> persons = new List<Person>();

    void Start()
    {
        // Student와 Soldier 객체 각각 10개씩 생성하여 persons 리스트에 추가
        for (int i = 0; i < 10; i++)
        {
            Student student = new Student(); // 학생 객체 생성
            persons.Add(student);            // 리스트에 추가
            
            Soldier soldier = new Soldier(); // 군인 객체 생성
            persons.Add(soldier);            // 리스트에 추가
        }
    }

    public void AllMove()
    {
        // persons 리스트에 있는 모든 객체에게 Walk() 호출
        // Student, Soldier 구분 없이 부모(Person)의 기능으로 처리
        foreach (var person in persons)
            person.Walk();
    }
}

*리마인드 

  • .Add() :리스트에 새 요소 추가하는 함수
  • foreach (var 변수 in 컬렉션) → 컬렉션의 요소들을 하나씩 꺼내서 순서대로 반복 실행하는 구문

3. 가상화/추상화

 

[Character 상속 예시]

public abstract class Character : MonoBehaviour
{
    public float hp;
    public float moveSpeed;
    
    void Update()
    {
        Move();
    }
    
 // 가상 메서드: 필요 시 자식 클래스에서 오버라이드 가능
    public virtual void Attack()
    {
        Debug.Log("공격1");
    }

 // 추상 메서드: 반드시 자식 클래스에서 구현해야 함
    public abstract void Move();
}

 

 

Attack과 Move를 재정의한 자식 클래스

// Character 클래스를 상속받는 Player 클래스
public class Player : Character
{
    public override void Attack() // Attack 메서드 오버라이드 (재정의)
    {
        base.Attack();  // 부모 클래스(Character)의 Attack() 먼저 실행 → "공격1" 출력
        Debug.Log("Player 공격"); // 이후 Player 고유 공격 동작 출력
    }
    
    // 반드시 구현해야 하는 추상 메서드 Move() → Player 클래스 전용 이동 동작 정의
    public override void Move()
    {
        Debug.Log("Player 이동");  // "Player 이동" 출력
    }
}
base.메서드() 부모 클래스의 메서드를 그대로 호출기본 동작 유지 + 추가 동작 덧붙이기에 사용

 

 

[이어폰 예시]

public class EarPhone : MonoBehaviour
{
    // 이어폰 이름 (예: Galaxy Buds, AirPods 등)
    public string name;

    // 이어폰 가격
    public float price;

    // 출시 연도
    public int releaseYear;

    // 음악 재생 기능 메서드
    public void PlayMusic()
    {
        Debug.Log("음악 재생"); // 콘솔에 "음악 재생" 출력
    }
}
// WireEarPhone 클래스는 EarPhone 클래스를 '상속'받음 → EarPhone의 변수와 메서드 사용 가능
public class WireEarPhone : EarPhone
{
    void Start()
    {
        // 상속받은 name, price, releaseYear 변수에 값 할당
        name = "EarPod";
        price = 30f;
        releaseYear = 2005;
    }
}
// WirelessEarPhone 클래스는 EarPhone 클래스를 '상속'받음 → EarPhone의 변수와 메서드 사용 가능
public class WirelessEarPhone : EarPhone
{
    public float batterySize;         // 배터리 용량 (예: 70f)
    public bool isWirelessCharged;    // 무선 충전 지원 여부 (true/false)

    void Start()
    {
        name = "AirPod1";         // 상속받은 변수 초기화
        price = 100f;
        releaseYear = 2007;
        batterySize = 70f;        // 새로운 멤버 변수 값 설정
    }

    // 충전 방식 출력하는 메서드
    public void Charged()
    {
        // 삼항 연산자 사용: isWirelessCharged가 true면 "무선 충전", false면 "유선 충전"왜 ms
        string msg = isWirelessCharged ? "무선 충전" : "유선 충전";
        Debug.Log(msg);
    } 
}

 (msg -> message)

 

 

 

 

 

4. 인터페이스

4.1 인터페이스 생성 예시

public interface IDamageable
{
    void TakeDamage(float damage);
    void Death();
}
//공격 불가능한 wall 클래스
public class Wall : MonoBehaviour
{

}

//공격 가능한 Door 클래스
public class Door : MonoBehaviour, IDamageable
{
    public float hp = 100f;
    
    public void TakeDamage(float damage)
    {
        Debug.Log($"{damage}만큼의 피해를 입었습니다.");

        hp -= damage;
        if (hp <= 0f)
            Death();
    }

    public void Death()
    {
        Debug.Log("문이 파괴되었습니다.");
    }
}

 

 

Trigger 감지 _ 공격하는 Sword

public class Sword : MonoBehaviour
{
    public float damage = 10f;
    
    private void OnTriggerEnter(Collider other)
    {
       // 충돌한 대상이 IDamageable 인터페이스를 구현하고 있는지 확인
        if (other.GetComponent<IDamageable>() != null)
        {
            // 대상에게 데미지를 입히기 위해 TakeDamage 함수 호출
            other.GetComponent<IDamageable>().TakeDamage(damage);
        }
    }
}

 

 

공격받는 Monster

// 몬스터 기본 클래스 - IDamageable 인터페이스 구현, 추상 클래스
public abstract class Monster : MonoBehaviour, IDamageable
{
    public float hp;  // 몬스터 체력

    // 몬스터마다 체력 설정 방식이 다르므로 추상 메서드로 선언
    public abstract void SetHealth();

    void Start()
    {
        // 게임 시작 시 각 몬스터의 체력 설정 호출
        SetHealth();
    }
    
    // 데미지를 입었을 때 호출되는 함수
    public void TakeDamage(float damage)
    {
        hp -= damage;  // 체력 차감
        if (hp <= 0f)  // 체력이 0 이하가 되면
            Death();    // 사망 처리 함수 호출
    }

    // 몬스터 사망 시 호출되는 함수
    public void Death()
    {
        Debug.Log("몬스터 죽음");
        // 여기서 몬스터 사망 효과나 오브젝트 제거 처리 가능
    }
}

 

체력설정_ 추상화와 다형성

public class Orc : Monster
{
    public override void SetHealth()
    {
        hp = 100f;
    }
}

public class Goblin : Monster
{
    public override void SetHealth()
    {
        hp = 30f;
    }
}

 

 

 

[아이템 획득/사용/버리기 실습]

public class Character : MonoBehaviour
{
    // 현재 장착한 아이템을 저장하는 인터페이스 타입 변수
    public IDropItem currentItem;

    void Update()
    {
        // 마우스 왼쪽 버튼 클릭 시 현재 아이템 사용
        if (Input.GetMouseButtonDown(0))
            currentItem.Use();

        // B 키를 누르면 현재 아이템 버리기
        if (Input.GetKeyDown(KeyCode.B))
            currentItem.Drop();
    }
    
    private void OnTriggerEnter(Collider other)
    {
        // 충돌한 대상이 IDropItem 인터페이스를 구현하는지 확인
        if (other.GetComponent<IDropItem>() != null)
        {
            충돌한 대상에서 IDropItem 컴포넌트 가져오기 (인터페이스 타입으로)
            IDropItem item = other.GetComponent<IDropItem>();
            
            item.Grab(); // 아이템 획득 처리

            currentItem = item; // 현재 아이템으로 설정
        }
    }
}

 

Q:  IDropItem item = other.GetComponent<IDropItem>(); 에서 왜 인터페이스 타입으로 캐스팅 하나? (이미 인터페이스 타입 같은데)

A: GetComponent<T>()해당 컴포넌트가 존재하면 그 컴포넌트의 참조를 반환

그런데 GetComponent<T>() 호출은 비용이 꽤 크기 때문에 한 번 호출해서 반환받은 값을 변수에 저장해두면 이후에 재사용할 때 다시 호출하지 않아도 됨.

 

 

 

오버로딩, 상속, 추상화, 인터페이스를 배우면서 코드 구조를 효율적으로 설계하는 방법을 알게 되었다.

비슷한 기능을 깔끔하게 정리하고, 확장성과 재사용성을 높일 수 있어 실무에서도 꼭 필요한 개념이라는 걸 느꼈다.

앞으로 직접 여러 구조를 만들어 보면서 더 익숙해지고 싶다.

반응형
salmu
@salmu :: SMU 각종 기록

목차