무슨 로직이 있게 만들어야 하는 게 아닌이상 그냥 레벨에 끌어다넣는걸로 만들어도 됨(쌤은 이러라 하심)
위치나 크기는 아웃라이너에서 선택해서 직접 입력하거나, 레벨 표시창에서 기즈모를 땡겨서 조절할 수 있다.
액터스폰 로직적 처리 - 블루프린트에서 하는 방법이 있고 CPP에서 하는 방법이있음
게임모드의 BeginPlay는 Pawn의 BeginPlay 실행 전임
블루프린트로 만든 변수를 CPP에서 쓰려고 하지마세요 안알랴드립니다
블루프린트로 변수 선언하고 뭐하고 했으면 컴파일 누르는거 잊지말장
UClass 언리얼은 내 프로젝트에서 클래스들을 다 감시함.
언리얼이 빌드툴 및 헤더툴로 내 클래스를 실행하기 전에 다 조사 ⇒ UClass로 만들어서 파일로 저장해둠 ⇒ 로드해서 사용한다
TSubclassOf<AActor>
: UClass 중ㅇ에서 AActor를 상속받는 클래스들만 보이게 만든다.
TArray<TSubclassOf<AActor>
> : AActor를 상속받는 클래스들만 보이는 배열
UClass* 는 모든 클래스의 기본이되는애? 같은 것.
언리얼이 UClass 다 조사하는 작업 자체를 CDO라고 함.
왜조사?
언리얼 GC(가비지콜렉터), 생성방식 등등에 대해 알아야 함.
언제조사? 어떻게 조사?
에디터 켤 때 로딩 75%쯤에 갑자기 내가 만든 UObject 생성자 호출됨. 엔진이 실행도 안 됐는데 생성자가 왜 호출될까? ⇒ 이게 CDO 조사중인거임. 언리얼이 이 클래스를 생성하면서 정확한 크기를 조사하고 있는 것임.
클래스 내에서 동적할당을 많이 하게되면 메모리가 멀리 떨어지게되는 경우가 많음. 같은 클래스끼리는 메모리가 붙어있어야 속력이 효율적이다. 멀리 떨어져있으면 캐시적중률이 떨어짐. 언리얼은 CDO를 하면서 전체 클래스에 필요한 크기를 알아냄.
조사한걸 UCLASS라는거에 저장해둠. 나중에 클래스를 Spawn할 때 메모리가 붙어있을 수 있게 함.
그래서 절대 직접 new하지말고… 엔진에서 제공하는 생성 함수 사용해서 new를 해야함. 엔진 스스로 관리하는 구조를 만들어야 함.
가비지컬렉터(메모리관리자)는 언리얼이 미리 만들어둔 거대한 메모리 덩어리. 언리얼이 그 메모리 덩어리를 최적화하고 정리해서 관리하려고 한다. 가비지 컬렉터는 쓸모없는 메모리를 정리하기도 함.
vector 500만 만들어놓고 클래스 메모리 붙어있을 수 있게 해주는 거임. 그래서 메모리 지울때 그냥 그 vector 한 번 클리어하면 끝. 정확히 이런식이라는 건 아니고 원리가 이렇다는거임. 가비지 컬렉터의 메모리들은 모두 다 레퍼런스 카운트를 가지고 있다. 이때 이 레퍼런스 카운트 관리 방식을 제대로 적용받으려면 UPROPERTY()를 붙여주면 좋다. (가비지컬렉터는 쓸모없는 메모리를 지워버리기도 하기 때문에 내가 잘 쓰고 있는 메모리도 그냥 지워버릴때도 있음.)
그래서 CDO가 있는 거고, 언리얼에서는 UCLASS로 클래스 정보를 갖고 있는거임. 이런 시스템을 갖췄기 때문에 UCLASS 써야 한다는 거임.
언리얼에서는 문자를 표현할 때 TEXT("")
로 함.
언리얼에서는 우리 다렉엔진때처럼 레벨이 다 로드되어있는 게 아니고 그때그때 로드되는거임. 항상 존재하는 무언가가 필요하면 어떻게해야하는가?
static은 언리얼 가비지컬렉터나 메모리 관리 방식을 생각해봤을 때 좋지 않다. 그 메모리는 언리얼의 관리 바깥의 존재가 된다.
레벨이랑 상관없이 항상 존재하는 클래스를 제공함. 시작할 때부터 게임이 끝날 때까지 존재하는 애.
언리얼은 우리에게 GameInstance를 제공한다.
얘는 BeginPlay()가 없음. 레벨이 생성되고 시작하는 것과 상관없이 존재하기 때문에 BeginPlay가 의미가 없음.
중요한 건 얘를 GameInstance로 해줘 라는 걸 지정을 해줘야한다. 세팅 > 맵&모드 에 게임인스턴스 지정해주는 곳이 있음
레벨에서 GetWorld()→GetGameInstance() 또는 GetWorld()→GetGameInstanceChecked() 할 수 있음. 여기서는 로직을 짜는 건 좋지 않고, 모두가 알아야 하는 데이터, 옵션 같은 것들을 위해 쓰는 거임.
근데 UGameInstance형으로 반환되기때문에 GIMATGameInstance(직접만든애)로 형변환해줘야함.
UGIMATGameInstance* Ptr = Cast<UGIMATGameInstance>(GetWorld()→GetGameInstanceChecked();
언리얼에서 모든 기능들을
플러그인 에디터 상태에서 기능 추가
모듈은 C++에서 추가 (build.cs)
플러그인은 메뉴에서 체크만 해주면 바로 적용되거나 엔진 껐다 켜면 간단히 적용됨
폰을 상속받는 클래스만이 디폴트 폰 클래스가 될 수 있다. (월드 세팅에 디폴트 폰 클래스 - 레벨이 시작되면 디폴트 폰을 1개를 무조건 만듦. 주인공이됨.)
컨트롤러는 왜 따로 있는가. 컨트롤러는 캐릭터마다 따로짜주는게아님. 자세하게짜는게아니라 공통적인부분만 짜는거임 A키누르면 공격해 라고 짜고
궁수면 활을 쏘고 검사면 칼을 휘두르는거임 공격이라는 행위를 하라고만하고 상세하게 무슨 애니메이션인지, 어떤 행동인지까지 정확하게 지시는 X. 개념이 그렇다는 거임.
보통 캐릭터를 기반으로 게임을 만들 때가 많다.
이유 ⇒ 대부분 언리얼에서 주인공이 3D메쉬이면서 애니메이션이 되는 게임을 만들고 싶어하기 때문.
2D 퍼즐게임같은거는 캐릭터 놓고 한곳만 바라보게만들면됨.
그럼 메시가 이미 스켈레탈 메시 에셋이라고 돼있음
폰에서 애니메이션되는 스켈레탈메시붙여주고 캐릭터 무브먼트 붙여주면 캐릭터랑 똑같음.
거기 붙는 약간의 설정들이 귀찮으니까 캐릭터 쓰는거임.
캐릭터에서 카메라를 캐릭터 눈에놓는것도함
캐릭터는 캐릭터무브먼트에 중력이 설정되어있음. 다 세팅돼있음. 마찰력, 언덕올라갈때 느려지기 등등. 중력을 없애고 싶으면 캐릭터 무브먼트에서 수정하면 됨.
폰은 중력없었음.
중력기능 넣었던 곳에 나중에 공격기능들어가고 그러다 어쩔땐 중력끄는기능도들어가고 해야하게됨. 이건 XXX.
중력기능 하나 떼서 컴포넌트, 스테이트기능 컴포넌트, 애니메이션 컴포넌트 등등으로 기능을 다 컴포넌트로 만드는 게 좋다. 그래서 캐릭터에 컴포넌트가 주루룩달리게하는게맞음.
언리얼에서 내가 뭔가를 구현하고싶으면 다음을 따르세요.
1. 내가 지금 뭘 구현하려고 하는지 명확히 한다.
무조건 직관적으로 질문해야 한다.
1-1. 언리얼이 그 기능을 포함한 액터를 제공해주는지 확인.
1-2. 블루프린트에서 그런 기능을 하는 컴포넌트가 있는지 확인한다.
2-1. 그 기능의 옵션들에 대해 공부해야 한다. 블루프린트적으로 공부하고.
2-2. C++로도 그 기능을 제어할 수 있게 되는 게 좋다.
캡슐 컴포넌트는 충돌범위를 나타낸다. 박스 컴포넌트나 다른것도 충돌계열이면 상관없음. 캐릭터는 보통을 캡슐을 씀. 아니면 box를 덧붙여써도되고 아무튼.
화살표 컴포넌트는 에디터 전용이다. 이건 빌드가 되면 삭제된다. 딱히 기능이 있는게 아니고, 캐릭터의 전방(바라보는 방향)이 어디 방향인지 알려줌.
디폴트 폰에 자동으로 붙는 카메라의 위치도 조작할 수 있다.
카메라 컴포넌트 붙여서.
플레이어가 벽에 붙을 때 카메라가 벽을 뚫고가면 벽이 보임. 카메라가 벽을 뚫지는 못하게 하려면.
SpringArm 기능이 있음. 스프링암에 카메라를 끌어다 놔야함.
== 카메라 컴포넌트를 스프링암의 자식으로 만든다.
⇒ 그러고나서 위치를 다시초기화
스프링암의 역할은 셀카봉과 비슷.
웬만하면 넣어주는 게 좋음. 카메라에 타깃 암 길이를 나중에 0 으로 할 수 있으니까 일단 붙이는 게 좋음. 시선만 회전하기 혹은 카메라와 벽의 충돌체크 등의 옵션을 기본으로 제공하기 때문에 웬만하면 카메라의 부모로 스프링암을 넣어주는 게 좋음.
카메라 디테일에서 직교투영, 원근투영 바꿀 수 있음.
캐릭터 무브먼트에서 중력 스케일 0.0으로 넣어줌.
CPP로 캐릭터 상속받는 클래스 만들었음. 수업에서 만든 블루프린트 캐릭터의 에디터 창을 연다.
클래스 세팅을 열고 디테일 창으로 가서 내가 만든 C++ 클래스를 부모로 지정해 줄 수 있다.
직접 만든 C++ 클래스로 디폴트 폰이랑 똑같이 움직일 수 있게 해보기.
?
언리얼에서는 툴을 써라. 언리얼을 이용해서 쉽게 툴을 만들 수 있음.
TMap을 쓸때는 웬만하면 UPROPERTY 쓰자. TMap이 메모리가 그냥 이유없이 날아갈 때가 많다.
#include “Engine\DataTable.h” 도 하고.
⇒ 한 줄 요약 : FTableRowBase 상속받는 struct 하나만 만들면 되는 거임.
UPROPERTY(EditAnywhere, BluprintReadWrite, Catefoty = “Resourses”)
TSubClassOf<AActor> Actor;
언리얼에서 제공하는 콘솔 로그 기능
UE_LOG(LogTemp, Fatal, TEXT(”어쩌구”))
로그를 띄울 때 중요한 것 2가지 : LogTemp부분 (로그 분류), Fatal 부분(로그 종류).
로그 종류 - Fatal, Error, Log, Warning
UE_LOG(LogTemp, Fatal, TEXT(”%S(%u)”), **FUNCTION**, __**LINE__**);
: FUNCTION 함수이름으로 변경, LIne 해당 라인 출력
이제부터 로그뿌릴때 LogTemp대신 GIMATLog 사용한다고함 (헤더 GIMAT.h)
횡스크롤 게임. 러닝 게임. (ex:쿠키런)
게임에서 가장 현실적인 결정은 일단 뷰. ( == 시점, 시야)
언리얼은 CDO때문에 중간에 컴폰넌트를 변경할 수 없다. 다렉때 기형적으로 따라하려고 어거지로 생성자에서만 렌더러 만들 수 있게 했었음.
C++로 만든 컴포넌트는 삭제할 수 없음.
언리얼에서 충돌 컴포넌트가 부모임.
다렉때처럼 언리얼에서도 USceneComponent를 상속받아야 위치크기회전을 가짐.
자식 스케일이 3, 부모 스케일이 2이면 월드 스케일은 6
언리얼 회전에서 벡터사용하지않고
Rotator, Quat 사용. 상호보완적 관계.
우리가 알고 있는 Euler오일러각에서 FQuat, FRotator
람베르트 조명공식 사용. N내적L
면의 반사방향 normal N
빛의 방향 -L (횡스크롤 어쩌구 ppt에있음)
디렉셔널 라이트 : L이 태양. 어디에나 존재하고 같은 값 같은 방향으로 비춘다. 빛의 시작점이라는 게 존재하지 X.
포인트 라이트 : L은 점에서부터 시작한다. 면의 한 점의 위치 - 빛의 위치 가 빛의 방향. 면마다 빛의 방향이 달라짐
배드 사이즈 : 이 캐릭터가 생성될 때 어떤 물체와 겹쳐서 생성이 안 될 가능성도 있다는 이야기.
가능한 위치 처리해보고 적당한 위치 못찾으면 스폰하지 않음. 스폰 콜리전 처리 메서드를 콜리전과 관계없이 스폰하는 옵션 해두는 게 좋을 수 있음.
카메라 콜리전 - 콜리전 테스트 실행
을 키면 스프링암이 땡겨지는 그거 켜는거임.
언리얼에서 크기(스케일) 기준은 자기 리소스 크기 설정한 스케일 인거임. 100100100 짜리 큐브 스케일 10으로하면 10001000*1000 인거임.
리플리케이트 : 네트워크 동기화
게임모드는 서버만 만듦. 클라한테는 게임모드안생김. 그래서 스폰액터는 웬만하면 게임모드에만 넣으라고 하심. 몬스터 게임모드에서 스폰하면 서버만 만들어서 동기화시켜주는거임. 리플리케이트 옵션만 켜주면 동기화 됨.
수업 마지막쯤 상태 : 디폴트 폰에 ScrollPlayer 넣어놓고 얘한테 카메라 달아둠. 그리고 여기서 캐릭터 하나 스폰. 그 캐릭터가 노란 사람. 점프가안댐.