반응형
구조체란?
여러 데이터를 하나로 묶어서 관리할 수 있게 해주는 사용자 정의 데이터 타입
실생활로 예시를 들어보자
학생 정보를 예시로 든다면, 학생의 이름, 나이, 번호을 하나로 묶어서 "학생" struct로 만들 수 있다
게임으로 예를 든다면 체력,마나,공격력을 하나로 묶어서 "스탯" struct를 만들 수 있다
간단예시
// 학생 정보를 담는 struct
struct Student
{
public string name; // 이름
public int age; // 나이
public int studentId; // 학번
}
// 실제 사용
Student newStudent;
newStudent.name = "김철수";
newStudent.age = 20;
newStudent.studentId = 202401;
특성
- 값 타입(Value Type)이다. 즉, 변수를 복사할 때 값이 복사된다
- 스택(Stack) 메모리에 할당된다
- 상속을 지원하지 않는다 (단, 인터페이스 구현은 가능)
- 매개변수가 없는 기본 생성자를 명시적으로 선언할 수 없다
- 모든 필드는 생성자에서 반드시 초기화되어야 한다
Strut를 사용하면 좋은 경우
- 작은 크기의 데이터를 다룰 때 (16바이트 이하 권장)
- 값의 불변성이 중요할 때
- 자주 할당/해제되는 데이터
- 벡터나 좌표와 같은 수학적 데이터
- 데이터를 그룹화할 때
예시코드
// 기본적인 struct 선언
public struct Position
{
public float x;
public float y;
public float z;
// 생성자
public Position(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
// 메서드도 가질 수 있습니다
public float GetMagnitude()
{
return Mathf.Sqrt(x * x + y * y + z * z);
}
}
// readonly struct - 불변성 보장
public readonly struct ImmutablePosition
{
public readonly float x;
public readonly float y;
public readonly float z;
public ImmutablePosition(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
// 값 타입의 특성을 보여주는 예제
void StructVsClass()
{
Position pos1 = new Position(1, 2, 3);
Position pos2 = pos1; // 값이 복사됨
pos2.x = 5; // pos1의 x값은 변경되지 않음
Vector3 vec1 = new Vector3(1, 2, 3);
Vector3 vec2 = vec1; // Unity의 Vector3도 struct입니다
}
주의 사항
- 큰 크기의 struct는 성능 저하를 일으킬 수 있습니다.
- struct를 자주 박싱/언박싱하면 오히려 성능이 저하될 수 있습니다.
- struct의 메서드에서 필드를 수정할 때는 반드시 ref 키워드를 사용해야 합니다.
예시코드
// ref 사용 예제
public struct Counter
{
private int count;
public void Increment() // 이렇게 하면 작동하지 않습니다!
{
count++; // struct의 복사본이 수정됨
}
public void IncrementCorrect(ref Counter counter) // 올바른 방법
{
counter.count++;
}
}
struct의 성능 최적화
- struct는 값 타입이므로 메모리 단편화를 줄일 수 있습니다.
- 작은 데이터를 자주 전달할 때는 class보다 효율적입니다.
- 하지만 큰 struct를 배열이나 리스트에 저장할 때는 주의가 필요합니다.
struct 활용 예시
캐릭터 스탯 시스템
[System.Serializable] // Inspector에서 보이게 하기 위한 속성
public struct CharacterStats
{
public int health;
public int attack;
public float moveSpeed;
public float attackSpeed;
// 스탯 계산 메서드
public float GetDPS()
{
return attack * attackSpeed;
}
}
아이템 정보
[System.Serializable]
public struct ItemData
{
public string itemName;
public int itemId;
public ItemType type;
public int stackCount;
}
게임 설정 데이터
public struct GameSettings
{
public float musicVolume;
public float sfxVolume;
public int graphicsQuality;
public bool isFullscreen;
}
좌표/방향 데이터 ( Vector3 처럼 )
public struct GridPosition
{
public int x;
public int y;
public GridPosition(int x, int y)
{
this.x = x;
this.y = y;
}
// 두 위치 간의 거리 계산
public float GetDistanceTo(GridPosition other)
{
int dx = x - other.x;
int dy = y - other.y;
return Mathf.Sqrt(dx * dx + dy * dy);
}
}
Struct와 Class의 주요 차이점
1. 메모리 저장 위치와 관리
Struct: 스택 메모리에 저장
접근 속도가 빠름
생성/제거가 빠름
크기가 작은 데이터에 적합
Class: 힙 메모리에 저장
가비지 컬렉션의 대상
참조를 통한 접근
큰 데이터나 복잡한 객체에 적합
2. 값 전달 방식
// struct의 경우 - 값 복사
public struct PositionStruct
{
public float x, y;
}
PositionStruct pos1 = new PositionStruct();
pos1.x = 5;
PositionStruct pos2 = pos1; // 값이 완전히 복사됨
pos2.x = 10; // pos1의 x는 여전히 5
// class의 경우 - 참조 복사
public class PositionClass
{
public float x, y;
}
PositionClass pos1 = new PositionClass();
pos1.x = 5;
PositionClass pos2 = pos1; // 참조만 복사됨
pos2.x = 10; // pos1의 x도 10으로 변경됨
3. 상속과 인터페이스
Struct
다른 struct나 class를 상속할 수 없음
인터페이스 구현은 가능
단일 책임에 충실한 작은 데이터 구조에 적합
Class
다른 class를 상속 가능
다중 인터페이스 구현 가능
복잡한 객체 계층 구조 구현에 적합
4. 성능 특성
// struct는 큰 데이터를 다룰 때 성능 저하 가능
public struct HugeStruct
{
public float[] largeArray; // 큰 배열
public List<float> largeList; // 큰 리스트
} // 이런 경우는 class를 사용하는 것이 좋음
// 작은 데이터는 struct가 유리
public struct SmallStruct
{
public float x, y, z;
} // Vector3 같은 작은 데이터는 struct가 효율적
5. 초기화와 생성자
Struct
매개변수 없는 생성자를 명시적으로 선언 불가
모든 필드는 자동으로 기본값 초기화
생성자에서 모든 필드를 초기화해야 함
예시코드
public struct PlayerData
{
public int health;
public float speed;
// 반드시 모든 필드 초기화
public PlayerData(int health, float speed)
{
this.health = health;
this.speed = speed;
}
}
Class
모든 종류의 생성자 선언 가능
필드의 명시적 초기화 필요
예시코드
public class PlayerDataClass
{
public int health;
public float speed;
// 기본 생성자 가능
public PlayerDataClass() { }
// 매개변수 있는 생성자도 가능
public PlayerDataClass(int health, float speed)
{
this.health = health;
this.speed = speed;
}
}
차이점을 활용한 예시
Struct 활용
// 1. 개별 아이템 데이터 정보
[System.Serializable]
public struct ItemData
{
public int id;
public string itemName;
public int price;
public ItemType type;
public string description;
}
// 2. 캐릭터 기본 스탯 정보
[System.Serializable]
public struct CharacterStats
{
public int hp;
public int attack;
public int defense;
public float moveSpeed;
}
// 3. 게임 설정 데이터
[System.Serializable]
public struct GameSettings
{
public float bgmVolume;
public float sfxVolume;
public int graphicsQuality;
public Resolution screenResolution;
}
Class 활용
// 1. 데이터 매니저 자체 (싱글톤 패턴)
public class DataManager : MonoBehaviour
{
private static DataManager instance;
public static DataManager Instance => instance;
// 데이터 컬렉션들
private Dictionary<int, ItemData> itemDatabase;
private List<CharacterStats> characterDatabase;
void Awake()
{
if (instance == null)
instance = this;
else
Destroy(gameObject);
InitializeData();
}
void InitializeData()
{
LoadItemData();
LoadCharacterData();
}
}
// 2. 복잡한 데이터 관계가 필요한 경우
public class QuestData
{
public int questId;
public string questName;
public List<ItemData> rewards;
public List<QuestObjective> objectives;
public QuestState currentState;
public event System.Action<QuestState> OnQuestStateChanged;
public void UpdateQuestProgress()
{
// 퀘스트 진행도 업데이트 로직
}
}
// 3. 지속적으로 상태가 변하는 플레이어 데이터
public class PlayerData
{
public CharacterStats baseStats;
private List<BuffEffect> activeBuffs;
private Dictionary<int, ItemData> inventory;
public void AddBuff(BuffEffect buff)
{
activeBuffs.Add(buff);
RecalculateStats();
}
public void AddItem(ItemData item)
{
// 인벤토리 관리 로직
}
}
전체관리자
public class GameDataManager : MonoBehaviour
{
private static GameDataManager instance;
public static GameDataManager Instance => instance;
// struct로 구현된 개별 데이터들
private Dictionary<int, ItemData> itemDatabase;
private Dictionary<int, CharacterStats> characterDatabase;
private GameSettings currentSettings;
// class로 구현된 복잡한 데이터 매니저들
public PlayerDataManager PlayerData { get; private set; }
public QuestDataManager QuestData { get; private set; }
public SaveDataManager SaveData { get; private set; }
void Awake()
{
InitializeDatabases();
}
private void InitializeDatabases()
{
itemDatabase = new Dictionary<int, ItemData>();
LoadItemData();
PlayerData = new PlayerDataManager();
QuestData = new QuestDataManager();
SaveData = new SaveDataManager();
}
// 데이터 저장/로드 예시
public void SaveGameData()
{
SaveData.SaveToJson(new GameSaveData
{
playerStats = PlayerData.GetCurrentStats(),
settings = currentSettings,
inventory = PlayerData.GetInventory()
});
}
}
사용 기준
struct
단순한 데이터 구조(아이템, 스탯 등)
자주 복사되어야 하는 데이터
변경이 적은 읽기 위주의 데이터
직렬화가 필요한 간단한 데이터
class
데이터 매니저 자체 구현
복잡한 상태 관리가 필요한 경우
이벤트나 콜백이 필요한 데이터
상속이나 인터페이스 구현이 필요한 경우
지속적으로 업데이트되는 데이터
다른 객체들과 관계를 맺는 데이터
작고 단순한 데이터 구조 → Struct
큰 데이터나 복잡한 객체 → Class
반응형
'유니티 > 문법' 카테고리의 다른 글
| [Unity] 유니티 C#) async/await (0) | 2025.01.21 |
|---|---|
| [Unity] 유니티 C#) Property ( 프로퍼티 ) (0) | 2025.01.20 |
| [Unity] 유니티 C#) Casting ( 형변환 ) (0) | 2025.01.16 |
| [Unity] 유니티 C#) try-catch문 ( 예외처리 ) (0) | 2025.01.15 |
| [Unity] 유니티 C#) LINQ ( Language Integrated Query ) (1) | 2025.01.14 |