목차
유니티 2D 팀 프로젝트 시작!
오늘 드디어 새로운 팀 프로젝트가 시작되었다.
간단한 브레인스토밍부터 시작해서 와이어프레임, 역할 분담까지 draw.io에서 진행했는데 figma, lucidchart 등등보다 훨씬 깔끔하고 좋은데다가 무료여서 좋은 것 같다.
이미 각자 다른 팀에서 비슷한 프로젝트를 진행하고 와서 그런지,
이전 팀에서 진행했던 부분들을 따와서 장점들만 추려서 규칙 삼으려고 노력했다.
특히나 디스코드를 따로 파서 깃 연동을 한 것이 인상깊었다.
Discord 채널 WebHook으로 GitHub 연동

서버 우클릭 -> 연동 -> 웹후크 -> 새 웹후크
위와 같이 지정해준 뒤, 웹 후크 URL을 복사한다.
연동하고자 하는 깃 리포지터리에 가서
Setting -> Webhooks -> Add webhook -> Payload URL
항목에다가 복사된 URL을 붙여넣으면 된다.
단, 마지막에 /github을 붙여주고, Content Type은 application/json으로 설정해주어야 한다.
우리는 Send me everything을 선택했다.
이럴 경우 메인이 아니더라도 모든 깃 업데이트를 감지하여 디스코드 채널로 event를 보내준다.

develop 브랜치에 editorconfig 생성하는 커밋을 감지하여 디스코드에 전달해준 모습
한글깨짐 방지를 위한 파일인데 예전에 TIL로 작성했던 것을 활용하고 싶었다!
[내일배움캠프_2025JUL22]한글 깨짐 설정, FindObjectsOfType, TextMesh gitignore
[Github] Unity 프로젝트 파일 Languages 비율 문제(ShaderLab, HLSL) github에 분명 Unity 프로젝트 파일을 올렸는데 Language 비율이 이런 식으로 알 수 없게 나왔다 https://github.com/github/gitignore Github에서 제공하
j000.tistory.com
팀 프로젝트 게임 기획



일부러 엄청 작게 스샷을 찍었다!
우선은 브레인 스토밍을 통해 온갖 아이디어를 내고, 그 뒤에 컨셉이나 기능별로 구분을 했다.
그리고 채택한 아이디어만 남기고 나머지는 탈락 시키는 방식으로 진행했다.
이 과정이 2시간 정도로 가장 오래 걸렸지만, 가치 있는 시간이었다고 생각한다.


