- 유니티 C# 프로그래밍
형변환 / 객체지향프로그래밍
public enum GameState
{
Menu, // 0
Playing, // 1
Paused, // 2
GameOver // 3
}
void Start()
{
// 1. 기본 명시적 형변환
GameState currentState = GameState.Playing;
int stateValue = (int)currentState;
Debug.Log($"GameState.Playing의 값: {stateValue}"); // 출력: 1
// 2. Convert.ToInt32() 사용
GameState menuState = GameState.Menu;
int menuValue = System.Convert.ToInt32(menuState);
Debug.Log($"Convert.ToInt32로 변환: {menuValue}"); // 출력: 0
}
enum을 int로 형변환
enum은 기본적으로 정수 타입을 기반으로 하므로, 명시적 형변환을 통해 int로 변환 가능
int와 float의 형변환할때의 암시적 형변환과 명시적 형변환
// ========== INT → FLOAT ==========
int intValue = 42;
// 암시적 (자동 변환, 데이터 손실 없음)
float result1 = intValue;
Debug.Log($"int→float 암시적: {intValue} → {result1}");
// 명시적 (의도 명확화)
float result2 = (float)intValue;
float result3 = Convert.ToSingle(intValue);
Debug.Log($"int→float 명시적: {result2}, {result3}");
// ========== FLOAT → INT ==========
float floatValue = 42.7f;
// 암시적 변환 불가능 (컴파일 에러)
// int error = floatValue; // ❌ 에러!
// 명시적 변환만 가능 (소수점 절삭, 데이터 손실 가능)
int result4 = (int)floatValue; // 42 (절삭)
int result5 = Convert.ToInt32(floatValue); // 43 (반올림)
int result6 = Mathf.FloorToInt(floatValue); // 42 (내림)
int result7 = Mathf.CeilToInt(floatValue); // 43 (올림)
int result8 = Mathf.RoundToInt(floatValue); // 43 (반올림)
Debug.Log($"float→int: {floatValue} → 캐스트:{result4}, Convert:{result5}");
Debug.Log($"Mathf: Floor:{result6}, Ceil:{result7}, Round:{result8}");
INT → FLOAT
- 암시적: float f = intValue; ✅ (데이터 손실 없음)
- 명시적: (float)intValue, Convert.ToSingle()
FLOAT → INT
- 암시적: 불가능 ❌ (컴파일 에러)
- 명시적:
- (int)floatValue - 소수점 절삭
- Convert.ToInt32() - 반올림
- Mathf.RoundToInt() - 반올림 (Unity)
- Mathf.FloorToInt() - 내림 (Unity)
- Mathf.CeilToInt() - 올림 (Unity)
핵심 차이점
- int→float: 안전해서 암시적 변환 가능
- float→int: 데이터 손실 위험으로 명시적 변환만 가능
Parse, Convert.ToBoolean, Convert.ToInt32의 특징
Parse (문자열 → 특정 타입)
- int.Parse("123") → 123
- float.Parse("45.67") → 45.67f
- bool.Parse("true") → true
- 실패 시: 예외 발생 ⚠️
Convert.ToBoolean (다양한 타입 → bool)
- Convert.ToBoolean(1) → true (0이 아니면 true)
- Convert.ToBoolean("False") → false
- Convert.ToBoolean(0) → false
Convert.ToInt32 (다양한 타입 → int)
- Convert.ToInt32(42.8f) → 43 (반올림)
- Convert.ToInt32(true) → 1
- Convert.ToInt32("999") → 999
핵심 차이점
- Parse: 문자열 전용, 실패 시 예외
- Convert: 다양한 타입 지원, null 안전
- ToInt32: 반올림 처리 (캐스팅은 절삭)
상속과 클래스 형변환
부모클래스와 자식클래스의 상속 관계 및 업캐스팅(Upcasting), 다운캐스팅(Downcasting)
// 부모 클래스
public class Monster
{
public int hp = 100;
public string name;
public virtual void Attack()
{
Debug.Log($"{name}이(가) 공격!");
}
public void TakeDamage(int damage)
{
hp -= damage;
Debug.Log($"{name} HP: {hp}");
}
}
// 자식 클래스 1
public class Orc : Monster
{
public int rage = 50;
public Orc()
{
name = "오크";
hp = 150;
}
public override void Attack()
{
Debug.Log($"{name}이(가) 도끼로 강력하게 공격!");
}
public void Charge()
{
Debug.Log($"{name}이(가) 돌진 공격!");
}
}
// 자식 클래스 2
public class Goblin : Monster
{
public int agility = 80;
public Goblin()
{
name = "고블린";
hp = 80;
}
public override void Attack()
{
Debug.Log($"{name}이(가) 단검으로 빠르게 공격!");
}
public void Stealth()
{
Debug.Log($"{name}이(가) 은신!");
}
}
public class InheritanceCastingExample : MonoBehaviour
{
void Start()
{
// ========== 업캐스팅 (Upcasting) - 자식 → 부모 ==========
// 암시적 형변환 (자동, 안전)
Orc orc = new Orc();
Monster monster1 = orc; // 암시적 업캐스팅 ✅
Goblin goblin = new Goblin();
Monster monster2 = goblin; // 암시적 업캐스팅 ✅
Debug.Log("=== 업캐스팅 ===");
monster1.Attack(); // 오크의 Attack() 호출 (다형성)
monster2.Attack(); // 고블린의 Attack() 호출 (다형성)
// 부모 타입으로는 자식 고유 메서드 접근 불가
// monster1.Charge(); // ❌ 컴파일 에러!
// monster2.Stealth(); // ❌ 컴파일 에러!
// ========== 다운캐스팅 (Downcasting) - 부모 → 자식 ==========
Debug.Log("\n=== 다운캐스팅 ===");
// 명시적 형변환 (위험, 실행시 에러 가능)
Monster[] monsters = { new Orc(), new Goblin(), new Orc() };
foreach (Monster monster in monsters)
{
// 타입 확인 후 다운캐스팅
if (monster is Orc)
{
Orc downcastOrc = (Orc)monster; // 명시적 다운캐스팅
downcastOrc.Charge();
Debug.Log($"오크 분노: {downcastOrc.rage}");
}
else if (monster is Goblin)
{
Goblin downcastGoblin = (Goblin)monster; // 명시적 다운캐스팅
downcastGoblin.Stealth();
Debug.Log($"고블린 민첩: {downcastGoblin.agility}");
}
}
// ========== 안전한 형변환 방법들 ==========
Debug.Log("\n=== 안전한 형변환 ===");
Monster testMonster = new Goblin();
// 1. as 연산자 (실패 시 null 반환)
Orc asOrc = testMonster as Orc; // null (고블린이므로)
Goblin asGoblin = testMonster as Goblin; // 성공
if (asOrc != null)
{
asOrc.Charge();
}
else
{
Debug.Log("오크가 아님");
}
if (asGoblin != null)
{
asGoblin.Stealth();
Debug.Log("고블린 형변환 성공!");
}
// 2. is 연산자로 타입 확인
if (testMonster is Goblin specificGoblin)
{
specificGoblin.Stealth();
Debug.Log($"패턴 매칭으로 변환: {specificGoblin.name}");
}
}
}
업캐스팅 (Upcasting) - 자식 → 부모
- 암시적 형변환 ✅: Monster monster = new Orc();
- 안전함: 항상 성공 (자식은 부모의 모든 기능 보유)
- 제한: 부모 타입으로는 자식 고유 메서드 접근 불가
다운캐스팅 (Downcasting) - 부모 → 자식
- 명시적 형변환 ⚠️: Orc orc = (Orc)monster;
- 위험함: 잘못된 타입이면 런타임 에러
- 용도: 자식 고유 기능에 접근할 때
안전한 형변환 방법
- as 연산자: Orc orc = monster as Orc; (실패 시 null)
- is 연산자: if (monster is Orc orc) (타입 확인 + 변환)
핵심 포인트
- 업캐스팅: 암시적, 안전, 다형성 활용
- 다운캐스팅: 명시적, 위험, 타입 확인 필수
- 다형성: 부모 타입으로 자식 메서드 호출 가능
- 실무: as와 is 연산자로 안전하게 처리
Monster클래스에서 Virtual을 사용한 이유
다형성(Polymorphism) 구현
Monster monster = new Orc();
monster.Attack(); // Orc의 Attack() 호출 (부모 타입이지만!)
- virtual 없으면 항상 부모의 Attack() 호출
- virtual 있으면 실제 객체(Orc)의 Attack() 호출
Virtual vs Abstract 비교
Virtual (가상 메서드)
- 기본 구현 제공 ✅
- 오버라이드 선택사항 (안 해도 됨)
- 인스턴스 생성 가능
public virtual void Attack() { /* 기본 구현 */ }
// 자식: 오버라이드 해도 되고, 안 해도 됨
Abstract (추상 메서드)
- 구현 없음 ❌
- 오버라이드 필수 (안 하면 컴파일 에러)
- 인스턴스 생성 불가
public abstract void UseWeapon(); // 구현 없음
// 자식: 반드시 구현해야 함
언제 사용할까?
Virtual 사용 시기
- 기본 동작이 있지만, 자식마다 다르게 할 수 있을 때
- 예: Die() - 기본적으로 "쓰러졌다"지만, 몬스터마다 다른 연출 가능
Abstract 사용 시기
- 자식마다 반드시 다른 구현이 필요할 때
- 예: UseWeapon() - 무기마다 완전히 다른 사용법
핵심 요약
- Virtual: "기본은 이거지만, 바꿔도 돼"
- Abstract: "이건 네가 반드시 만들어야 해"
- 다형성: 부모 타입으로 자식 메서드 호출 가능
오버라이딩(Overriding)과 오버로딩(Overloading)의 차이
오버라이딩 (Overriding) - "재정의"
- 상속 관계에서 부모 메서드를 새롭게 구현
- 같은 이름, 같은 매개변수
- 런타임에 어떤 메서드 호출할지 결정 (다형성)
- 키워드: virtual, override
// 부모
public virtual void Attack() { /* 구현 */ }
// 자식
public override void Attack() { /* 새로운 구현 */ }
오버로딩 (Overloading) - "중복 정의"
- 같은 클래스에서 같은 이름의 메서드를 여러 개 정의
- 다른 매개변수 (타입, 개수, 순서)
- 컴파일 타임에 어떤 메서드 호출할지 결정
- 키워드: 없음
public void Attack() { } // 매개변수 없음
public void Attack(int damage) { } // int 1개
public void Attack(string weapon) { } // string 1개
public void Attack(int damage, string weapon) { } // 2개
비교표
구분오버라이딩오버로딩
관계 | 부모-자식 클래스 | 같은 클래스 |
목적 | 기능 재정의 | 편의성 제공 |
매개변수 | 동일해야 함 | 달라야 함 |
결정 시점 | 런타임 | 컴파일 타임 |
다형성 | 지원 ✅ | 지원 안함 ❌ |
실무에서 언제 사용?
오버라이딩 사용 시
- 몬스터마다 다른 공격 방식
- UI 요소마다 다른 클릭 이벤트
- 캐릭터마다 다른 이동 방식
오버로딩 사용 시
- 함수 호출을 편리하게 (Attack(), Attack(damage))
- 다양한 타입 지원 (Add(int), Add(float))
- 선택적 매개변수 효과
'멋사교육' 카테고리의 다른 글
멋쟁이사자처럼부트캠프 유니티 게임 개발 5기 52일차 (1) | 2025.08.01 |
---|---|
멋쟁이사자처럼부트캠프 유니티 게임 개발 5기 51일차 (0) | 2025.07.30 |
멋쟁이사자처럼부트캠프 유니티 게임 개발 5기 11일차 (2) | 2025.05.28 |
멋쟁이사자처럼부트캠프 유니티 게임 개발 5기 10일차 (0) | 2025.05.27 |
멋쟁이사자처럼부트캠프 유니티 게임 개발 5기 9일차 (2) | 2025.05.25 |