가상 함수(function)란?
가상 함수(function)는 자식 클래스(class)에서 재정의(override)할 수 있는 함수(function)입니다. 부모 클래스(class)에서 virtual 키워드를 사용하여 선언하고, 자식 클래스(class)에서 override 키워드로 재정의합니다.
기본 개념
virtual 키워드
- 부모 클래스(class)에서 함수(function)를 가상 함수(function)로 선언
- 자식 클래스(class)에서 재정의 가능하도록 허용
override 키워드
- 자식 클래스(class)에서 부모의 가상 함수(function)를 재정의
- 부모 클래스(class)의 함수(function)를 완전히 대체
기본 예시: 동물 클래스(class)
부모 클래스 (Animal)
public class s01_animal : MonoBehaviour
{
public string animalName;
// 가상 함수
public virtual void Speak()
{
Debug.Log("동물이 말하는 소리를 냅니다.");
}
}
자식 클래스 (Cat)
public class s02_cat : s01_animal
{
// 가상 함수 재정의
public override void Speak()
{
Debug.Log("고양이는 야옹~ 야옹~");
}
}
자식 클래스 (Dog)
public class s03_dog : s01_animal
{
// 가상 함수 재정의
public override void Speak()
{
Debug.Log("강아지는 멍멍~ 멍멍~");
}
}
사용 예시
public class s04 : MonoBehaviour
{
void Start()
{
s01_animal myDog = new s03_dog();
s01_animal myCat = new s02_cat();
myDog.Speak(); // "강아지는 멍멍~ 멍멍~"
myCat.Speak(); // "고양이는 야옹~ 야옹~"
}
}
핵심: 부모 클래스(class) 타입(type)으로 선언했지만, 실제 객체(object)의 타입(type)에 따라 재정의된 함수(function)가 호출됩니다!
실전 예시 1: 적(Enemy) 클래스(class)
부모 클래스(class)
public class Enemy : MonoBehaviour
{
public int hp = 50;
public int damage = 10;
public virtual void Die()
{
Debug.Log("적이 죽었습니다.");
// 기본 동작: 아이템 드롭, 경험치 지급 등
DropItem();
GiveExperience(10);
}
protected virtual void DropItem()
{
Debug.Log("아이템을 드롭합니다.");
}
protected virtual void GiveExperience(int exp)
{
Debug.Log("경험치 " + exp + " 획득!");
}
}
자식 클래스(class)들
// 고블린
public class Goblin : Enemy
{
void Start()
{
hp = 30;
damage = 5;
}
public override void Die()
{
Debug.Log("고블린이 비명을 지르며 쓰러집니다!");
// 고블린만의 특별한 동작
PlayDeathSound();
DropItem();
GiveExperience(5); // 고블린은 경험치 적음
}
void PlayDeathSound()
{
Debug.Log("고블린 비명 소리!");
}
}
// 슬라임
public class Slime : Enemy
{
void Start()
{
hp = 20;
damage = 3;
}
public override void Die()
{
Debug.Log("슬라임이 녹아내립니다!");
// 슬라임은 아이템을 드롭하지 않음
GiveExperience(3);
}
protected override void DropItem()
{
// 슬라임은 아이템을 드롭하지 않음 (빈 함수)
}
}
// 오크
public class Orc : Enemy
{
void Start()
{
hp = 100;
damage = 20;
}
public override void Die()
{
Debug.Log("오크가 큰 소리를 내며 쓰러집니다!");
// 오크는 더 많은 경험치와 아이템
DropItem();
DropItem(); // 아이템 2개 드롭
GiveExperience(50); // 많은 경험치
}
}
사용 예시
public class CombatSystem : MonoBehaviour
{
void Start()
{
// 부모 클래스 타입으로 선언하지만 실제 객체는 자식 클래스
Enemy enemy1 = new Goblin();
Enemy enemy2 = new Slime();
Enemy enemy3 = new Orc();
enemy1.Die(); // "고블린이 비명을 지르며 쓰러집니다!"
enemy2.Die(); // "슬라임이 녹아내립니다!"
enemy3.Die(); // "오크가 큰 소리를 내며 쓰러집니다!"
// 배열로 관리할 수도 있음
Enemy[] enemies = { new Goblin(), new Slime(), new Orc() };
foreach (Enemy enemy in enemies)
{
enemy.Die(); // 각각 다른 방식으로 죽음
}
}
}
실전 예시 2: 무기(Weapon) 클래스(class)
부모 클래스(class)
public class Weapon : MonoBehaviour
{
public int damage = 10;
public float attackSpeed = 1.0f;
// 가상 함수 - 기본 공격 동작
public virtual void Attack()
{
Debug.Log("기본 공격! 데미지: " + damage);
}
// 가상 함수 - 특수 공격
public virtual void SpecialAttack()
{
Debug.Log("특수 공격 사용!");
}
}
자식 클래스(class)들
// 검
public class Sword : Weapon
{
void Start()
{
damage = 20;
attackSpeed = 1.0f;
}
public override void Attack()
{
Debug.Log("검으로 베기 공격! 데미지: " + damage);
}
public override void SpecialAttack()
{
Debug.Log("검의 연속 베기 공격!");
}
}
// 활
public class Bow : Weapon
{
void Start()
{
damage = 15;
attackSpeed = 1.5f;
}
public override void Attack()
{
Debug.Log("활로 원거리 공격! 데미지: " + damage);
}
public override void SpecialAttack()
{
Debug.Log("활의 다중 발사!");
}
}
// 지팡이
public class Staff : Weapon
{
void Start()
{
damage = 25;
attackSpeed = 0.8f;
}
public override void Attack()
{
Debug.Log("마법 공격! 데미지: " + damage);
}
public override void SpecialAttack()
{
Debug.Log("지팡이의 강력한 마법 폭발!");
}
}
사용 예시
public class Player : MonoBehaviour
{
Weapon currentWeapon;
void Start()
{
// 무기를 선택할 수 있음
currentWeapon = new Sword();
// 또는
// currentWeapon = new Bow();
// currentWeapon = new Staff();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
currentWeapon.Attack(); // 선택한 무기에 따라 다른 공격
}
if (Input.GetKeyDown(KeyCode.Q))
{
currentWeapon.SpecialAttack(); // 특수 공격
}
}
}
실전 예시 3: 캐릭터 타입(type)
부모 클래스(class)
public class s07_girl : MonoBehaviour
{
// 가상 함수
public virtual void type()
{
Debug.Log("일반적인 캐릭터");
}
}
자식 클래스(class)들
// 귀여운 타입
public class s07_girl_child01 : s07_girl
{
public override void type()
{
Debug.Log("귀여운 캐릭터");
}
}
// 쿨한 타입
public class s07_girl_child02 : s07_girl
{
public override void type()
{
Debug.Log("쿨한 캐릭터");
}
}
virtual vs override 비교
| 키워드 | 사용 위치 | 역할 |
|---|---|---|
virtual |
부모 클래스(class) | 자식 클래스(class)에서 재정의 가능하도록 허용 |
override |
자식 클래스(class) | 부모의 가상 함수(function)를 재정의 |
가상 함수(function)의 장점
- 다형성(Polymorphism): 같은 타입(type)으로 선언해도 실제 객체(object)에 따라 다른 동작
- 유연성: 각 자식 클래스(class)마다 다른 구현 가능
- 코드 재사용: 공통 인터페이스(interface) 제공하면서 개별 구현 허용
주의사항
- virtual과 override는 쌍으로 사용: 부모에서
virtual, 자식에서override - override 없이는 재정의 불가:
virtual함수(function)를 재정의하려면 반드시override사용 - private 함수(function)는 virtual 불가:
virtual은public또는protected와 함께 사용
abstract vs virtual
| 키워드 | 특징 | 사용 시기 |
|---|---|---|
virtual |
기본 구현 제공, 선택적 재정의 | 기본 동작이 있는 경우 |
abstract |
구현 없음, 반드시 재정의 필요 | 기본 동작이 없고 반드시 구현해야 하는 경우 |
실전 활용 팁
팁 1: 공통 인터페이스(interface) 제공
// 모든 무기가 Attack() 함수를 가지지만, 각각 다른 방식으로 공격
public class Weapon
{
public virtual void Attack() { }
}
팁 2: 기본 동작 제공
// 기본 동작을 제공하되, 필요시 재정의 가능
public class Enemy
{
public virtual void Die()
{
Debug.Log("적이 죽었습니다."); // 기본 동작
}
}
팁 3: 다형성(polymorphism) 활용
// 같은 타입으로 선언해도 실제 객체에 따라 다른 동작
Enemy[] enemies = { new Goblin(), new Slime(), new Orc() };
foreach (Enemy enemy in enemies)
{
enemy.Die(); // 각각 다른 메시지 출력
}