그 후에는 와이어프레임을 잡았다.
우리는 초기에는 필수 구현만 진행하고, 구현함에 따라서 도전과제까지 진행하기로 했기 때문에
우선은 필수 구현과 관련된 와이어프레임만 진행했다.
역할 분담은 튜터님의 조언을 받아 Player, Enemies(장애물과 아이템 및 점수 같은 습득체), UI, GameManager, Design으로 나누어 진행하기로 했다.
우선 인원별로 나누어서 역할을 분담하였고, 그 후에는 각 역할들이 어떤 부분들을 구현할지에 대해서 나열했다.
그리고나서 중복되거나 간섭되는 부분들은 없는지, 역할의 분량이 적은 경우 혹은 많은 경우 어떻게 나눌지에 대해서 상의했다.
마지막으로 마일스톤을 정했다!
필수 구현은 언제까지, 합치는건 언제하고, 도전 구현 가능한 시간, 그리고 발표 준비 시간까지 분배해서 마감에 쫓기지 않도록 했다.
일단 우리팀은 주말 출석을 하지 않는 것을 디폴트로 두기로 했다.
나는 GameManager와 총괄을 담당하기로 했다!
총괄이라고 대단한건 아니고, 필요한 메서드들을 구현하고, 연결 과정에서 삐끄덕 거리는 부분들을 매끄럽게 보완하는 역할을 하게 될 것 같다.
챌린지반 특강 수업 1회차
메모리 구조에 관해서 수업을 들었다.
학교 전공 수업과는 다르게 실전! 메모리 구조! 같은 느낌으로, 실제 면접에서 자주 들을 수 있는 부분들을 강조하며 진행해주셨다.
일단 필기한 것들은 접은 글로!
컴퓨터 구조
ㄴ컴퓨터가 이해하는 정보
ㄴ데이터
ㄴ명령어
ㄴ컴퓨터의 네 가지 핵심 부품
ㄴCPU
ㄴ메모리 (램, 롬) - 휘발성
ㄴ보조기억장치 (하드, SSD) - 비휘발성
ㄴ입출력장치 (마우스, 키보드, 모니터 등)
메모리 - 현재 실행하는 프로그램의 명령어와 데이터를 저장
CPU - 메모리에 저장된 명령어를 읽어 들이고 읽어 들인 명령어를 해석, 실행하는 부품
ALU - 산술논리 연산장치
메모리 영역
ㄴ코드 (코드 영역)
ㄴ전역변수 (데이터 영역)
ㄴ힙 영역 (런 타임 크기 결정)
ㄴ스택 영영 (컴파일 타임에 결정)
코드(텍스트)영역
우리가 작성한 소스 코드가 저장되는 영역으로 기계어 형태(0, 1)로 저장
실행 파일을 구성하는 명령어들이 올라가는 메모리 영역으로 함수, 제어문, 상수 등이 여기에 저장된다.
CPU는 코드 영역에 저장된 명령어들을 하나씩 가져가서 실행한다.
데이터 영역
전역 변수의 static 변수가 할당되는 영역
프로그램의 시작과 동시에 할당되고 프로그램이 종료돼야 메모리가 소멸된다.
스택 영역
지역변수, 매개변수, struct
스택 영역은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸
for(int i = 0; ...)도 지역 변수 (for문 끝나면 소멸)
힙 영역
이 공간에 할당하는 것을 동적 할당이라고 한다.
사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다.
응용 프로그램이 종료될 때까지 메모리가 유지되기 때문에 사용하고 난 후 반드시 메모리 해제를 해야한다.
참조형 데이터 타입을 갖는 객체(인스턴스), 배열, 문자열 등이 저장되는 공간
힙 영역 -> new하는 것들, 가비지 컬렉터가 관리해줌
(강제로 GC를 호출할 수도 있지만 부담이 많이 된다.)
클래스와 구조체의 차이 <면접 단골 질문
Class: 참조 타입, 힙 영역, 상속 가능
Struct: 값 타입, 스택 영역, 상속 불가
가비지 컬렉터 (GC) <면접 단골 질문 unity-programming-study.tistory/com/33
.Net Framework의 일부. 개발자가 메모리 관리에 신경쓰지 않아도 되도록 한다
가비지 컬렉션을 줄이기 위한 방법
오브젝트 풀링->총알 같이 자주 출현하고 사라지는 것을 재사용한다
ex) 플래피 플레인에서 배경 지나간 것들 앞으로 당겨서 재사용 하기.
진행하면서 흥미로운 질문들을 많이 하셨는데 그 중에 나도 궁금한 부분들은 따로 정리해보았다.
Struct은 스택인가, 힙인가
주로 스택 영역에 할당되지만, 구조체가 클래스의 멤버로 사용될 경우, 클래스가 힙에 해당되므로 구조체 또한 힙에 할당 될 수 있다.
변수가 장기인지 단기인지에 따라 힙 또는 레지스터 또는 스택에 할당된다. (msdn)
단기 기억으로는 스택과 레지스터를 사용하고, 힙은 장기기억용으로 사용된다고 한다.
컴파일러나 런타임에서 해당 변수가 장기일지 단기일지를 결정하기 쉽지 않다면 힙 영역에 할당되게끔 구성된다고 한다.
하지만! 면접에서는 이렇게까지 답변하는 것을 기대하진 않는다고 한다.
즉, 면접 상황에 따라 기술할 수도 있지만, 다른 부분에서 더욱 이야기하고 싶다면 생략하는 것이 좋아보인다.
GameManager가 다른 매니저들을 GetComponent로 보유한 형태 vs 매니저들을 싱글턴으로 둘 때 메모리 영역 차이
메모리 영역 관점에서의 실질적인 차이는 거의 없다고 보면 된다.
두 경우 모두 MonoBehaviour를 상속하여 힙(Heap)에 할당되고, 성능이나 메모리 낭비에도 큰 차이가 나지 않는다.
GameManager가 다 들고 있는 경우
- GameManager 한 곳에서 전부 연결됨
- 의존 관계가 명확해짐
- 메모리는 전부 힙에 있음
각자 싱글턴인 경우
- 각각의 매니저가 싱글톤으로 있음
- 아무데서나 UIManager.Instance하면 접근 가능
- Instance는 static 변수니까 앱 종료 전까지 살아 있음
- 이것도 결국 내용물은 힙에 있음
따라서 구조적인 차이를 두고 결정하면 되는 것 같다.
규모가 작은 경우 GameManager 한 군데로 관리하는 것이 깔끔해보인다.
다만, Sound 같이 전역에서 자주 쓰이는 경우, 싱글턴으로 구현하는 것도 괜찮을 것 같다.
가비지 컬렉터
더 이상 사용되지 않는 객체를 자동으로 식별하고 해제하여 메모리를 회수하는 메커니즘이다.
이는 .Net 프레임워크의 일부로, 개발자가 메모리 관리에 신경쓰지 않아도 되도록 한다.
힙(heap) 영역에서 동작하며, 이는 동적으로 할당되는 메모리 공간이다.
자동으로 메모리가 관리되어 메모리 누수를 방지하고 더 이상 참조되지 않는 메모리를 해제하므로 안전성이 향상된다.
메모리 관리에 신경쓰지 않아도 되어 개발에 집중할 수 있고 메모리 사용의 효율성을 높인다는 장점이 있다.
다만, GC가 주기적으로 실행되며 성능 오버헤드가 발생할 수 있고, 예측 불가능한 중단이 일어날 수 있다.
또한 GC 자체적으로 추가 메모리를 필요로 하므로 메모리 사용량이 증가하고, 최적화를 위해선 메모리 프로파일링 및 관리 전략이 필요해 복잡성이 증가된다는 단점이 있다.
가비지 컬렉션을 줄이기 위한 방법
- 오브젝트 풀링을 사용하여 객체를 재사용한다.
- 값 타입(struct)을 사용하여 힙 할당을 줄인다.
- 큰 객체는 LOH(Large Object Heap)에 할당되므로, 큰 객체 사용을 최소화하고 필요할 때는 분할하여 사용한다.
- 임시 객체 생성을 최소화하고, 필요한 경우 메모리를 명시적으로 해제한다.
- 코드의 성능을 최적화하여 메모리 사용을 줄인다. (예. LINQ를 사용할 때 불필요한 메모리 할당을 줄이기 위해 주의)
LINQ 사용 시 불필요한 메모리 할당을 줄이는 방법
나는 LINQ를 사랑한다.
가독성도 좋고, 간단하고 코드를 줄일 수 있기 때문이다.
그러나 과도한 LINQ 사용은 성능에 부담을 줄 수 있다.
그 동안은 그렇게까지 성능에 신경쓸 거면 C# 말고 다른 걸 써야지.. 라는 생각을 했었다.
웹 개발에 한해서는 아직까지도 어느정도 동의하고 있다.
하지만 게임 개발은 성능도 매우 중요하고 Unity로 개발하려면 C#을 써야하기 때문에,
이제 성능을 위해서 LINQ를 덜 쓰는 것을 고려하며 코드를 짜는 것이 좋겠다는 생각이 들었다.
다음은 너무 당연한 예제 같지만, LINQ를 사용하는 것과 사용하지 않는 것의 차이점을 확연히 보여준다.
//불필요한 LINQ
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
//Where() 와 ToList() 를 함께 쓰면 임시 컬렉션이 생성됨
List<int> evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
//LINQ를 사용하지 않는 방법
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
List<int> evenNumbers = new List<int>();
//for 문으로 바로 필요한 작업 수행
foreach (var n in numbers)
{
if (n % 2 == 0)
{
evenNumbers.Add(n);
}
}
LINQ를 사용하여 numbers와 evenNumbers를 제외한, ToList()를 통한 임시 컬렉션이 또 생성된다.
LINQ를 사용하지 않고 foreach를 사용한 예제에서는 numbers와 evenNumbers만 생성된다.
아래와 같이 FirstOrDefault를 예시로 들면 더욱 명확하다.
// 거의 메모리 할당 없음
var firstOrDefaultEven = numbers.FirstOrDefault(n => n % 2 == 0);
// 메모리 할당 발생 (임시 리스트 생성)
var firstOrDefaultFromList = numbers.Where(n => n % 2 == 0).ToList().FirstOrDefault();
따라서 컬렉션을 반환하거나 생성하는 다음과 같은 LINQ 메서드들은 되도록 피해야하는 것이다.
ToList(), ToArray()와 같이 새 컬렉션 생성하는 경우 -> 할당과 GC 발생
Dictinct(), GroupBy(), Join(), OrderBy() -> 내부적으로 자료구조를 새로 만들어 메모리 할당 발생
Where(), Select() -> 대부분 지연 실행이라 즉시 컬렉션을 생성하지 않지만 람다식 클로저 등으로 작은 할당 가능.
참고
https://unity-programming-study.tistory.com/33
이외에도 많은 글을 읽었으나... (특히나 LINQ) 너무 많고 오래된 글들도 많고... 창은 이미 껐고.. 다시 켜도 구분하기 힘들고..
또 필요할 때 다시 찾아보는 것도 좋을 듯.
Scriptable Object
Unity에서 제공하는 데이터 컨테이너 클래스이다.
MonoBehaviour와 달리 게임 오브젝트에 붙일 수 없으며, 프로젝트의 에셋 형태로 저장된다.
런타임 시 여러 오브젝트가 같은 인스턴스를 참조하여 메모리를 절약할 수 있다.
즉, 체력을 갖고 있는 적 100마리를 만들면 hp = 100인 Enemy가 100개 각각 존재하게되어 메모리를 100개 사용하게 된다.
ScriptableObject를 사용하면 공통 데이터를 ScriptableObject에 넣고, 여러 객체가 이걸 레퍼런스로 사용하게 되면,
메모리에는 ScriptableObject 하나만 올라가고, 여러 오브젝트가 이것을 참조해서 사용하게 된다.
다만, 공유 데이터기 때문에 변하지 않는 설정 값(maxHP, speed, attack 등)을 공유할 때 쓰고,
변하는 상태 값은 개별 오브젝트에 따로 두는 것이 좋다.
그 외에도 인스턴스 복사 없이 데이터를 공유할 수 있고,
에디터 및 런타임에서 [SerializeField] 없이 사용할 수 있다. (런타임 수정은 휘발됨)
에셋으로 저장되기 때문에 세션 간 데이터가 유지된다. (인스펙터에서 수정하고 에디터를 종료해도 유지됨)
커스텀 에디터, 툴 제작 등 에디터 스크립트와 궁합이 좋다.


