이번 Step에서 잡아야 할 것 CPU: 연산 담당 (빠르지만 기억 못 함) RAM: 실행 중 필요한 코드/데이터 임시 저장 (빠르지만 휘발성) SSD/HDD: 파일 보관소 (느리지만 비휘발성) RAM 영역: Code / Data / Heap / Stack이 역할을
이번 Step에서 잡아야 할 것 CPU가 이해하는 언어: 기계어(0과 1) = 명령어들의 집합 컴파일(Compile): 소스 코드 → 기계어로 “번역” 빌드(Build): 컴파일 + 링크 등 “실행 파일을 만드는 전체 과정” 실행(Run): 만들어진 실행 파일을 OS
이번 Step에서 잡아야 할 것 데이터: 게임 세상의 현재 상태(HP/좌표/레벨/인벤토리/몬스터 상태…) 로직: 데이터를 읽고/계산하고/조건을 판단해 다음 상태로 업데이트하는 규칙 저장 위치: 데이터는 실행 중에 주로 RAM에 올라가 있고, CPU가 연산해 다시 RA
이번 Step에서 잡아야 할 것 변수 = 이름 붙은 메모리 공간 타입(Type): 그 공간에 “무슨 형태의 값”을 저장할지(크기/해석 방식) 기본 사용: 선언(공간 확보) → 대입(쓰기) → 사용(읽기) HP를 관리한다고 가정 게임에서 HP 같은 데이터를 관리하려
이번 Step에서 잡아야 할 것 주석은 실행되지 않는다: 컴파일러가 무시하는 텍스트 언제 필요한가: 코드만 보고는 드러나지 않는 “의도/규칙/주의사항”을 남길 때 피해야 할 것: 코드 그대로 읽으면 알 수 있는 설명(중복, 소음) 좋은 주석의 기준 (실전에서 바로 쓰는 기준) 의도를 남긴다: “왜 이렇게 했는가?” 제약/규칙을 남긴다: “여기서 바꾸면...
이번 Step에서 잡아야 할 것 선언(Declaration): 변수를 만든다(공간 확보) 초기화(Initialization): “처음 값”을 넣는다(선언과 함께 자주 등장) 대입(Assignment): 이미 있는 변수에 값을 다시 넣는다 스코프/수명: 어디서 보이고(
이번 Step에서 잡아야 할 것 타입을 정하는 이유: 해석 방식(정수/문자/실수) + 크기(범위) 결정 Signed vs Unsigned: 음수 표현 여부가 범위를 바꾼다 실전 기준: 대부분 int, 큰 수는 long long, 메모리 최적화는 “필요할 때만” 타입
이번 Step에서 잡아야 할 것 엔트리 포인트(Entry Point): 프로그램 시작점 = main() 실행 순서: main() 안에서 위 → 아래 쓰기/읽기: =는 “같다”가 아니라 대입(쓰기), cout은 읽어서 출력 초기화: 값을 쓰기 전에 “첫 값”을 넣어 안
이번 Step에서 잡아야 할 것 하드코딩(magic number): 의미가 숨어 있는 숫자를 코드에 직접 박는 것 기준값 분리: maxHp처럼 “의미 있는 이름”을 가진 값으로 한 번만 정의 상수화(const/constexpr): 변하지 않는 기준값은 실수 방지를 위해 고정 maxHp(기준값) 만들기 최대 HP가 “게임 규칙”이라면, 먼저 그 값을 이...
이번 Step에서 잡아야 할 것 bit / byte: 저장 단위의 기본 2의 보수(two's complement): 음수를 비트로 표현하는 표준 방식 진법(2/10/16): 코드에서 숫자를 읽는 법 오버플로우: 범위를 넘으면 값이 깨지고, 실제 게임/코테 버그로 이어
이번 Step에서 잡아야 할 것 char: 1바이트 값(문자처럼 보이게 출력 가능) + ASCII 개념 float / double: 실수는 근사값(정확한 값이 아님) + 기본은 double bool: 의미가 분명한 참/거짓 표현(조건문/상태값에 최적) char (문
이번 Step에서 잡아야 할 것 정수 나눗셈: /는 (정수끼리면) 소수점이 버려진다 증감 연산자 함정: hp++와 ++hp는 “값을 사용하는 타이밍”이 다르다 시프트 연산자: \($2^k$\) 곱/나눗셈처럼 보이지만, 범위/부호/오버플로우에 주의 우선순위: 헷갈리면
이번 Step에서 잡아야 할 것 비교 연산: 값들을 비교해 true/false를 만든다 논리 연산: true/false를 조합해 더 복잡한 조건을 만든다 단락 평가(short-circuit): &&, ||는 필요할 때만 오른쪽을 평가한다 우선순위: 헷갈리면 괄호 ()
이번 Step에서 잡아야 할 것 if의 조건은 결국 bool (true/false) else if는 위에서 아래로 순서대로 검사 → 순서가 로직 else는 “나머지 모든 경우”를 안전하게 처리하는 기본값 중괄호 {}는 버그를 막는 안전장치(초반엔 항상 쓰는 습관 추천
이번 Step에서 잡아야 할 것 switch는 보통 정수/문자/열거형(enum)처럼 “딱 떨어지는 값” 분기에 적합 case 끝의 break가 흐름을 끊는다 (없으면 다음 case까지 실행될 수 있음) default는 “예외/잘못된 입력”을 안전하게 처리하는 보험
이번 Step에서 잡아야 할 것 반복에는 항상 3요소가 있다: 초기 상태 → 종료 조건 → 변화(증가/감소) for: 반복 횟수/인덱스가 명확할 때(0~N-1) while: “언제 끝날지 조건”이 핵심이거나, 게임처럼 계속 돌다가 break하는 구조 break/con
N×N 별 출력 문제 입력: 정수 \(N\) 출력: * 문자를 사용해 \(N $\times$ N\) 정사각형을 출력 예) \(N = 3\) 이라면: 풀이 핵심 바깥 for(i): 행을 N번 반복 안쪽 for(j): 한 행에 별을 N개 출력 한 행을 다 찍었으면
2단만 먼저 문제 출력: 2단( \(2 $\times$ 1\) 부터 \(2 $\times$ 9\) )을 형식에 맞게 출력 예) 풀이 핵심 number는 “고정된 단” i는 1~9까지 변하며 곱해지는 수 2단~9단 문제 출력: 2단부터 9단까지를 순서대로
사양 몬스터 HP: 100 플레이어 데미지: 20 (매 라운드 고정) 라운드 제한: 5 5라운드 안에 처치 못하면 실패 출력 예시(형식) 구조 풀이 핵심 데이터: hp (몬스터 체력), damage (플레이어 공격력), maxRounds (제한 라운드) 로직:
이번 Step에서 잡아야 할 것 난수(의사 랜덤): rand()는 “진짜 랜덤”이 아니라 규칙 기반 숫자 생성 시드(seed): srand(time(0))를 한 번만 호출해 실행마다 결과를 바꾼다 매직 넘버 제거: enum으로 가위/바위/보에 이름을 붙인다 승패 판정
이번 Step에서 잡아야 할 것 함수 = 기능 단위로 묶은 코드 (재사용/가독성/디버깅 단위) 호출 흐름: 호출(점프) → 실행 → return(복귀) 선언 vs 정의: “나중에 나온다”를 미리 알리는 것 vs 실제 구현 스코프/스택: 지역 변수는 함수가 끝나면 사라
이 Step에서 다루는 것 스택이 “함수들이 같이 쓰는 임시 공간”이라는 말이 정확히 무슨 뜻인지 함수 호출/리턴이 일어날 때 스택이 어떻게 쌓이고(호출) 어떻게 정리되는지(리턴) 스택 오버플로우가 왜/언제 터지는지, 그리고 디버깅에서 어떻게 보이는지 학습 목표
이 Step에서 다루는 것 스택 프레임이 “함수 1번 호출의 작업 공간”이라는 말의 정확한 의미 반환 주소/매개변수/지역 변수가 스택에 어떤 구조로 묶이는지 변수 주소가 “고정 주소”가 아니라 기준점(BP/FP) + 오프셋으로 계산된다는 감각 32bit/64bit에서
이 Step에서 다루는 것 “왜 디버깅이 필수 스킬인지” (현업 관점) 브레이크포인트로 흐름을 멈추고, 변수/메모리를 증거로 확인하는 방법 조건부/로그 출력(Tracepoint)으로 “멈추지 않고 추적”하는 방법 Call Stack으로 “어디서 왔는지”를 역추적하는
이 Step에서 다루는 것 “작은 게임”을 함수 단위로 쪼개서 흐름을 만드는 연습 데이터(스탯)와 로직(전투/선택)을 분리하는 감각 디버깅 연습: 브레이크포인트/조건부/콜스택/트레이스를 실제로 어디에 쓰는지 학습 목표 main → 로비 → 필드 → 전투 흐름을 머
이 Step에서 다루는 것 C++에서 “파일을 나눈다”는 게 단순히 파일을 쪼개는 게 아니라, 컴파일 단위(.cpp) + 전처리(#include) + 링킹(연결) 을 이해해야 하는 이유 선언(Declaration)과 정의(Definition)를 분리하는 기본 규칙
이 Step에서 다루는 것 “변수 1개로는 한계”가 생길 때, 데이터를 묶어서 다루는 가장 기본 구조 = 배열 배열의 핵심 성질: 연속된 메모리 + 인덱스(0부터) + 크기 고정 반복문과 결합했을 때 왜 강력해지는지 배열에서 가장 흔한 치명적 실수: 범위 초과(Out
이 Step에서 다루는 것 2D 맵을 배열로 표현하고, 플레이어(좌표)를 이동시키는 최소 로그라이크 구조 게임 루프를 Input → Logic → Output으로 나눠서 사고하는 습관 “콘솔에서 부드럽게 그리기”를 위해 커서를 이동해서 다시 그리는 방식 가장 흔한 버
이 Step에서 다루는 것 “복사 전달(Call by Value)”의 한계를 해결하기 위해 주소를 다루는 이유 포인터의 2가지 핵심 연산자: &(주소 얻기), *(역참조/따라가기) 포인터 타입이 왜 중요한지(크기/해석/메모리 오염) 포인터가 위험해지는 대표 상황: 미
이 Step에서 다루는 것 포인터 연산이 “정수 덧셈”이 아니라 요소 단위 이동인 이유 배열과 포인터가 왜 자주 같이 등장하는지(배열의 첫 원소 주소, ptr[i] 문법) 이중 포인터(T**)가 필요한 대표 상황(포인터 자체를 바꾸기 / 동적 2D) 포인터로 원본을
이 Step에서 다루는 것 포인터에서 자주 쓰는 연산자 &, *, ->의 역할을 “한 문장”으로 설명하기 . vs ->를 헷갈리지 않게 구분하기 가장 흔한 실수: ptr.hp 같은 우선순위 실수*를 왜 괄호로 막아야 하는지 학습 목표 &x는 주소, *p는 따라가기
이 Step에서 다루는 것 구조체(예: StatInfo)를 함수에서 만들고 전달하는 3가지 방식 값으로 반환(가장 안전/가장 추천) 참조/포인터로 채우기(원본 수정) 포인터 반환(수명 문제로 위험해질 수 있음) 가장 치명적인 버그: Dangling Poin
이 Step에서 다루는 것 C 스타일 문자열(C-string)이 왜 char 배열이고, 왜 '\0'가 필수인지 const char*(문자열 리터럴)과 char[](수정 가능한 버퍼)의 차이 const 위치에 따른 의미를 정확히 읽는 법 strlen/strcpy/str
값 전달 (Call by Value) 인자를 함수로 넘길 때 값을 복사해서 전달하는 방식입니다. 장점: 함수가 인자를 마음대로 바꿔도 원본에 영향이 없음 API가 단순해짐(널/수명 문제 감소) 단점: 구조체/컨테이너가 크면 복사 비용이 커질 수 있음 짧은
이 Step에서 다루는 것 배열을 함수로 전달할 때 “포인터로 받는 이유”를 체감하기 중복 없는 난수 뽑기(1~45에서 6개) + 정렬 + 출력까지 한 사이클 완성 버블 정렬의 동작 원리(정렬이 “점점 확정되는 영역”) 학습 목표 ChooseLotto(int* n
이 Step에서 다루는 것 게임/프로그램을 데이터(상태) + 로직(행동) 으로 나눠서 보는 이유 절차 지향에서 규모가 커질수록 생기는 문제(함수 폭발, 책임 분산, 유지보수 비용 증가) 객체 지향의 핵심 아이디어: 데이터와 로직을 한 덩어리(객체)로 묶어 책임을 모으
이 Step에서 다루는 것 클래스와 객체(인스턴스)의 관계를 “설계도/실물”로 이해하기 멤버 변수/멤버 함수가 메모리에 어떻게 배치되는지(데이터는 객체마다, 함수는 공유) this 포인터가 무엇이고, 왜 멤버 함수가 “객체를 스스로 수정”할 수 있는지 학습 목표
이 Step에서 다루는 것 클래스가 커지기 시작하면 .h/.cpp로 나누는 이유 “선언(인터페이스)”과 “정의(구현)”를 분리해서 의존성/빌드 시간/협업 충돌을 줄이는 방법 멤버 함수 정의에서 Player::가 왜 필요한지 학습 목표 .h에는 무엇을 두고, .cp
이 Step에서 다루는 것 생성자/소멸자가 “자동으로” 언제 호출되는지(수명, 스코프) 초기화가 왜 중요한지(디버그에서 보이는 쓰레기 값의 정체) 기본 생성자/복사 생성자 자동 생성 규칙(언제 막히는지) 반복문에서 객체를 어디에 생성하느냐에 따른 비용 차이 학습 목
이 Step에서 다루는 것 상속이 필요한 이유: 공통 기능/데이터 재사용 + 타입을 하나로 묶어 다루기 상속이 “무조건 좋은 것”이 아니라, is-a일 때만 맞는 도구라는 기준 상속에서 자주 터지는 함정: 객체 slicing(잘림), 부모 포인터로 접근 한계, 접근 지정자 변화 학습 목표 “is-a vs has-a”를 예시로 구분할 수 있다. Play...
이 Step에서 다루는 것 접근 지정자(public/protected/private)가 “문법”이 아니라 설계 도구인 이유 데이터를 직접 열어두면 왜 유지보수/디버깅 비용이 폭발하는지(불변식 깨짐) Getter/Setter를 “자동 생성”이 아니라 규칙을 한 곳에 모
이 Step에서 다루는 것 “부모 타입 하나로 여러 자식 타입을 다루는 방법” = 다형성 오버로딩/오버라이딩 차이 virtual이 없을 때(정적 바인딩)와 있을 때(동적 바인딩)의 차이를 코드로 증명 VTable/VPtr이 왜 생기는지(감각) 상속에서 virtual
이 Step에서 다루는 것 생성자 : 뒤에 붙는 멤버 이니셜라이저 리스트(초기화 리스트)의 의미 “생성자 본문에서 대입” vs “초기화 리스트에서 생성”의 차이(성능/정확성) 생성/초기화 순서 규칙: (중요) “리스트 순서”가 아니라 선언 순서 const 멤버 / 참
이 Step에서 다루는 것 연산자 오버로딩이 “문법 설탕”인 이유(기능 추가가 아니라 표현을 자연스럽게) 멤버 함수 버전 vs 비멤버(전역) 함수 버전: 언제 무엇을 선택하는가 +/+=, =에서 많이 쓰는 정석 시그니처(const/참조/반환 타입) “생성(초기화)”
이 Step에서 다루는 것 struct와 class의 진짜 차이 2가지 기본 멤버 접근 지정자: struct는 public, class는 private 기본 상속 접근 지정자: struct는 public 상속, class는 private 상속 실전에서 어떤 기
이 Step에서 다루는 것 static의 2가지 의미 공유(클래스 단위): static 멤버 변수/함수 유지(함수 단위): 지역 static 변수(호출 사이 값 유지) static 멤버 변수가 “어디에 하나만 존재”하는지(감각) static 멤버 변수의 정의(
이 Step에서 다루는 것 다중 상속이 위험한 대표 이유 모호성(ambiguity): 같은 이름/시그니처가 여러 부모에 있을 때 호출 대상이 애매해짐 다이아몬드 문제(diamond): 공통 조상을 중복으로 들고 들어오는 구조(상태 중복/초기화/캐스팅 난이도 상
이 Step에서 다루는 것 프로그램 메모리 영역 4가지(코드/데이터/스택/힙) 복습 “왜 힙(동적 할당)이 필요한가?”를 스택·데이터의 한계로 증명 new/malloc이 실제로 하는 일: OS에 메모리 요청 → 시작 주소(pointer) 반환 힙을 쓰면 생기는 필수
이 Step에서 다루는 것 C 계열 malloc/free와 C++ 계열 new/delete의 차이 “메모리만 잡기” vs “객체를 만들기(생성자/소멸자)”의 차이 가장 위험한 실수: 짝 불일치 (malloc↔delete, new↔free, new[]↔delete)
이 Step에서 다루는 것 new/delete의 기본 패턴과 해제 후 처리 가장 흔한 2대 사고 메모리 누수(Memory Leak): new만 하고 delete를 안 함 Use-After-Free: delete한 객체를 다시 사용(해제 후 사용) 사고의 뿌리
이 Step에서 다루는 것 nullptr가 왜 필요한지: 0/NULL의 모호성을 제거 포인터 함수가 “못 찾으면 없음”을 표현하는 가장 흔한 방식: null 반환 널 체크의 핵심 기술: 짧은 회로(Short-circuit) 로 안전하게 깊은 멤버 접근하기 학습 목표
이 Step에서 다루는 것 상속 구조로 “공통 데이터 + 타입별 데이터”를 분리하는 방법 다형성 포인터(Item*)로 “Weapon/Armor를 한 타입으로 다루는” 이유 (필수 규칙) 부모 소멸자는 virtual Drop 함수 설계의 핵심: 소유권(누가 delete?)을 코드로 명확히 학습 목표 Item* 하나로 무기/방어구를 다룰 수 있는 이유를 ...
이 Step에서 다루는 것 Item* 하나로 Weapon/Armor를 받아도 “실제 타입 출력”이 되는 이유 해결 우선순위: 가상 함수(다형성) 우선, 캐스팅은 최소화 캐스팅이 필요한 상황에서의 안전 규칙(dynamic_cast 또는 타입 체크 후 변환) 학습 목표 왜 item->PrintInfo()만으로도 타입별 출력이 가능한지 설명할 수 있다. “...
이 Step에서 다루는 것 값 타입 캐스팅: “값을 다른 타입 표현으로 변환” 참조/포인터 강제 캐스팅: “메모리 해석을 바꿔버리는 위험한 변환” 암시적 변환 vs 명시적 변환 실무 함정: 정수 나눗셈 학습 목표 int -> float가 “비트 재해석”이 아니라 값 변환임을 설명할 수 있다. 축소 변환(큰 타입 -> 작은 타입)에서 왜 데이터 손실이 ...
이 Step에서 다루는 것 “관계 없는 타입 포인터 캐스팅”이 왜 위험한지 C-Style 캐스팅이 왜 버그를 숨기기 쉬운지 안전한 대안: staticcast/dynamiccast 등 목적별 캐스팅 학습 목표 Dog* d = (Dog*)k; 같은 코드가 왜 UB인지
이 Step에서 다루는 것 업캐스팅(자식 -> 부모)과 다운캐스팅(부모 -> 자식)의 차이 다운캐스팅 시 안전하게 타입을 확인하는 방법 ItemType 기반 분기와 dynamic_cast 기반 분기의 선택 기준 학습 목표 왜 업캐스팅은 자연스럽고, 다운캐스팅은 검
이 Step에서 다루는 것 인벤토리를 Item* 배열로 설계할 때의 핵심 계약(소유권/삭제 책임) 전방 선언(Forward Declaration)으로 헤더 의존성을 줄이는 방법 순환 참조가 발생하는 이유와 실전에서 끊는 패턴 학습 목표 AddItem/RemoveItem API에서 “누가 delete 책임인지”를 명확히 설계할 수 있다. class It...
이 Step에서 다루는 것 얕은 복사(Shallow Copy)가 포인터 멤버에서 왜 위험한지 깊은 복사(Deep Copy)로 독립 객체를 만드는 방법 Rule of 3(소멸자/복사 생성자/복사 대입 연산자)를 왜 함께 고려해야 하는지 실전 대안: 복사 금지 또는 스마
이 Step에서 다루는 것 C++의 4가지 캐스팅(staticcast, dynamiccast, constcast, reinterpretcast) 각 캐스팅의 목적/안전성/제약 실전에서 “어떤 상황에 무엇을 써야 하는지” 판단 기준 학습 목표 C-Style 캐스팅
이 Step에서 다루는 것 메모리 버그의 핵심 원인: “누가 delete할지”가 불명확한 설계 소유권(ownership) 규칙을 코드/함수 계약으로 고정하는 방법 dangling pointer 시나리오를 구조적으로 줄이는 방법 uniqueptr/sharedptr/we
이 Part에서 다루는 것 디버깅 파트를 “동적 할당 이후”에 배우는 이유 실무에서 자주 만나는 버그 4종 Null 크래시 정수 오버플로우/언더플로우 메모리 누수 메모리 오염(UAF/버퍼 오버플로우/잘못된 캐스팅) 크래시형 버그와 오동작형 버그의 추적 방식 차이 재현 중심 디버깅 루프(가설 -> 검증 -> 수정 -> 회귀 확인) 학습 목표...
이 Part에서 다루는 것 브레이크포인트/Step 실행/F11/F10으로 흐름을 통제하는 방법 호출 스택으로 “누가 이 함수를 호출했는지” 역추적하는 방법 Watch/메모리 창으로 가설을 숫자와 주소로 검증하는 방법 학습 목표 버그 유형에 따라 어떤 디버깅 도구를 먼저 써야 할지 선택할 수 있다. “멈춘 위치”와 “원인 시작점”을 구분해 호출 스택으로...
실습 방식 증상을 먼저 재현한다. 가설을 세운다. (“null일 것 같다”, “인덱스 범위일 것 같다”) 브레이크포인트/Watch/호출 스택으로 가설을 검증한다. 수정 후 같은 입력으로 다시 실행하고 회귀 여부를 확인한다. 초기화 누락 (쓰레기값) 증상 2번 기사 _attack이 음수/이상값으로 출력됨. 원인 오버로드 생성자에서 _attack 초...
이 Part에서 다루는 것 자료구조의 큰 분류: 선형 vs 비선형 선형 자료구조 3종의 핵심 차이 배열(Array) 동적 배열(Dynamic Array, vector) 연결 리스트(Linked List) “실전에서 무엇을 먼저 선택할지” 판단 기준 학습
이 Part에서 다루는 것 self-referential Node가 왜 가능한지 단방향/양방향 리스트 차이와 더미 노드(Dummy)의 의미 핵심 연산(AddAtHead, AddAtTail, Insert, Remove)의 연결 순서 “리스트 삽입/삭제가 빠르다”의 정확
이 Part에서 다루는 것 고정 배열(Array)과 동적 배열(Vector)의 구현 관점 차이 벡터의 증설(reallocation)과 size/capacity 관계 Big-O를 읽는 방법과 실전에서 자주 쓰는 복잡도 벡터 vs 리스트 연산 복잡도 비교 학습 목표
이 Part에서 다루는 것 실전에서 배열/벡터/리스트를 선택하는 기준 C# List와 LinkedList의 정확한 의미 콘솔 미로 프로젝트 구성요소(Board, Pos, Direction, 루프) Binary Tree 미로 생성 + 우수법(오른손법) 동작 원리 학습
이 Part에서 다루는 것 템플릿이 “중복 제거”를 넘어 “타입 안전한 재사용”을 만드는 방식 함수/클래스 템플릿, 특수화, 인스턴스화 시점의 핵심 개념 스택/큐의 추상화(ADT)와 벡터/리스트 기반 구현 선택 기준 원형 큐의 인덱스 규칙과 자주 나는 버그 포인트
이 Part에서 다루는 것 재귀 함수의 본질(자기 호출 + 기저 사례 + 점진적 수렴) 스택 프레임 관점에서 본 재귀 동작 원리 대표 예제: 카운트다운, 팩토리얼, 유클리드 호제법(GCD) 재귀 디버깅 방법과 자주 발생하는 실수 학습 목표 재귀 함수를 작성할 때
이 Part에서 다루는 것 트리의 핵심 개념(루트/리프/깊이/높이)과 용어 정리 서브트리 관점으로 재귀가 트리에 잘 맞는 이유 Node 설계, 생성/해제, 순회, 높이 계산 구현 게임 개발 맥락에서 트리의 실사용 포인트 학습 목표 트리 구조를 배열/리스트와 구분해
이 Part에서 다루는 것 우선순위 큐가 필요한 문제 패턴(정렬/선형 탐색과 비교) 힙의 2가지 핵심 법칙(힙 속성 + 완전 이진트리) Push/Pop 동작 원리와 시간 복잡도 C++ STL에서 최소 힙/커스텀 우선순위 설정 방법 A*·다익스트라에서 우선순위 큐가 성능을 바꾸는 이유 학습 목표 “왜 힙을 쓰는지”를 복잡도 근거로 설명할 수 있다. 최대...
그래프의 핵심 개념 기본 용어 정점(Vertex, V): 객체(사람, 위치, 상태 등). 간선(Edge, E): 정점 간 관계/이동 가능성. 차수(Degree): 한 정점에 연결된 간선 수. 경로(Path): 정점들을 따라 이동한 순서. 사이클(Cycle): 시작 정점으로 다시 돌아오는 경로. 연결 요소(Connected Component): 서로 도달...
DFS 개념 한 줄 정의 DFS(Depth-First Search)는 한 정점에서 시작해 한 경로를 끝까지 내려간 뒤, 막히면 되돌아와 다른 경로를 탐색하는 방식입니다. 동작 원리 (핵심) 현재 정점을 방문 처리한다. 인접 정점 중 미방문 정점으로 이동한다. 더 갈 곳이 없으면 직전 정점으로 되돌아간다(백트래킹). 시작점에서 도달 가능한 모든 정...
BFS 개념 한 줄 정의 BFS(Breadth-First Search)는 시작 정점에서 가까운 정점부터 거리 순서(레벨 순서)로 탐색하는 알고리즘입니다. 동작 원리 시작 정점을 큐에 넣고 발견 처리한다. 큐에서 하나 꺼내 그 정점의 인접 정점을 확인한다. 아직 발견하지 않은 정점이면 큐에 넣고, 부모/거리 정보를 기록한다. 큐가 빌 때까지 반복한다....
미로를 그래프로 해석하기 매핑 규칙 정점(Vertex) = 좌표 (y, x) 한 칸 간선(Edge) = 상/하/좌/우로 이동 가능하면 연결 벽('#')은 정점으로 보지 않거나, 정점이더라도 이동 불가 상태로 취급 즉, 미로는 "격자형 그래프"이며 BFS를 그대로 적용할 수 있습니다. 왜 BFS가 맞는가? 격자에서 한 번 이동 비용을 모두 1로 보면...
9-1. 다익스트라 핵심 개념 Step 1. 언제 쓰는가? 단일 시작점 최단거리(Single-Source Shortest Path) 문제에서 사용합니다. 단, 모든 간선 가중치가 0 이상(음수 없음)이어야 합니다. Step 2. BFS와의 관계 BFS는 "모든
A* 핵심 개념 다익스트라 + 휴리스틱 A*는 다익스트라에 "목적지까지 얼마나 남았는지" 추정치(H)를 더한 알고리즘입니다. 점수: G(n): 시작점 -> 현재 노드까지 실제 누적 비용 H(n): 현재 노드 -> 목적지까지 추정 비용(휴리스틱) F(n) = G(n) + H(n): 우선순위 큐 정렬 기준 직관 다익스트라(H=0)는 사방으로 ...
함수 포인터란? 정의 함수도 코드 영역에 올라가며 주소를 가집니다. 이 주소를 저장하는 변수가 함수 포인터입니다. 핵심은 "호출을 데이터처럼 다룰 수 있다"는 점입니다. 포인터 기본 상기 int* ptr → ptr은 주소값을 담는 변수. ptr → 그 주소를 타고 가면 int*가 있다. 함수도 마찬가지: 주소를 따라가면 함수 코드가 있다. 왜 중요...
왜 일반 함수 포인터로는 안 되는가? 함수 종류별 비교 | 구분 | 예시 | 저장 가능한 포인터 타입 | |------|------|-------------------------| | 전역 함수 | int Add(int,int) | int (*)(int,int) | | 정적 멤버 함수 | static void Foo() | void (*)() | | 비...
함수 객체의 핵심 펑터란? 함수처럼 호출 가능한 객체입니다. 클래스/구조체에서 operator()를 정의하면 객체를 obj(args...) 형태로 호출할 수 있습니다. 즉, "행동(코드) + 상태(데이터)"를 한 덩어리로 다루는 방법입니다. 왜 필요한가? 함수 포인터는 주소만 들고 있어서 상태를 함께 담기 어렵습니다. 멤버 함수 포인터는 객체(thi...
STL 컨테이너를 나누는 기준 STL 컨테이너는 "어떻게 저장하고, 어떻게 찾는가" 관점으로 나눕니다. | 분류 | 대표 컨테이너 | 핵심 특징 | |------|---------------|-----------| | 시퀀스 컨테이너 | vector, list, deque | 삽입 순서 중심, 선형 구조 | | 정렬 연관 컨테이너 | map, set, ...
vector의 본질 핵심 정리 vector는 연속 메모리(Contiguous Memory)에 요소를 저장합니다. 그래서 배열처럼 인덱스 접근(v[i])이 빠르고, 캐시 친화적입니다. STL에서 가장 자주 쓰이는 기본 컨테이너입니다. 왜 기본 선택인가? 순회 성능이 좋고 사용법이 단순합니다. 끝 삽입(push_back)이 평균적으로 빠릅니다. 대부분의...
개념 정의 이터레이터(iterator)는 포인터처럼 요소를 가리키며 이동하는 객체입니다. begin(), end(), *, ++, ==, != 연산으로 순회합니다. 핵심 목표는 "컨테이너가 달라도 같은 방식으로 순회/알고리즘 적용"입니다. 왜 중요한가? vector, list, deque, set 등 내부 구조가 달라도 이터레이터 인터페이스는 유사합...
왜 erase가 까다로운가? vector 내부에서 일어나는 일 vector는 연속 메모리이므로 중간 원소를 지우면 뒤 원소들이 앞으로 당겨집니다. 이 과정에서 삭제 지점 이후 이터레이터/참조/포인터는 무효화될 수 있습니다. 즉, erase 직후 "기존 it를 계속 쓰는 코드"는 매우 위험합니다. 삭제 시 메모리 이동 개념 잘못된 패턴 (Undefin...
왜 커스텀 이터레이터를 구현하나? 커스텀 컨테이너를 STL처럼 쓰기 위해 필요합니다. 목표는 다음 3가지입니다. 1) for (auto& x : container) 지원 2) find, copy, for_each 같은 알고리즘과 연동 3) 내부 구조(배열,
list의 본질 std::list는 양방향 연결 리스트입니다. 원소는 연속 메모리가 아니라 노드로 흩어져 있고, 포인터로 연결됩니다. 앞/뒤 삽입(pushfront, pushback)은 O(1)입니다. reserve/capacity 개념이 없습니다. vector vs list 핵심 비교 | 항목 | vector | list | |------|-----...
auto의 본질 auto는 초기화 식으로부터 타입을 추론합니다. 변수 선언에서 auto를 쓰려면 초기화가 필요합니다. 타입 이름을 줄여 가독성을 높이되, "무슨 타입인지 전혀 보이지 않는 코드"는 피하는 것이 좋습니다. 핵심 추론 규칙 기본 auto는 top-level const와 참조를 제거 참조를 유지하려면 auto& / const auto& ...
핵심 문법 범위 기반 for는 "컨테이너의 모든 요소"를 간결하게 순회하는 문법입니다. 인덱스보다 요소 자체에 집중할 때 가장 읽기 쉽습니다. 내부적으로 어떻게 동작하나? 아래와 거의 같은 코드로 변환되어 동작합니다. 즉, 본질은 begin(), end(), !=, ++, * 기반 이터레이터 순회입니다. 커스텀 컨테이너도 이 규약을 제공하면 rang...
성립 조건 (매우 중요) 탐색 대상 구간이 정렬되어 있어야 합니다. 정렬 기준과 탐색 비교 기준이 같아야 합니다(오름차순 정렬 후 오름차순 비교). 인덱스로 직접 구현하는 이진 탐색은 array/vector처럼 랜덤 접근 가능한 구조에서 가장 효율적입니다. 핵심 아이디어 가운데(mid)를 보고, 정답이 있을 수 있는 절반만 남깁니다. 매 단계마다 탐색...
BST의 본질 (불변식) BST(Binary Search Tree)는 다음 불변식을 만족하는 이진 트리입니다. 임의의 노드 X에 대해: 왼쪽 서브트리의 모든 값 O(log N)` 한쪽으로 치우치면 h ~= N -> O(N) search (탐색)
왜 RBT가 필요한가? 일반 BST는 입력 순서에 따라 편향되어 O(N)까지 느려질 수 있습니다. RBT는 "완전 균형"은 아니지만, 규칙으로 높이를 충분히 작게 제한해 O(log N)을 보장합니다. 핵심은 회전(rotation) + 색 변경(recoloring)으로
map의 본질 std::map는 키-값(Key-Value) 쌍을 저장하는 정렬 연관 컨테이너입니다. 키는 항상 정렬 상태를 유지합니다(기본: 오름차순, 내부는 보통 레드-블랙 트리). 키는 중복 불가입니다. (중복 키가 필요하면 std::multimap) #inclu
unordered_map의 본질 std::unordered_map는 해시 테이블 기반 키-값 컨테이너입니다. 키는 중복 불가이며, 순서는 정렬되지 않습니다. 평균적으로 find/insert/erase가 O(1)이라 조회 중심 작업에 강합니다. #include 내부
목표: 게임 월드의 객체를 안전하게 관리하기 Field(또는 World)는 다음을 책임집니다. 객체 생성/등록 (AddObject) 조회 (FindObject) 프레임 업데이트 (Update) 제거/정리 (RemoveObject, FlushDestroyQueue) 핵심은 다형성 + 수명 관리(ownership) 입니다. Object 계층 설계 Ob...
#include 이 필요한 이유 직접 순회의 한계 특정 값을 찾는 코드를 매번 직접 작성하면, 로직은 단순해도 코드가 길어집니다. "순회 -> 비교 -> break" 패턴이 반복되어 의도(검색)보다 구현 디테일이 눈에 띕니다. std::find로 의도 표현 st
조건 검색이란? find의 한계 std::find는 값이 정확히 같은지를 찾을 때 사용합니다. 하지만 실제 코드에서는 "조건을 만족하는 첫 원소"를 찾는 경우가 더 많습니다. 예: 홀수인가? 예: 10으로 나누어 떨어지는가? 예: rarity가 Uniqu
count_if 용도 조건을 만족하는 요소의 개수를 셉니다. "몇 개나 있는가?"라는 질문에 정확히 대응합니다. 람다와 함께 반환 타입은 반복자 차이 타입(difference_type)이며, 보통 정수형으로 사용합니다. 시간 복잡도는 선형 탐색 O(N). 실전 예시 allof, anyof, none_of 의미 비교 | 함수 | 질문 형태 | ...
for_each의 핵심 정의 std::for_each는 주어진 범위의 모든 요소에 함수(Callable)를 적용합니다. 목적은 "요소마다 같은 작업 수행"을 의도적으로 표현하는 것입니다. 반환값까지 이해하기 for_each는 호출한 함수 객체(func)를 반환합
핵심 오해: remove_if는 erase가 아니다 remove_if가 실제로 하는 일 remove_if는 요소를 지우지 않고, 남길 요소를 앞쪽으로 재배치합니다. 반환값은 new_end(유효 구간의 새 끝) 이터레이터입니다. 컨테이너 size()는 그대로입니다.
정의 람다란? 람다는 이름 없는 함수 객체(closure object)를 만드는 문법입니다. 즉, "익명 함수"처럼 보이지만 실제로는 operator()를 가진 객체에 가깝습니다. 외부 변수를 캡처해 상태를 들고 다닐 수 있어 STL 알고리즘과 궁합이 좋습니다.
실무에서는 std::sort 기본 사용 실무에서는 대부분 std::sort로 충분합니다. 내부는 보통 Introsort(Quick + Heap + Insertion 하이브리드) 계열로 매우 최적화되어 있습니다. 사용자 기준 정렬 (비교 함수) Comparator 규칙 (중요) 비교 함수는 "lhs가 rhs보다 앞에 와야 하면 true"를 반환해야 ...
왜 C스타일 문자열이 까다로운가? 비교 함정 배열/포인터 문맥에서는 문자열 내용이 아니라 주소가 비교됩니다. 내용 비교는 strcmp(name, name2) == 0 같은 함수를 써야 합니다. null-terminator와 버퍼 관리 부담 C 문자열은 '\0' 종단 문자를 직접 관리해야 합니다. strcpy, strcat 사용 시 버퍼 길이 계산 실...
먼저 용어를 분리하자 문자열 깨짐의 원인은 대부분 용어 혼동에서 시작됩니다. | 용어 | 의미 | |------|------| | 문자 집합(Character Set) | 어떤 문자를 다루는가 (예: ASCII, Unicode) | | 코드 포인트(Code Point) | 문자에 부여된 번호 (예: U+AC00) | | 인코딩(Encoding) | 그 ...
L-value vs R-value 정의 L-value: 이름/식별성이 있고 주소를 가질 수 있는 값(변수, 참조 반환값 등) R-value: 주로 임시값/리터럴처럼 곧 사라지는 값 참조 바인딩 규칙 (중요) | 파라미터 타입 | L-value 인수 | R-val
raw 포인터의 한계 문제점 new/delete 짝 관리 실패 -> 메모리 누수 해제 후 접근(use-after-free) -> 크래시/메모리 오염 소유권이 불명확해 "누가 delete해야 하는지" 코드 리뷰마다 해석이 갈림 스마트 포인터의 핵심 원리 (RAII)
DP가 필요한 상황 핵심 정의 DP(Dynamic Programming)는 중복되는 부분 문제 결과를 저장해 재사용하는 기법입니다. 목표는 "같은 계산을 여러 번 하지 않도록" 만드는 것입니다. DP가 성립하는 2가지 조건 중복 부분 문제(Overlapping
서버란 무엇인가 한 줄 정의 > 서버(강의 맥락): 네트워크 요청을 받아 처리하도록 대기(listen)하는 프로그램 게임 서버 맥락에서는 보통 24/7 상시 운영을 전제로 이야기합니다. 핵심은 "컴퓨터"가 아니라 "요청을 처리하는 실행 중인 프로세스"입니다. "
레스토랑 비유 (영혼과 로봇) 비유 구성 요소 | 비유 | 실제 개념 | |------|----------| | 레스토랑 | 프로세스(프로그램) | | 직원(로봇) | 스레드 | | 영혼 | CPU 코어의 실행권 (한 시점에 한 스레드 실행) | 동작 원리 한
단일 스레드의 한계 MMO의 부담 수천~수만 세션의 네트워크 패킷 처리 전투/이동/스킬/AI 로직 계산 DB/캐시 연동, 로그/분석 기록 이 모든 일을 한 스레드가 처리하면 틱 지연이 누적됩니다. FPS(배그)는? FPS도 실제로는 멀티스레드를 사용하지만, 세션
std::thread (C++11) 기본 생성과 종료 대기 std::thread 객체는 "실행 중인 OS 스레드 핸들"을 소유합니다. 가장 중요한 규칙: 생성했으면 반드시 join 또는 detach로 정리해야 합니다. join(), detach(), joinabl
메모리 4영역을 스레드 관점으로 보기 영역별 특성 정리 | 영역 | 대표 예시 | 프로세스 내 공유 여부 | 멀티스레드 위험도 | 핵심 포인트 | |------|-----------|------------------------|-------------------|-
캐시 계층과 지역성(Locality) 캐시 계층 구조 CPU는 RAM보다 훨씬 빠르기 때문에 중간에 캐시를 둡니다. 일반적으로 L1(가장 빠름) -> L2 -> L3(가장 큼) 계층을 사용합니다. 데이터는 보통 캐시 라인(대개 64바이트) 단위로 이동합니다. |
레이스 컨디션을 정확히 이해하기 재현 예제 왜 깨지는가 ++sum/--sum은 읽기-수정-쓰기(Read-Modify-Write) 복합 연산입니다. 두 스레드가 같은 값을 읽고 각자 저장하면 업데이트가 유실됩니다. 비원자 공유 변수의 동시 읽기/쓰기는 C++에서 데
왜 뮤텍스가 필요한가 임계 영역(Critical Section) 여러 스레드가 동시에 접근하면 안 되는 코드/데이터 구간을 임계 영역이라고 합니다. 대표 예: 공용 vector 수정, 계정 잔액 갱신, 월드 상태 맵 업데이트 임계 영역은 한 번에 한 스레드만 들어가
스핀락이란? 정의 스핀락은 락이 풀릴 때까지 스레드가 잠들지 않고 반복 시도(busy-wait) 하는 락입니다. 커널 스케줄링 전환 비용 없이 빠르게 재시도할 수 있어, 매우 짧은 임계 영역에서 유리할 수 있습니다. 언제 문제가 되는가 락 보유 시간이 길거나 경
데드락이란? 정의 두 개 이상 스레드가 서로의 자원 해제를 기다리며 영원히 진행하지 못하는 상태입니다. CPU 사용률이 낮아도 서비스는 멈출 수 있어, "조용한 장애"로 나타나는 경우가 많습니다. 비슷한 문제와 구분 | 문제 | 특징 | |------|-----
역할과 목표 | 역할 | 담당 | |------|------| | 프로듀서(Producer) | 일감 생성 후 큐에 넣음 | | 컨슈머(Consumer) | 큐에서 일감을 꺼내 처리 | 왜 이 패턴을 쓰는가 생성 속도와 처리 속도를 분리해 시스템 안정성을 높입니다. 네트워크 수신 스레드와 게임 로직/DB 처리 스레드를 느슨하게 연결할 수 있습니다. 큐...
소유권 모델 먼저 정리 | 타입 | 소유권 의미 | 대표 용도 | |------|-------------|-----------| | std::unique_ptr | 단독 소유(복사 불가, 이동만 가능) | 명확한 단일 owner | | std::shared_ptr |
왜 프로젝트를 3분할하는가 | 프로젝트 | 책임 | 의존 방향 | |----------|------|-----------| | ServerCore (정적 라이브러리) | 네트워크, 스레드, 동기화, 유틸 공통 기반 | (가능하면) 다른 앱에 의존하지 않음 | | Se
소켓 프로그래밍이란 한 줄 정의 > 소켓 프로그래밍: OS가 제공하는 소켓 API를 사용해, 서로 다른 프로세스/컴퓨터 간 데이터를 송수신하는 프로그래밍 소켓을 3가지 관점으로 보기 | 관점 | 핵심 설명 | |------|-----------| | API 관점
필수 헤더 및 라이브러리 기본 include / link 세팅 핵심은 WinSock2.h를 먼저 포함하는 것입니다. ws2_32.lib 링크가 빠지면 컴파일은 되더라도 링크 단계에서 실패합니다. 헤더 순서가 왜 중요한가 | 순서 | 결과 | |------|---
TCP 서버 4단계 개요 핵심 흐름 | 단계 | 함수 | 목적 | |------|------|------| | 1 | socket() | 리스너 소켓 생성 | | 2 | bind() | IP/포트에 소켓 매핑 | | 3 | listen() | 접속 대기 상태 진입
서버보다 단순한 이유 흐름 비교 | 구분 | 서버 | 클라이언트 | |------|------|------------| | 기본 흐름 | socket -> bind -> listen -> accept | socket -> connect | | 주소/포트 할당 | 서
send() - 전송의 진짜 의미 기본 코드 반환값 해석 | 반환값 | 의미 | |--------|------| | > 0 | 이번 호출에서 전송 버퍼로 넘긴 바이트 수 | | SOCKET_ERROR | 실패 (WSAGetLastError 확인) | 핵심 오해
커널 버퍼가 필요한 이유 큰 그림 애플리케이션 속도와 네트워크 속도는 항상 다릅니다. 커널 버퍼는 이 속도 차이를 흡수하는 완충 장치 역할을 합니다. 그래서 send/recv는 "즉시 전송/즉시 수신"이 아니라 "버퍼와 상호작용"입니다. 소켓별 버퍼 | 항목 |
엔디언(Endian) 핵심 개념 정의 | 방식 | 0x12345678 저장 순서 | |------|----------------------| | 리틀 엔디언 | 78 56 34 12 (하위 바이트 먼저) | | 빅 엔디언 | 12 34 56 78 (상위 바이트 먼저
먼저 결론: "무엇이 더 좋다"가 아니라 "무엇에 맞다" 핵심 관점 TCP/UDP는 우열 관계가 아니라 요구사항 적합성 문제입니다. 질문은 "빠른가?"보다 "유실/순서/지연 중 무엇이 더 중요한가?"입니다. 게임 서버는 대부분 TCP만 쓰거나 UDP만 쓰기보다 혼합
왜 패킷 포맷이 필요한가 TCP 스트림의 본질 TCP는 메시지 단위가 아니라 바이트 스트림입니다. 따라서 1회 send가 1회 recv와 1:1로 대응된다는 보장이 없습니다. 실무에서는 다음 두 현상이 항상 발생 가능하다고 가정해야 합니다. | 현상 | 설명 |
먼저 정리: "웹 vs 게임"은 역할 분리 문제 핵심 비교 | 항목 | 웹 서버 계열 (HTTP 중심) | 게임 실시간 서버 (TCP/UDP 세션) | |------|---------------------------|---------------------------
블로킹 소켓의 의미와 장단점 블로킹 동작 블로킹 소켓에서 accept, recv, send는 준비될 때까지 호출 스레드를 멈춥니다. 코드가 직관적이라 입문/단일 연결 테스트에는 이해하기 쉽습니다. 왜 서버에서 문제가 되는가 | 상황 | 결과 | |------|-
setsockopt / getsockopt 기본 규칙 기본 형태 level 의미 | Level | 용도 | |-------|------| | SOL_SOCKET | 소켓 공통 옵션 | | IPPROTO_IP | IPv4 레벨 옵션 | | IPPROTO_TCP |
왜 SocketUtils가 필요한가 단순 편의 이상의 역할 소켓 API 호출을 한 곳에 모아 에러 처리 규칙을 일관화합니다. 옵션 설정/바인딩/리스닝 순서를 표준화해 휴먼 에러를 줄입니다. 플랫폼 의존 코드(Winsock 확장 함수 로딩)를 캡슐화해 상위 로직을 단
왜 Select가 등장했는가 논블로킹 단독의 한계 논블로킹으로 바꾸면 블로킹은 사라지지만, 준비되지 않은 소켓을 계속 확인하는 busy polling 문제가 남습니다. 연결 수가 늘수록 "아무 일도 없는 소켓 확인 비용"이 커져 CPU를 낭비합니다. Select의
등장 배경과 핵심 아이디어 왜 Select 다음에 배우는가 Select는 매 루프마다 fd_set 재구성과 순회가 필요합니다. WSAEventSelect는 "소켓에 이벤트를 연결"해서 준비 상태를 더 직접적으로 기다립니다. 즉, Reactor 모델을 Windows
먼저 축(Axis)을 분리하자 블로킹 vs 논블로킹 (호출 반환 시점) | 구분 | 블로킹(Blocking) | 논블로킹(Non-blocking) | |------|------------------|------------------------| | 핵심 질문 | "
Overlapped의 본질: 요청과 완료를 분리한다 핵심 사고 전환 recv/send는 "지금 실행해서 지금 결과 확인"에 가깝습니다. WSARecv/WSASend(Overlapped)는 "지금 요청 등록, 완료는 나중에 통지"입니다. 즉, 호출 스레드가 오래 기다
IOCP를 쓰는 이유 문제 정의 동접이 커질수록 Select/이벤트 방식은 "소켓/핸들 관리 비용"이 빠르게 증가합니다. IOCP는 "완료된 작업만 큐로 전달"하여 워커가 필요한 일만 처리하게 만듭니다. 즉, 대규모 서버에서 컨텍스트 스위칭과 불필요한 검사 비용을
Re-posting 정책 (수신 재등록) 왜 필수인가 IOCP는 "등록된 비동기 요청"이 있어야 완료를 돌려줍니다. 따라서 recv 완료 처리 후 다음 WSARecv를 등록하지 않으면 해당 세션 수신이 멈춥니다. 정책 선택 | 정책 | 장점 | 단점 | 권장 상
아키텍처 목표부터 정하자 MMO 서버의 핵심 목표 정합성: 같은 입력이면 같은 월드 상태가 나와야 함 지연 제어: 피크 상황에서도 틱 지연이 폭증하지 않아야 함 확장성: 동접 증가 시 구조를 갈아엎지 않고 확장 가능해야 함 설계 원칙 네트워크 처리와 게임 로직
배경 및 목표 지금까지 만든 것과 한계 IOCP 기반 송수신(WSARecv, WSASend) 자체는 이미 동작합니다. 하지만 샘플 코드 수준에서는 네트워크 처리, 게임 로직, 수명 관리가 한곳에 섞이기 쉽습니다. 이 상태로는 다른 프로젝트에서 재사용하거나 팀 단위로
IocpCore의 책임 한 줄 정의 > IocpCore는 "IOCP 핸들 소유 + 등록(Register) + 완료 분배(Dispatch)"를 담당하는 엔진의 중심 클래스입니다. 왜 별도 클래스로 분리하는가 IOCP 핸들 생명주기를 한곳에서 관리할 수 있습니다.
IocpObject: 완료를 받는 주체 한 줄 정의 > IocpObject는 IOCP에 등록되어 완료 이벤트를 Dispatch 받는 엔진 객체의 공통 인터페이스입니다. 핵심 인터페이스 책임 경계 | 함수 | 책임 | |------|------| | GetHan
Listener의 책임과 경계 한 줄 정의 > Listener는 "접속 수락 파이프라인(AcceptEx)을 유지"하는 서버 전용 IocpObject입니다. 책임/비책임 | 구분 | Listener가 한다 | Listener가 하지 않는다 | |------|---
Session의 역할과 경계 한 줄 정의 > Session은 "클라이언트 1개 연결의 네트워크 상태와 I/O 파이프라인"을 대표하는 객체입니다. Session이 담당하는 일 | 담당 | 설명 | |------|------| | 송수신 파이프라인 | Registe
왜 레퍼런스 카운팅이 필요한가 IOCP 완료 지연의 본질 I/O 등록(WSARecv, WSASend)과 완료 통지(GQCS) 사이에는 시간이 있습니다. 이 사이에 Session이 해제되면, 늦게 도착한 완료가 해제된 객체를 참조해 크래시가 납니다. 대표 크래시 타
Service의 본질 한 줄 정의 > Service는 "세션 생성 규칙 + 접속 모델(Server/Client) + 세션 생명주기"를 조율하는 오케스트레이터입니다. 왜 필요한가 Listener/Session만으로는 "누가 세션을 만들고, 누가 보관하고, 언제 정
Register vs Process: 구현의 뼈대 두 단계의 역할 | 구분 | Register | Process | |------|----------|---------| | 의미 | 비동기 I/O 요청 등록 | 완료된 I/O 결과 처리 | | 시점 | "지금 일 시
ReceiveBuffer가 필요한 이유 TCP는 스트림이다 보낸 단위와 받은 단위가 일치하지 않습니다. 100B를 보내도 10 + 90, 40 + 30 + 30, 또는 여러 패킷이 합쳐져 올 수 있습니다. 즉, "수신 콜백 1회 = 패킷 1개"라는 가정은 틀립니다.
SendBuffer가 필요한 이유 복사 비용 문제 같은 패킷을 여러 세션에 보낼 때 세션마다 바이트 복사를 하면 비용이 큽니다. 복사 비용은 대략 대상 세션 수 * 패킷 크기에 비례해 증가합니다. 해결 방향 > SendBuffer = 불변 payload + 참조
패킷 규약(Contract) 먼저 고정 기본 형식 Size: 패킷 전체 길이(헤더 포함)로 고정하는 것이 가장 직관적입니다. ID: 패킷 종류 식별자(로그인/이동/공격 등). 구조체와 검증 규약 문서화 항목 size가 헤더 포함인지 여부 최대 패킷 크기(kMa
PacketSession의 역할 상속 위치 Session은 바이트 송수신 파이프라인을 담당합니다. PacketSession은 그 위에 "바이트 스트림 -> 완전체 패킷" 조립 계층을 추가합니다. 핵심 목적 > 컨텐츠(GameSession)가 반쪽 패킷을 절대 보
PacketHandler의 책임 한 줄 정의 > PacketHandler는 PacketSession이 넘긴 완전체 패킷을 packetId 기준으로 비즈니스 처리 함수에 라우팅하는 계층입니다. 책임 분리 | 계층 | 역할 | |------|------| | Pac
왜 ProtoBuf를 쓰는가 한 줄 정의 > Protocol Buffers는 "메시지 스키마(.proto) 기반으로 직렬화 코드를 자동 생성"하는 포맷/도구 체계입니다. 게임 서버 관점 장점 | 장점 | 설명 | |------|------| | 자동 직렬화/역직
최종 목표 아키텍처가 만족해야 할 것 | 목표 | 의미 | |------|------| | 정합성 | 패킷 처리 순서/상태 전이가 일관되게 유지됨 | | 안정성 | 종료/실패/late completion 경로에서도 크래시 없이 동작 | | 확장성 | 동접 증가 시
기존에는 정점(Vertex)과 인덱스(Index)를 Game 클래스 내부에 직접 하드코딩했지만, 이제는 기하학적인 정보를 외부에서 받아서 처리할 수 있도록 Geometry 클래스를 설계한다.정점과 인덱스 데이터를 구조화해서 관리하면, 사각형뿐 아니라 다양한 도형, 나아
렌더링 파이프라인에서 VertexShader / PixelShader / ConstantBuffer / Texture는 핵심 요소이다. 이들을 효율적으로 관리하기 위해 클래스로 분리하여 프레임워크화한다.ShaderScope는 리소스가 어떤 쉐이더 단계에서 사용될지 표현
RasterizerState는 정점 처리 후 픽셀 처리 전 단계인 Rasterizer 단계에서 삼각형을 그리는 방식과 면을 제거할지를 설정하는 상태 객체다.SamplerState는 픽셀 셰이더에서 텍스처를 어떻게 샘플링할지(주소 모드, 필터 등)를 결정하는 상태다.Bl
🧭 주제 DirectX 기반 게임 엔진 설계에서의 GameObject 클래스 설계 및 구현 방법 게임 내 렌더링 가능한 오브젝트를 GameObject 클래스로 추상화하고, 각 오브젝트가 독립적인 렌더링 자원과 상태를 가지도록 구성한다. 기존 Game 클래스에서 처리하던 렌더링 로직을 오브젝트별로 분리하여 유지보수성과 확장성을 확보한다. VertexSh...
Component 클래스는 GameObject의 부품이 되는 클래스입니다. 즉, GameObject는 다양한 기능(예: Transform, Renderer, Collider)을 조합하여 하나의 동작 가능한 객체가 됩니다.Transform은 Component를 상속받아
DirectX 또는 OpenGL을 활용한 엔진 구조 연습은 다음 두 가지 큰 의미를 가진다:그래픽스 지식 습득저수준 API를 통해 렌더링이 실제로 어떻게 작동하는지 이해할 수 있다.게임 엔진 구조 설계 학습GameObject 중심의 구조를 직접 설계하며 컴포넌트 기반
주제 본 강의의 핵심은 GameObject의 렌더링 책임을 MeshRenderer 컴포넌트로 완전히 분리하고, Camera/Transform 데이터를 독립적으로 Shader에 전달하는 효율적 구조를 설계하는 데 있다. 유니티와 유사한 조합형(Component-Based) 아키텍처로 전환하며, Mesh(기하 정보)와 Material(렌더링 방식) 을 분리할...
지금까지는 Game 클래스에서 직접 오브젝트(\_monster, \_camera)를 만들고 렌더링했지만, 이는 규모가 커지면 유지보수가 어려워집니다. 그래서 Unity처럼 오브젝트들을 Scene이라는 하나의 단위로 묶고, 여러 Scene을 동적으로 로드하거나 전환하는
이렇게 선언하고 초기화하면 전역 어디서든 GGame->을 통해 Game 인스턴스의 기능을 호출할 수 있다.✅ 이제부터는 GGame->Update(), GGame->Render() 등의 방식으로 접근하도록 변경해야 한다.MeshRenderer는 렌더링 시 Pipeline
RenderManager는 게임의 모든 포탈 에너지 목록을 관리하고 레네드링을 수행한다.이중 관리 구성용 지원 및 데이터 전달을 위한 RenderHelper, 공용 Buffer와 레이어드 구성, 모든 어빌제트 구도 수행\*\* 가 가능해지는 것이 항목.해당 값들은 쉐이
Mesh는 물체의 형태(지오메트리)를 정의합니다.VertexBuffer와 IndexBuffer로 구성되며, Geometry를 기반으로 정점을 설정합니다.GeometryHelper를 통해 사각형 정점을 생성생성된 Vertex/Index Buffer는 Create() 함수
애니메이션은 여러 개의 이미지를 일정 시간 간격으로 순서대로 재생하여 움직임처럼 보이게 하는 기술입니다. 우리는 하나의 큰 이미지(Sprite Sheet)에서 일정한 영역을 잘라내어 순차적으로 보여주는 방식으로 애니메이션을 구현할 것입니다.Sprite Sheet: 여러
지금까지 우리는 애니메이션 정보를 메모리상에서만 다루고 있었다. 하지만 실전 게임 엔진에서는 애니메이션 정보를 파일에 저장하고, 실행 시 파일로부터 불러오는 기능이 필수다.이때 우리가 직접 포맷을 정의해도 되지만, 읽고 쓰기 편한 구조를 위해 일반적으로 많이 사용하는
Windows Desktop Wizard 선택 프로젝트 이름: Client 솔루션 이름: GameCoding2솔루션 탐색기에서 Client 제거 및 폴더 삭제새 프로젝트 추가 → Static Library → 이름: Enginepch.h, framework.h →
DirectX에서 사각형(Quad)을 그리기 위해선 두 가지 방법이 있습니다:삼각형 2개를 조합하여 사각형을 표현인덱스 버퍼(Index Buffer)를 활용하여 정점 수를 최소화하고 사각형 표현이번에는 두 번째 방식인 인덱스 버퍼를 사용하는 방식을 선택하여 구현해보겠습
Constant Buffer는 CPU에서 GPU로 일정한 값(행렬, 조명 정보 등)을 전달할 때 사용하는 구조입니다.대표적인 예로 World, View, Projection 행렬을 들 수 있으며, 셰이더 내에서 이 값들을 활용하여 정점 데이터를 변환합니다.먼저 셰이더에
우리는 유니티 스타일을 참고하여 카메라를 다음과 같은 구성으로 조립합니다:Transform: 위치와 방향을 제어Camera: View / Projection 행렬 계산CameraScript: 입력에 따라 위치/회전을 제어🔍 View 행렬은 카메라가 어디를 바라보는지,
텍스처 매핑은 3D 모델의 정점(Vertex)에 UV 좌표(2D 좌표)를 부여하여, 2D 이미지를 3D 모델 표면에 입히는 기술입니다.U와 V는 X, Y 좌표와 동일한 개념이며, 이미지의 비율에 따라 정점에 위치한 픽셀 색상을 매핑합니다.✅ 텍스처 UV 좌표를 기반으로
큐브는 총 6개의 면으로 구성각 면마다 텍스처 좌표(UV)가 다르기 때문에, 8개의 정점으로는 부족함각 면에 4개의 정점 필요 → 총 24개의 정점한 면은 2개의 삼각형으로 구성 → 총 12개 삼각형, 36개의 인덱스실행하면 텍스처가 맵핑된 큐브가 생성됩니다.구는 도넛
Sampler는 텍스처 좌표를 기준으로 어떤 색상을 가져올지, 즉 텍스처를 어떻게 샘플링할지 결정하는 객체다.텍스처 좌표(UV)가 텍셀(Texel) 중심에 딱 맞지 않을 때,텍스처가 확대되거나 축소될 때,또는 좌표가 0,1 범위를 벗어났을 때,Sampler가 샘플링 방
Height Map은 각 위치의 "높이" 정보를 담고 있는 흑백 이미지입니다. 밝은 픽셀일수록 높은 지형어두운 픽셀일수록 낮은 지형으로 표현됩니다.이런 Height Map 이미지를 기반으로 2D Mesh의 각 정점에 높이 값을 적용하여 실제 3D 지형처럼 보이게 만드
Normal Vector는 어떤 표면에 수직인 방향을 나타내는 단위 벡터입니다.3D 그래픽스에서 Normal은 조명 계산, 음영 처리, 충돌 감지 등 다양한 곳에 사용됩니다.빛이 수직으로 들어올수록 밝게 표현빛이 수평으로 비출수록 어둡게 표현→ 빛과 Normal 벡터의
3D 그래픽스에서 Mesh는 정점(Vertex), 인덱스(Index), 도형 형상(Geometry), 월드 변환(World Matrix) 등의 요소를 통합적으로 묶은 리소스 객체(Resource Object)이다. 주로 다음과 같은 구성 요소를 가진다.Geometry&
초기에는 다음과 같은 방식으로 MeshRenderer::Update() 내에서 직접 Shader 값을 세팅했습니다.이는 전역 변수처럼 보이지만, 내부적으로 모든 값을 한꺼번에 갱신하는 fake 방식입니다. 하나의 값만 변경해도 다른 값들이 덮어써지는 비효율적이고 위험한
Depth Buffer (깊이 버퍼): 픽셀의 z-값(깊이)을 저장하여 화면에서 가장 앞에 있는 물체만 렌더링되도록 합니다.Stencil Buffer (스텐실 버퍼): 특정 픽셀만 렌더링되도록 필터링합니다. 복잡한 이펙트, 마스킹, 포털 등의 구현에 사용됩니다.3D 공
우리가 물체를 보는 건 ‘빛’ 덕분입니다. 현실 세계에서 빛은 반사와 굴절을 거쳐 눈에 들어오지만, 그래픽스에서는 이러한 과정을 실시간으로 계산하기엔 비용이 너무 큽니다.핵심은 "최소한의 비용으로 그럴싸한 조명 효과를 구현하는 것"그래서 우리는 현실 세계의 조명을 단순
Diffuse Light는 빛이 물체의 표면에 닿아 여러 방향으로 퍼지는 현상을 묘사합니다. 표면에 수직일수록 더 밝고, 비스듬하면 어둡게 보입니다.빛의 방향과 물체의 표면 법선(Normal)의 관계에 따라 색상이 결정됩니다.시점(Viewpoint)과는 무관합니다. 즉
Specular Light는 빛이 매끄러운 표면에 부딪혀 특정 방향으로 반사될 때 생기는 하이라이트 효과입니다.빛의 방향, 표면의 법선(Normal), 관찰자(카메라)의 위치 모두가 영향을 미칩니다.반사된 빛의 방향과 눈의 위치가 일치할 때, 물체에 강한 빛 번쩍임(눈
Emissive 조명은 물체가 자발적으로 빛을 내는 것처럼 보이게 하는 조명입니다.외부 광원과 무관물체 자체의 발광 속성(Material Emissive)에 따라 표현주로 외곽선(림라이트), 불빛, 마법 효과 등에 사용Ambient~Specular은 외부 조명의 영향을
기존의 12. Lighting_Emissive.fx 쉐이더를 복제하여 13. Lighting.fx로 만들고 Shaders/Week2에 저장합니다.15\. EmissiveDemo 클래스를 복제해 16. LightingDemo 클래스를 생성합니다.Main에서는 다음과 같이
Material(재질)은 물체가 어떤 빛을 받았을 때 어떤 시각적 효과를 낼지를 결정하는 중요한 요소입니다.여기서 말하는 Material은 단순히 텍스처뿐만 아니라 Shader, MaterialDesc(ambient, diffuse, specular, emissive)
Normal Mapping은 3D 모델의 표면을 더욱 디테일하고 사실적으로 표현하기 위한 그래픽스 기법입니다. 겉보기에는 단순한 메시라도, 빛의 반사 방향(노멀 벡터)을 픽셀 단위로 조작함으로써 울퉁불퉁한 질감이나 미세한 요철을 표현할 수 있습니다.핵심 개념: 정점을
Emissive 조명이 이상하게 동작한다는 제보를 받고 문제를 재현해보았습니다.NormalMappingDemo::Init:NormalMappingDemo::Update:빛의 다른 성분은 모두 꺼두고 Emissive만 남긴 상태로 테스트.✅ 실행 결과: 오브젝트 외곽만
FBX는 Autodesk에서 만든 3D 모델 포맷으로, 단순히 메시 정보만 담고 있는 것이 아니라 애니메이션, 머티리얼, 텍스처, 본 정보, 라이트, 카메라 등 다양한 정보를 포함합니다. Unity나 Unreal Engine에서도 .fbx를 사용하면 복잡한 캐릭터, 애
Assimp에서 머티리얼 정보를 파싱해 asMaterial에 담고, 벡터 \_materials에 저장합니다.실행 후 Resources/Textures/House/House.xml 파일 생성됨!
Assimp를 통해 FBX 파일의 루트 노드부터 재귀적으로 트리를 순회하며 Bone 정보를 가져옵니다. 현재는 스켈레톤보다 "계층 노드 = Bone" 이라고 가정하고 구조를 구성합니다.📌 FBX의 Transform 정보는 Relative(부모 기준) 좌표계입니다. 따
ReadAssetFile로 FBX를 메모리로 읽고ExportMaterialData: XML로 저장 (텍스처/머티리얼 정보)ExportModelData: .mesh 바이너리로 저장 (정점/인덱스/Bone 정보 포함)텍스처처럼 바이트 단위로 저장해야 할 때 FileUtil
실행하면 .mesh, .xml 형식의 커스텀 바이너리/텍스트 파일이 생성됩니다. 이는 FBX로부터 추출한 모델 및 머티리얼 정보를 우리 엔진 형식으로 저장한 결과입니다.이제 실행하면 화면에 탱크가 출력됩니다. 하지만 모양이 이상하게 왜곡되어 중앙에 몰려 있는 듯한 모습
GitHub에서 "ImGui"를 검색해 소스코드를 다운로드합니다.압축 해제 후 examples 폴더에서 example_win32_directx11.sln을 실행합니다.해당 예제는 별도 라이브러리가 아닌 소스코드 직접 포함 방식입니다.example_win32_direct
2D 애니메이션에서는 프레임마다 스프라이트(이미지)를 틀어주는 방식으로 애니메이션을 구현한다.하지만 3D에서는 각 프레임마다 모델 전체를 교체하는 것은 비효율적이다. 대신 뼈대(Bone)를 기준으로 정점을 변형시키는 방식이 사용된다.즉, 정점(Vertex)은 고정되어
스키닝은 정점(Vertex)이 어떤 뼈대(Bone)에 얼마나 영향을 받는지를 정의하는 기술입니다.2D에서는 이미지(Sprite)를 프레임별로 교체해 애니메이션을 표현하지만,3D에서는 하나의 메쉬 모델이 뼈대의 움직임에 따라 함께 움직입니다.그래서 정점은 뼈대의 움직임을
이번 포스팅에서는 Assimp를 통해 FBX 파일에서 애니메이션 정보를 추출하고, 이를 직접 정의한 구조체에 담아 바이너리 파일(.clip)로 저장하는 시스템을 정리한다.핵심은 "애니메이션이란 프레임 단위로 본(Bone)의 SRT(Scale, Rotation, Tran
FBX 파일은 Assimp를 통해 불러올 수 있지만, 매번 파싱하는 건 속도와 효율성에서 큰 손해입니다.따라서 Tank.anim이라는 Binary 애니메이션 파일을 미리 저장하고, 런타임에는 빠르게 읽어오는 구조를 설계합니다.ReadMaterial() → 머티리얼 데이
스카이박스는 일반적으로 큐브 형태로 구현하는 경우가 많다. 카메라를 중심으로 큐브를 배치하고, 각 면에 하늘 텍스처를 입히는 방식이다. 하지만 이는 큐브 6면에 각각 텍스처를 입혀야 하고, 텍스처 좌표 계산도 번거롭다.그래서 우리는 더 간단하게 구(Sphere) 하나를
DirectX11을 포함한 모든 렌더링 파이프라인에서 "드로우콜"은 굉장히 중요한 개념입니다. 쉽게 말해, 드로우콜은 GPU에게 "이거 그려!"라고 명령을 보내는 호출입니다.예를 들어, 큐브 하나를 그리고 싶다면:메쉬(Mesh) 정보머테리얼(Material)쉐이더(Sh
매 프레임마다 동일한 메쉬와 머티리얼을 가진 오브젝트들을 하나의 버퍼에 묶어 GPU에 전달해야 한다. 이 역할을 하는 클래스가 InstancingBuffer다.CreateBuffer: 최대 인스턴스 수 기준으로 CPU write가 가능한 버퍼 생성PushData: Ma
이전에 학습한 Mesh Instancing은 구나 큐브처럼 하나의 메시로 구성된 오브젝트를 효율적으로 렌더링하는 방식이었습니다. 이번엔 FBX로 임포트한 복잡한 모델을 같은 방식으로 최적화합니다. 이 모델은 여러 개의 메시와 뼈(Bone) 구조를 가지고 있으며, Mod
ModelInstancingDemo를 복사해서 AnimInstancingDemo로 이름을 바꾼다.Init, Update, Render 함수는 뼈대는 동일하되, ModelRenderer → ModelAnimator로 교체한다.애니메이션 데이터를 가진 모델 Kachujin
지금까지 따로따로 관리하던 MeshInstancing, ModelInstancing, AnimationInstancing을 하나로 통합.하나의 Shader(23. RenderDemo.fx)로 모든 종류의 렌더링을 처리할 수 있게 구성.GameObject마다 어떤 타입의
기존에는 RenderDemo에서 직접적으로 \_camera, \_objs, LightDesc 등을 관리하면서 모든 오브젝트와 조명을 수동으로 Update 및 렌더링했음.이 방식은 기능이 추가될수록 관리 포인트가 늘어나며, 재사용성도 떨어지는 구조.→ Scene 단위로
우리가 잘 아는 실수(real number)는 √(-1)을 계산할 수 없다.그래서 수학자들은 상상의 수를 도입했는데, 이게 바로 허수(i)이다.i² = -1 복소수: z = a + bi (a는 실수, b는 허수 계수)복소수는 실수부와 허수부로 구성되며, 복소수끼리의
Compute Shader는 기존의 렌더링 파이프라인에서 벗어나 GPU의 병렬 처리 능력을 범용 연산(GPGPU: General-Purpose computing on Graphics Processing Units)에 사용할 수 있게 해주는 쉐이더 단계다. CPU는 연산
Dispatch(5, 3, 2)→ 총 30개의 Thread Group이 생성된다.→ 각 Group은 우리가 numthreads(10, 8, 3)으로 지정한 대로 240개의 쓰레드를 포함한다.즉, 총 30 × 240 = 7200개의 쓰레드가 동시에 동작할 수 있다는 말이
텍스처 이미지도 결국은 수많은 픽셀 데이터로 구성된 "데이터 덩어리"입니다.ComputeShader가 이 데이터를 조작하려면, GPU에서 읽고 쓸 수 있는 구조로 버퍼를 준비해야 합니다.전달받은 텍스처의 정보(크기, 포맷 등)를 바탕으로 \_input 자원을 새로 생성
StructuredBuffer는 GPU와의 연산을 위해 데이터를 구조체 단위로 주고받을 수 있게 해주는 DirectX11의 고급 데이터 버퍼입니다.RawBuffer는 단순한 바이트 배열StructuredBuffer는 명확한 구조체 배열즉, 우리가 정해준 struct 타
RenderManager는 Init 시점에 특정 쉐이더와 관련된 ConstantBuffer를 초기화해. 그런데 쉐이더가 바뀌면 이전 상태가 날아가버려. 즉, 여러 개의 쉐이더를 동시에 쓰는 상황에 적합하지 않았어.LightDesc, MaterialDesc 같은 정보는
Viewport는 3D 그래픽에서 렌더링된 결과가 실제 화면에 표시되는 영역을 말한다.쉽게 말해, 우리가 만든 3D 세계가 실제로 “어디에”, “어떻게” 보여질지를 결정하는 중요한 개념이다.3D → 2D 변환의 마지막 단계가 바로 이 Viewport 변환이다.3D 오브
게임에서 충돌은 크게 두 가지로 나뉜다.레이저를 쏴서 무언가를 맞추는 경우 (RayCasting)두 개의 오브젝트가 서로 충돌하는 경우 (Object vs Object)두 경우 모두 충돌(Collision)이라는 개념을 기반으로 한다.먼저 테스트를 위한 씬을 만들기 위
축 정렬 박스 충돌체.x, y, z의 축과 항상 평행한 박스.연산이 매우 간단하고 빠름.회전이 없는 물체 또는 정적인 오브젝트에 적합.회전 가능한 박스 충돌체.축과 일치하지 않아도 됨.박스 자체가 회전 가능하기 때문에 정밀한 충돌 판정 가능.회전이 잦은 오브젝트, 복잡
마우스로 Terrain을 클릭했을 때 정확히 클릭한 위치를 알아내기향후 해당 위치로 캐릭터 이동, 지형 편집 등을 구현하기 위한 기반 만들기그냥 하나의 커다란 Collider를 Terrain에 붙이면 안 될까?❌ 불가능합니다.왜냐하면 Terrain은 단순 평면이 아니라
도형들의 정의(구조체) 방식 이해도형의 수학적 의미와 기능적인 용도 파악추후 충돌 판정, 피킹 테스트, 디버깅 등에 활용할 수 있도록 구조화파일 위치: Engine/99.Headers/Math/Primitive3D.h도형은 하나의 클래스로 모으기보다 각 도형별로 구조체
먼저 유틸리티 기능을 담당할 MathUtils 클래스를 생성합니다.위치: Engine/99.Headers/Math/주요 기능: PointInXXX, ClosestPoint 시리즈구 내부에 점이 있는지를 판단하려면, 중심과 점 사이의 거리를 구하고 그것이 반지름보다 작거
두 구가 충돌하고 있는지는 중심 간 거리와 반지름 합을 비교하면 된다.중심 간 거리 제곱이 두 구의 반지름 합의 제곱보다 작거나 같으면 겹치는 것이다.제곱 비교를 통해 루트 연산을 생략하여 성능 최적화.AABB 박스에서 가장 가까운 점을 찾고, 그 점과 구 중심 간의
Ray는 시작점과 방향을 가지는 직선이다. 일반적인 선분(Line)과는 달리, Ray는 한 방향으로 무한히 뻗어 나간다. 따라서 Ray의 핵심은:origin (시작점)direction (방향 벡터)이다. 이 Ray가 Sphere, AABB, Plane, Triangle
일반적으로 UI를 구성할 때는 게임의 3D 씬과는 별도의 2D 공간에서 버튼과 같은 인터페이스 요소를 렌더링하고, 이 위에 클릭 이벤트를 처리할 수 있어야 한다. Unity에서 EventSystem과 InputModule이 이런 역할을 해주는데, 여기서는 직접 구현해본
Billboard는 3D 공간에 존재하지만 항상 카메라를 바라보는 특성을 가진 2D 사각형(Quad) 형태의 오브젝트야. 마치 게임 속의 NPC 이름표, 체력바, UI 표지판, 파티클(비, 눈, 연기 등)이 항상 정면을 유지해야 할 때 쓰이지.3D 모델링보다 훨씬 가볍