우선 C# 에서는 ref, out, in 키워드가 있다.
ref와 out은 C#에서 값 타입을 함수에서 참조처럼 다루고 싶을 때 사용하는 키워드!
키워드 설명 사용조건
ref | 읽고 쓸 수 있는 참조 전달 | 변수는 메서드 호출 전에 반드시 초기화되어 있어야 함 |
out | 메서드가 값을 설정해서 반환할 용도 | 메서드 안에서 반드시 값을 설정해야 함 |
in | 읽기 전용 참조 전달 (성능 최적화) | 메서드 내부에서 변경 불가 (읽기만 가능) |
그럼 언제 사용을 하는가?
[ref]
public class Player
{
private int _hp = 10;
// hp 접근을 위한 메서드
public ref int GetHPRef()
{
return ref _hp;
}
}
public class Manager : MonoBehaviour
{
public static Manager Instance;
private void Awake()
{
Instance = this;
}
public void Heal(ref int hp, int amount)
{
hp += amount;
}
}
public class Item : MonoBehaviour
{
public Player player;
private void Function(Player player)
{
ref int hpRef = ref player.GetHPRef(); // ref로 hp 참조 가져오기
Manager.Instance.Heal(ref hpRef, 10); // ref로 전달해서 직접 수정
}
private void OnTriggerEnter2D(Collider2D other)
{
Function(player);
}
}
이런식으로 Player class 내부에서 Heal이 달려있으면 참조해서 사용하면 되지만 공용으로 사용되는 Heal으로 해결할 수 있다면 ref 키워드를 사용해서 처리할 수 있다. (반드시 호출 전 값이 초기화 되어 있어야 함)
단, 구조가 복잡해질 경우엔 캡슐화를 유지하는 게 더 중요할 수 있으니 상황에 따라 유동적으로 써야 한다.
[out]
public class Player
{
private int _hp = 10;
// hp 반환 메서드
public int GetHP()
{
return _hp;
}
}
public class Manager : MonoBehaviour
{
public static Manager Instance;
private void Awake()
{
Instance = this;
}
// 여러 값을 반환하려면 여러 개의 out 파라미터를 사용
public void TryHeal(Player player, int amount, out int healedHP, out bool isMaxHP)
{
healedHP = player.GetHP() + amount; // out으로 hp 값 계산
isMaxHP = healedHP >= 100; // 예시로 HP가 100 이상이면 MaxHP로 설정
}
}
public class Item : MonoBehaviour
{
public Player player;
private void Function(Player player)
{
int healedHP; // 첫 번째 out 변수
bool isMaxHP; // 두 번째 out 변수
// TryHeal 메서드를 호출하여 여러 값을 반환
Manager.Instance.TryHeal(player, 20, out healedHP, out isMaxHP);
Debug.Log("Healed HP: " + healedHP); // healedHP 출력
Debug.Log("Is Max HP: " + isMaxHP); // MaxHP 여부 출력
}
private void OnTriggerEnter2D(Collider2D other)
{
Function(player);
}
}
이런식으로 TryHeal 에서 매개변수를 여러개를 반환하고 싶을 때 out을 사용하게 된다. ( 값을 할당해야만 사용 가능)
[in]
읽기 전용 참조를 사용하지만 class를 전달할 때는 주소가 복사 되기 때문에 성능의 이점은 없다. 하지만 구조체는 다르다.
데미지 정보 처리 방식 정리
게임에서는 여러 가지 데미지 정보를 처리해야 하는데, 이를 효율적으로 관리하려면 **구조체(struct)**를 사용하는 것이 성능 측면에서 유리합니다. struct를 사용하면 값 타입으로 데이터가 관리되므로, 메모리 할당을 최소화하고 GC 발생을 줄이는 데 유리합니다.
1. 데미지 클래스 또는 구조체 설계
- 클래스(class)를 사용할 경우, 인스턴스를 생성할 때마다 힙에 할당되므로 GC 발생에 영향을 미칩니다.
- 구조체(struct)를 사용할 경우, 값 타입이기 때문에 스택 메모리에 할당되며 GC 영향을 받지 않게 됩니다.
2. 데미지 정보 예시
게임에서 데미지 정보는 보통 다음과 같은 값들로 구성됩니다:
- 기본 데미지(Base Damage): 무기나 캐릭터의 기본 공격력
- 추가 데미지(Additional Damage): 버프나 아이템 효과로 추가되는 데미지
- 피해 유형(Damage Type): 물리적 피해, 마법 피해 등
- 상태 효과(Status Effects): 추가적인 상태 변화, 예를 들어 출혈, 독 등
- 크리티컬 확률(Critical Chance): 크리티컬 히트가 발생할 확률
- 크리티컬 배율(Critical Multiplier): 크리티컬 히트가 발생했을 때 데미지 배율
3. 구조체를 사용한 데미지 정보 처리
// 데미지 정보를 나타내는 구조체
public struct DamageInfo
{
public int BaseDamage;
public int AdditionalDamage;
public DamageType Type;
public bool IsCritical;
public float CriticalMultiplier;
public DamageInfo(int baseDamage, int additionalDamage, DamageType type, bool isCritical, float criticalMultiplier)
{
BaseDamage = baseDamage;
AdditionalDamage = additionalDamage;
Type = type;
IsCritical = isCritical;
CriticalMultiplier = criticalMultiplier;
}
public int CalculateTotalDamage()
{
int totalDamage = BaseDamage + AdditionalDamage;
if (IsCritical)
{
totalDamage = Mathf.CeilToInt(totalDamage * CriticalMultiplier);
}
return totalDamage;
}
}
// 데미지 유형을 나타내는 enum
public enum DamageType
{
Physical,
Magical,
True
}
4. 데미지 계산 예시
public class CombatManager : MonoBehaviour
{
public int CalculateDamage(DamageInfo damageInfo)
{
return damageInfo.CalculateTotalDamage();
}
public void ApplyDamage(Enemy enemy, DamageInfo damageInfo)
{
int totalDamage = CalculateDamage(damageInfo);
enemy.TakeDamage(totalDamage);
}
}
데미지 처리 다이어그램
다이어그램을 통해 데미지 정보 처리 흐름을 시각적으로 이해할 수 있도록 그려볼게요.
- DamageInfo 구조체에 데미지 정보가 담기고,
- CalculateTotalDamage() 메서드를 통해 총 데미지가 계산됩니다.
- 계산된 총 데미지가 적에게 적용됩니다.
+------------------------+
| DamageInfo |
|--------------------------|
| BaseDamage |
| AdditionalDamage |
| DamageType |
| IsCritical |
| CriticalMultiplier |
+-------------------------+
|
v
+-------------------------------------------+
| CalculateTotalDamage() |
|--------------------------------------------|
| TotalDamage = BaseDamage + |
| AdditionalDamage |
| If Critical, apply multiplier |
+-------------------------------------------+
|
v
+------------------------+
| ApplyDamage |
|-------------------------|
| Apply calculated |
| damage to enemy |
+------------------------+
성능 최적화 포인트
- 구조체 사용: 데미지 정보처럼 작고 간단한 데이터는 구조체로 관리하는 것이 메모리 사용과 성능에 유리합니다.
- GC 방지: struct는 값 타입이라 GC 부담을 줄여줍니다. 매번 데미지 계산 시마다 힙 메모리에서 객체를 할당하지 않으므로 성능이 향상됩니다.
- 이해하기 쉬운 코드: DamageInfo 구조체를 사용하면 데미지 처리 로직이 한 곳에 모여 있어 코드 관리가 용이하고, 확장성이 뛰어납니다.
'유니티 > Performance' 카테고리의 다른 글
Unity - 자식 클래스 참조 비교 (0) | 2025.03.20 |
---|---|
Unity - GetComponent 위치에 따른 퍼포먼스 (0) | 2025.03.20 |
Unity - Json 비교 (0) | 2025.03.20 |
유니티 Dictionary 키 찾기 비교 (0) | 2025.01.31 |