우측 사진과 같은 코드를 아래 코드와 같이 참조할 수 있다.
//PlayerHealth.cs
public class PlayerHealth : MonoBehaviour, IDamageable
{
[Header("Config")]
[SerializeField] private PlayerStats stats;
//...
}
적, 아이템, 무기, 스킬, 레벨, 웨이브 구성 및 설정 값 모듈화 혹은 테스트 간편화에 활용할 수 있다.
Animation: Any State

만약 한가지 애니메이션에서 다른 애니메이션으로 전환되는 것이 아니라,
모든 애니메이션에서 전환될 가능성이 있다면 Any State으로부터 Transition을 만들어주는 방법도 있다.
다만, 전환 시 조건과 전환 시간이 명확히 설정되어야 의도한 대로 자연스럽게 전환되므로 주의해야 한다.
예를 들어서 상단 사진과 같은 관계일때, 전환 조건이 없으면 바로 Dead 애니메이션으로 전환된다.
Any State 전환 시에는 주로 Has Exit Time 체크를 끄고 바로 전환하게 설정한다.
Any State 전환이 너무 많으면 관리가 어려워지므로 남발하지 않고 꼭 필요한 전환에만 사용해야 한다.
느낀점
팀 회의를 진행하면서 스타트가 좋다는 느낌을 받았다!
팀 프로젝트 진행이 기대된다.
내일 학습 할 것은 무엇인지
일단 인풋 시스템 코드로 바인딩 해제하고 새로 바인딩 하는 방법을 찾아서 적용하기!
시간이 남으면 해설 영상이 마침 올라왔대서 해설 영상을 들어볼 생각이다.
'부트캠프 > 본캠프' 카테고리의 다른 글
| [내일배움캠프_2025JUL31]팀 프로젝트 3일차, 챌린지 분반 수업 3회차 (0) | 2025.07.31 |
|---|---|
| [내일배움캠프_2025JUL30] Input System 키 바인딩 변경 Setting (0) | 2025.07.30 |
| [내일배움캠프_2025JUL28] 과제 제출 및 유니티 추가 개인 학습 (0) | 2025.07.28 |
| [내일배움캠프_2025JUL27] 유니티 학습 (0) | 2025.07.27 |
| [내일배움캠프_2025JUL25] MetaBUS 개인 과제 (0) | 2025.07.25 |