면접 준비 (3)
C#
- 얕은 복사 깊은 복사 차이
- 얕은 복사는 객체의 참조만 복사, 깊은 복사는 객체와 그 안에 포함된 모든 참조 객체까지 새로 복사
- 그래서 얕은 복사를 하면 두 객체가 내부적으로 같은 데이터를 공유하게 되고, 하나를 수정하면 다를 하나도 영향을 받을 수 있다. 반면 깊은 복사는 완전히 독립적인 복사본이 만들어져서 그렇지 않음
- 박싱과 언박싱이 일어나는 과정을 메모리 관점에서 설명
- 박싱은 값 형식을 참조형식으로 변환하는 과정. 이때 힙에 새로운 객체가 생성되고, 원래 값이 그 안에 복사된다.
- 반대로 언박싱은 힙에 있는 데이터를 다시 값 형식으로 꺼내오는 과정. 이때도 명시적인 캐스팅이 필요하고, 값이 다시 스택에 복사된다.
- 박싱/언박싱은 메모리 할당과 복사가 일어나기 때문에 반복되면 성능에 영향을 줄 수 있다.
- 클래스를 다른 클래스로 상속하기 위한 방법
- 클래스를 상속하려면 : 기호를 사용해서 부모 클래스 이름 명시
- 상속된 자식 클래스는 부모 클래스의 모든 public, protected 멤버를 물려받고, 필요한 경우 오버라이드해서 수정할 수 있다.
- 다이아몬드 문제, 해결방법
- 다이아몬드 문제는 다중 상속에서 두 클래스가 같은 부모 클래스를 상속할 때 중복되는 경로로 상속이 이루어지는 상황. 이럴 경우 어떤 부모의 멤버를 상속할 지 모호해지고, 충돌 발생 가능.
- C#에서는 이런 문제를 방지하기 위해 다중 클래스 상속을 금지하고, 대신 인터페이스를 통해 여러 타입을 구현하는 방식으로 해결
- 인터페이스
- 인터페이스는 클래스가 반드시 구현해야하는 메서드나 속성의 집합을 정의한 구조.
- 구현은 없고 정의만 있고, 클래스를 설계할 때 인터페이스를 통해 유연성과 확장성을 확보할 수 있다.
- 인터페이스와 추상 클래스 차이점
- 인터페이스는 구현 없이 정의만 제공하고, 다중 상속이 가능.
- 추상 클래스는 일부 구현을 포함할 수 있고, 단일 상속만 가능.
- 공통 기능이 필요한지, 순수한 설계만 필요한 지에 따라 결정
- 가비지 컬렉터
- 더 이상 참조되지 않은 객체를 찾아서 자동으로 메모리에서 제거해주는 시스템
- 직접 메모리 해제를 하지 않아도 돼서, 메모리 누수 예방 가능
- 가비지 컬렉터 장/단점
- 장점: 메모리 관리를 자동화해주기 때문에 코드 작성이 간단하고 안정성이 높다.
- 단점: 언제 수집이 일어날지 예측할 수 없고, 실행 타이밍에 따라 일시적인 프레임 저하가 생길 수 있다.
- 가비지 컬렉터 세대 개념
- 성능을 높이기 위해 객체를 세대로 나눠 관리한다.
- Gen 0: 새로 생성된 객체, 가장 자주 수집
- Gen 1: 0에서 살아남은 객체
- Gen 2: 장기적으로 살아남은 객체. 수집이 거의 일어나지 않으며 비용이 가장 크다.
- 세대를 나누면 불필요한 전체 스캔을 줄이고, 살아남을 확률이 높은 객체를 덜 건드려서 GC 성능 최적화 가능
Unity
- CPU와 GPU 작동 방식 차이
- CPU는 복잡한 연산을 바르게 처리하는 범용 프로세서, GPU는 단순한 연산을 대량으로 동시에 처리하는 병렬 프로세서.
- CPU는 게임 로직, AI, 물리 처리에 적합하고, GPU는 그래픽 렌더링이나 셰이더 연산처럼 대규모 병렬 처리에 특화되어 있다.
- 월드 스페이스, 로컬 스페이스
- 로컬 스페이스는 오브젝트 자체 기준의 좌표 공곤. 오브젝트의 부모를 기준으로 상대적인 위치나 회전을 표현할 때 사용.
- 월드 스페이스는 게임 씬 전체 기준의 절대 좌표. 예를 들어 플레이어가 부모 오브젝트 안에서 이동하면 로컬 포지션은 변하고 월드 포지션을 전체 위치를 기준으로 표현된다.
- 벡터의 내적과 외적 사용 상황
- 내적은 두 벡터의 방향이 얼마나 같은지 확인할 때 사용
- 외적은 두 벡터로부터 수직 벡터를 구할 때 사용
- 예를 들어 내적은 시야각 판별, 외적은 캐릭터의 좌우 방향, 회전 방향을 판단할 때 사용
- 쿼터니언을 사용하는 이유
- 회전을 표현할 때 일반적으로 오일러 각을 사용할 수 있지만 Gimbal Lock 현상으로 인해 축이 겹치는 문제가 생긴다.
- 쿼터니언은 네 개의 값으로 회전을 표현해서 이 문제를 방지하고, 부드러운 회전 보간이 가능하다는 장점이 있어 사용한다.
- 네트워크 프로토콜
- IP는 인터넷 주소와 라우팅을 관리하는 기본 프로토콜
- TCP는 연결지향 방식으로 데이터를 순서대로 전달. 신뢰성이 보장되어야 할 때 사용
- UDP는 비연결성 방식으로 속도가 빠르지만 데이터 유실 가능성이 있다. 실시간으로 보내지는 게 중요할 때 사용
- 렌더링 파이프라인
- 3D 데이터를 픽셀로 변환해 화면에 그리는 전 과정. Unity 기준으로는 기본 내장 파이프라인, URP, HDRP 같은 스크립터블 렌더 파이프라인이 있고, 각각 단계는 모델 처리 -> 월드 변환 -> 카메라 투영 -> 래스터화 -> 셰이딩 -> 출력 순서로 이루어진다.
- 3D 오브젝트가 픽셀로 표현되기까지의 과정
- 모델의 로컬 좌표가 월드 좌표로 변환
- 카메라의 뷰 행렬을 통해 시점에 맞게 조정된 뒤
- 투영 행렬로 크립 공간으로 바뀐다.
- 이 정보는 스크린 공간으로 변환되고, 픽셀 단위로 래스터화한다.
- 각 픽셀에 셰이더가 적용돼서 조명, 그림자, 색상이 최종적으로 계산되고 화면에 출력된다.
알고리즘
A* 알고리즘
- 그래프 탐색 알고리즘의 일종으로, 시작 지점에서 목표 지점까지 가장 효율적인 경로를 찾는 방식
- 다익스트라 알고리즘처럼 실제 비용을 고려하면서도 Greedy Best-First Search처럼 목표에 얼마나 가까운지도 함께 고려하는 게 특징
스크립트에서 MeshRenderer에 Material 추가
상호작용 시 Mesh를 추가하도록 구현했다.
Material 또한 ResourceManager에서 캐싱해서 사용한다.
private void AddOutlineMaterial()
{
var curMaterials = blockMesh.materials.ToList();
bool exists = curMaterials.Any(m => m.name.StartsWith(ghostOutline.name));
if (!exists)
{
curMaterials.Add(ghostOutline);
blockMesh.materials = curMaterials.ToArray();
}
}
private void RemoveOutlineMaterial()
{
var curMaterials = blockMesh.materials.ToList();
curMaterials.RemoveAll(m => m.name.StartsWith(ghostOutline.name));
blockMesh.materials = curMaterials.ToArray();
}