언리얼 사전캠프 4일차
오늘 학습 진행 내용
C++강의
2-1. 알고리즘 효율성
빅오 표기법의 개념
알고리즘의 목적은 대규모 입력값이 들어오는 문제를 효율적으로 해결하는 것
알고리즘 성능 측정 방식
절대 시간 측정
입력값 들어온 시간부터 최종 반환 시점까지 경과 시간 측정
PC의 성능에 따라서 수행시간이 달라질 수 있음.
코딩테스트 목적에는 맞지 않음.
연산횟수 측정
코드 시작부터 종료까지 연산이 몇번 수행되었는지 세는 것
코딩테스트에서는 해당 방식으로 성능을 측정
연산
알고리즘의 성능=연산 횟수
연산의 종류
반복문의 수행 횟수
비교,대입,산술 연산자 사용 횟수
대규모 데이터에서는 연산자들의 사용만으로도 많은 연산 횟수 필요.
조건에 따라 달라지는 연산 횟수도 존재
점근적 표기법의 개념
코딩테스트 시험에서는 내 코드가 제한 시간 내에 문제를 해결할 수 있는지 여부
연산 횟수의 추이를 파악하면 충분
즉 연산 횟수에 가장 많은 영향을 주는 부분을 기준으로 그 변화 양상을 분석
빅오 표기법의 개념
연산횟수의 추이를 활용해도 조건에 따라지는 경우 있어서 명확한 기준이 필요
빅오 표기법 : 최악의 경우를 기준으로 점근적 표기법으로 성능 측정하는 방식(점근적 상한)
입력값이 적은 경우에는 성능적으로 차이가 없지만 입력값이 충분히 큰 경우를 기준으로 평가
최소한의 상한 = 점근적 상한
최소한의 상한 구하려면
다항식에서 최고차항만 나미고 계수를 제거하는 방식으로 구함
빅오 표기 상
팩토리얼 함수 > 지수 함수> 다항함수> 로그함수와 다항함수의 조합>다항함수> 로그함수>상수
해당 우선순위에 따라서 최고차항 제거 후 최소한의 상한 구하기
코딩 테스트에서 시간 복잡도 활용
성능=1초에 1억번 정도의 연산 가능하다고 가정
단순 1차원 반복 = O(N)
단순 n차원 배열 반복 = O(N^n)
경우의 수 x 케이스 N번 시행 조합 반복 = O(x^N)
2-2 코딩 테스트 필수 문법
변수의 정의
변수는 데이터를 저장하고 처리하는데 사용하는 구성 요소
c++에서 가장 많이 사용하는 스칼라 타입
unsigned일 경우에는 (~40억)
signed일 경우에는 (-20억~20억)
long long은 굉장히 큰 단위
정수형(int), 부동 소수형 (float 혹은 double), 논리형(bool), 문자형(string)
| 접미사 | 의미 | 예시 | 타입 |
|---|---|---|---|
U | unsigned | 10U | unsigned int |
L | long | 10L | long int |
LL | long long | 10LL | long long int |
UL, LU | unsigned long | 10UL | unsigned long int |
ULL, LLU | unsigned long long | 10ULL | unsigned long long int |
💡 Tip: 큰 정수 상수를 사용할 때는
ULL을 붙여unsigned long long타입으로 명시하면
오버플로우나 타입 변환 경고를 피할 수 있습니다.
배열
배열은 하나의 이름으로 동일한 타입의 변수를 묶어서 관리하는 것
배열은 임의 접근이 가능(인덱스로 원하는 위치에 접근 가능)
스칼라 타입처럼 대입해서 값 복사 불가능
문자열
C++에서는 문자열 사용 시 #include을 헤더에 포함
문자열 찾기
문자열에서 특정 문자 또는 문자열 찾을 때는 find()를 사용
find()의 시간 복잡도는 O(N)
문자열 추가 혹은 수정 시는
+, +=, append를 사용
+= 혹은 append가 더 성능이 좋으므로 해당 것들을 사용하기
수정시에는 [] 연산자 혹은 replace() 메소드를 사용
replace(시작 인덱스, 바꿀 문자열 갯수, 바꿀 문자열)
문자열 관련 메서드
length() : 문자열 길이 반환
size() : 문자열 길이 반환
empty() : 문자열 비움 상태 bool 체크
clear(() : 문자열 비움
append(문자열) : 문자열 끝에 str 추가
insert(인덱스, 길이) : 문자열 특정 위치에 추가
erase(인덱스, 길이) : 문자열 특정 위치 특정 길이 만큼 삭제
replace(인덱스, 길이, 문자열) : 문자열 특정 위치 특정 길이 문자열 추가
substr(인덱스, 길이) : 특정 위치 부터 특정 길이 문자열 반환, 길이 지정 안할 경우 끝까지 지정
compare(비교 문자열) : 문자열 사전 순 비교
at(인덱스) : 특정 위치의 문자 반환
operator[인덱스] : 특정 위치 문자 반환
push_back(문자) : 문자열 끝에 str 추가
pop_back() : 문자열 끝 문자 제거
resize(크기,추가할 문자) : 문자열 크기 조정
swap(문자열) : 문자열 교환
toupper(문자열) : 대문자 변경
tolower(문자열) : 소문자 변경
size_t는 부호 없는(unsigned) 정수형
메모리 크기, 배열 인덱스, 컨테이너 크기 등을 표현하는 데 사용됨
자주하는 실수
단락평가
논리연산자 && , || 는 첫번 째 피연산자 평과 결과로 전체 표현식의 값 결정되는 경우 두번째 피연산자는 평가 X
이러한 오류 최소화 하려면 조건 식에서는 함수 호출 결과를 직접 사용하는 것은 비추천
off by one
루프가 의도한 횟수보다 한번 더 많이 수행 OR 한더 적게 수행
0으로 나누기
입력값이나 중간 계산 결과에 따라 분모가 0이 되는 경우 오류가 발생
유효성 검사하는 습관 들여서 방지하기
결과가 아닌 중간값의 오버플로
자료형의 범위를 넘어서는 값이 발생할 경우 오버플로가 발생
최종값에서 해당 문제가 생길 경우에는 쉽게 해결 가능하지만
중간 계산 값에서 생길 경우에 디버깅에 어려움 발생
2-3 로컬에서 입출력 데이터 다루기 실습
문제의 입력값 효율적으로 다루는 연습
숫자 다루는 케이스
#include
반올림 : round() , 소수점은 0에서 먼쪽으로 반올림
올림 : ceil(),
소수점 버리기, 내림 : floor(), 음수에서는 소수점 이하 버리는게 아니라 숫자가 더 줄어듬.
문자열 스트림
stringstream : 문자열을 스트림처럼 다루게 해줌.
공백 기준으로 분리
string str = "123 X 67";
stringstream stream(str);
int num;
char c;
float f;
stream >> num >> c >> f;
num = 123
c= X
f = 67
이런 형태로 반영
진법 변환(16진수 <-> 10진수)
int decimalNumber = 4095;
stringstream ss;
ss << hex << decimalNumber;
string hexStr = ss.str();
hexStr = fff
하면 16진수 문자열로 변환
16진수를 10진수로 변환할 경우에는
ss >> hex >> decimalNumber;
2-4. STL 사용하기
표준 템플릿 라이브러리(STL)의 주요 구성 요소인 벡터, 리스트, 맵, 세트 등 사용법 이해
반복자, 알로기름, 함수 개체에 대해서 공부
STL의 정의
STL은 C++ 표준 라이브러리의 일부로 자주 사용하는 컨테이너와 알고리즘을 템플릿 기반으로 제공
STL 구성요소
컨테이너
데이터를 저장하고 관리하는 객체
vector, map, list 등
알고리즘
컨테이너에 저장된 데이터를 처리하는 다양한 함수
sort(), next_permutation() 등
반복자
컨테이너의 요소들을 순회하고 접근하는 방법
포인터처럼 동작
컨테이너와 알고리즘이 서로 독립적으로 작동하도록 연결해주는 것
순방향 반복자
컨테이너 요소들을 앞에서부터 차례 순회, 각 원소에 접근 반복
맨 처음 위치는 begin(), 마지막 원소 다음 위치는 end()
반복자가 가리키는 곳을 읽거나 수정 가능
++ 연산자는 지원, -- 연산자는 지원 X, 한방향으로만 동작
역방향 반복자는 뒤에서 부터 순회, 각 원소에 접근 반복
마지막 요소는 rbegin(), 첫번째 요소 바로 앞 위치는 rend()
컨테이너
벡터
배열과 굉장히 유사한 컨테이너, 크기 동적 조절 가능
#include 헤더에 포함해야 사용가능
벡터의 선언 및 초기화
vector name;
크기 지정없이 생성했을 경우 벡터의 크기는 0
vector vec = {1,2,3}; 이런 형태로 하면 초기값 지정 가능
vector v2 = v1 이런 형태로 기존 벡터의 모든 요소 복사 가능
vector v(5,10) 이렇게 하면 크기가 5이고 10으로 채워진 벡터 생성 가능
벡터의 원소 변경
v[idx] 이걸로 접근 가능
벡터의 삽입과 삭제
push_back 사용해서 원소 삽입 가능(가장 마지막에 추가), 하지만 벡터의 용량 초과하면 메모리 재할당 발생해서 시간 복잡도 데이터 크기만큼 증가
insert 로 중간 삽입 가능
clear로 모든 원소 삭제 가능
사용 시 유의할 점
벡터의 내부 동작은 배열 기반이므로
중간 삽입, 삭제 빈번한 경우에는 deque 사용을 고려
reserve() 사용해서 메모리 미리 확보해서 재할당 방지
셋
셋은 중복되지 않는 원소들을 졍렬된 상태로 저장하는 컨테이너
#include 헤더 파일에 포함해야 사용 가능
셋의 선언 및 초기화
set name;
set s 이런 형태로 빈 셋 선언 가능
셋의 삽입과 삭제
insert로 삽입 가능 원소 삽입 때마다 정렬 상태 유지
erase로 삭제 가능 원소 삭제 때마다 정렬 상태 유지
clear로 모든 원소 삭제 가능
셋의 원소 변경
셋은 정렬 상태 유지하는 특성 상 원소 바로 수정 불가능
기존 원소 삭제하고 새로운 값 삽입하는 방식으로 처리
시용 시 주의해야 할 점
만약 정렬된 상태 유지하면서 중복값 저장해야될 경우
multiset 사용
원소 정렬 필요하지 않고 빠른 탐색이 중요한 경우 해시 기반 unordered_set 사용이 적합
삽입 순서를 그대로 유지해야 한다면 vector나 list 컨테이너 사용하는 것 이 좋음
set은 인덱스를 지원하지 않아서 vector를 사용하는 것이 적합
맵
맵은 키와 값을 쌍으로 갖는 연관 컨테이너
중복 키를 허용하지 않고 키를 기준으로 데이터가 자동으로 오름차순 정렬
#include 헤더 파일에 포함해야 사용 가능
맵의 선언 및 초기화
map<KeyType, valueType> name;
map<string,int> m; 이런 형태로 빈 셋 선언 가능
map<string,int> m1 = m2; 이런 형태로 기존 맵 복사 가능
키 값 기준으로 정렬 됨
맵의 원소 변경
[] 연산자 사용해서 맵의 값 변경
m["Alice"] = 31 이런 식으로 변경 가능
만약 키가 없다면 신규 키-값 생성
at 메서드 사용해서 맵의 값 변경도 가능
단 at 사용 시 키가 없다면 out_of_range 예외가 발생
맵의 삽입과 삭제
insert로 삽입 가능, 이미 존재하는 키 삽입은 무시(반환 값은 삽입된 위치 반복자와 함께 성공 여부 False)
myMap.insert(make_pair(1,"Apple") 혹은 myMap.insert({2,"Banana"}) 이런 형태로 insert
auto result = myMap.insert({1, "Apple"});
이럴 경우 result는 insert의 결과로 result.first에 insert 위치, result.second에 insert 성공 여부
erase로 특정 키를 이용해서 삭제 가능, 반복자를 통해 특정 위치 원소 삭제도 가능
myMap.erase(2), auto it = myMap.find(3) -> 키가 3인 위치 반환 받아서 myMap.erase(it)으로 삭제 가능
clear로 모든 원소 삭제 가능
맵 사용하지 말아야 하는 경우
단순히 키만 필요하고 값 필요하지 않은 겨웅에는 set 사용이 적합
만약 원소의 삽입 순서를 유지 해야 한다면 vector 사용을 고려
키는 명확해야 하고, 부동 소수점을 키로 사용하는 것은 X
맵에서 for 문으로 반복 시 auto& pair : studentGrades(맵)
이런식으로 하면 pair.first로 키 , pair.second로 값 출력 가능
map<string, double> studentGrades = {{"John", 3.7},{"Emma", 3.9},{"Sophia", 4.0}};
for (auto& pair : studentGrades) {
cout <<pair.first << ": " << pair.second << endl;
}
맵은 키 값 기준 오름차순으로 정렬되서 입력 순서와 다를 수 있음.
맵에서는 인덱스 대신 키로 접근하는 특징 고려
예외 발생 처리
try{
}catch{out_of_range(에러 유형)& e) {
cout << e.what << endl;
}
알고리즘
sort()
오름차순으로 [first,last)에 있는 원소들을 정렬
sort(first,last)
sort(first,last,comp)
comp는 비교함수로 비교함수 기준으로 정렬 가능
bool comp(a,b)
comp(a,b)가 true 면 a가 b보다 앞, false일 경우 반대로
기본 비교 기준은 < 이거 기준으로
내림차순 정렬 구현
bool comp(int a, int b) {
return a>b;
}
int main(){
vector<int> vec = {5,2,9,1,7};
sort(vec.begin(), vec.end(), comp);
}
문자열 길이 기준 정렬
bool comp(const string& a, const string& b){
return a.length() < b.length();
}
string& 레퍼런스를 붙여야 함수 호출할 때마다 값 복사 되어서 비용 많이 발생
레퍼런스 사용할 경우 값이 수정될 수 있어서 앞에 const 까지 붙여야 됨.
const & 짝꿍으로 쓰기
find()
특정 값과 일치하는 첫번째 원소 선형 탐색
find(first,last,vlaue)
count()
특정 범위 내 특정 값 갯수 카운트
count(first, last, value)
count(v.begin(),v.end(),5)
5가 v 벡터에 몇개 있는지 카운트
unique()
uniuqe()는 범위 [first, last) 내에서 연속된 중복 요소를 제거
실제로 컨테이너에서 원소 삭제되는 것이 아닌 원소의 첫번째 발생만 유지하도록
원소들을 앞쪽으로 덮어씌워 재배열
그 이후 의 영역은 남아있음(undefined)
보통 erase() 함께 사용해서 완전히 제거(erase-remove-idiom)
언리얼 강의
1-8.플레이어 캐릭터 만들기
블루프린트 클래스, C++ 클래스 같은 클래스들이 존재하고 해당 클래스들은 재활용이 가능
BP로 블루프린트 클래스 네이밍 지칭 자주 사용
Static Mash는 고정된 형태
Skeletal Mash는 애니메이션이 가능한 형태
캐릭터에 SpringArm 컴포넌트 추가해서 카메라와 캐릭터 사이에 사물 배치될 때 카메라 간격 자동으로 조절되게 설정
캐릭터에 입력에 따라 동작 가능하게 하려면
InputMappingContext 생성
InputAction Move 생성
앞뒤양옆 이동은 Axis2D로 설정
그 다음에 INputMappingContext에서
W,A,S,D 값 할당
A 는 modifier에서 Swizzle Input Axis Value, Negate 할당
S 는 modifier에서 Negate 할당
D 는 modifier에서 Swizzle Input Axis Value 할당
InputAction Look 생성
Axis2D로 설정 후
InputMappingContext에서
Modifier에서 Negate 설정 후 인덱스 Y만 설정하면 카메라 상하반전 가능(좀 더 3인칭 캐릭터 이동다워짐)
InputAction Jump 생성
Axis2D로 설정
그리고 실제로 동작하게 하기 위해서
블루프린트 이벤트 그래프에서
Get PlayerController
AddMappingContext(위에서 생성한 컨텍스트 추가)
뷰포트에서 Character Detail에서 auto Possess Player에서 Player 0로 선택하면 플레이어 움직임 반영 가능
시점 이동의 경우
Add Controller Yaw Input, Add Controller Pitch Input을 블루프린트 그래프에 추가해서 설정 가능
캐릭터 블루 프린트 디테일 패널에서 컨트롤러의 폰 부분에서 use controller rotation pitch, use controller rotation yaw 체크 시 케릭터 전체와 컨트롤러 회전 연결
카메라와 캐릭터 움직임 별개로 조정되게 하려면
캐릭터 무브먼트 컴포넌트에서 아래 옵션 체크
무브먼트 방향으로 회전 조정 : orient rotation to movement
스프링 암 컴포넌트의 폰 제어 회전 사용(use pawn control rotation)
1-9. 애니메이션 블루프린트와 블렌드 스페이스
애니메이션 블루프린트
애니메이션 블루프린트는 스켈레탈 메시의 애니메이션 로직과, State Machine, Event Graph 등을 시각적으로 설계하는 시스템
Anim Graph
State Machine과 Blend 로직을 연결하는 메인 그래프
최종저긍로 캐릭터 메시에 적용될 포즈를 결정
Event Graph
캐릭터의 속도, 방향, 입력 값 등을 수집하여 애니메이션 변수(Speed, IsInAir 등)을 업데이트
1-3 애니메이션의 종류
애니메이션 시퀀스(Animation Sequence)
걷기,뛰기,공격 등 단일 모션이 들어있는 파일
애니메이션 몽타주(Animation Mongtage)
여러 시퀀스를 재생 구간별로 편집, 합칠 수 있는 특수 에셋
블렌드 스페이스(Blend Space)
여러 애니메이션 시퀀스를 축값에 따라 자연스럽게 섞어 재생하기 위한 에셋
캐릭터 움직임 시퀀스 및 로직 구성
캐릭터 이동, 점프, 카메라 움직임 AnimGraph 연결 구성
Sequence로 변수 수집 동시에 처리한 캐릭터 이동, 점프, 카메라 움직임 AnimGraph 연결 구성
State Machine에 따른 동작을 위한 AnimGraph 연결 구성
State Machine에 따른 걷기, 점프, 떨어짐, 착지 상태 구성
사전캠프 퀘스트
게임 분석 템플릿 (복사해서 사용, 참고하세요)
1.선택한 게임의 이름은 무엇인가요?
포켓몬스터 레전즈 Z-A
2.선택한 게임의 장르는 무엇인가요?
액션 RPG
3.선택한 게임의 어떤 시스템에 집중하셨나요?
포켓몬 존 리스폰 시스템
4.해당 시스템이 동작하는 구조의 시작부터 과정을 자세하게 분석, 나열해봅시다.
맵에서 일정 존 안에서는 포켓몬이 리스폰이 되는 상태로 구현이 되어 있다.
사용자가 맵에서 일정 범위 내(월드 맵 상 거리 50)를 벗어나거나 맵을 새로 리로드 했을 때 몬스터는 새로 리스폰이 된다.
포켓몬 중에는 우두머리 혹은 이로치라고 하는 특정 확률로 발생하는 포켓몬들이 존재한다.
해당 포켓몬들 중 이로치일 경우에는 맵의 리로드 혹은 맵 일정 범위를 벗어나는 케이스가 발생해도 해당 이로치는 세이브가 되어서 그대로 유지가 된다.
아주 특이한 케이스로 우두머리와 이로치 케이스가 합쳐진 우두머리 이로치 케이스가 발생할 경우 우두머리, 이로치 상태 둘 다 유지한 케이스 상태로 세이브된 상태로 리스폰이 된다.
월드맵 내에 최대 10마리까지만 이로치 상태의 포켓몬이 세이브가 되고 그 이후에는 가장 먼저 스폰 되었던 이로치 상태 포켓몬부터 리스폰이 진행이 된다.
5.직접 분석해본 내용 중 가장 핵심이 되는 구성 요소는 무엇이라 생각하나요?
예시) 총알과 구조물, 적과의 충돌
포켓몬 리스폰 로직을 유지하면서 이로치와 우두머리 생성 메커니즘이 꼬이지 않게 하기 위해서 우두머리 보단 이로치를 우선순위를 높게 두고서 리스폰 로직을 구성해놨다는 점
그리고 포켓몬의 리스폰 시 무한으로 세이브 될 경우 시스템 리소스를 많이 잡아먹기 때문에 10마리 제한을 둠으로써 서비스를 진행하는데 있어 로직 원할하게 동작하게 했다는 점
STEP 1. 학습 내용을 요약하기
1.언리얼 엔진 5와의 첫 시간 영상을 보고 아래 내용에 답해주세요.
Unreal Engine 5에서 "프로젝트 생성" 단계의 주요 흐름은 무엇인가요?
언리얼 엔진 버전 선택 -> 실행 -> 프로젝트 브라우저 로드 -> 프로젝트 템플릿 혹은 기본 프로젝트 선택 -> 프로젝트 옵션 설정(Target Platform,Quality Preset, BlUEPRINT or C++등)->프로젝트 생성
"에디터 인터페이스"의 핵심 요소 3가지는 무엇인가요?
뷰포트(Viewport), 월드 아웃라이너(World Outliner), 디테일 패널(Details Panel)
Unreal Engine에서 "Actor"란 무엇이며, 어떤 역할을 하나요?
액터는 레벨 내에 존재하는 모든 오브젝트 기반의 클래스(건물, 캐릭터,라이트,카메라 등)
액터의 생명주기는 4단계
초기화 -> 시작(Spawned) -> Tick(프레임마다 업데이트) -> Destroy
블루프린트나 C++을 통해 액터가 특정 동작을 수행
액터는 위치, 회전, 스케일 정보를 가지고 있어 공간에 배치되고 조작 가능
STEP 2. 실습하기
1.Unreal Engine 5에서 "Blank" 또는 "Third Person" 템플릿으로 새 프로젝트를 생성합니다.
2."Viewport"에서 기본 오브젝트를 배치해 보세요.
3."Outliner"와 "Details" 패널에서 추가된 오브젝트의 속성을 확인해 보세요.