목차
스킬 구현 시작, 레이어비트마스크
오전은 에셋 애니메이션을 씌우고, 그걸 비주얼적으로 확인하고 나서 코드리뷰를 하고 싶다.
그 후 스킬들을 인원별로 1명씩 배정한 뒤 구현할 생각.
[SerializeReference]
이거! 어제 마지막에 찾아서 적용했었는데, 완전 깜빡했다.
근데 팀원분이 검색해서 이걸 찾아주셨다.
인터페이스나 추상 클래스를 필드에 직렬화할 수 있게 해주는 속성이다.
유니티는 스크립트의 필드에 클래스를 할당하면 해당 클래스의 인스턴스를 값으로 복사하여 저장한다.
이 과정에서 [Serializable] 속성을 사용하는데,
안타깝게도 인터페이스나 추상 클래스같은 다형성은 지원하지 않는다.
[SerializeReference]를 사용하면 구체적인 구현 클래스들을 부모 인터페이스나 추상 클래스 타입으로 직렬화할 수 있다.
즉, 다형성을 직렬화할 수 있고, 값 타입을 직렬화하는 것을 방지하며 참조를 유지하며 직렬화한다.
abstract class Node 값 타입의 변수를 인스펙터에서 확인할 수 있는 것이다.
다만, 이 경우 값을 변경할 수는 없다.
=> 생성형AI한테 TIL 검토 맡기니까 값 변경도 된다고 한다! 예이! 내일 가서 싹다 바꿔야지.
모의 면접 대비

내일은 튜터님과 모의면접을 하기로 했다.
1회차 모의면접은 1번부터 20번까지라고 문제 범위를 지정해주셔서 우리팀 개발인원 5명이서 각자 4문제씩 맡아서 정리해오고, 그걸 들으며 회의하고, 츳코미 걸고 그러면서 보다 다양한 시각에서의 답변을 찾으려고 노력했다.
내가 담당한 부분
Q. 접근제한자란 무엇이며, 각각 어떤 차이가 있는지 비교해서 설명해주세요.
A. Access Modifiers
클래스, 메서드, 속성 등 멤버에 대한 접근 수준을 제어하는 키워드.
이를 통해 객체 지향 프로그래밍의 중요한 원칙 중 하나인 캡슐화(Encapsulation)를 구현할 수 있다.
C#의 경우 public, private, protected, internal이 있으며
public은 모든 곳에서 접근할 수 있고,
private은 해당 멤버가 선언된 클래스 내부에서만 접근할 수 있습니다.
protected는 해당 클래스 내부 또는 이 클래스를 상속 받은 자식 클래스에서만 접근할 수 있습니다.
internel은 같은 어셈블리, 즉 같은 프로젝트 내에서만 접근할 수 있습니다.
Q. 확장 메서드에 대해 설명하고 어떻게 활용했는지 알려주세요.
A. Extension Methods
확장 메서드는 기존 클래스의 코드를 수정하지 않고도 새로운 매서드를 추가하는 기능이다.
라이브러리나 프레임워크의 클래스에 기능을 추가하거나, 여러 곳에서 반복적으로 사용되는 유틸리티성 기능을 간결하게 표현할 때 유용하게 활용됩니다.
예를 들어, 유저에게 입력을 받을 때 숫자만 입력 받아야하는 경우, Input 문자열이 숫자인지 아닌지 검사해야할 것입니다.
이때 string 클래스에 IsNumeric()이라는 확장 메서드를 추가하여 코드를 훨씬 간결하고 간편하게 작성할 수 있습니다.
Q. 참조 형식과 값 형식에 대해 설명해주세요.
값 형식 (Value Type)은 데이터 자체를 스택 메모리에 직접 저장합니다.
변수에 값을 할당하면 실제 값이 복사됩니다.
따라서 한 변수의 값을 변경해도 다른 변수에는 영향을 주지 않습니다.
예를 들면 int, char, bool, struct, enum 등이 있습니다.
참조 형식 (Reference Type)은 데이터가 저장된 메모리 주소(참조)를 스택에 저장하고, 실제 데이터는 힙(Heap) 메모리에 저장됩니다.
변수에 할당할 때는 데이터의 주소만 복사되므로, 여러 변수가 같은 데이터를 가리킬 수도 있습니다.
이 경우 한 변수를 통해 값을 변경하면 같은 주소를 참조하는 다른 변수들에도 영향을 줍니다.
class, string, array, object 등이 참조 형식입니다.
한 변수를 수정했을 때 같은 참조를 가리키는 다른 변수들 또한 영향을 받게되는 데이터 오염을 방지하려면 Clone() 메서드나 생성자를 구현하여 깊은 복사(Deep Copy)를 한 뒤 사용하는 것이 좋습니다.
Q. 클래스를 다른 클래스로 상속하기 위한 방법은 무엇인가요?
A. C#은 자식 클래스 뒤에 :(콜론)을 붙이고 부모 클래스 이름을 명시합니다. 이는 프로그래밍 언어마다 다를 수 있으며 Java의 경우는 extends를 붙입니다.
virtual과 override 키워드, abstract키워드 포함해서 할 것.
레이어 비트마스크
private void OnTriggerStay2D(Collider2D other)
{
if ((playerLayer.value & (1 << other.gameObject.layer)) != 0) //(other.gameObject.layer == playerLayer)
{
if (damagable != null && damage > 0)
{
//구현
}
}
}
위 코드의 주석 처리 되어있는 조건은 트리거에 진입되어있는 객체를 체크하여 레이어를 비교하려 했던 흔적이다.
playerLayer는 LayerMask.GetMask("Player")를 통해 얻은 값인데, 이 값은 단순한 레이어 번호가 아니다!
LayerMask는 특정 레이어의 비트마스크(bitmask)를 나타내는 정수값이다.
LayerMask.GetMask는 "Player"라는 이름의 레이어를 찾아서 해당 레이어 번호에 해당하는 비트만 1로 설정된 정수 값을 반환한다.
예를 들어, "Player"레이어의 인덱스가 8이라고 가정했을때,
other.gameObject.layer는 레이어의 인덱스 번호인 8을 반환한다.
playerLayer는 LayerMask.GetMask("Player") 로 1 << 8, 즉 256이라는 정수 값을 반환한다.
따라서 8 == 256은 당연히 false가 된다.
사실 계속되는 오류에 코파일럿에게 도움을 요청했었다.
여전히 이해가 되지 않아서 계속 물어보면서 단계를 이해해보았다.
- 1 << other.gameObject.layer
- 1 << N은 비트 시프트 연산으로, 1을 N만큼 왼쪽으로 이동시킨다. 이렇게 하면 N번째 비트만 1인 정수가 만들어진다.
- 충돌한 오브젝트의 레이어가 8번이라면 1 << 8은 0000 0001을 8번 왼쪽으로 옮기게 된다.
- 결과적으로 0001 0000 000이 되고 이는 정수 값으로 256이다.
- playerLayer.value & (1 << other.gameObject.layer)
- 비트 AND 연산자를 사용하여 두 이진수에서 같은 자리에 있는 비트가 모두 1일 때만 결과가 1이 된다.
- playerLayer.value는 "Player"레이어만 포함하는 비트마스크(0001 0000 0000)다.
- != 0
- AND 연산의 결과가 0이 아니라서, 겹치는 비트(1)가 있다는 뜻이다.
- 충돌한 오브젝트의 레이어가 playerLayer에 포함되어있다는 것을 의미한다.
이 단계들을 이해하고 나서야 비트마스크가 여러개의 레이어를 포함하는 것이라는 것을 깨달았다.
예를 들어 8번 Player 레이어와 9번 Enemy 레이어를 포함하는 비트마스크는 0011 0000 0000이 되는 것이다.
이를 깨닫고 나니 연산과정이 크게 어렵게 느껴지지 않는다.
Collider2D 애니메이션
이건 조금 바보 같은 포인트인 것 같은데.
Animation에서 Collider를 조정할 수 있다는 것을 우리 팀원 전부 완전히 깜빡하고 있었다.
코드에서 일일히 offset과 size, enable까지 조절하려 했었는데 애니메이션 조정하는 것으로 간단히 해결


