아이템 드랍 시 기존에 사용해왔던 확률 방식: Random.Range(0f, 1f)로 뽑은 숫자가 아이템의 드랍 확률(0~1)의 숫자와 비교하여 드랍 여부를 결정 - 각 아이템마다 실행
가중치 랜덤 알고리즘: 드랍 아이템 리스트 중 가중치를 이용해 하나를 뽑는 방식
드랍될 수 있는 아이템마다 가중치를 설정.
0부터 아이템들의 가중치 총합 수치까지의 숫자 중 랜덤으로 기준값을 정함.
가중치를 기준으로 드랍 아이템 리스트를 정렬한 뒤 각 가중치를 누적하면서 기준값이 누적된 수치 안에 포함되는 순간 해당되는 아이템을 뽑아 드랍.
or
반복문을 이용해 드랍 아이템의 가중치 리스트를 순차적으로 돌면서 기준값이 요소의 가중치 안에 포함되지 않으면 기준값에서 해당 가중치를 빼고 넘어감. 기준값이 가중치 안에 들어가는 순간의 아이템을 드랍.
float Choose (float[] probs) {
float total = 0;
foreach (float elem in probs) {
total += elem;
}
float randomPoint = Random.value * total;
for (int i= 0; i < probs.Length; i++) {
if (randomPoint < probs[i]) {
return i;
}
else {
randomPoint -= probs[i];
}
}
return probs.Length - 1;
}
[참고자료] 유니티 Documentation - 랜덤 게임플레이 요소 추가 https://docs.unity3d.com/kr/530/Manual/RandomNumbers.html
인터페이스란 무엇인가요?
클래스가 따라야 하는 규약을 정의한 것으로 인터페이스의 멤버는 선언만 있고 구현은 인터페이스를 상속 받은 클래스에서 한다. 인터페이스를 상속 받는 클래스에서는 구현을 반드시 해야한다. 추상화와 다형성 구현에 매우 유용하다. 인터페이스를 상속 받아 구현한 클래스의 인스턴스는 모두 같은 인터페이스 타입으로 처리할 수 있기 때문에 유지보수와 확장성이 용이해진다. 코드의 유연성과 재사용성을 높일 수 있다.
메서드, 프로퍼티, 이벤트, 인덱서만 가질 수 있다.
인터페이스와 추상클래스의 차이는 무엇인가요?
그 자체로 인스턴스화 할 수 없고 상속되어야 한다는 공통점이 있다.
추상 클래스는 구현을 포함할 수 있지만 인터페이스는 포함할 수 없다.
추상 클래스는 다른 클래스와 인터페이스를 다 상속 받을 수 있다. 반면 인터페이스는 다른 인터페이스만 상속할 수 있다.
C#에서 클래스는 단일 상속만 가능하기 때문에 추상 클래스는 하나만 상속 받을 수 있고, 인터페이스는 여러 개를 상속 받을 수 있기 때문에 클래스 다중 상속의 역할을 할 수 있다.
인터페이스는 접근 제한자를 사용할 수 없고, public으로 선언된다.
인터페이스에서 선언된 메서드는 override 키워드없이 구현할 수 있다.
가비지 컬렉터란 무엇인가요?
메모리는 한정되어 있기 때문에 쓰이지 않는 메모리는 해제되어야 한다. 자동으로 메모리가 할당되고 해제되는 스택 메모리와는 달리 힙 영역에서 동적으로 할당되는 메모리는 어떤 조치도 하지 않으면 자동으로 해제되지 않는다. C#에서는 힙 영역의 메모리를 사용자가 직접하지 해제하지 않고 가비지 컬렉션이라는 메모리 관리 기능이 있는데, 이 가비지 컬렉션을 담당하는 것을 가비지 컬렉터라고 한다.
힙 영역에서 더 이상 사용되지 않는(참조되지 않는) 객체를 가비지라고 한다.
가비지 컬렉터의 장점과 단점에 대해 설명해주세요.
가비지 컬렉터의 장점:
- 개발 생산성 향상: 메모리를 알아서 해제해주기 때문에 개발자가 메모리 관리에 소비하는 비용을 줄일 수 있다.
- 자동 메모리 관리를 통한 메모리 누수 방지: 명시적으로 메모리를 해제하지 않아도 자동적으로 메모리를 해제해주기 때문에 메모리 누수를 줄일 수 있다.
- 안전성 향상: 사용되지 않는 객체의 메모리가 해제되기 때문에 잘못된 메모리 접근으로 인한 오류를 방지할 수 있다. 이는 프로그램의 안정성을 높인다.
- 메모리 단편화 감소: 힙 메모리를 재배치하고 압축하여 메모리 단편화를 줄이고 메모리 효율성을 높인다.
단점: GC 역시 컴퓨터 리소스를 사용하기 때문에 성능적으로 큰 영향을 준다.
- 성능 오버헤드 및 예측 불가능한 중단: GC는 주기적으로 실행되는데, 이 과정에서 프로그램이 중단될 수 있다. 또한 GC는 예측할 수 없는 시점에 실행될 수 있기 때문에 예상치 못한 상황에서 프로그램이 중단될 수 있다.
- 메모리 사용: GC가 동작하기 위해 사용되는 컴퓨터 메모리로 인해 프로그램의 성능에 악영향을 줄 수 있다.
- 메모리 관리 복잡성: GC는 자동화된 시스템이지만 최적화를 위해서는 추가적인 메모리 프로파일링 및 관리 전력이 필요하다.
가비지 컬렉터의 세대 개념에 대해 설명해주세요.
GC가 메모리를 해제해야 되는지 판단하기 위해 모든 객체를 확인하는 것은 비효율적이다. 따라서 대상이 얼마나 오래 살아있었는지를 기준으로 세대를 구분하여 GC 호출 빈도를 다르게 설정해 메모리를 효율적으로 관리한다.
세대는 GC를 몇 번 거쳤는지를 나타낸다. 새로 힙 메모리에 할당되는 객체는 0세대(minor GC)에 할당되고, 이후 0세대가 가득 차면 가비지 컬렉션이 발생한다.
0세대에서 GC를 거치고도 사용이 되고 있는 객체는 1세대가 된다.
동일한 과정으로 2세대(major GC)가 되고, 2세대로 옮겨진 객체들은 더 이상 다른 곳으로 옮겨가지 않는다.
만약 2세대도 꽉차서 가비지 컬렉션이 수행된다면 가비지 컬렉터는 힙 영역의 모든 대상에 대해 가비지 컬렉션을 진행(Full GC)한다. 가장 비용이 많이 드는 프로세스다.
박싱, 언박싱을 사용할 때 주의해야 할 점은 무엇일까요?
박싱이 일어나면 힙 영역에 새로운 객체가 생성되는데, 이후 언박싱을 하면 이 객체는 그대로 가비지가 되기 때문에 가비지 컬렉션 유발로 인한 성능 저하가 발생할 수 있다.
언박싱 시 타입 불일치로 인한 예외가 발생할 수 있다. 언박싱은 박싱이 있었던 객체를 대상으로 이루어져야 한다.
오브젝트 풀을 사용하면 메모리 관리에 도움이 되는 이유가 무엇일까요?
총알과 같이 생성과 파괴가 빈번하게 반복되는 오브젝트의 경우 생성 시 힙 영역에 메모리가 새로 할당되고 파괴 시 그대로 가비지가 되어 가비지 컬렉터의 호출 빈도가 높아진다.
오브젝트 풀을 사용하면 필요한 만큼 오브젝트를 먼저 생성하고 사용이 완료되었을 때 비활성화한 후 다시 필요할 때 활성화하는 방식으로 재활용하는데 초기에 생성될 때 메모리 할당이 이루어지고 이후 비활성화/활성화를 통한 재사용으로 메모리 해제할 필요가 없어 메모리 관리를 최적화할 수 있다.