문자열을 boolean으로 변환하면 True, False 값을 제외하고는 에러가 발생한다.
2.4. ToInt32()
bool 타입을 Int 타입으로 변환
using System;
using UnityEngine;
public class StudyCasting : MonoBehaviour
{
void Start()
{
bool isBool1 = true;
bool isBool2 = false;
int num1 = Convert.ToInt32(isBool1);
int num2 = Convert.ToInt32(isBool2);
Debug.Log(num1); // 1
Debug.Log(num2); // 0
}
}
3. 클래스 변환
🥕 예행 작업 1. Scripts 생성 (Moster, Orc, Goblin) 2. Orc, Goblin → Monster 클래스 형 변환을 하기 위해 부모/자식 관계 만들기
// public class Orc : Monster
// public class Goblin : Monster
3.1. Up Casting
using System;
using System.Collections.Generic;
using UnityEngine;
public class StudyCasting : MonoBehaviour
{
List<Orc> orcs = new List<Orc>();
List<Goblin> goblins = new List<Goblin>();
List<Monster> monsters = new List<Monster>();
void Start()
{
Orc o = new Orc();
Goblin g = new Goblin();
// 명시적 클래스 형 변환 (가독성)
Monster m1 = (Monster) o;
Monster m2 = (Monster) g;
// 암시적 클래스 형 변환 (개발 효율)
Monster m3 = o;
Monster m4 = g;
monsters.Add(o);
monsters.Add(g);
// 부모로 통합해서 공격
// monster.AllAttack();
// 자식 전체 공격 -> 몬스터 종류가 많아질수록 관리 불가!! = 부모로 통합할 것
// orcs.AllAttack();
// goblins.AllAttack();
}
}
3.2. Down Casting
// 다운 캐스팅 -> 예외 발생 가능!
Monster m5 = new Monster();
Orc o2 = (Orc)m5;
Debug.Log(o2);
3.3. Is와 As
Is : 타입을 확인하여 bool 타입 반환 → 값, 참조 타입 모두 가능
As : 형 변환이 가능한지 확인한 후 변환 or Null 반환 → 참조 타입만 가능
using System;
using System.Collections.Generic;
using UnityEngine;
public class StudyCasting : MonoBehaviour
{
void Start()
{
Monster m = new Monster();
// Orc o1 = m; // 암시적 변환 X
// Orc o = (Orc)m; // 명시적 변환 X -> 에러
Orc o = m as Orc; // 성공시 형변환 / 실패시 Null
if (o != null)
{
Debug.Log(o);
}
else
{
Debug.Log("형변환 되지 않음");
}
}
}
다운캐스팅을 할 때에는 객체의 실제 타입이 대상 타입과 다를 수 있으므로
is 키워드로 타입을 먼저 검사하거나, as 키워드로 캐스팅 후 null 체크를 통해 형변환이 안전하게 이루어졌는지 확인하는 것이 좋다.
4. Boxing(박싱)
값 타입→ 참조 타입으로 변환하는 과정으로,
int, float, bool, struct 등이 값 타입인데 이걸 object나 interface로 변환할 때 박싱이 발생한다.
4.1. Boxing
int number = 10;
object obj = number; // OK
int는 값 타입이라 스택에 저장됨
object는 참조 타입이니까 힙에 저장됨
number 값을 힙에 복사해서 object 형태로 감쌈 = 10을 힙에 올리고 그 참조를 obj에 저장함
4.2. UnBoxing
int number2 = (int)obj;
여기서 obj는 실제로 힙에 있는 int(10)의 참조를 가지고 있음
(int)obj는 다시 힙에서 값을 꺼내 스택에 저장하는 과정
이때 정확한 타입으로 캐스팅해야 함 여기서 float, string으로 캐스팅하면 InvalidCastException 발생
이렇게 스택 → 힙 / 힙 → 스택 변환 과정을 반복하기 때문에 박싱은 성능 저하의 원인이 될 수 있다.
무엇보다 내가볼땐 가독성도 에바참치임
5. Generic (제너릭) <T>
데이터 타입을 파라미터로 활용하는 변수
Boxing & UnBoxing 성능 저하 문제 해결
제너릭은 컴파일 타임에 타입이 고정되므로 런타임 중 박싱이나 캐스팅 같은 성능 비용이 발생하지 않는다.
코드 재사용성 ↑
보통 게임 개발에서 여러 타입의 오브젝트(예: 몬스터, 오크, 고블린 등)를 프리팹으로 만들어 두고, 각각의 팩토리나 매니저를 따로 만드는 경우가 많다.
// 제너릭을 사용하지 않았을 때
public class Factory : MonoBehaviour
{
public GameObject prefab;
public Monster monster;
public Orc orc;
public Goblin goblin;
}
해당 방식은 간단하지만, 타입이 늘어날수록 점점 더 필드가 복잡해지고, 유지보수가 어려워진다. 이 문제를 해결하기 위해 제너릭(Generic) 을 사용한다.
5.1. 제너릭을 적용한 팩토리 클래스
🥕 예행 작업 1. Scene 생성 (Generic) 2. Scripts 생성 (StudyGeneric, Factory)
using UnityEngine;
public class Factory<T> : MonoBehaviour
{
public T prefab;
public Factory()
{
Debug.Log($"Factory는 {typeof(T)} 타입 입니다.");
}
}
using UnityEngine;
public class StudyGeneric : MonoBehaviour
{
void Start()
{
Factory<Monster> factory = new Factory<Monster>();
}
}
이 방식은 타입별로 팩토리 클래스를 중복 정의할 필요 없이 타입만 바꿔서 유연하게 사용할 수 있다.
그런데!!
제너릭 타입은 컴파일 타임에 결정되기 때문에, Factory<Monster>로 만들면 해당 팩토리는 끝까지 Monster 타입만 쓸 수 있다
factory.prefab = new Orc();
위 코드처럼 Orc를 넣는 것은 허용되지 않는다는 뜻이다.
다시 말해 Orc는 Monster의 자식이지만, T는 Monster 타입으로 고정되어 있어 런타임에 다형성을 이용해도, 제너릭 자체는 바뀌지 않는다.
이처럼 제너릭은 타입 안전성과 성능을 확보해주지만, 동적으로 여러 타입을 동시에 다뤄야 하는 경우(하나의 리스트에 Monster, Orc, Goblin을 섞어 저장 등)에는 object를 사용할 수밖에 없고 이때 박싱/언박싱이 발생할 수 있다.
2. 객체 지향 프로그래밍 (OOP)
현실 세계를 모형으로 모든 것을 객체(Object)로써 표현하는 프로그래밍 방식
객체 지향 프로그래밍의 특징
추상화 (확장성)
상속 (다양성, 유연성)
다형성 (다양성)
캡슐화 (은닉성)
1. Overloading
동일한 함수명에 매개변수만 다르게 사용하는 방법
using UnityEngine;
public class StudyGeneric : MonoBehaviour
{
public void CreateAccount(string name)
{
int dummyAge = 20;
CreateAccount(name, dummyAge);
}
public void CreateAccount(string name, int age)
{
string dummyPhoneNumber = "01012345678";
CreateAccount(name, age, dummyPhoneNumber);
}
public void CreateAccount(string name, int age, string phoneNumber)
{
string dummyMail = "HelloUnity@unity.com";
CreateAccount(name, age, phoneNumber, dummyMail);
}
public void CreateAccount(string name, int age, string phoneNumber, string eMail)
{
// 계정 생성
}
}
2. Inheritance (상속)
클래스가 다른 클래스를 물려받아 기존 기능을 그대로 사용하거나 새롭게 확장해서 사용하는 방법
public class Monster
{
public void Move()
{
Debug.Log("몬스터가 이동합니다.");
}
}
이 Monster 클래스를 상속해서 Orc, Goblin 같은 자식 클래스를 만들면
public class Orc : Monster
{
public void Shout()
{
Debug.Log("오크의 분노!!");
}
}
Orc는 Monster의 기능인 Move()를 그대로 사용 가능하면서 Shout() 같은 고유 기능도 추가할 수 있다.
3. Override (오버라이드)
부모 클래스의 메서드를 자식 클래스에서 재정의하는 방법
단, 반드시 부모 메서드가 가상화(virtual) 또는 추상화(abstract)로 선언되어 있어야 자식에서 override 할 수 있다.
// 가상화 예시
public class Monster
{
public virtual void Attack()
{
Debug.Log("몬스터가 공격합니다!");
}
}
public class Orc : Monster
{
public override void Attack()
{
Debug.Log("오크가 도끼로 공격합니다!");
}
}
Monster monster = new Orc(); // 생성자
monster.Attack(); // 오크가 도끼로 공격합니다!
다형성(Polymorphism)
변수는 부모 타입(Monster)으로 선언되었지만, 실제 인스턴스는 Orc이기 때문에 자식 클래스에서 재정의한 메서드가 실행된다.
이처럼 실행 시점에 실제 타입의 메서드를 호출하는 것을 '다형성(Polymorphism)'이라고 한다.
▶ 가상화와 추상화의 차이점
구분
설명
Virtual
선택적으로 오버라이드 가능
Abstract
자식 클래스가 반드시 오버라이드 해야 함
// 추상화 예시
public abstract class Monster
{
public abstract void Die(); // 구현 X
}
public class Orc : Monster
{
public override void Die() => Debug.Log("오크 사망");
}
드디어 금요일!
오늘 수업은 유니티 실습은 하지않고 OOP 개념, 이론에 대해 공부하였다.
어제까지만 해도 유니티 실습이 이제 뭐 적응이 다 돼서 대충 하게 된다~ 막 이랬는데 오늘에서야 유니티가 천국이었음을 다시 깨닫고.. 한 두시간? 정도는 졸았던 거 같다.. ^-^.. 그래서 점심도 안 먹고 그냥 그 시간에 쿨쿨띠 😪