히트박스와 허트박스(캐릭터 콜라이더)의 구분을 쉽게 하기 위해서 오른쪽과 같이 기즈모를 그려주었다.
#if UNITY_EDITOR
void OnDrawGizmos() //히트박스 색상 변경
{
Gizmos.color = Color.red;
if (hitbox != null && hitbox.enabled)
{
Gizmos.DrawWireCube(transform.position + (Vector3)hitbox.offset, hitbox.size);
}
Gizmos.color = Color.yellow;
BoxCollider2D hurtBox = GetComponent<BoxCollider2D>();
if (hurtBox != null)
{
Gizmos.DrawWireCube(transform.position + (Vector3)hurtBox.offset, hurtBox.size);
}
}
#endif
느낀점
내가 말을 정말 못한다!
오전 오후 동안 진행한 코드리뷰 때, 설명을 진짜 개떡같이 해서 팀원들 보기 민망할정도였다.
진짜 창피함..
더 쉽게 구조를 짜는 능력과 설명하는 능력이 필요한데, 이 역량은 어떻게 하면 키울 수 있을지 고민을 해봐야겠다.
+"왜"라는 질문을 스스로에게 던져봐야함. "왜" 이 코드를 이렇게 짰을까? "왜" 이 기능이 필요할까?
기획자들만 "왜" 지옥에 빠지는 것이 아니었다...
내일 학습 할 것은 무엇인지
우선 추상화를 내던질 수 밖에 없었던 클래스들을 다시 추상클래스로 돌려놓고 [SerializeReference]로 변경을 해야한다.
스킬이 구현되는 속도를 보니 생각보다 금방 나올 것 같아서 그 전에 스킬 데이터 모델을 정립해야겠다는 생각이 든다.
또한 복잡한 데이터 베이스 관계를 정립해서 팀의 데이터 사용법을 통일하면 협업 효율을 높힐 수 있을 것 같다.
그리고 오늘 마지막에 급하게 구현한 MonsterAttackController를 한번 더 체크해보고,
플레이어 팀에게 IDamagable을 구현해달라고 요구할 생각이다.
'부트캠프 > 본캠프' 카테고리의 다른 글
| [내일배움캠프_2025SEP17] 데이터매니저, RangeCheckHelper (1) | 2025.09.17 |
|---|---|
| [내일배움캠프_2025SEP16] 1차 모의면접, DataManager 및 Data 관련 정립 (0) | 2025.09.16 |
| [내일배움캠프_2025SEP14] BT 구현 시작, 코드 리뷰 가능할까...? (0) | 2025.09.14 |
| [내일배움캠프_2025SEP13]리팩토링 (0) | 2025.09.13 |
| [내일배움캠프_2025SEP12] DataHandler, 상속 설계의 중요성 (0) | 2025.09.12 |